- Usuwanie zadania we FreeRTOS Arduino
- Jaka jest kolejka we FreeRTOS?
- Tworzenie kolejki w FreeRTOS
- Schemat obwodu
- Implementacja kolejki FreeRTOS w Arduino IDE
W poprzednim samouczku przedstawiliśmy FreeRTOS w Arduino Uno i stworzyliśmy zadanie dla migającej diody LED. Teraz, w tym samouczku, zajmiemy się bardziej zaawansowanymi koncepcjami interfejsów API RTOS i nauczymy się komunikacji między różnymi zadaniami. Tutaj również dowiadujemy się o kolejce do przesyłania danych z jednego zadania do drugiego i demonstrujemy działanie interfejsów API kolejki poprzez połączenie 16x2 LCD i LDR z Arduino Uno.
Zanim przejdziemy do omówienia kolejek, zobaczmy jeszcze jedno FreeRTOS API, które jest pomocne w usuwaniu zadań po zakończeniu przydzielonej pracy. Czasami zadanie wymaga usunięcia, aby zwolnić przydzieloną pamięć. Kontynuując poprzedni kurs, użyjemy funkcji API vTaskDelete () w tym samym kodzie, aby usunąć jedno z zadań. Zadanie może użyć funkcji API vTaskDelete () do usunięcia siebie lub dowolnego innego zadania.
Aby korzystać z tego interfejsu API, należy skonfigurować plik FreeRTOSConfig.h . Ten plik jest używany do dostosowania FreeRTOS do aplikacji. Służy do zmiany algorytmów planowania i wielu innych parametrów. Plik można znaleźć w katalogu Arduino, który jest ogólnie dostępny w folderze Dokumenty na komputerze. W moim przypadku jest dostępny w \ Documents \ Arduino \ libraries \ FreeRTOS \ src, jak pokazano poniżej.
Teraz otwórz ten plik za pomocą dowolnego edytora tekstu i szukać na #define INCLUDE_vTaskDelete i upewnij się, że jego wartość wynosi „1” (1 oznacza włączyć i 0 oznacza wyłączenie). Domyślnie jest to 1, ale sprawdza.
Będziemy często używać tego pliku konfiguracyjnego w naszych następnych samouczkach do ustawiania parametrów.
Zobaczmy teraz, jak usunąć zadanie.
Usuwanie zadania we FreeRTOS Arduino
Aby usunąć zadanie, musimy skorzystać z funkcji API vTaskDelete (). Wystarczy jeden argument.
vTaskDelete (TaskHandle_t pxTaskToDelete);
pxTaskToDelete: Jest to uchwyt zadania, które ma zostać usunięte. Jest taka sama, jak 6 -tego argumentu xTaskCreate () API. W poprzednim samouczku ten argument ma wartość NULL, ale można przekazać adres zawartości zadania przy użyciu dowolnej nazwy. Powiedzmy, że chcesz ustawić uchwyt zadania dla Task2, który jest zadeklarowany jako
TaskHandle_t any_name; Przykład: TaskHandle_t xTask2Handle;
Teraz, w vTaskCreate () API zestaw 6 th argument jako
xTaskCreate (TaskBlink2, "task2", 128, NULL, 1, & xTask2Handle);
Dostęp do zawartości tego zadania można teraz uzyskać za pomocą podanego przez Ciebie uchwytu.
Ponadto zadanie może się usunąć, przekazując NULL w miejsce prawidłowego uchwytu zadania.
Jeśli chcemy usunąć Zadanie 3 z samego Zadania 3, musimy napisać vTaskDelete (NULL); wewnątrz funkcji Task3, ale jeśli chcesz usunąć zadanie 3 z zadania 2, napisz vTaskDelete (xTask3Handle); wewnątrz funkcji task2.
W poprzednim kodzie samouczka, aby usunąć Task2 z samego zadania2, po prostu dodaj vTaskDelete (NULL); in void Funkcja TaskBlink2 (void * pvParameters) . Wtedy powyższa funkcja będzie wyglądać tak
void TaskBlink2 (void * pvParameters) { Serial.println („Zadanie2 jest uruchomione i wkrótce zostanie usunięte”); vTaskDelete (NULL); pinMode (7, WYJŚCIE); while (1) { digitalWrite (7, WYSOKI); vTaskDelay (300 / portTICK_PERIOD_MS); digitalWrite (7, LOW) vTaskDelay / portTICK_PERIOD_MS (300); } }
Teraz prześlij kod i obserwuj diody LED i monitor szeregowy. Zobaczysz, że druga dioda LED nie miga teraz, a zadanie 2 jest usuwane po napotkaniu funkcji usuwania API.
Więc ten interfejs API może służyć do zatrzymania wykonywania określonego zadania.
Teraz zacznijmy od kolejki.
Jaka jest kolejka we FreeRTOS?
Kolejka to struktura danych, która może pomieścić skończoną liczbę elementów o stałym rozmiarze i działa w schemacie FIFO (First-in First-out). Kolejki zapewniają mechanizm komunikacji typu zadanie-zadanie, zadanie-przerwanie i przerwanie-zadanie.
Maksymalna liczba elementów, które może przechowywać kolejka, nazywana jest jej „długością”. Zarówno długość, jak i rozmiar każdego elementu są ustawiane podczas tworzenia kolejki.
Przykład wykorzystania kolejki do przesyłania danych został dobrze zilustrowany w dokumentacji FreeRTOS, którą można znaleźć tutaj. Podany przykład można łatwo zrozumieć.
WcześniejszePo zapoznaniu się z kolejkami spróbujmy zrozumieć proces tworzenia kolejki i spróbujmy zaimplementować go w naszym kodzie FreeRTOS.
Tworzenie kolejki w FreeRTOS
Najpierw opisz problem, który ma zostać zaimplementowany za pomocą kolejki FreeRTOS i Arduino Uno.
Chcemy wydrukować wartość czujnika LDR na wyświetlaczu LCD 16 * 2. Więc teraz są dwa zadania
- Zadanie 1 pobiera analogowe wartości LDR.
- Zadanie 2 drukuje wartość analogową na wyświetlaczu LCD.
Więc tutaj kolejka odgrywa swoją rolę, ponieważ przesyła dane wygenerowane przez zadanie 1 do zadania 2. W zadaniu1 wyślemy wartość analogową do kolejki, aw zadaniu2 otrzymamy ją z kolejki.
Istnieją trzy funkcje do pracy z kolejkami
- Tworzenie kolejki
- Wysyłanie danych do kolejki
- Odbieranie danych z kolejki
Aby utworzyć kolejkę, użyj funkcji API xQueueCreate (). Wymaga dwóch argumentów.
xQueueCreate (UBaseType_t uxQueueLength, UBaseType_t uxItemSize);
uxQueueLength: maksymalna liczba elementów, które tworzona kolejka może jednocześnie pomieścić.
uxItemSize: rozmiar w bajtach każdego elementu danych, który może być przechowywany w kolejce.
Jeśli ta funkcja zwróci NULL, kolejka nie zostanie utworzona z powodu niewystarczającej ilości pamięci, a jeśli zwróci wartość inną niż NULL, kolejka zostanie utworzona pomyślnie. Zapisz tę wartość zwracaną w zmiennej, aby użyć jej jako uchwytu w celu uzyskania dostępu do kolejki, jak pokazano poniżej.
QueueHandle_t queue1; queue1 = xQueueCreate (4, sizeof (int));
Spowoduje to utworzenie 4-elementowej kolejki w pamięci sterty o rozmiarze int (2 bajty każdego bloku) i zapisanie wartości zwracanej w zmiennej uchwytu queue1 .
2. Wysyłanie danych do kolejki we FreeRTOS
Aby wysłać wartości do kolejki, FreeRTOS ma do tego celu 2 warianty API.
- xQueueSendToBack (): Służy do wysyłania danych na koniec (koniec) kolejki.
- xQueueSendToFront (): Służy do wysyłania danych na początek (początek) kolejki.
Teraz , xQueueSend () jest równoważne, a dokładnie taka sama jak xQueueSendToBack ().
Wszystkie te interfejsy API mają 3 argumenty.
xQueueSendToBack (QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait);
xQueue: uchwyt kolejki, do której dane są wysyłane (zapisywane). Ta zmienna jest taka sama, jak używana do przechowywania wartości zwracanej przez interfejs API xQueueCreate.
pvItemToQueue: wskaźnik do danych, które mają zostać skopiowane do kolejki.
xTicksToWait: maksymalny czas, przez jaki zadanie powinno pozostawać w stanie Zablokowane, aby czekać na zwolnienie miejsca w kolejce.
Ustawienie xTicksToWait na portMAX_DELAY spowoduje, że zadanie będzie czekać w nieskończoność (bez przekroczenia limitu czasu), pod warunkiem, że INCLUDE_vTaskSuspend jest ustawiona na 1 w FreeRTOSConfig.h , w przeciwnym razie możesz użyć makra pdMS_TO_TICKS () do konwersji czasu określonego w milisekundach na czas określony w tickach.
3. Odbieranie danych z kolejki we FreeRTOS
Aby otrzymać (odczytać) element z kolejki, używana jest metoda xQueueReceive (). Otrzymana pozycja jest usuwana z kolejki.
Ten interfejs API również przyjmuje trzy argumenty.
xQueueReceive (QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait);
Pierwszy i trzeci argument są takie same, jak wysyłanie API. Tylko drugi argument jest inny.
const pvBuffer: Wskaźnik do pamięci, do której zostaną skopiowane odebrane dane.
Mam nadzieję, że rozumiesz trzy interfejsy API. Teraz zaimplementujemy te API w Arduino IDE i spróbujemy rozwiązać opisany powyżej problem.
Schemat obwodu
Tak to wygląda na płytce stykowej:
Implementacja kolejki FreeRTOS w Arduino IDE
Zacznijmy pisać kod dla naszej aplikacji.
1. Najpierw otwórz Arduino IDE i dołącz plik nagłówkowy Arduino_FreeRTOS.h . Teraz, jeśli używany jest jakikolwiek obiekt jądra, taki jak queue, dołącz jego plik nagłówkowy. Ponieważ używamy wyświetlacza LCD 16 * 2, dołącz również bibliotekę.
#include #include
2. Zainicjuj uchwyt kolejki, aby zapisać zawartość kolejki. Zainicjuj także numery pinów wyświetlacza LCD.
QueueHandle_t queue_1; LiquidCrystal lcd (7, 8, 9, 10, 11, 12);
3. W void setup (), zainicjalizuj LCD i monitor szeregowy z szybkością transmisji 9600. Utwórz kolejkę i dwa zadania za pomocą odpowiednich interfejsów API. Tutaj utworzymy kolejkę o rozmiarze 4 z typem całkowitoliczbowym. Stwórz zadanie o równych priorytetach, a później spróbuj grać z tą liczbą. Na koniec uruchom program planujący, jak pokazano poniżej.
void setup () { Serial.begin (9600); lcd.begin (16, 2); queue_1 = xQueueCreate (4, sizeof (int)); if (queue_1 == NULL) { Serial.println ("Nie można utworzyć kolejki"); } xTaskCreate (TaskDisplay, "Display_task", 128, NULL, 1, NULL); xTaskCreate (TaskLDR, "LDR_task", 128, NULL, 1, NULL); vTaskStartScheduler (); }
4. Teraz utwórz dwie funkcje TaskDisplay i TaskLDR . W funkcji TaskLDR odczytaj analogowy pin A0 w zmiennej, ponieważ mamy LDR podłączone do pinu A0 Arduino UNO. Teraz wyślij wartość przechowywaną w zmiennej, przekazując ją do interfejsu API xQueueSend i wyślij zadanie do stanu blokowego po 1 sekundzie za pomocą funkcji API vTaskDelay (), jak pokazano poniżej.
void TaskLDR (void * pvParameters) { int current_intensity; while (1) { Serial.println ("Zadanie1"); current_intensity = analogRead (A0); Serial.println (current_intensity); xQueueSend (queue_1, & current_intensity, portMAX_DELAY); vTaskDelay (1000 / portTICK_PERIOD_MS); } }
5. Podobnie utwórz funkcję dla TaskDisplay i odbierz wartości ze zmiennej, która jest przekazywana do funkcji xQueueReceive . Ponadto xQueueReceive () zwraca pdPASS, jeśli dane mogą zostać pomyślnie odebrane z kolejki i zwraca errQUEUE_EMPTY, jeśli kolejka jest pusta.
Teraz wyświetl wartości na wyświetlaczu LCD za pomocą funkcji lcd.print () .
void TaskDisplay (void * pvParameters) { int intensywność = 0; while (1) { Serial.println ("Zadanie2"); if (xQueueReceive (queue_1, & intensywność, portMAX_DELAY) == pdPASS) { lcd.clear (); lcd.setCursor (0, 0); lcd.print ("Intensywność:"); lcd.setCursor (11, 0); lcd.print (intensywność); } } }
Otóż to. Zakończyliśmy kodowanie w implementacji kolejki. Kompletny kod z działającym filmem można znaleźć na końcu.
Teraz podłącz LCD i LDR do Arduino UNO zgodnie ze schematem obwodu, wgraj kod. Otwórz monitor szeregowy i obserwuj zadania. Zobaczysz, że zadania się przełączają, a wartości LDR zmieniają się w zależności od intensywności światła.
UWAGA: Większość bibliotek utworzonych dla różnych czujników nie jest obsługiwana przez jądro FreeRTOS z powodu implementacji funkcji opóźnienia wewnątrz bibliotek. Opóźnienie powoduje całkowite zatrzymanie procesora, dlatego jądro FreeRTOS również przestaje działać, a kod nie będzie dalej wykonywany i zaczyna działać nieprawidłowo. Musimy więc sprawić, by biblioteki działały z FreeRTOS bez opóźnień.