[3] STM32CUBE w przykładach (USART)

 

W tej części cyklu stworzymy naszemu mikrokontrolerowi możliwość kontaktu z komputerem. Wykorzystamy w tym celu interfejs USART oraz znajdujący się na płytce rozwojowej KA-NUCLEO-F411CE programator ST-LINK lub osobny adapter UART<->USB. Przy pomocy interfejsu USART możemy również sterować wieloma dostępnymi na rynku układami rozszerzającymi funkcjonalność naszego procesora – na przykład modemami GSM lub układem ESP8266 pełniącym funkcję karty sieciowej Wi-Fi.

Znajdujący się na płytce programator, to nic innego, jak kolejny układ z serii STM32, z wyprowadzonym portem USB od strony użytkownika oraz interfejsami SWD i UART przyłączonymi do układu, który programujemy. Ma on wgrane oprogramowanie emulujący programator ST-LINK, a po podłączeniu do komputera, przedstawia się, jako trzy niezależne urządzenia USB – pamięć masowa, właściwy programator oraz port szeregowy (COM).

Na wykorzystywanej przeze mnie płytce KA-NUCLEO-F411CE również i z głównego układu wyprowadzono port USB dostępny dla użytkownika, moglibyśmy więc wykorzystać go w roli interfejsu komunikacyjnego i nie korzystać z adaptera lub programatora. Znajomość konfiguracji interfejsu UART będzie nam jednak potrzebna w kolejnych częściach kursu, do komunikacji z innymi układami.

Wykrywanie przez system portu COM to nie przypadek. Interfejs USART jest częściowo kompatybilny z dawnym standardem RS232. Różnica polega na wykorzystywaniu innych napięć – standardowej logiki układu (u nas CMOS – 0 i 3.3 V) oraz kilku dodatkowych wyprowadzeniach w dawnym standardzie. Format przesyłanej ramki jest ten sam i przy pomocy popularnego układu MAX232 możemy podłączyć nasz układ do portu COM komputera.

Wspomniałem już o interfejsach UART i USART. Czym jednak się one różnią? Interfejs UART (ang. Universal Asynchronous Receiver and Transmitter) jak wskazuje nazwa, jest interfejsem asynchronicznym, co oznacza, że urządzenia po obu stronach mogą zacząć nadawać w każdej chwili i nie wymieniają się sygnałem zegara. UART wykorzystuje jedynie dwa wyprowadzenia – jedno nadawcza, a drugie odbiorcze (łącząc układy należy jeszcze pamiętać o połączeniu ich mas). Interfejs USRT (ang. Universal Synchronous Receiver and Transmitter), oprócz sygnałów nadawczego i odbiorczego, wysyła również sygnał zegara określający jasno, w których momentach transmitowane są poszczególne bity. Jest on łatwiejszy w implementacji sprzętowej i zabezpiecza nas przed rozsynchronizowaniem transmisji przy wyższych szybkościach przesyłu, jednak wykorzystuje dodatkowe wyprowadzenie. USART (ang. Universal Synchronous and Asynchronous Receiver and Transmitter) to zbiorcza nazwa określająca oba standardy oraz interfejs mikrokontrolera, który może pracować w obu wymienionych trybach.

Dawny interfejs RS232 posiadał więcej wyprowadzeń – min. sygnały Ready to Send oraz Clear to Send sygnalizujące kolejno, że dane urządzenia ma w swoim buforze dane gotowe do wysłania oraz że przeciwne może je w tej chwili odebrać (w buforze jest na to miejsce). Standard RS232 pierwotnie wykorzystywany był do łączenia komputerów z modemami, a dalej poprzez linie telefoniczne z innymi komputerami.

W środowisku Arduino, interfejs UART wykorzystywany jest do programowania mikrokontrolerów z wgranym bootloaderem oraz do debugowania – okienko Serial Monitor. My wykorzystamy lekki i darmowy program PuTTY. Możemy go pobrać z tej strony: http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html (potrzebujemy jedynie pliku putty.exe). Program ten dostępny jest również dla systemów Unixowych, jednak ich użytkownikom polecam skorzystać z konsolowego minicoma lub screena.

 

Pierwszy projekt

W dalszej części tego artykułu, omówimy działanie przerwań oraz wykorzystamy je, aby nie blokować pracy procesora, w trakcie oczekiwania na polecenia wysyłane z komputera. Nasz pierwszy projekt będzie jednak bardzo prymitywny – będzie on odbierał z komputera tekst wprowadzony przez użytkownika i odsyłał go, poprzedzając słowem „Odebrano: […]”, aktywnie czekając na odbiór danych. Będzie to również bardzo dobry przykład tego, jak nie należy wykorzystywać interfejsu UART. 🙂

Uruchamiamy zatem oprogramowanie STM32CubeMX, tworzymy nowy projekt oraz wybieramy układ. Dla przypomnienia, przykłady z niniejszego cyklu wykonywane są na płytce rozwojowej KA-NUCLEO-F411CE z układem STM32F411CEU6.

Naszym oczom ukażą się wyprowadzenia układu. Musimy teraz zdecydować, na jakich pinach chcemy uruchomić interfejs. Rzecz jasna, nie na każdym z nich możemy to zrobić. Wykorzystywany przeze mnie układ ma trzy osobne interfejsy USART, które możemy uruchamiać na pięciu różnych parach pinów. Jeśli korzystamy z płytki KA-NUCLEO i chcemy wykorzystać w roli adaptera UART<->USB wbudowany programator, wykorzystywane przez nas wyprowadzenia to PA2 w roli pinu nadawczego oraz PA3 w roli pinu odbiorczego. Jeśli korzystamy z innej płytki lub chcemy podłączyć adapter USB albo układ MAX232 do wyprowadzeń płytki, musimy wyszukać wyprowadzeń w jej datasheecie. Z poziomu STM32CubeMX możemy natomiast sprawdzić, na których pinach układu można uruchomić interfejs – klikamy w tym celu lewym przyciskiem myszy na piny i szukamy funkcji alternatywnej o nazwie „USARTx_TX” lub „USARTx_RX”. Gdy chcemy ustawić wybrane piny, wybieramy z menu te funkcje. Do działania interfejsu UART potrzebujemy pinu TX oraz RX. Dla interfejsu USRT, dodatkowo pinu CK – zegara.

Po wybraniu odpowiednich pinów, z listy po lewej stronie wyszukujemy wybranego interfejsu „USARTx” (gdzie x to jego numer) i wybieramy tryb jego pracy – w polu „Mode” ustawiamy opcję „Asynchronous” lub „Synchronous”. Możemy także dodać omawiane wcześniej piny RTS i CTS – odpowiada za to pole „Hardware Flow Control (RS232)”. Na razie jednak ustawmy tryb pracy na „Asynchronous” i nie włączajmy pinów RTS/CTS.

 

Rys. 1. Konfiguracja pinów w programie STM32CubeMX

W kolejnym kroku konfigurujemy pętlę PLL, w sposób identyczny jak opisany w pierwszej części kursu. Najpierw, w zakładce „Pinout”, na liście po lewej stronie odszukujemy pozycję „Peripherals” -> „RCC” i z listy rozwijanej w polu „High Speed Clock (HSE)” wybieramy pozycję „Cristal/Ceramic Resonator”. Następnie, w zakładce „Clock Configuration”, w polu „PLL Source Mux”, jako źródło wybieramy HSA i ustawiamy mu prawidłową częstotliwość (na płytce KA-NUCLEO jest to 8 MHz), w polu „System Clock Mux” wybieramy opcję „PLLCLK”. Następnie w polu „HCLK” ustawiamy pożądaną częstotliwość. Do nauki, możemy wybrać maksymalną dostępną (dla układu na płytce KA-NUCLEO będzie to 100 MHz).

Dalej przechodzimy do zakładki „Configuration”. W polu „Connectivity” pojawił się nowy interfejs USART. U mnie – USART2. Aby go skonfigurować, klikamy przycisk „USARTx”. Możemy teraz ustawić szybkość nadawania i odbioru, a także inne parametry przesyłanych ramek danych. Popularnymi szybkościami pracy są 115200 bps oraz 9600 bps. Jeśli chcemy sterować układem, który posiada interfejs USART, powinniśmy sprawdzić, jaką szybkość ustawić, w datasheecie tego układu. Podobnie sprawa ma się z ilością bitów stopu, a także bitem parzystości.

Rys. 2. Ramka danych interfejsu UART (źródło: Wikipedia)

Ramka interfejsu USART składa się z pojedynczego bitu startu ramki (zawsze logicznego zera), ośmiu bitów danych, bitu parzystości oraz jednego lub dwóch bitów stopu (logicznych jedynek). Bit parzystości obliczany jest z przesyłanych bitów danych – jeśli występuje w nich parzysta ilość jedynek, to bit parzystości będzie logiczną jedynką, przeciwnie – zerem. Domyślnie, jego obliczanie jest wyłączone, możemy je włączyć, ustawiając w polu „Parity” wartość „Odd”. Możliwe jest także ustawienie bitu „nieparzystości” działającego przeciwnie do bitu parzystości – wtedy wybieramy wartość „Even”.

Rys. 3. Konfiguracja interfejsu UART w programie STM32CubeMX

Na potrzeby komunikacji z komputerem, pozostawiamy domyślne ustawienia. Możemy już wygenerować projekt dla środowiska System Workbench for STM32 – klikamy w ikonę zębatki na pasku narzędziowym, zmieniamy wartość pola „Toolchain/IDE” na „SW4STM32” i klikamy przycisk OK, następnie importujemy projekt w tymże środowisku – zamykamy planszę powitalną, klikamy PPM w puste pole w ramce „Project Explorer” i wybieramy kolejno: „Import…” -> „General” -> „Existing Project into Workspace”, odszukujemy nasz projekt na dysku i klikamy OK.

Następnie otwieramy plik „Src/main.c” i w sekcji „USER CODE 0” dopisujemy poniższe funkcje. Początkowo, ułatwią nam one komunikacje z komputerem. W dalszej części artykułu omówione zostaną także bezpośrednie wywołania HAL-a.

Do sekcji „USER CODE 3” wpisujemy poniższy kod – wywołanie zadeklarowanych wcześniej funkcji.

Program będzie odbierał od użytkownika tekst, aż do naciśnięcia klawisza Enter na klawiaturze, a następnie odeśle go z powrotem, poprzedzając słowem „Odebrano: […]”.

W pierwszej linii powyższego kodu, tworzymy tablicę 1024 komórek typu char (przechowujących kolejne znaki ASCII wprowadzonego tekstu – litery, cyfry oraz symbole). Następnie wywołujemy funkcję uart_read_line(). Funkcja ta przyjmuje na wejściu wskaźnik na bufor i w trakcie swojego działania, zapisuje do niego kolejne odebrane znaki, aż do odebrania znaku nowej linii lub przekroczenia długości bufora – wtedy w miejscu, w którym kończy się nasz ciąg znaków, w kolejnej komórce bufora, zapisywany jest znak ‘\0’ – null-terminator, oznaczający koniec ciągu. Funkcja uart_read_line(), poza wskaźnikiem na bufor, przyjmuje na wejściu również wskaźnik na strukturę konfiguracyjną interfejsu UART (%huartX, gdzie X to nr interfejsu, struktura ta jest generowana automatycznie przez CubeMX). Następnie, w niemal identyczny sposób, korzystając z funkcji uart_write() oraz uart_write_line() wyświetlamy napis „Odebrano: […]” oraz odebrany ciąg zwieńczony znakami końca linii (“\r\n” – kody ASCII powodujące powrót kursora na początek linii oraz przejścia do nowej).

Rys. 4. Menedżer Urządzeń

Teraz możemy skompilować kod i wgrać go na układ (ikony młotka i robaka na pasku menu) oraz uruchomić program PuTTY i „porozmawiać” z mikrokontrolerem. W oknie programu PuTTY, w polu „Connection Type” wybieramy opcję „Serial”, w polu „Speed” wpisujemy wybraną szybkość połączenia (domyślnie 115200 bps), a w polu „Serial line” podajemy nazwę portu szeregowego. Tą ostatnią poznamy w systemowym Menadżerze Urządzeń, w sekcji „Porty (COM i LPT)”, pod Windowsem oraz w logu kernela, w systemach Unixowych (Linuksowe polecenie dmesg).

Rys. 5. Ustawienia połączenia w PuTTY

W ten sposób, możemy przesyłać z naszego mikrokontrolera do komputera informacje o parametrach jego pracy, wartościach odebranych z czujników, czy odbierać od komputera polecenia. W dalszej części spróbujemy wysterować diodę RGB na podstawie odebranych od użytkownika poleceń zwiększenia lub zmniejszenia jasności kolejnych barw składowych. Do zamiany wartości numerycznych, na ciągi znaków możemy wykorzystać funkcję sprintf(), działającą podobnie jak funkcja printf(), z tą różnicą, że zwraca wynik w postaci ciągu znaków, który następnie wysyłamy przez UART, a nie wyświetla go.

Rys. 6. Komunikacja z mikrokontrolerem w PuTTY

Do pobrania