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:
Uruchomienie UARTu:
W funkcji powyżej przedstawiłem sposób uruchomienia dla prędkości transmisji 9600.
Teraz zestaw funkcji odpowiedzialnych za wyświetlanie danych:
Najpierw czeka na opróżnienie bufora po czym przesyłany jest znak do wysłania.
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:
Funkcja wysyłająca dane w przerwaniu wraz z obsługą przerwania:
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:
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;.
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.
Ustalenie prędkości można wykonać na podstawie wzoru:
- /*
- * Calc for 9600:
- * XTAL = 11,0592 MHz = 11059200
- * BSEL: ((11059200)/((2^0)*16*9600)) - 1 = 71
- * Calculation: Fbaud = ((11059200)/((2^0) * 16 * (71+1))) = 9600
- *
- * Calc for 115200:
- * XTAL = 11,0592 MHz = 11059200
- * BSEL: ((11059200)/((2^0)*16*115200)) - 1 = 5
- * Calculation: Fbaud = ((11059200)/((2^0) * 16 * (5+1))) = 115200
- */
Uruchomienie UARTu:
- void hc_05_EnableUart(void)
- {
- setPinInput(RX_PORT, RX_PIN);
- setPinOutput(TX_PORT, TX_PIN);
- cli();
- USARTE0_BAUDCTRLB = 0;
- USARTE0_BAUDCTRLA = 71;
- USARTE0_CTRLC = USART_CHSIZE_8BIT_gc;
- USARTE0_CTRLA = USART_RXCINTLVL_HI_gc;
- USARTE0_CTRLB = USART_RXEN_bm | USART_TXEN_bm;
- PMIC.CTRL |= PMIC_LOLVLEX_bm;
- sei();
- }
W funkcji powyżej przedstawiłem sposób uruchomienia dla prędkości transmisji 9600.
Teraz zestaw funkcji odpowiedzialnych za wyświetlanie danych:
- void usart_putc(char c)
- {
- while (!(USARTE0_STATUS & USART_DREIF_bm));
- USARTE0.DATA = c;
- }
Najpierw czeka na opróżnienie bufora po czym przesyłany jest znak do wysłania.
- void Send_Table_UART(char data[], uint8_t length)
- {
- int Counter = 0x00;
- while(Counter < length)
- {
- while (!(USARTE0.STATUS & USART_DREIF_bm));
- USARTE0.DATA = data[Counter];
- Counter++;
- }
- while (!( USARTE0.STATUS & USART_DREIF_bm));
- USARTE0.DATA = 0x0A;
- while (!( USARTE0.STATUS & USART_DREIF_bm));
- USARTE0.DATA = 0x0D;
- }
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:
- typedef enum uartEndSign_t {
- NONE_Sign = 0x00,
- CR_Sign= 0x01,
- LF_Sign = 0x02,
- CR_LF_Sign = 0x03,
- LF_CR_Sign = 0x04
- }uartEndSign_Typedef;
- void sendStringWithEnd(char *text, uartEndSign_Typedef last_sign)
- {
- while(*text){
- sendChar(*text++);
- }
- if(last_sign==CR_Sign){
- sendChar('\r');
- }
- else if(last_sign==LF_Sign){
- sendChar('\n');
- }
- else if(last_sign==CR_LF_Sign){
- sendChar('\r');
- sendChar('\n');
- }
- else if(last_sign==LF_CR_Sign){
- sendChar('\n');
- sendChar('\r');
- }
- else if(last_sign==NONE_Sign){
- }
- }
Funkcja wysyłająca dane w przerwaniu wraz z obsługą przerwania:
- void sendDataWithInterrupt(char data[])
- {
- int length = 0x00;
- length = strlen(data);
- for(uint8_t i = 0; i<length; i++)
- {
- g_send_buffer[i] = data[i];
- }
- while (!(USARTE0_STATUS & USART_DREIF_bm));
- USARTE0_CTRLA |= USART_DREINTLVL_LO_gc;
- }
- ISR(USARTE0_DRE_vect)
- {
- if(g_send_buffer[g_send_dat_pos]!=0)
- {
- USARTE0_DATA = g_send_buffer[g_send_dat_pos++];
- }
- else
- {
- USARTE0_CTRLA |= USART_DREINTLVL_OFF_gc;
- }
- }
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:
- ISR(USARTE0_RXC_vect)
- {
- g_rec_buffer_data[g_rec_position] = (uint8_t)usart_getc();
- g_rec_position++;
- }
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;.
- static uint8_t extractString(uint8_t *stringToExtract)
- {
- const uint8_t *PATTERN1 = "st:";
- const uint8_t *PATTERN2 = "end;";
- uint8_t *target = NULL;
- uint8_t *start;
- uint8_t *end;
- if(start = strstr(stringToExtract, PATTERN1))
- {
- start += strlen(PATTERN1);
- if(end = strstr(start, PATTERN2))
- {
- target = (char *)malloc(end - start + 1);
- memcpy(target, start, end - start);
- target[end - start] = '\0';
- }
- }
- if(stringToExtract)
- {
- memset(stringToExtract, 0, strlen(stringToExtract));
- memcpy(stringToExtract, target, end - start);
- target = NULL;
- start = NULL;
- end = NULL;
- return 1;
- }
- return 0;
- }
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.