środa, 15 listopada 2017

[6] Atxmega - UART

W tym poście przedstawię w jaki sposób wykonać komunikację po interfejsie UART. Wysyłanie danych będzie się odbywało w trybie blokującym bądź poprzez przerwanie, natomiast odbieranie będzie odbywało się tylko na przerwaniach.

[Źródło: http://www.atmel.com/images/Atmel-8135-8-and-16-bit-AVR-microcontroller-ATxmega16D4-32D4-64D4-128D4_datasheet.pdf]

Program:


W programie posłużę się układem uart UARTE0. Pin PE3 odpowiada za TX, natomiast pin PE2 odpowiada za RX.

Ustalenie prędkości można wykonać na podstawie wzoru:

  1. /*
  2.     * Calc for 9600:
  3.     * XTAL = 11,0592 MHz = 11059200
  4.     * BSEL: ((11059200)/((2^0)*16*9600)) - 1 = 71
  5.     * Calculation: Fbaud = ((11059200)/((2^0) * 16 * (71+1))) = 9600
  6.     *
  7.     * Calc for 115200:
  8.     * XTAL = 11,0592 MHz = 11059200
  9.     * BSEL: ((11059200)/((2^0)*16*115200)) - 1 = 5
  10.     * Calculation: Fbaud = ((11059200)/((2^0) * 16 * (5+1))) = 115200
  11. */

Uruchomienie UARTu:

  1. void hc_05_EnableUart(void)
  2. {
  3.     setPinInput(RX_PORT, RX_PIN);
  4.     setPinOutput(TX_PORT, TX_PIN);
  5.    
  6.     cli();
  7.    
  8.     USARTE0_BAUDCTRLB = 0;
  9.     USARTE0_BAUDCTRLA = 71;
  10.        
  11.     USARTE0_CTRLC = USART_CHSIZE_8BIT_gc;
  12.     USARTE0_CTRLA = USART_RXCINTLVL_HI_gc;
  13.     USARTE0_CTRLB = USART_RXEN_bm | USART_TXEN_bm;
  14.     PMIC.CTRL |= PMIC_LOLVLEX_bm;
  15.    
  16.     sei();
  17. }

W funkcji powyżej przedstawiłem sposób uruchomienia dla prędkości transmisji 9600.

Teraz zestaw funkcji odpowiedzialnych za wyświetlanie danych:

  1. void usart_putc(char c)
  2. {
  3.     while (!(USARTE0_STATUS & USART_DREIF_bm));
  4.     USARTE0.DATA = c;
  5. }

Najpierw czeka na opróżnienie bufora po czym przesyłany jest znak do wysłania.

  1. void Send_Table_UART(char data[], uint8_t length)
  2. {
  3.     int Counter = 0x00;
  4.    
  5.     while(Counter < length)
  6.     {
  7.         while (!(USARTE0.STATUS & USART_DREIF_bm));
  8.         USARTE0.DATA = data[Counter];
  9.         Counter++;
  10.     }
  11.    
  12.     while (!( USARTE0.STATUS & USART_DREIF_bm));
  13.     USARTE0.DATA = 0x0A;
  14.     while (!( USARTE0.STATUS & USART_DREIF_bm));
  15.     USARTE0.DATA = 0x0D;
  16. }

Do funkcji podawany jest bufor danych wraz z wielkością tablicy. Po przesłaniu tablicy przesyłany jest znak powrotu karetki oraz przejścia do nowej linii.

Wysłanie ciągu znaków wraz z zdefiniowanym znakiem końcowym:

  1. typedef enum uartEndSign_t {
  2.     NONE_Sign = 0x00,
  3.     CR_Sign= 0x01,
  4.     LF_Sign = 0x02,
  5.     CR_LF_Sign = 0x03,
  6.     LF_CR_Sign = 0x04
  7. }uartEndSign_Typedef;
  8. void sendStringWithEnd(char *text, uartEndSign_Typedef last_sign)
  9. {
  10.     while(*text){
  11.         sendChar(*text++);
  12.     }
  13.     if(last_sign==CR_Sign){
  14.         sendChar('\r');
  15.     }
  16.     else if(last_sign==LF_Sign){
  17.         sendChar('\n');
  18.     }
  19.     else if(last_sign==CR_LF_Sign){
  20.         sendChar('\r');
  21.         sendChar('\n');
  22.     }
  23.     else if(last_sign==LF_CR_Sign){
  24.         sendChar('\n');
  25.         sendChar('\r');
  26.     }
  27.     else if(last_sign==NONE_Sign){
  28.     }
  29. }

Funkcja wysyłająca dane w przerwaniu wraz z obsługą przerwania:

  1. void sendDataWithInterrupt(char data[])
  2. {  
  3.     int length = 0x00;
  4.        
  5.     length = strlen(data);
  6.    
  7.     for(uint8_t i = 0; i<length; i++)
  8.     {
  9.         g_send_buffer[i] = data[i];
  10.     }
  11.    
  12.     while (!(USARTE0_STATUS & USART_DREIF_bm));
  13.     USARTE0_CTRLA |= USART_DREINTLVL_LO_gc;
  14. }
  15. ISR(USARTE0_DRE_vect)
  16. {
  17.     if(g_send_buffer[g_send_dat_pos]!=0)
  18.     {
  19.         USARTE0_DATA = g_send_buffer[g_send_dat_pos++];
  20.     }
  21.     else
  22.     {
  23.         USARTE0_CTRLA |= USART_DREINTLVL_OFF_gc;
  24.     }
  25. }

Odbieranie danych w przerwaniu polega na zapisywaniu danych do bufora i zwiększaniu pozycji. Sprawdzanie bufora aby odnaleźć poszukiwanego stringa jak i jego czyszczenie przed przepełnieniem znajduje się w pętli głównej:

  1. ISR(USARTE0_RXC_vect)
  2. {
  3.     g_rec_buffer_data[g_rec_position] = (uint8_t)usart_getc();
  4.     g_rec_position++;
  5. }

Implementacja odbieranych danych leży już w własnym zakresie. Poniżej przedstawię jeszcze funkcje czyszczącą bufor oraz wyłuskującą szukaną wiadomość z ciągu znaków. Ramka wiadomości wygląda następująco st:<szukanaWiadomość>end;.

  1. static uint8_t extractString(uint8_t *stringToExtract)
  2. {
  3.     const uint8_t *PATTERN1 = "st:";
  4.     const uint8_t *PATTERN2 = "end;";
  5.    
  6.     uint8_t *target = NULL;
  7.     uint8_t *start;
  8.     uint8_t *end;
  9.    
  10.     if(start = strstr(stringToExtract, PATTERN1))
  11.     {
  12.         start += strlen(PATTERN1);
  13.         if(end = strstr(start, PATTERN2))
  14.         {
  15.             target = (char *)malloc(end - start + 1);
  16.             memcpy(target, start, end - start);
  17.             target[end - start] = '\0';
  18.         }
  19.     }
  20.    
  21.     if(stringToExtract)
  22.     {
  23.         memset(stringToExtract, 0, strlen(stringToExtract));
  24.         memcpy(stringToExtract, target, end - start);
  25.         target = NULL;
  26.         start = NULL;
  27.         end = NULL;
  28.         return 1;
  29.     }
  30.    
  31.     return 0;
  32. }

Powyższa funkcja ma za zadanie znaleźć podanie ciągi znaków w zmienny PATTERN. Po ich wyszukaniu wprowadza tekst jaki znajduje się pomiędzy nimi do wskaźnika podawanego w argumencie funkcji.

Bibliotekę można pobrać z dysku Google pod tym linkiem.