[3] Kurs: pierwsze kroki z STM32F0DISCOVERY. Zabawy z USART

Samouczek jest dedykowany szczególnie tym projektantom, którzy stają przed perspektywą zmiany mikrokontrolera z 8-bitowego na nowszy i tańszy 32-bitowy. Wszystkie programy są napisane w języku C. W przykładach nie użyto bibliotek służących do obsługi peryferiali, dzięki czemu kod programów jest krótki i czytelny, a zajętość pamięci znacznie mniejsza, niż w typowych programach demonstracyjnych, udostępnianych przez producentów mikrokontrolerów. Zwrócono również szczególną uwagę na poprawność prezentowanych rozwiązań i sposób zapisu programu ułatwiający optymalizację kodu i wychwytywanie błędów przez kompilator. Przedstawione programy zostały napisane w taki sposób, że nie generują one żadnych ostrzeżeń kompilatora.

 

**************

Pierwsza część kursu jest dostępna pod adresem.

Druga część kurssu jest dostępna pod adresem.

**************

 

Port szeregowy USART

Mikrokontrolery STM32F051 są wyposażone w sterownik transmisji szeregowej USART, który może służyć do transmisji zarówno asynchronicznej, jak i synchronicznej. Użyty w trybie asynchronicznym, może on np. sterować interfejsem RS232. Na płytce DISCOVERY nie ma translatorów poziomów RS232, jednak własności elektryczne i logiczne STM32F05x umożliwiają współpracę z interfejsem RS232 bez użycia translatora, o ile transmisja odbywa się w warunkach laboratoryjnych – na małą odległość i bez zakłóceń. Do połączenia mikrokontrolera z interfejsem RS232C komputera  PC potrzebny będzie tylko jeden rezystor o wartości od 47 do 100 k?.

Przedstawiony poniżej projekt oprogramowania powstał przez modyfikację projektu BtnBlink. Nosi on nazwę USART1.

 

Zaprogramowanie modułu USART

Przed zaprogramowaniem modułu USART należy określić parametry transmisji. Wybierzemy szybkość 9600 b/s, format 8-bitowy bez parzystości, z jednym bitem stopu. Wartość, która posłuży do zaprogramowania szybkości transmisji, zdefiniujemy jako wyrażenie preprocesora. Ponieważ nie użyjemy układu translatora poziomów RS232C, zaprogramujemy odwracanie poziomów napięć na liniach RX i TX.

W celu zaprogramowania modułu USART należy:

  • Włączyć moduł poprzez ustawienie bitu USART1EN w rejestrze APB2ENR.
  • Włączyć port GPIOA.
  • Wybrać funkcję AF 1 (RX i TX USART1) dla wyprowadzeń 10 i 9 portu GPIOA.
  • Włączyć ściąganie w dół dla linii RX
  • Ustawić linie 10 i 9 portu GPIOA jako AF, pozostawiając funkcje interfejsu SWD dla linii 14 i 13.
  • Ustawić podzielnik szybkości transmisji w rejestrze BRR modułu USART1.
  • Włączyć odwracanie polaryzacji linii RX i TX – bity TXINV i RXINV rejestru CR2.
  • Włączyć przerwanie odbioru danych, zezwolenie na nadawanie i odbiór oraz cały układ USART1 w rejestrze CR1 – musi to być ostatnia czynność podczas programowania rejestrów modułu USART.
  • Włączyć przerwanie USART1 w module NVIC – rejestr ISER[0].

Ponieważ w projekcie użyjemy przerwania modułu USART, należy je oprogramować. Jedynym włączonym źródłem tego przerwania będzie odebranie danej. Procedura obsługi przerwania USART1 odczytuje odebrany znak i poddaje go prostemu przetwarzaniu. Małe litery są zamieniane na wielkie i odwrotnie, a odebranie kodu CR powoduje, poprzez ustawienie zmiennej mchange, zmianę trybu świecenia diod. Po przetworzeniu znak zostaje odesłany z powrotem.

Podczas wysyłania znaku do PC nie jest sprawdzana gotowość nadajnika USART. W naszym przykładzie nie ma to znaczenia, gdyż przy nadawaniu z komputera PC znaków wprowadzanych przez użytkownika w programie terminala nie ma możliwości przepełnienia bufora nadajnika interfejsu USART mikrokontrolera znakami odsyłanymi zwrotnie Nie jest to jednak uniwersalny sposób programowej obsługi nadawania, który może być zastosowany w każdej sytuacji. Poprawne nadawanie danych z użyciem przerwań zostanie zaprezentowane w następnym przykładzie.

 

Połączenie modułu DISCOVERY z komputerem

W celu umożliwienia komunikacji modułu z komputerem PC należy poprowadzić trzy połączenia – masy oraz dwóch linii danych. Ponieważ zasilamy moduł STM32F0DISCOVERY z komputera, masa jest już połączona przez port USB. Do nawiązania transmisji wystarczy więc wykonać połączenia linii danych z komputera do modułu i z modułu do komputera. Wyjście danych mikrokontrolera – linię 9 portu GPIOA – łączymy bezpośrednio z wejściem danych komputera PC – linią 2 złącza DB9. Wejście danych mikrokontrolera – linię 10 portu GPIOA – łączymy przez rezystor szeregowy 100 k? z wyjściem danych komputera PC – linią 3 złącza DB9. Podczas wykonywania połączeń należy zachować szczególną uwagę – błędne podłączenie wyjścia danych mikrokontrolera do niewłaściwej linii złącza DB9 może spowodować trwałe uszkodzenie mikrokontrolera.

 

Obsługa nadajnika USART z użyciem przerwań

Poprzedni przykład pokazuje dość prostą i nie nadającą się do szerszego zastosowania metodę nadawania danych przez moduł USART. W kolejnym projekcie zaprezentujemy nadawanie ciągów znaków przy użyciu przerwania zgłaszanego przy gotowości nadajnika. W każdym projekcie urządzenia nadającego dane musimy zadbać o to, by strumień danych nie przekraczał możliwości nadajnika. Nasz program będzie zliczał naciśnięcia przycisku. Po każdym naciśnięciu wartość licznika będzie wysyłana do PC przez interfejs UART w postaci 8-cyfrowej liczby szesnastkowej zakończonej sekwencją przejścia do nowego wiersza CR+LF. Każde naciśnięcie przycisku będzie więc powodowało transmisję 10 bajtów. Przesyłane dane można będzie oglądać przy użyciu programu terminala. Ponieważ z zastosowanego wcześniej algorytmu obsługi przycisku wynika, że można zarejestrowań maksymalnie 25 naciśnięć w ciągu sekundy, przy szybkości transmisji 9600 bit/s nie zachodzi obawa nasycenia nadajnika. Aby obsługa transmisji była uniwersalna, zastosujemy dla transmitowanych danych bufor cykliczny, pomimo że nie jest on niezbędny w naszym zastosowaniu, w którym długość przesyłanego bloku danych jest stała. Program powstanie przez modyfikację poprzedniego przykładu użycia USART. Gotowy projekt nosi nazwę USART1cnt.

 

Procedury obsługi nadawania

Zaczynamy od sekwencji inicjującej USART1. Tym razem nie będziemy używali przerwania odbiornikia. Musimy za to oprogramować nadawanie danych przy użyciu przerwania nadajnika.

W programie zdefiniujemy bufor nadawanych znaków u1_outbuf[] oraz dwa indeksy – wskaźniki znaków wstawianych i pobieranych z bufora – u1_obiptr i u1_oboptr. Gdy bufor jest pusty, oba wskaźniki są równe. Przerwanie nadajnika będzie włączane tylko wtedy, kiedy w buforze znajdą się dane do nadawania.

Procedura obsługi przerwania USART1 jest wywoływana, gdy interfejs jest gotowy na przyjęcie danej do nadawania i włączone jest przerwanie nadajnika. Procedura najpierw oblicza przyszłą wartość wskaźnika danych pobieranych. Jeżeli po wysłaniu kolejnego znaku bufor zostałby opróżniony, przerwanie nadajnika jest wyłączane. Następnie znak z bufora jest ładowany do rejestru nadajnika oraz modyfikowany jest wskaźnik pobieranych danych.

Procedura wstawienia znaku do bufora USART1_put sprawdza, czy w buforze jest miejsce, a następnie wstawia znak do bufora, przesuwa wskaźnik wstawiania i włącza przerwanie nadajnika. W przypadku zapełnienia bufora procedura zwraca wartość 1, sygnalizującą błąd. Procedura nie czeka w takim przypadku na zwolnienie miejsca w buforze.

Procedura wysyłania cyfry szesnastkowej puthexdigit zamienia wartość tetrady na jej reprezentację szesnastkową w kodzie ASCII, a następnie wstawia ją do bufora przy użyciu funkcji USART1_put.

 

Procedura obsługi przerwania timera

W procedurze zadeklarowano zmienną count – licznik zliczający naciśnięcia przycisku.

Procedura obsługi timera w przypadku wykrycia naciśnięcia przycisku inkrementuje licznik i wstawia do bufora nadawczego jego reprezentację znakową, zakończoną sekwencją końca wiersza. Równocześnie jest zaświecana niebieska dioda, sygnalizująca naciśnięcie przycisku.

 

 

Zliczanie czasu i transmisja USART z użyciem DMA

Kolejny program będzie transmitował dane przez USART korzystając z modułu bezpośredniego dostępu do pamięci, czyli bez udziału oprogramowania w transmisji każdego bajtu. Tam razem program będzie pełnił rolę prostego timera, odliczającego czas w sekundach. Naciśnięcie przycisku umieszczonego na płytce będzie powodowało wyzerowanie i restart timera. Co jedną sekundę bieżąca wartość timera będzie transmitowana przez USART – można ją wyświetlić na PC korzystając z programu terminala. Program, powstały przez modyfikację poprzedniego przykładu, jest zawarty w projekcie UART1dma.

 

Oprogramowanie modułu USART i DMA

Moduł USART1 jest inicjowany podobnie jak w poprzednim przykładzie. Wprowadzono jedną istotną zmianę – przed finalnym uaktywnieniem USART w rejestrze CR modułu jest ustawiony bit włączający zgłaszanie żądania DMA przez nadajnik.

Na początku sekwencji inicjującej jest włączany moduł DMA – wymaga to ustawienia odpowiedniego bitu w rejestrze AHBENR modułu RCC.

Sterownik bezpośredniego dostępu do pamięci zrealizowany w STM32F05x dysponuje pięcioma kanałami transmisji. Z nadajnikiem USART1 jest związany kanał 2; programując moduł DMA będziemy używali rejestrów tego kanału, dostępnych w postaci struktury poprzez wskaźnik DMA1_Channel2.

Do jednorazowego zainicjowania modułu DMA potrzebne są dwa zapisy rejestrów adresów. Ponieważ specyfikowanie adresów jako stałych typów numerycznych generuje ostrzeżenie w ANSI C, zapisy te zostały umieszczone w funkcji main. Pełne programowanie modułu DMA do współpracy z nadajnikiem USART1 wymaga łącznie czterech operacji:

  • Ustawienia adresu początkowego bufora w pamięci, z którego będą pobierane dane, w rejestrze CMAR.
  • Ustawienia adresu rejestru danych nadajnika USART w rejestrze CMAR.
  • Ustawienia liczby transmitowanych bajtów w rejestrze CNDTR.
  • Ustawienia trybu transmisji oraz uaktywnienia kanału DMA poprzez zapis do rejestru sterującego kanału CCR bitów: inkrementacji adresu pamięci – MINC, kierunku transmisji – z pamięci do modułu peryferyjnego – DIR oraz aktywacji kanału – EN.

Z powodu wymagań standardu ANSI języka C zapis rejestrów adresowych kanału DMA następuje w funkcji main – umieszczenie go w tablicy inicjowania peryferiali spowodowałoby wygenerowanie przez kompilator ostrzeżeń o użyciu stałych wbrew standardowi.

Moduł DMA po zakończeniu transmisji zachowuje początkowe wartości rejestrów adresowych, ale wymaga zapisu długości bloku danych przed każdą transmisją. Dodatkowo rejestr długości bloku może być zapisany tylko wówczas, gdy kanał jest nieaktywny, a kanał nie deaktywuje się samoczynnie po zakończeniu transmisji bloku. Z tych powodów w procedurze obsługi przrwania timera, w miejscu, gdzie następuje uaktywnienie kanału i rozpoczęcie transmisji znalazła się sekwencja trzech instrukcji:

  • deaktywacji kanału – zapis 0 do CCR,
  • zapisu długości bloku do CNDTR,
  • powtórnej aktywacji kanału – zapis słowa sterującego do CCR.

 

Obsługa przerwania timera

Przerwanie timera generującego przebiegi PWM sterujące diodami jest zgłaszane tak samo, jak we wcześniejszych projektach, z częstotliwością 400 Hz. Jego obsługa obejmuje kilka czynności, wykonywanych z różnymi częstotliwościami:

  • płynną modyfikację wypełnień PWM dla diod – 400 Hz;
  • reakcję na przycisk zerowania timera – 100 Hz;
  • zliczanie czasu w timerze programowym i błyskanie zieloną diodą – 1 Hz.

W celu uniknięcia konwersji wartości timera z postaci binarnej na reprezentację znakową, wartość ta jest przechowywana w postaci znakowej, w tablicy znakowej timer[]. Tablica ta, o długości 8 bajtów, zawiera na pozycjach 0..5 sześć cyfr timera, a na pozycjach 6 i 7 – sekwencję końca wiersza, przesyłaną wraz z wartością timera przez łącze szeregowe.

Blok reakcji na naciśnięcie przycisku jest wywoływany, tak jak poprzednio, w co czwartym przerwaniu. W bloku tym sprawdzany jest stan przycisku oraz inkrementowany jest licznik sdiv, zliczający setne części sekundy. Przy naciśnięciu przycisku zerowany jest timer odliczający sekundy oraz licznik setnych części sekundy. Dodatkowo jest zaświecana niebieska dioda; jej wygaszanie rozpoczyna się po upływie ? sekundy po naciśnięciu przycisku. Po zinkrementowaniu licznika setnych do wartości 100 jest on zerowany oraz następuje inkrementacja timera programowego. Inkrememtacja ta jest wykonywana w prostej pętli for(). Zarówno inkrementacja, jak i wyzerowanie licznika kończy się ustawieniem zmiennej logicznej upd_time, co w dalszej części przerwania timera powoduje zainicjowanie transmisji całego bufora timera programowego przez USART.

 

Grzegorz Mazur

gbm@ii.pw.edu.pl

 

 

f

Do pobrania

Autor: