ISIX-RTOS. Wymiana danych pomiędzy wątkami

ISIX-RTOS. Wymiana danych pomiędzy wątkami
Pobierz PDF Download icon
W artykule przedstawiamy komunikację pomiędzy procesami ISIX-RTOS z wykorzystaniem wątków, na przykładzie prostej aplikacji dla zestawu STM32Butterfly. Jej działanie przejawia się miganiem diody LED (jeden wątek) oraz wyświetlaniem grafik odpowiadających kierunkom wychylania dźwigni joysticka zamontowanego na płytce STM32Butterfly (drugi wątek).
93ELEKTRONIKA PRAKTYCZNA 7/2010 Wymiana danych pomiędzy wątkami 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: 11825, pass: 81036471 Dodatkowe materiały na CD i FTP W  przykładzie zastosowano wyświet- lacz LCD z telefonu Nokia 3310 zamontowa- ny na module KAmodLCD1 (fotografia  1). Dołączono go do płytki STM32Butterfly w  sposób pokazany na rysunku  2. Dane przesyłane przez sprzętowy interfejs SPI1, linie sterujące pracą wyświetlacza LCD do- łączono bezpośrednio do innych linii GPIO mikrokontrolera. Cykliczne miganie LED jest realizowane przez wątek pracujący niezależnie od reszty aplikacji. Część odpowiedzialną za wyświet- lanie komunikatów po wciśnięciu pozycji joysticka zrealizowano jako dwa oddziel- ne wątki: wątek serwera wyświetlania oraz wątek obsługi joysticka. Zadaniem wątku serwera wyświetlania jest odbiór rozkazów poprzez kolejkę FIFO z  innych wątków, zwanych dalej klientami serwera wyświetla- nia, ich interpretację i  fizyczne sterowanie wyświetlacza LCD. Wątek obsługi joysticka z punktu widzenia serwera wyświetlania jest klientem. Jego zadaniem jest odczyt stanu styków joysticka oraz, w wyniku interpreta- cji stanu styków joysticka, wysyłanie odpo- wiednich rozkazów do serwera wyświetla- ISIX-RTOS Wymiana danych pomiędzy wątkami nia, np. wypisania tekstu. Aplikacja zo- stała napisana w  C++. Hierarchię klas projektu przedstawiono na rysunku 3. Obiekt the_application jest głów- nym obiektem, którego instancja sta- tyczna tworzona jest w  funkcji głównej main() ? listing 1. Klasa the_application zawiera kla- sy odpowiedzialne za realizację poszczególnych zadań. W klasie tej tworzone są pojedyncze instancje klas: led_ blink, display_ server, graph_ key. Dzięki zawar- ciu aplikacji w  oddzielnej klasie mamy możliwość utworzenia instancji klasy w dowolny sposób, np. na stercie, na stosie czy statycznie, bez ko- nieczności zmiany pozostałych części. Klasa ledblink odpowiedzialna jest za miganie diodą LED i  jest dziedziczona z  obiektu isix::task_base implementującej obsługę wątków. Implementacja klasy jest praktycznie identyczna jak w  poprzednio omówionym przykładzie dotyczącym diod LED, dlatego zostanie tutaj pozostawiona bez szerszego komentarza. Miganie diodami LED odbywa się niezależnie od pozostałej części aplikacji, więc klasa ta nie komunikuje się z innymi obiektami. Fotografia 1. Wygląd modułu KAmod- LCD1 Rysunek 2. Sposób dołączenia wyświetlacza KAmodLCD1 do płytki STM32Butterfly Klasa display_server odpowiedzialna jest bezpośrednio za odbiór komunikatów z  kolejki FIFO oraz wyświetlanie ich na wyświetlaczu. Klasa display_msg jest klasą bazową dla klas text_msg oraz klasy graph_ Notatnik konstruktora W  artykule przedstawiamy komunikację pomiędzy procesami ISIX-RTOS z  wykorzystaniem wątków, na przykładzie prostej aplikacji dla zestawu STM32Butterfly. Jej działanie przejawia się miganiem diody LED (jeden wątek) oraz wyświetlaniem grafik odpowiadających kierunkom wychylania dźwigni joysticka zamontowanego na płytce STM32Butterfly (drugi wątek). 94 ELEKTRONIKA PRAKTYCZNA 7/2010 Notatnik konstruktora Rysunek 3. Hierarchia klas przykładu Listing 1. //App main entry point int main() { //Application object static app::the_application application; //Start scheduler isix::isix_start_scheduler(); } Listing 2. //Images namespace namespace images { //Image definition from pure C a SPGL library. struct img_def { int width; int height; const unsigned char *data; }; } Listing 3. //Nokia display class class nokia_display { public: //Constructor nokia_display(); //Put char void put_char(char code); //Set position void set_position(unsigned x, unsigned y); //Put string void put_string(const char *str, unsigned x, unsigned y); //Put bitmap void put_bitmap(const images::img_def &image); //Clear display void clear(); private: inline void dc_pin(bool en); inline void res_pin(bool en); inline void sel_pin(bool en); void hw_init(); void hw_spi_write(unsigned char byte); void lcd_init(); void busy_delay(unsigned delay); }; Listing 4. Konstruktor klasy nokia_display odpowiedzialny za inicjalizację obiektu //Display constructor nokia_display::nokia_display() { //Initialize hardware hw_init(); //Initialize LCD lcd_init(); } 95ELEKTRONIKA PRAKTYCZNA 7/2010 Wymiana danych pomiędzy wątkami msg, dzięki czemu do kolejki FIFO możemy przekazywać zarówno wskaźniki do obiek- tów klas text_msg (komunikat tekstowy), jak i  display_msg (komunikat graficzny). Klasa text_msg jest prostym wrapperem przecho- wującym wskaźnik do C-stringu const char*, natomiast klasa display_msg jest wrappe- rem przechowującym strukturę img_def (li- sting 2). Struktura ta pochodzi bezpośrednio z bi- blioteki graficznej SPGL (napisanej w ?czy- stym? C) i przechowuje informację na temat bitmap zawartych w  pamięci Flash mikro- kontrolera. Wygenerowanie odpowiednich struktur umożliwia narzędzie konwertera umożliwiającego tworzenie na podstawie plików graficznych plików wynikowych *.c zawierających definicję obrazków. Wszystkie wygenerowane bitmapy zawarte w projekcie znajdują się w  pliku images.cpp, natomiast ich definicje zawarte są w pliku images.hpp i zostały zawarte w przestrzeni nazw images. Klasa nokia_display implementuje fi- zyczną obsługę wyświetlacza graficznego z  telefonu NOKIA-3310. Deklaracja klasy zawarta jest w  pliku nokia_display.hpp (li- sting 3). Klasa zawiera metody zapewniające mechanizmy podstawowej obsługi wyświet- lacza, takie jak wyświetlanie tekstu, wyświe- tlanie bitmap czy ustawienie kursora wy- świetlacza na zadanej pozycji. Konstruktor klasy (listing 4) jest odpowiedzialny za ini- cjalizację obiektu. Wywołuje on metodę prywatną hw_ init(), która jest odpowiedzialna za inicjali- zację układów peryferyjnych mikrokontrole- ra (portów GPIO oraz SPI1). Następnie jest wywoływana metoda prywatna lcd_init(), odpowiedzialna za fizyczną inicjalizację wy- Listing 5. //Write character at current position void nokia_display::put_char(char code) { std::size_t code_point; //Data dc_pin(1); //Char in array calculation code_point = static_cast(code) * 6; //Write character. sel_pin(0); hw_spi_write(cg_rom[code_point++]); hw_spi_write(cg_rom[code_point++]); hw_spi_write(cg_rom[code_point++]); hw_spi_write(cg_rom[code_point++]); hw_spi_write(cg_rom[code_point++]); hw_spi_write(cg_rom[code_point]); //Write end sel_pin(1); } Listing 6. //Set cursor at selected pos (14 cols, 6 rows) void nokia_display::set_position(unsigned x, unsigned y) { x=x*6; //Write activation sel_pin(0); //Instr dc_pin(0); hw_spi_write(y|0x40); //set row position hw_spi_write(x|0x80); //set col position // Write stop sel_pin(1); dc_pin(1); } Listing 7. Metoda wirtualna main() klasy display_server //Main thread void display_server::main() { const display_msg *msg = NULL; for(;;) { //Try read message from the fifo if(io_fifo.pop(msg) == isix::ISIX_EOK) { //Validate message if( msg ) { //Clear display display.clear(); //If text message if(msg->get_type()==display_msg::MSG_TEXT) { //Cast to the text message class const text_msg *t = static_cast(msg); if(t->get_text()) { //Write text on the display display.put_string(t->get_text(),t- >get_x(),t->get_y()); } } //If graphics message else if(msg->get_type()==display_msg::MSG_ GRAPHICS) { //Get image const images::img_def *img = static_cast(msg)->get_image(); if(img) { //Display bitmap display.put_bitmap(*img); } } } } } } świetlacza oraz zerowanie pamięci obrazu. Metoda put_char() odpowiada za wypisanie pojedynczego znaku na wyświetlaczu LCD (listing 5). Kontroler wyświetlacza nie ma wbudo- wanego generatora znaków, dlatego tablica znaków została utworzone w kodzie progra- mu, gdzie pojedynczy znak zajmuje 6 bajtów pamięci Flash. Na podstawie numeru po- rządkowego znaku wyznaczany jest począ- tek znaku w tablicy znaków, a następnie po- szczególne bajty znaku przepisywane są do pamięci obrazu wyświetlacza. Wyświetlacz domyślnie jest skonfigurowany w trybie po- ziomym, więc zapisanie każdego bajtu powo- duje zapalenie lub zgaszenie 8 pionowych pikseli. Ustawienie wyświetlania na zadanej pozycji umożliwia metoda set_position(unsi- gned x, unsigned y) ? listing 6. Ustawienie wybranej pozycji kursora w pamięci obrazu sprowadza się do wysłania do wyświetlacza komendy Set Row Position oraz Set Col Position. Działanie klasy dis- play_server (wątku serwera) sprowadza się do odczytywania komunikatów (wskaźników do klas komunikatów) z kolejki FIFO, a na- stępnie odpowiednie wykorzystanie metod klasy nokia_display do sterowania wyświet- laczem. Zadanie to realizowane jest przez metodę wirtualną main() klasy display_ser- ver (listing 7). W pętli głównej wątku wywoływana jest metoda pop() obiektu kolejki FIFO (io_fifo). Metoda ta blokuje wykonywanie wątku do momentu odebrania od innego zadania ko- munikatu przekazanego za pomocą meto- dy push(). W  przypadku, gdy kolejka FIFO zawiera jakiś komunikat, wówczas metoda pop() wybudza wątek i zwraca kod błędu isix ::ISIX_EOK. W przypadku odebrania prawi- dłowego komunikatu, zawartość wyświet- lacza LCD jest czyszczona metodą clear() obiektu wyświetlacza display, a  następnie sprawdzany jest typ przekazanego komuni- katu (obiektu) za pomocą metody get_type() 96 ELEKTRONIKA PRAKTYCZNA 7/2010 Notatnik konstruktora Listing 8. //Task/thread method void graph_key::main() { //Previous key variable static short p_key = -1; //Graphics message class static graph_msg gmsg; //Text message class static text_msg tmsg(?Wcisnales OK?); for(;;) { //Get key short key = get_key(); //Check if any key is pressed if(key!=0 && p_key==0) { switch(key) { case KEY_OK: disp_srv.send_message(tmsg); break; case KEY_LEFT: gmsg.set_image(images::isixlogo_bmp); disp_srv.send_message(gmsg); break; case KEY_RIGHT: gmsg.set_image(images::disk_bmp); disp_srv.send_message(gmsg); break; case KEY_UP: gmsg.set_image(images::printer_bmp); disp_srv.send_message(gmsg); break; case KEY_DOWN: gmsg.set_image(images::scriba_bmp); disp_srv.send_message(gmsg); break; } } //Previous key assignement p_key = key; //Wait short time isix::isix_wait( isix::isix_ms2tick(DELAY_TIME) ); } } Dodatkowe informacje na temat systemu ISIX-RTOS, nowości ze świata STM32 oraz prezentacje multimedialne są dostępne na stronie www.stm32.eu. klasy bazowej display_msg. Wykrywanie ty- pów klas można zrealizować mechanizmem RTTI (Run Time Type Information) i operato- rem typeid(), jednak z uwagi na dużą zaję- tość pamięci Flash mechanizm RTTI został wyłączony za pomocą flagi kompilatora -fno-rtti Jeżeli przekazany obiekt jest typu tekstowego (text_msg) wówczas jest on rzu- towany na wskaźnik do klasy text_msg, i na- stępnie wywoływane są metody pobierające wskaźnik do napisu oraz pozycję tekstu na ekranie. Następnie wywoływana jest metoda obiektu wyświetlacza (display), wypisująca tekst na wyświetlaczu LCD na zadanej pozy- cji. W  przypadku, gdy mamy do czynienia z  obiektem typu graficznego (graph_msg), wówczas jest on rzutowany na wskaźnik do tego obiektu, a  następnie wywoływana jest metoda pobierająca wskaźnik na struk- turę opisującą obrazek (img_def). Następnie wskaźnik ten jest przekazywany do metody put_bitmap() obiektu wyświetlacza, który jest odpowiedzialny za wyświetlenie bitma- py. Obiekt keys klasy graph_key jest odpo- wiedzialny za odczyt stanu joysticka oraz wysyłanie komend do serwera wyświetlania w zależności od kierunku przechylenia joy- sticka. Obiekt inicjalizowany jest za pomocą konstruktora, który jako argument przyjmuje referencję do klasy display_server. W  kon- struktorze inicjalizowane są porty GPIO, do których podłączono poszczególne styki joy- sticka. Odczyt stanu joysticka oraz wysyła- nie komunikatów jest realizowane przez me- todę główną wątku main() ? listing 8. Wykrywanie kierunku przechylenia joy- sticka jest realizowane w pętli głównej wąt- ku na zasadzie wykrywania zbocza na linii GPIO, do której dołączono styki joysticka. W przypadku wykrycia zbocza opadającego na którymś z wejść program ustala, jaki kie- runek wskazuje joystick, następnie do kolej- ki FIFO serwera wyświetlania jest przekazy- wany obiekt komunikatu metodą send_mes- sage(). Lucjan Bryndza, EP lucjan.bryndza@ep.com.pl R E K L A M A
Artykuł ukazał się w
Lipiec 2010
DO POBRANIA
Pobierz PDF Download icon
Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik wrzesień 2020

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio październik 2020

Świat Radio

Magazyn użytkowników eteru

Automatyka Podzespoły Aplikacje wrzesień 2020

Automatyka Podzespoły Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna wrzesień 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 wrzesień 2020

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów