- Видалення завдання у FreeRTOS Arduino
- Що таке Черга у FreeRTOS?
- Створення черги у FreeRTOS
- Кругова діаграма
- Впровадження черги FreeRTOS в середовищі Arduino IDE
У попередньому підручнику ми представили FreeRTOS в Arduino Uno і створили завдання для миготливого світлодіода. Тепер, у цьому посібнику, ми детальніше заглибимось у попередні концепції RTOS API та дізнаємося про взаємодію між різними завданнями. Тут ми також дізнаємося про Чергу для передачі даних від одного завдання до іншого та продемонструємо роботу API черги шляхом взаємодії 16x2 LCD та LDR з Arduino Uno.
Перш ніж обговорювати черги, давайте подивимось ще один API FreeRTOS, який буде корисним для видалення завдань, коли воно закінчиться із призначеною роботою. Іноді завдання потрібно видалити, щоб звільнити виділену пам’ять. На продовження попереднього підручника ми використовуватимемо функцію API vTaskDelete () у тому самому коді, щоб видалити одне із завдань. Завдання може використовувати функцію API vTaskDelete () для видалення себе або будь-яке інше завдання.
Щоб використовувати цей API, вам потрібно налаштувати файл FreeRTOSConfig.h . Цей файл використовується для адаптації FreeRTOS відповідно до програми. Він використовується для зміни алгоритмів планування та багатьох інших параметрів. Файл можна знайти в каталозі Arduino, який зазвичай доступний у папці "Документи" на вашому ПК. У моєму випадку він доступний у \ Documents \ Arduino \ libraries \ FreeRTOS \ src, як показано нижче.
Тепер відкрийте цей файл з допомогою будь-якого текстового редактора і пошук в #define INCLUDE_vTaskDelete і переконайтеся, що його значення «1» (1 означає включення і 0 означає відключення). За замовчуванням воно дорівнює 1, але перевіряє його.
Ми будемо часто використовувати цей конфігураційний файл у наступних підручниках для встановлення параметрів.
Тепер давайте подивимося, як видалити завдання.
Видалення завдання у FreeRTOS Arduino
Щоб видалити завдання, ми повинні використовувати функцію API vTaskDelete (). Він приймає лише один аргумент.
vTaskDelete (TaskHandle_t pxTaskToDelete);
pxTaskToDelete: видаляється дескриптор завдання. Це те саме, що 6- й аргумент API xTaskCreate () . У попередньому навчальному посібнику цей аргумент встановлюється як NULL, але ви можете передати адресу вмісту завдання, використовуючи будь-яке ім'я. Скажімо, якщо ви хочете встановити дескриптор завдання для Task2, який оголошено як
TaskHandle_t any_name; Приклад: TaskHandle_t xTask2Handle;
Тепер у vTaskCreate () API встановіть 6- й аргумент як
xTaskCreate (TaskBlink2, "task2", 128, NULL, 1, & xTask2Handle);
Зміст цього завдання тепер можна отримати за допомогою дескриптора, наданого вами.
Крім того, завдання може видалити себе, передавши NULL замість дійсного дескриптора завдання.
Якщо ми хочемо видалити Завдання 3 із самого завдання 3, Вам потрібно написати vTaskDelete (NULL); усередині функції Task3, але якщо ви хочете видалити завдання 3 із завдання 2, тоді напишіть vTaskDelete (xTask3Handle); усередині функції task2.
У попередньому коді підручника, щоб видалити Task2 із самого task2, просто додайте vTaskDelete (NULL); у функції void TaskBlink2 (void * pvParameters) . Тоді вищевказана функція буде виглядати так
void TaskBlink2 (void * pvParameters) { Serial.println («Завдання 2 запущено і збирається видалити»); vTaskDelete (NULL); pinMode (7, ВИХІД); while (1) { digitalWrite (7, HIGH); vTaskDelay (300 / портTICK_PERIOD_MS); digitalWrite (7, LOW); vTaskDelay (300 / portTICK_PERIOD_MS); } }
Тепер завантажте код і спостерігайте за світлодіодами та послідовним монітором. Ви побачите, що другий світлодіод зараз не блимає, а task2 видаляється після зустрічі з API видалення.
Отже, цей API може бути використаний для зупинки виконання конкретного завдання.
Тепер почнемо з Черги.
Що таке Черга у FreeRTOS?
Черга - це структура даних, яка може містити кінцеву кількість елементів фіксованого розміру, і вона працює в схемі FIFO (First-in First-out). Черги забезпечують механізм зв'язку завдання до завдання, завдання до переривання та переривання до завдання.
Максимальна кількість елементів, яку може містити черга, називається її “довжиною”. І довжина, і розмір кожного елемента встановлюються при створенні черги.
Приклад використання черги для передачі даних добре проілюстровано в документації FreeRTOS, яку ви можете знайти тут. Ви легко можете зрозуміти наведений приклад.
UЗрозумівши Черги, спробуємо зрозуміти процес створення черги та спробувати реалізувати його в нашому коді FreeRTOS.
Створення черги у FreeRTOS
Спочатку опишіть постановку проблеми, яка повинна бути реалізована за допомогою черги FreeRTOS та Arduino Uno.
Ми хочемо надрукувати значення датчика LDR на РК-дисплеї 16 * 2. Тож зараз є два завдання
- Завдання 1 - отримання аналогових значень LDR.
- Завдання 2 друкує аналогове значення на РК-дисплеї.
Отже, тут чергу відіграє свою роль, оскільки надсилає дані, згенеровані task1, до task2. У task1 ми надішлемо аналогове значення в чергу, а в task2 - з черги.
Існує три функції для роботи з чергами
- Створення черги
- Надсилання даних до черги
- Отримання даних із черги
Для створення черги використовуйте API функції xQueueCreate (). Для цього потрібно взяти два аргументи.
xQueueCreate (UBaseType_t uxQueueLength, UBaseType_t uxItemSize);
uxQueueLength: Максимальна кількість елементів, яку може створювати створена черга в будь-який час.
uxItemSize: розмір у байтах кожного елемента даних, який можна зберігати в черзі.
Якщо ця функція повертає NULL, то черга не створюється через недостатню пам'ять, а якщо вона повертає значення, що не NULL, черга створюється успішно. Збережіть це повернене значення у змінну, щоб використовувати його як дескриптор для доступу до черги, як показано нижче.
QueueHandle_t queue1; queue1 = xQueueCreate (4, sizeof (int));
Це створить 4-елементну чергу в купі пам’яті розміру int (2 байти кожного блоку) і збереже значення, що повертається, до змінної дескриптора queue1 .
2. Надсилання даних до черги у FreeRTOS
Для надсилання значень до черги FreeRTOS має 2 варіанти API для цієї мети.
- xQueueSendToBack (): Використовується для надсилання даних у задню частину (хвіст) черги.
- xQueueSendToFront (): Використовується для надсилання даних на фронт (головку) черги.
Тепер , xQueueSend () еквівалентний, і точно так само, як, xQueueSendToBack ().
Всі ці API приймають 3 аргументи.
xQueueSendToBack (QueueHandle_t xQueue, const void * pvItemToQueue, TickType_t xTicksToWait);
xQueue: дескриптор черги, куди надсилаються (записуються) дані. Ця змінна така ж, як і для зберігання поверненого значення API xQueueCreate.
pvItemToQueue: вказівник на дані, які потрібно скопіювати в чергу.
xTicksToWait: Максимальна кількість часу, коли завдання повинно залишатися в заблокованому стані, щоб чекати, поки місце стане доступним у черзі.
Якщо встановити xTicksToWait на portMAX_DELAY , завдання буде чекати нескінченно довго (без тайм- ауту), за умови, що INCLUDE_vTaskSuspend встановлено на 1 у FreeRTOSConfig.h, інакше ви можете використовувати макрос pdMS_TO_TICKS () для перетворення часу, вказаного в мілісекундах, у час, зазначений у галочках.
3. Отримання даних із черги у FreeRTOS
Щоб отримати (прочитати) елемент із черги, використовується xQueueReceive (). Отриманий елемент видаляється з черги.
Цей API також приймає три аргументи.
xQueueReceive (QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait);
Перший і третій аргументи - це те саме, що надсилання API. Тільки другий аргумент відрізняється.
const pvBuffer: вказівник на пам'ять, в яку будуть скопійовані отримані дані.
Сподіваюся, ви зрозуміли три API. Тепер ми застосуємо ці API в середовищі IDE Arduino і спробуємо вирішити проблему, описану вище.
Кругова діаграма
Ось як це виглядає на макеті:
Впровадження черги FreeRTOS в середовищі Arduino IDE
Почнемо писати код для нашої програми.
1. Спочатку відкрийте Arduino IDE та включіть заголовочний файл Arduino_FreeRTOS.h . Тепер, якщо використовується будь-який об’єкт ядра, наприклад, черга, включіть його заголовочний файл. Оскільки ми використовуємо 16 * 2 РК-дисплей, включіть для нього і бібліотеку.
# включити # включити
2. Ініціалізуйте дескриптор черги, щоб зберегти вміст черги. Крім того, ініціалізуйте номери PIN-кодів РК-дисплея.
QueueHandle_t queue_1; Рідкий кристал рідкий (7, 8, 9, 10, 11, 12);
3. У разі порожнього налаштування () ініціалізуйте РК-дисплей та послідовний монітор зі швидкістю передачі 9600 бод. Створіть чергу та два завдання, використовуючи відповідні API. Тут ми створимо чергу розміром 4 із цілочисельним типом. Створіть завдання з однаковими пріоритетами, а пізніше спробуйте зіграти з цим номером. Нарешті, запустіть планувальник, як показано нижче.
void setup () { Serial.begin (9600); lcd.begin (16, 2); queue_1 = xQueueCreate (4, sizeof (int)); if (queue_1 == NULL) { Serial.println ("Чергу не можна створити"); } xTaskCreate (TaskDisplay, "Display_task", 128, NULL, 1, NULL); xTaskCreate (TaskLDR, "LDR_task", 128, NULL, 1, NULL); vTaskStartScheduler (); }
4. Тепер зробіть дві функції TaskDisplay і TaskLDR . У функції TaskLDR прочитайте аналоговий контакт A0 у змінній, оскільки LDR підключений до контакту A0 Arduino UNO. Тепер надішліть значення, що зберігається у змінній, передавши його в API xQueueSend і надішліть завдання блокувати стан через 1 секунду за допомогою API vTaskDelay (), як показано нижче.
void TaskLDR (void * pvParameters) { int current_intensity; while (1) { Serial.println ("Завдання1"); current_intensity = analogRead (A0); Serial.println (поточна_інтенсивність); xQueueSend (queue_1, & current_intensity, portMAX_DELAY); vTaskDelay (1000 / портTICK_PERIOD_MS); } }
5. Подібним чином створіть функцію для TaskDisplay і отримайте значення у змінній, яка передається функції xQueueReceive . Крім того, xQueueReceive () повертає pdPASS, якщо дані можуть бути успішно отримані з черги, і повертає errQUEUE_EMPTY, якщо черга порожня.
Тепер виведіть значення на РК-екран за допомогою функції lcd.print () .
void TaskDisplay (void * pvParameters) { int intenzitet = 0; while (1) { Serial.println ("Завдання2"); якщо (xQueueReceive (черга_1, & інтенсивність, портMAX_DELAY) == pdPASS) { lcd.clear (); lcd.setCursor (0, 0); lcd.print ("Інтенсивність:"); lcd.setCursor (11, 0); lcd.print (інтенсивність); } } }
Це воно. Ми закінчили частину кодування реалізації черги. Повний код із працюючим відео можна знайти в кінці.
Тепер підключіть РК-дисплей та LDR до Arduino UNO відповідно до схеми завантаження коду. Відкрийте послідовний монітор і спостерігайте за завданнями. Ви побачите, що завдання змінюються, а значення LDR змінюються залежно від інтенсивності світла.
ПРИМІТКА: Більшість бібліотек, створених для різних датчиків, не підтримуються ядром FreeRTOS через затримку реалізації функції всередині бібліотек. Затримка змушує CPU повністю зупинитися, отже, ядро FreeRTOS також перестає працювати, і код не буде виконуватися далі, і він почне працювати неправильно. Отже, ми повинні зробити бібліотеки без затримок для роботи з FreeRTOS.