Zostań w domu, zamów taniej!
Nie wychodź z domu i zamów online swoje ulubione pisma 20% taniej. Skorzystaj z kodu rabatowego: czytajwdomu

ISIX-RTOS. Obsługa portu szeregowego w STM32

ISIX-RTOS. Obsługa portu szeregowego w STM32
Pobierz PDF Download icon
W artykule prezentujemy sposób przygotowania uniwersalnego sterownika zapewniającego obsługę portu szeregowego mikrokontrolera STM32. Przyda się z pewnością w większości aplikacji, chociażby do tworzenia komunikatów diagnostycznych na etapie uruchamiania projektu.
98 ELEKTRONIKA PRAKTYCZNA 8/2010 Notatnik konstruktora Sterownik dla portu szeregowego przy- gotujemy z  wykorzystaniem systemu prze- rwań. W  przypadku urządzeń znakowych najbardziej odpowiednim będzie użycie kolejek FIFO, jednej nadawczej oraz drugiej odbiorczej. Ponieważ w kontekście przerwań nie możemy wywoływać funkcji blokują- cych, należy użyć specjalnych metod z przy- rostkiem _isr (push_isr(), pop_isr()) klasy fifo. Dodatkowe informacje: Artykuł poświęcony systemowi operacyjnemu ISIX-RTOS opublikowaliśmy w  EP 3/2010. Dodatkowe materiały na CD i  FTP: ftp://ep.com.pl, user: 10765, pass: 4t4q4glg Dodatkowe materiały na CD i FTPISIX-RTOS Obsługa portu szeregowego w STM32 W  artykule prezentujemy sposób przygotowania uniwersalnego sterownika zapewniającego obsługę portu szeregowego mikrokontrolera STM32. Przyda się z  pewnością w  większości aplikacji, chociażby do tworzenia komunikatów diagnostycznych na etapie uruchamiania projektu. Aby pokazać możliwości pracy wie- lowątkowej, w  przykładzie utworzymy dwa wątki: ? odbiorczy, służący do odbioru da- nych z  portu szeregowego, który w  zależności od odebranego znaku będzie sterował pracą diod LED: D1 i D2 zamontowanych na płytce STM- 32Butterfly, ? nadawczy, którego działanie sprowa- dzać się będzie do odczytania stanu joysticka oraz transmisję poprze UART informacji tekstowej o  jego a k t u a l n e j pozycji. Po podłączeniu zestawu STM32Butterfly do interfejsu RS232 komputera, w  celu przetestowania działania aplikacji należy uruchomić program terminalowy (np. Mi- Rysunek 1. Sposób działania aplikacji opisanej w artykule z podziałem na wątki Rysunek 2. Hierarchia klas aplikacji w przykładowym projekcie nicom, Hyperterminal itp.) oraz skonfigu- rować wybrany port szeregowy z następu- jącymi parametrami transmisji: prędkość ? 115200 b/s, liczba bitów danych: 8, 1 bit stopu, brak kontroli parzystości i  kontroli przepływu. Po zaprogramowaniu mikro- kontrolera, w  oknie terminala powinien pojawić się komunikat informujący o uru- chomieniu programu. Po wciśnięciu na kla- wiaturze PC klawisza A mamy możliwość włączenia diody LED D1 i jej wyłączenia za pomocą klawisza B. W podobny sposób mo- żemy sterować pracą diody LED D2 ? służą do tego celu klawisze C i  D. Przechylenie joysticka powoduje wyświetlenie informa- cji tekstowej o jego położeniu. Sposób dzia- łania aplikacji z podziałem na wątki przed- stawiono na rysunku 1. 99ELEKTRONIKA PRAKTYCZNA 8/2010 Obsługa portu szeregowego w STM32 Listing 1. Funkcja główna main //App main entry point int main() { //The application object static app::the_serialapp app; //Start the isix scheduler isix::isix_start_scheduler(); } Listing 2. Deklaracja klasy serialapp //The application class class the_serialapp { public: //App Constructor the_serialapp(): usart(USART2),ledrcv(usart),keytran(usart) {} private: //Serial device dev::usart_buffered usart; //The blinker class led_receiver ledrcv; //The key transmitter class key_transmitter keytran; }; w przypadku wykrycia odchylenia od poło- żenia standardowego ? wysłania informacji o  kierunku wychylenia jego osi. Hierarchię klas aplikacji przedstawiono na rysunku 2. Podobnie jak we wszystkich prezento- wanych przykładach, klasa the_serialapp jest klasą aplikacji przechowującą wszystkie obiekty. Statyczny obiekt tej klasy jest two- rzony w funkcji głównej main() (listing 1). Deklaracje klasy obiektu aplikacji przed- stawiono na listingu 2. Klasa the_serialapp, zawiera obiekt usart klasy led_receiver, która stanowi obiekt por- tu szeregowego RS232. Obiekt ledrcv klasy led_receiver odpowiedzialny jest za odbiór znaków z  portu szeregowego oraz sterowa- nie pracą LED w zależności od odebranego znaku. Obiekt keytran klasy key_transmitter odpowiedzialny jest za odczyt stanu sty- ków joysticka oraz wysyłanie informacji do portu. Oba obiekty przyjmują referencję do wspólnego obiektu klasy usart_buffered oraz dziedziczą z  klasy isix::task_base, więc sta- nowią odrębne wątki. Transmisja z  wyko- rzystaniem portu szeregowego RS232 jest dupleksowa. Ponieważ jeden wątek tylko od- czytuje dane z portu, natomiast drugi tylko zapisuje dane do tego portu, pracują one zu- pełnie niezależnie i nie wymagają wzajemnej synchronizacji za pomocą semafora, jak jest w  przypadku obsługi magistrali I2 C, która jest simpleksowa. Klasa usart_buffered jest uniwersalną klasą sterownika portu szerego- wego RS232 wykorzystującą sprzętowy port USART mikrokontrolera rodziny STM32. Klasa została napisana tak aby była możli- wość użycia dowolnego portu szeregowego dostępnego w  mikrokontrolerze. Deklaracja klasy znajduje się w  pliku i2c_host.cpp (li- sting 3). Klasa została zaprzyjaźniona z  funkcja- mi obsługi przerwań portów szeregowych, które zostały wcześniej zadeklarowane z lin- kowaniem typu C, co powoduje wyłączenie manglowania nazw. Funkcje obsługi prze- rwań są wywoływane przez kontroler sprzę- towy w  momencie wystąpienia przerwania bez dodatkowych parametrów, co wymusza istnienie dostępu do instancji klasy obsługu- jącej port szeregowy, poprzez wskaźnik lub referencję globalną. Wskaźniki dostępu do poszczególnych instancji klas przypisanych do portów szeregowych zostały umieszczo- ne w nienazwanej przestrzeni nazw w pliku implementacji (usart_buffered.cpp), przez co dostęp do wskaźników jest możliwy tylko w obrębie danego modułu. Zadeklarowanie przyjaźni funkcji z kla- są umożliwia wywołanie dowolnych metod z  funkcji zaprzyjaźnionej, co zostało wyko- rzystane do wywołania metody isr() stano- wiącej wektor obsługi przerwania. Klasa obsługi portu szeregowego zawiera dwa obiekty tx_queue, rx_queue (listing 4) klasy isix::fifo, które są wykorzystywane jako bu- Listing 3. Deklaracja klasy sterownika portu szeregowego class usart_buffered { friend void usart1_isr_vector(void); friend void usart2_isr_vector(void); public: enum parity //Baud enumeration { parity_none, parity_odd, parity_even }; //Constructor explicit usart_buffered( USART_TypeDef *_usart, unsigned cbaudrate = 115200, std::size_t queue_size=192, parity cpar=parity_none ); //Set baudrate void set_baudrate(unsigned new_baudrate); //Set parity void set_parity(parity new_parity); //Putchar int putchar(unsigned char c, int timeout=isix::ISIX_TIME_INFINITE) { int result = tx_queue.push( c, timeout ); start_tx(); return result; } //Getchar int getchar(unsigned char &c, int timeout=isix::ISIX_TIME_INFINITE) { return rx_queue.pop(c, timeout ); } //Put string into the uart int puts(const char *str); //Get string into the uart int gets(char *str, std::size_t max_len, int timeout=isix::ISIX_TIME_ INFINITE); private: static const unsigned IRQ_PRIO = 1; static const unsigned IRQ_SUB = 7; private: void start_tx(); void isr(); void irq_mask() { ::irq_mask(IRQ_PRIO, IRQ_SUB); } void irq_umask() { ::irq_umask(); } void periphcfg_usart1(bool is_alternate); void periphcfg_usart2(bool is_alternate); private: USART_TypeDef *usart; isix::fifo tx_queue; isix::fifo rx_queue; volatile bool tx_en; private: //Noncopyable usart_buffered(usart_buffered &); usart_buffered& operator=(const usart_buffered&); }; Aplikacja składa się z dwóch wątków, które używają jednego portu szerego- wego. Jeden wątek jest odpowiedzialny za odczyt danych z  portu szeregowego oraz włączanie i wyłączanie diod LED D1 i D2. Drugi wątek jest odpowiedzialny za cykliczny odczyt stanu joysticka oraz ? 100 ELEKTRONIKA PRAKTYCZNA 8/2010 Notatnik konstruktora Listing 4. Implementacja konstruktora klasy portu szeregowego /*----------------------------------------------------------*/ //! Constructor called for usart buffered usart_buffered::usart_buffered(USART_TypeDef *_usart, unsigned cbaudrate, std::size_t queue_size ,parity cpar ) : usart(_usart), tx_queue(queue_size), rx_queue(queue_size) , tx_en( false ) { if(_usart == USART1) { periphcfg_usart1(false); } else if(_usart == USART2) { periphcfg_usart2(true); } //Enable UART usart->CR1 = CR1_UE_SET; //Setup default baudrate set_baudrate( cbaudrate ); set_parity( cpar ); //One stop bit usart->CR2 = USART_StopBits_1; //Enable receiver and transmitter and anable related interrupts usart->CR1 |= USART_Mode_Rx |USART_RXNEIE | USART_Mode_Tx ; if( _usart == USART1 ) { usart1_obj = this; //Enable usart IRQ with lower priority nvic_set_priority( USART1_IRQn,IRQ_PRIO, IRQ_SUB ); nvic_irq_enable( USART1_IRQn, true ); } else if( _usart == USART2 ) { usart2_obj = this; //Enable usart IRQ with lower priority nvic_set_priority( USART2_IRQn,IRQ_PRIO, IRQ_SUB ); nvic_irq_enable( USART2_IRQn, true ); } } Listing 5. Definicja metody wysłania znaku do portu szeregowego int usart_buffered::putchar(unsigned char c, int timeout=isix::ISIX_TIME_ INFINITE) { int result = tx_queue.push( c, timeout ); start_tx(); return result; } Listing 6. Definicja metody odebrania znaku z portu szeregowego //Getchar int getchar(unsigned char &c, int timeout=isix::ISIX_TIME_INFINITE) { return rx_queue.pop(c, timeout ); } Listing 7. Implementacja metody obsługi przerwań klasy portu szeregowego void usart_buffered::isr() { uint16_t usart_sr = usart->SR; if( usart_sr & USART_RXNE ) { //Received data interrupt unsigned char ch = usart->DR; //fifo_put(&hwnd->rx_fifo,ch); rx_queue.push_isr(ch); } if(tx_en && (usart_sr&USART_TXE) ) { unsigned char ch; if( tx_queue.pop_isr(ch) == isix::ISIX_EOK ) { usart->DR = ch; } else { usart->CR1 &= ~USART_TXEIE; tx_en = false; } } } fory nadajnika oraz odbiornika. Konstruk- tor klasy przyjmuje cztery parametry: adres wybranego kontrolera portu szeregowego (np. USART1, USART2), prędkość transmisji z  ustawionym argumentem domyślnym na 115200, wielkość kolejek FIFO ustawionych domyślnie na 192 bajty oraz tryb kontroli parzystości z domyślnym argumentem usta- wionym na parity_none. Konstruktor odpowiedzialny jest za ini- cjalizację wybranego układu USART zgodnie z zadanymi parametrami. Na liście inicjali- zacyjnej konstruktora tworzone są obiekty kolejek FIFO o zadanej wielkości. Następnie w zależności od numeru portu szeregowego inicjalizowane są linie GPIO tak, aby pełniły funkcję obsługi układu peryferyjnego, oraz włączany jest wybrany USART, co realizowa- ne jest przez metody periphcfg_usart1(), oraz periphcfg_usart2(). Następnie włączany jest port szeregowy oraz jest konfigurowany po- dzielnik układu tak, aby pracował z zadaną prędkością poprzez wywołanie metody set_ baudrate(). W  następnej kolejności wywo- ływana jest metoda set_parity(), której zada- niem jest odpowiednie skonfigurowanie bitu parzystości. W zależności od wykorzystane- go układu USART, do wskaźników obiektów przypisanych do przerwania przypisywane są adresy obiektu, oraz w kontrolerze NVIC włączane są przerwania. Sterownik posiada dwie podstawowe metody interfejsu umoż- liwiające wysłanie oraz odbieranie znaków, które mogą być wykorzystane przez inne klasy. Metoda putchar() (listing 5), odpowie- dzialna za wysyłanie znaku do portu sze- regowego, przyjmuje dwa parametry: znak do wysłania, oraz maksymalny dopuszczal- ny czas oczekiwania, na miejsce w  kolejce FIFO. Działanie tej metody jest bardzo proste i sprowadza się do próby zapisania danych do kolejki, a  następnie wywołanie metody start_tx(), której zadaniem jest rozpoczęcie nadawania znaków. Pozostała część jest re- alizowana przez procedurę obsługi przerwa- nia. Metoda getchar() (listing 6) umożliwia odbieranie znaków z  portu szeregowego. Przyjmuje dwa argumenty: referencję do znaku oraz maksymalny czas oczekiwania na ten znak. Działanie tej metody sprowa- dza się jedynie do wywołania metody pop() kolejki odbiorczej. Jeżeli w  buforze jest jakiś znak umieszczony przez procedurę obsługi przerwania, wówczas następuje jego odczytanie. Jeżeli w  buforze nie ma ani jednego znaku następuje zablokowanie aktualnego wątku do momentu odebrania znaku. Cała praca realizowana jest głównie przez procedury obsługi przerwania, które są wywoływane w  momencie, gdy na wy- branym porcie szeregowym jest miejsce w  buforze nadawczym lub został odebrany jakiś znak. Zgłoszenie przerwania od dane- go portu szeregowego powoduje rozpoczęcie wykonania funkcji usart1_isr_vector() lub usart2_isr_vector(). W przypadku, gdy do wskaźnika danego portu szeregowego został przypisany jakiś obiekt, wówczas wywoływana jest metoda isr() ? listing 7 ? odpowiedzialna za realiza- cję procedury obsługi przerwania. Działanie procedury obsługi przerwa- nia jest bardzo proste: sprowadza się do odczytania statusu kontrolera USART oraz 101ELEKTRONIKA PRAKTYCZNA 8/2010 Obsługa portu szeregowego w STM32 Listing 8. Deklaracja klasy led_receiver //Serial receiver task class class led_receiver: public isix::task_base { public: //Constructor led_receiver(dev::usart_buffered &_serial); protected: //Main thread method virtual void main(); private: //Stack configuration static const unsigned STACK_SIZE = 256; static const unsigned TASK_PRIO = 3; //The usart obj ref dev::usart_buffered &serial; }; Listing 9. Implementacja metody main klasy led_receiver //Main task/thread function void led_receiver::main() { while(true) { unsigned char c; //Receive data from serial if(serial.getchar(c)==isix::ISIX_EOK) { //Check for received char switch(c) { //On led 1 case ?a?: case ?A?: io_clr( LED_PORT, LED1_PIN ); break; //Off led 1 case ?b?: case ?B?: io_set( LED_PORT, LED1_PIN ); break; //On led 2 case ?c?: case ?C?: io_clr( LED_PORT, LED2_PIN ); break; //Off led 2 case ?d?: case ?D?: io_set( LED_PORT, LED2_PIN ); break; } } } } wygenerowane przerwanie przy braku da- nych w buforze nadawczym, wówczas znak odczytywany jest z kolejki nadawczej za po- mocą nieblokującej metody pop_isr(), a  na- stępnie odczytany znak zapisywany jest do rejestru danych układu USART. W przypad- ku, gdy nie ma danych w kolejce zerowana jest flaga zgłoszenia przerwania Klasa led_receiver (listing 8) odpowie- dzialna jest za odbieranie danych z  portu szeregowego oraz sterowanie diodami LED w  zależności od kodu odebranego znaku. Klasa dziedziczy z klasy bazowej isix::task_ base. Klasa zawiera referencję do obiektu ste- rownika portu szeregowego usart_buffered. Realizacja zadania odbywa się w  metodzie wirtualnej main(), stanowiącą odrębny wą- tek systemowy ? listing 9. Działanie metody sprowadza się do wy- wołania metody getchar() obiektu sterowni- ka portu szeregowego, która blokuje się do momentu odebrania znaku. W  przypadku odebrania prawidłowego kodu znaku, od- powiednie diody LED są włączane lub wy- łączane. Na początku, za pomocą metody puts() sterownika portu szeregowego, wysyłane są teksty powitalne do portu szeregowego, a na- stępnie program wchodzi do pętli głównej. Pętla główna wykonywana jest cyklicznie z czasem DELAY_TIME (25 ms), co umożli- wia sprawdzenie stanu joysticka z elimina- cją drgań zestyków. W przypadku wykrycia zmiany stanu portów, sprawdzany jest nu- mer klawisza, a następnie za pomocą metody puts sterownika portu szeregowego wypisy- wane są komunikaty informujące o  pozycji joysticka. Lucjan Bryndza, EP lucck@boff.pl podjęciu odpowiedniej akcji. W  przypad- ku, gdy przerwanie zostało wygenerowane w wyniku odebrania znaku, wówczas jest on odczytywany z rejestru danych, a następnie przekazywany do kolejki. Do wysłania znaku do kolejki FIFO używana jest nieblokująca metoda push_isr(), dedykowana procedurom obsługi przerwań. W przypadku, gdy zostało R E K L A M A forum.ep.com.pl
DO POBRANIA
Pobierz PDF Download icon
Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik listopad 2020

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio grudzień 2020

Świat Radio

Magazyn krótkofalowców i amatorów CB

Automatyka Podzespoły Aplikacje listopad 2020

Automatyka Podzespoły Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna listopad 2020

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Praktyczny Kurs Elektroniki 2018

Praktyczny Kurs Elektroniki

24 pasjonujące projekty elektroniczne

Elektronika dla Wszystkich listopad 2020

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów