ISIX-RTOS – przykład 1 – sterowanie LED w wątkach
Opis systemu ISIX-RTOS i jego funkcji opublikowaliśmy w artykule „Mini system operacyjny dla STM32 – wprowadzenie”, który można przeczytać tu. |
Pokażemy w jaki sposób stworzyć kod startowy dla systemu oraz dwa nowe zadania (wątki). Prezentowany program umożliwia sterowanie diodami LED zamontowanymi w zestawie STM32Butterflyw następujący sposób: dioda LED1 miga cyklicznie, natomiast dioda LED2 jest włączana i wyłączana po wciśnięciu manipulatora joysticka.
Start systemu zrealizowany jest w bibliotece lib-stm32, odpowiedzialnej za inicjalizację systemu zgodnie z wymogami ANSI C/C++. Przed wywołaniem globalnych konstruktorów obiektów wywoływana jest funkcja __external_startup() następnie funkcja główna main().
Kod funkcji __external_startup pokazano na list. 1.
List. 1. Kod funkcji __external_startup
void __external_startup(void) { //Initialize system perhipheral uc_periph_setup(); //1 bit for preemtion priority nvic_priority_group(NVIC_PriorityGroup_1); //System priorities nvic_set_priority(PendSV_IRQn,1,0x7); //System priorities nvic_set_priority(SVCall_IRQn,1,0x7); //Set timer priority nvic_set_priority(SysTick_IRQn,1,0x7); //Initialize isix isix::isix_init(ISIX_NUM_PRIORITIES); //Setup the systick timer timer_setup(); }
Funkcja uc_periph_setup() jest odpowiedzialna za konfigurację kontrolera pamięci oraz ustawienie pętli PLL mikrokontrolera tak, aby rdzeń był taktowany z maksymalną dozwoloną częstotliwością (72 MHz). Następnie konfigurowany jest kontroler przerwań NVIC, w trybie jeden bit priorytetu przerwania oraz trzy bity podpriorytetu. Dodatkowo ustalono priorytet przerwań używanych przez system ISIX-RTOS (PENDSV, SVC, SYSTICK) na najniższy możliwy. Kolejna wywoływana funkcja isix_init jest odpowiedzialna za inicjalizacje systemu, jako argument przyjmuję ona liczbę priorytetów wykorzystywanych przez scheduler. Liczba dostępnych priorytetów jest dowolna (ograniczona wielkością dostępnej pamięci), w praktyce wystarcza najczęściej kilkanaście priorytetów. Następnie wywoływana jest funkcjatimer_setup(), która jest odpowiedzialna za konfigurację przerwania zegarowego SYSTICK, tak aby było ono generowane z częstotliwością ISIX_HZ. Wartość tej częstotliwości w przykładzie ustalono na 1000 Hz. Po wykonaniu funkcji startowej oraz wywołaniu konstruktorów globalnych, wykonywana jest funkcja główna main() (list. 2).
List. 2. Główna funkcja main()
//App main entry point int main() { //The blinker class static app::ledblink led_blinker; //The ledkey class static app::ledkey led_key; //Start the isix scheduler isix::isix_start_scheduler(); }
Najpierw jest tworzony obiekt klasy ledblink, której zadaniem jest cykliczne miganie diodą LED1 (patrz dokumentacja zestawu STM32Butterfly), następnie obiekt klasy ledkey, której zadaniem jest cykliczna zmiana stanu diody LED2 w wyniku naciśnięcia manipulatora joysticka. Następnie jest wywoływana funkcja isix_start_scheduler(), która powoduje uruchomienie szeregowania zadań przez system ISIX-RTOS. Obie klasy (ledblink, ledkey) dziedziczą z klasy bazowej isix::task_base. Każda klasa dziedzicząca z klasy task_base musi implementować metodę wirtualną main(), która jest wykonywana w oddzielnym wątku tworzonym przez konstruktor klasy task_base. Implementację klasy blinker odpowiedzialnej za cykliczne miganie diodą LED1 przedstawiono na list. 3.
List. 3. Implementacja klasy blinker
/* ------------------------------- */ //Default constructor, construct base object ledblink::ledblink():task_base( STACK_SIZE, TASK_PRIO) { //Enable PE in APB2 RCC->APB2ENR |= RCC_APB2Periph_GPIOE; io_config(LED_PORT,LED_PIN, GPIO_MODE_10MHZ, GPIO_CNF_GPIO_PP); } /* ---------------------------- */ //Main task/thread function void ledblink::main() { while(true) { //Enable LED io_clr( LED_PORT, LED_PIN ); //Wait time isix::isix_wait( isix::isix_ms2tick( BLINK_TIME) ); //Disable LED io_set( LED_PORT, LED_PIN ); //Wait time isix::isix_wait( isix::isix_ms2tick( BLINK_TIME) ); } }
W liście inicjalizacyjnej konstruktora wywoływany jest konstruktor klasy bazowej task_base(), który jest odpowiedzialny za tworzenie nowego zadania (wątku). Jako argumenty przyjmuje on rozmiar stosu zadania oraz jego priorytet. W konstruktorze jest także konfigurowany port GPIO PE.14, do którego w zestawie STM32Butterfly podłączono diodę LED1. Wątek realizowany jest w pętli nieskończonej metody wirtualnej main(). Wątek zmienia stan LED, a następnie wywołuje funkcję isix_wait(), której zadaniem jest uśpienie wątku na czas określony przez stałą BLINK_TIME.
Najistotniejsze fragmenty implementacji klasyledkey przedstawiono na list. 4.
List. 4. Fragmenty implementacji klasyledkey
/Default constructor initialize GPIO and var ledkey::ledkey():task_base( STACK_SIZE,TASK_PRIO), is_enabled(false) { //Enable PE in APB2 RCC->APB2ENR |= RCC_APB2Periph_GPIOE; io_config(LED_PORT,LED_PIN, GPIO_MODE_10MHZ, GPIO_CNF_GPIO_PP); } /* ------------------------------------ */ void ledkey::main() { //Last key state bool p_state = true; //Task/thread main loop while(true) { //Change state on rising edge if(io_get(KEY_PORT, KEY_PIN) && !p_state) { is_enabled = !is_enabled; } //Get previous state p_state = io_get(KEY_PORT, KEY_PIN); //If enabled change state if(is_enabled) io_clr( LED_PORT, LED_PIN ); else io_set( LED_PORT, LED_PIN ); //Wait short time isix::isix_wait( isix::isix_ms2tick( DELAY_TIME) ); } }
Podobnie jak poprzednio w liście inicjalizacyjnej jest wywoływany konstruktor klasy bazowej oraz inicjalizowane są porty GPIO. Wątek odpowiedzialny za wspomniane wcześniej zadanie zrealizowany jest przez metodę wirtualną main() w pętli nieskończonej. Zmiana stanu diody LED2 następuje w momencie puszczenia manipulatora joysticka (zbocze narastające na wejściu mikrokontrolera). Detekcję zrealizowano w sposób klasyczny przez porównanie bieżącego stanu klawisza ze stanem poprzednim. W momencie, gdy wykryjemy zbocze jest zmieniany stan zmiennej is_enabled, następnie na podstawie stanu zmiennej dioda LED2 jest włączana lub wyłączana. Na koniec cyklu wywoływana jest funkcja isix_wait() z argumentem DELAY_TIME (25 ms), która usypia bieżący wątek na krótki czas, tak aby wyeliminować drgania zestyków.