Kurs STM32 LL cz. 7. Interfejs USART, transmisja danych w trybie polling

USART to jeden z najpopularniejszych interfejsów używanych w systemach mikrokontrolerowych. Stosowany jest między innymi do komunikacji z komputerem, modułami Bluetooth, a także skanerami laserowymi czy odbiornikami GPS. Stanowi ważny element wielu programów, dlatego warto poznać jak działa i w jaki sposób go efektywnie używać.

Struktura ramki protokołu USART

USART (Universal Synchronous Asynchronous Receiver-Transmitter) to rodzaj protokołu danych służącego do przesyłania informacji za pomocą portu szeregowego. Może pracować w trybie synchronicznym lub asynchronicznym. Z pierwszym trybem mamy do czynienia, gdy interfejs pracuje jako SPI. Mówiąc jednak o USART, częściej mamy na myśli komunikację asynchroniczną, czyli UART.

Słowo asynchroniczny oznacza, że do przesyłania danych nie jest wymagany sygnał zegarowy, który synchronizowałby oba urządzenia. UART jest szeroko stosowany w systemach wbudowanych m.in. do komunikacji z komputerem lub odbiornikiem GPS.

Ramka protokołu, czyli sposób, w jaki przesyłane są bity podczas transmisji, wygląda jak na grafice poniżej.

W czasie braku transmisji linia danych przyjmuje stan wysoki. Początek transmisji sygnalizowany jest przez bit startowy, który przez cały czas trwania ma stan niski. Następnie transmitowane są bity danych w kolejności od najmniej znaczącego (LSB) do najbardziej (MSB). Potem przesyłany jest bit parzystości (opcjonalny) i bit stopu, który ma stan wysoki.

Ze względu na brak synchronizacji za pomocą zegara, w przypadku transmisji UART kluczowe jest, aby oba urządzenia miały takie same parametry pracy UART. Wśród nich możemy wymienić:

  • Prędkość transmisji (baudrate)
  • Ilość bitów danych: 7, 8 lub 9
  • Kontrola parzystości: even lub odd
  • Bity stopu: 1, 1,5 lub 2

Identycznie dobrane parametry w obu urządzeniach pozwalają na prawidłową interpretację przesyłanych danych i nawiązanie komunikacji.

 Budowa interfejsu w STM32

Interfejs USART w STM32 można podzielić na blok obsługi rejestrów oraz blok funkcjonalny.

USART podłączony jest do szyny APB. Ma wbudowaną obsługę interfejsu przerwań (IRQ) oraz bezpośredniego dostępu do pamięci (DMA). Wyposażony został w dwie kolejki FIFO – Rx do odbierania danych i Tx do nadawania – oraz rejestry przesuwne współpracujące z kolejkami. Blok “Baudrate generator & oversampling” odpowiada za generowanie na podstawie wewnętrznego sygnału zegarowego prawidłowej prędkości transmisji i odbierania danych. Dodatkowo blok USART ma możliwość wykorzystania sprzętowej kontroli przepływu danych (Hardware flow control) stosowanej m.in. przy komunikacji RS232 i RS485.

Komunikacja UART jest dwukierunkowa i realizowana przy pomocy dwóch oddzielnych pinów TX i RX. Linie te w komunikujących się urządzeniach powinny być skrzyżowane – TX jednego urządzenia łączymy z RX drugiego.

Wybór zegara i oversampling w USART

W zależności od potrzeb, w mikrokontrolerach STM32G0 mamy możliwość wyboru zegara, jakim taktowany jest interfejs USART. Konfigurujemy go za pomocą rejestru RCC_CCIPR – w przypadku interfejsu USART2 będą to bity 3:2, czyli USART2SEL. Do wyboru mamy 4 rodzaje zegara:

  • PCLK
  • SYSCLK
  • HSI16
  • LSE

Od czego powinno zależeć to, który z zegarów wybieramy? W znacznej większości zastosowań odpowiedni będzie zegar domyślny, czyli PCLK. Zdarzają się jednak przypadki, kiedy powinniśmy wybrać inne źródło. Będzie to ważne w przypadku m.in. pracy interfejsu w trybie low-power lub w przypadku, gdy będziemy chcieli zastosować dużą prędkość transmisji i zegar PCLK będzie niewystarczający.

Zegar interfejsu USART możemy podzielić również poprzez odpowiednie ustawienia preskalera w rejestrze USART_PRESC.

W poprzednich lekcjach w trakcie konfiguracji prędkości transmisji (baudrate) jako jeden z argumentów podawaliśmy rodzaj oversamplingu. Czy jest oversampling?

Oversampling, czyli nadpróbkowanie, to mechanizm wychwytywania poprawnych danych w trakcie transmisji, gdy pojawia się szum na magistrali USART. Oznacza to, że w trakcie transmisji wykorzystywane są techniki, które pozwalają prawidłowo zinterpretować dane przychodzące pomimo pewnych zakłóceń.

Jak to wygląda w praktyce? Spójrzmy na wykres z dokumentacji.

Na pierwszym wykresie przedstawiony jest tryb Oversampling16. Jeden bit danych przychodzących do USART jest próbkowany 16 razy – czyli tyle taktów zegara potrzebne jest na odbiór jednego bitu. Pierwsze 7 i ostatnie 6 bitów to czas na ustabilizowanie się sygnału. Środkowe 3 bity służą do określenia stanu bitu. W zależności od odczytanych stanów w poszczególnych próbkach USART zwraca wartość bitu.

Jeżeli stan próbek okaże się niestabilny, USART zwróci błąd. Analogicznie odbywa się to w trybie Oversampling8, z taką różnicą, że na jeden bit przypada 8 próbek, a co za tym idzie 8 taktów zegara.

Domyślnie USART pracuje przy nadpróbkowaniu 16 próbkami. Jest dzięki temu dokładniejszy i bardziej precyzyjny. Oversampling 8 wykorzystywany jest głównie wtedy, gdy chcemy osiągnąć bardzo duże prędkości transmisji i USART przy danym taktowaniu  nie jest w stanie przetworzyć 16 próbek.

Mamy też możliwość rekonfiguracji sposobu interpretacji tego, jaki stan bitu jest przypisany. Domyślnie wykorzystywane są 3 próbki środkowe i analiza zgodnie z przedstawioną tabelą. W przypadku, gdy pracujemy w nie zaszumionym otoczeniu i jesteśmy pewni stabilności komunikacji, możemy włączyć tryb, w którym pod uwagę brana jest tylko jedna próbka środkowa i na jej podstawie określany jest stan bitu. Nie mamy wtedy możliwości zwrócenia błędu NE.

Podstawowe rejestry interfejsu USART

USART_CR1 – rejestr kontrolny interfejsu

M0 i M1 – konfiguracja ilości bitów danych

PCE i PS – kontrola parzystości

TE i RE – włączenie nadawania i odbierania danych

UE – włączenie interfejsu USART

USART_CR2 – rejestr kontrolny interfejsu

STOP – konfiguracja bitów stopu

USART_BRR – rejestr konfiguracyjny prędkości komunikacji (baudrate)

USART_ISR – rejestr przerwań i bitów statusu

TXE – bit określający, że rejestr nadawczy jest pusty

TC – bit określający, że dane z rejestru TX zostały wysłane

RXNE – rejestr odbiorczy nie jest pusty (odebrana dana oczekuje na odczytanie)

USART_RDR – rejestr odbiorczy interfejsu USART

USART_TDR – rejestr nadawczy interfejsu USART

Rodzaje komunikacji: polling, interrupt, DMA

W przypadku większości układów peryferyjnych w mikrokontrolerach możemy wyróżnić trzy sposoby obsługi interfejsu: polling, interrupt oraz DMA.

Polling to najprostsza z tych trzech metod, która polega na odczytywaniu z rejestru lub wpisywaniu danych do rejestru cyklicznie w pętli głównej. W przypadku komunikacji przez interfejs USART co przejście przez pętle (albo co jakiś odcinek czasu) sprawdzamy, czy zostały odebrane jakieś dane lub wysyłamy je, jeśli poprzednie dane już zostały wysłane (bufor nadawczy jest pusty). Ta metoda jest najbardziej obciążająca dla CPU, ponieważ co jakiś czas wykonujemy operację odczytu bitów statusu – bez względu na to, czy dane przyszły, czy nie.

Interrupt (czyli przerwanie) polega na wykorzystaniu sprzętowej obsługi zdarzeń. Po odpowiedniej konfiguracji interfejsu możemy dostać informację, gdy dane zostaną odebrane. Wywoływane jest wtedy przerwanie, czyli funkcja niezależna od pętli głównej programu. Po jej obsłudze program wraca do poprzednio wykonywanej czynności. Wykorzystanie przerwań pozwala nam uniknąć ciągłego sprawdzania tego, czy dane już przyszły. Dostaniemy o tym informację zaraz po ich odebraniu.

DMA (czyli Direct Memory Access, po polsku bezpośredni dostęp do pamięci) to mechanizm, który pozwala jeszcze w większym stopniu odciążyć CPU podczas komunikacji. Polega na bezpośrednim przesyłaniu danych z rejestru odbiorczego do pamięci mikrokontrolera (np. tablicy znaków) z pominięciem CPU. W przypadku przerwania, po każdym odebranym bajcie musimy odczytać go z rejestru odbiorczego i zapisać do naszej tablicy. DMA zrobi to za nas i poinformuje, np. gdy odbierzemy odpowiednią ilość znaków. DMA sprawdzi się w szczególności, gdy przesyłamy duże ilości danych.

Wszystkie projekty z kursu dostępne są w moim repozytorium GitHub.

[PROGRAM] Wysyłanie danych w trybie polling

Poznaliśmy podstawowe rejestry interfejsu USART oraz różne tryby (sposoby) komunikacji. Czas na pierwszy przykład. Zaczniemy od najprostszej metody, czyli nadawania w trybie polling.

Na początku musimy skonfigurować interfejs tak, aby był gotowy do transmisji danych. Wykorzystamy USART2 i wyprowadzenie PA2, na którym mamy dostępny pin TX. W pierwszej kolejności konfigurujemy pin PA2. Włączamy zegar i ustawiamy go w trybie funkcji alternatywnej, bez rezystorów podciągających.

LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);

LL_GPIO_SetPinPull(USART2_TX_GPIO_Port, USART2_TX_Pin, LL_GPIO_PULL_NO);
LL_GPIO_SetPinSpeed(USART2_TX_GPIO_Port, USART2_TX_Pin, LL_GPIO_SPEED_FREQ_LOW);
LL_GPIO_SetAFPin_0_7(USART2_TX_GPIO_Port, USART2_TX_Pin, LL_GPIO_AF_1);
LL_GPIO_SetPinMode(USART2_TX_GPIO_Port, USART2_TX_Pin, LL_GPIO_MODE_ALTERNATE);

Dlaczego wybraliśmy funkcję alternatywną LL_GPIO_AF_1? Żeby to wyjaśnić przejdźmy na chwilę do dokumentacji Datasheet mikrokontrolera. Na stronie 48. w tabeli 13. znajdziemy “rozpiskę” z funkcjami alternatywnymi dla każdego pinu. Chcemy wykorzystać USART2_TX na PA2, czyli właśnie AF1.

Teraz przechodzimy do konfiguracji interfejsu USART2. Włączamy taktowanie dla USART2.

LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART2);

Następnie ustawiamy parametry protokołu. Biblioteka Low Layer udostępnia bardzo wygodną funkcję, która jednocześnie ustawi bity w rejestrze CR1 i CR2.

LL_USART_ConfigCharacter(USART2, LL_USART_DATAWIDTH_8B, LL_USART_PARITY_NONE, LL_USART_STOPBITS_1);

Następnie wybieramy prędkość komunikacji, czyli baudrate. Domyślnie USART2 ma ustawione oversampling na poziomie 16 i tak też to zostawiamy. Nie używamy też preskalera dla zegara dla USART2, dlatego podzielnik wybieramy jako 1. Parametry te są istotne dla wyboru prawidłowego baudrate, ponieważ w zależności od ich wartości inaczej ustawiamy rejestr BRR.

LL_USART_SetOverSampling(USART2, LL_USART_OVERSAMPLING_16);
LL_USART_SetPrescaler(USART2, LL_USART_PRESCALER_DIV1);
LL_USART_SetBaudRate(USART2, SystemCoreClock, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, 115200);

Teraz możemy jeszcze wykorzystać funkcję do ustawienia komunikacji w trybie asynchronicznym. Teoretycznie nie jest ona niezbędna, ponieważ rejestry w niej ustawione są domyślnie właśnie w trybie asynchronicznym, ale dla pewnego porządku warto tę funkcję wywołać.

LL_USART_ConfigAsyncMode(USART2);

Pozostało włączyć nadawanie dla USART2 oraz uruchomić magistralę.

LL_USART_EnableDirectionTx(USART2);
LL_USART_Enable(USART2);

Teraz musimy tylko wpisać dane do rejestru nadawczego. Aby transmisja przebiegła prawidłowo, przed wysłaniem każdego bajtu należy poczekać, aż bufor nadawania będzie pusty (flaga TXE będzie ustawiona).

for (int var = 0; var < data_size; ++var)
{
	 while (!LL_USART_IsActiveFlag_TXE(USART2))
		 ;
	  LL_USART_TransmitData8(USART2, data_buf[var]);
}

Możemy uruchomić tak napisany program i połączyć się z dowolnym terminalem portu szeregowego (np. RealTerm). Po poprawnym skonfigurowaniu parametrów transmisji dane powinny pojawić się w oknie terminala.

[PROGRAM] Odbieranie danych w trybie polling

Nauczyliśmy się już, w jaki sposób wysyłać dane. Teraz czas na odbieranie. Konfiguracja będzie przebiegała w bardzo podobny sposób, jak w przypadku transmisji danych.

Wykorzystamy USART2 i wyprowadzenie PA3, na którym mamy dostępny pin RX. W pierwszej kolejności konfigurujemy pin PA3. Włączamy zegar i ustawiamy go w trybie funkcji alternatywnej, bez rezystorów podciągających.

LL_IOP_GRP1_EnableClock(LL_IOP_GRP1_PERIPH_GPIOA);

LL_GPIO_SetPinPull(USART2_RX_GPIO_Port, USART2_RX_Pin, LL_GPIO_PULL_NO);
LL_GPIO_SetPinSpeed(USART2_RX_GPIO_Port, USART2_RX_Pin, LL_GPIO_SPEED_FREQ_LOW);
LL_GPIO_SetAFPin_0_7(USART2_RX_GPIO_Port, USART2_RX_Pin, LL_GPIO_AF_1);
LL_GPIO_SetPinMode(USART2_RX_GPIO_Port, USART2_RX_Pin, LL_GPIO_MODE_ALTERNATE);

Wybieramy LL_GPIO_AF_1 w sposób analogiczny jak w przypadku pinu TX. Na stronie 48. w tabeli 13. dokumentacji Datasheet znajdziemy “rozpiskę” z funkcjami alternatywnymi dla każdego pinu. Chcemy wykorzystać USART2_RX na PA3, czyli właśnie AF1.

Teraz przechodzimy do konfiguracji interfejsu USART2. Włączamy zegar.

LL_APB1_GRP1_EnableClock(LL_APB1_GRP1_PERIPH_USART2);

Następnie ustawiamy parametry protokołu. Biblioteka Low Layer udostępnia bardzo wygodną funkcję, która jednocześnie ustawi bity w rejestrze CR1 i CR2.

LL_USART_ConfigCharacter(USART2, LL_USART_DATAWIDTH_8B, LL_USART_PARITY_NONE, LL_USART_STOPBITS_1);

Następnie wybieramy prędkość komunikacji, czyli baudrate. Domyślnie USART2 ma ustawione oversampling na poziomie 16 i tak też to zostawiamy. Nie używamy też preskalera dla zegara dla USART2, dlatego podzielnik wybieramy jako 1. Parametry te są istotne dla wyboru prawidłowego baudrate, ponieważ w zależności od ich wartości inaczej ustawiamy rejestr BRR.

LL_USART_SetBaudRate(USART2, SystemCoreClock, LL_USART_PRESCALER_DIV1, LL_USART_OVERSAMPLING_16, 115200);

Teraz możemy jeszcze wykorzystać funkcję do ustawienia komunikacji w trybie asynchronicznym.

LL_USART_ConfigAsyncMode(USART2);

Pozostało włączyć odbieranie dla USART2 oraz uruchomić magistralę.

LL_USART_EnableDirectionRx(USART2);
LL_USART_Enable(USART2);

Aby odebrać dane, musimy pobrać je z rejestru USARTX_RDR. Wcześniej musimy sprawdzić za pomocą flagi RXNE, czy dane zostały już odebrane i są gotowe do odczytania.

if(LL_USART_IsActiveFlag_RXNE(USART2))
{
	rx_buffer.data[rx_buffer.count] = LL_USART_ReceiveData8(USART2);
	rx_buffer.count++;
}

Uruchamiamy program i przechodzimy w tryb debugowania. W okrnie Live expression dodajemy bufor danych. Wysyłając dane z terminala, otrzymamy je w buforze.

Chciałbyś otrzymywać na bieżąco informacje o nowych artykułach z kursu? Zapisz się do newslettera!

TO NIE TYLKO MAIL Z INFORMACJĄ O NOWEJ LEKCJI, ALE TAKŻE DODATKOWE MATERIAŁY. NIE PRZEGAP NOWEJ TREŚCI I DODATKOWYCH BONUSÓW. PRZEJDŹ DO STRONY KURSU I PODAJ SWÓJ ADRES E-MAIL. NIE ZAPOMNIJ POTWIERDZIĆ CHĘCI DOŁĄCZENIA W PIERWSZEJ WIADOMOŚCI!
Repozytorium GitHub

    1. Sporo osób pyta się o timery. Są trochę problematyczne, ale mam nadzieje, ze uda mi się je objaśnić w przystępny sposób. Będą omawiane tuż po USART.

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *