- Удаление задачи в FreeRTOS Arduino
- Что такое очередь в FreeRTOS?
- Создание очереди в FreeRTOS
- Принципиальная электрическая схема
- Реализация очереди FreeRTOS в среде Arduino IDE
В предыдущем уроке мы представили FreeRTOS в Arduino Uno и создали задачу для мигающего светодиода. Теперь в этом руководстве мы подробнее рассмотрим передовые концепции API-интерфейсов RTOS и узнаем о взаимодействии между различными задачами. Здесь мы также узнаем об очереди для передачи данных от одной задачи к другой и продемонстрируем работу API-интерфейсов очередей путем взаимодействия ЖК-дисплея 16x2 и LDR с Arduino Uno.
Прежде чем обсуждать очереди, давайте рассмотрим еще один API FreeRTOS, который помогает удалять задачи, когда он завершает назначенную работу. Иногда задачу нужно удалить, чтобы освободить выделенную память. В продолжение предыдущего урока мы будем использовать функцию API vTaskDelete () в том же коде для удаления одной из задач. Задача может использовать функцию API vTaskDelete () для удаления себя или любой другой задачи.
Чтобы использовать этот API, вам необходимо настроить файл FreeRTOSConfig.h . Этот файл используется для настройки FreeRTOS в соответствии с приложением. Он используется для изменения алгоритмов планирования и многих других параметров. Этот файл можно найти в каталоге Arduino, который обычно доступен в папке Documents на вашем ПК. В моем случае он доступен в \ Documents \ Arduino \ libraries \ FreeRTOS \ src, как показано ниже.
Теперь откройте этот файл с помощью любого текстового редактора и поиск в #define INCLUDE_vTaskDelete и убедитесь, что его значение «1» (1 означает включение и 0 означает отключение). По умолчанию он равен 1, но проверяет его.
Мы будем часто использовать этот файл конфигурации в наших следующих руководствах для настройки параметров.
Теперь посмотрим, как удалить задачу.
Удаление задачи в FreeRTOS Arduino
Чтобы удалить задачу, мы должны использовать функцию API vTaskDelete (). Достаточно одного аргумента.
vTaskDelete (TaskHandle_t pxTaskToDelete);
pxTaskToDelete: это дескриптор задачи, которую нужно удалить. Это то же самое, как 6 - го аргумента xTaskCreate () API. В предыдущем руководстве этот аргумент был установлен как NULL, но вы можете передать адрес содержимого задачи, используя любое имя. Скажем, если вы хотите установить дескриптор задачи для Task2, который объявлен как
TaskHandle_t any_name; Пример: TaskHandle_t xTask2Handle;
Теперь в API vTaskCreate () установите 6- й аргумент как
xTaskCreate (TaskBlink2, «задача2», 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, ВЫХОД); в то время как (1) { digitalWrite (7, ВЫСОКИЙ); vTaskDelay (300 / портTICK_PERIOD_MS); digitalWrite (7, LOW); vTaskDelay (300 / portTICK_PERIOD_MS); } }
Теперь загрузите код и наблюдайте за светодиодами и последовательным монитором. Вы увидите, что второй светодиод не мигает, а задача 2 удаляется после обнаружения API удаления.
Таким образом, этот API можно использовать для остановки выполнения конкретной задачи.
Теперь давайте начнем с очереди.
Что такое очередь в FreeRTOS?
Очередь - это структура данных, которая может содержать конечное количество элементов фиксированного размера и работает по схеме FIFO (First-in First-out). Очереди обеспечивают механизм связи между задачами, задачами с прерываниями и прерываниями.
Максимальное количество элементов, которое может содержать очередь, называется ее «длиной». И длина, и размер каждого элемента устанавливаются при создании очереди.
Пример использования очереди для передачи данных хорошо проиллюстрирован в документации FreeRTOS, которую можно найти здесь. Вы легко можете понять данный пример.
Разобравшись с очередями, давайте попробуем разобраться в процессе создания очереди и попробуем реализовать его в нашем коде FreeRTOS.
Создание очереди в FreeRTOS
Сначала опишите постановку задачи, которую необходимо реализовать с помощью очереди FreeRTOS и Arduino Uno.
Мы хотим напечатать значение датчика LDR на ЖК-дисплее 16 * 2. Итак, теперь есть две задачи
- Задача 1 - получение аналоговых значений LDR.
- Задача 2 - это печать аналогового значения на ЖК-дисплее.
Итак, здесь очередь играет свою роль, потому что отправляет данные, сгенерированные task1, в task2. В задаче 1 мы отправим аналоговое значение в очередь, а в задаче 2 мы получим его из очереди.
Есть три функции для работы с очередями
- Создание очереди
- Отправка данных в очередь
- Получение данных из очереди
Для создания очереди используйте 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. Сначала откройте IDE Arduino и включите заголовочный файл Arduino_FreeRTOS.h . Теперь, если используется какой-либо объект ядра, такой как очередь, включите его заголовочный файл. Поскольку мы используем ЖК-дисплей 16 * 2, включите также и библиотеку для него.
#include #include
2. Инициализируйте дескриптор очереди для хранения содержимого очереди. Также инициализируйте номера контактов ЖК-дисплея.
QueueHandle_t queue_1; ЖК-дисплей LiquidCrystal (7, 8, 9, 10, 11, 12);
3. В режиме настройки void () инициализируйте ЖК-дисплей и последовательный монитор со скоростью 9600 бод. Создайте очередь и две задачи, используя соответствующие API. Здесь мы создадим очередь размером 4 с целочисленным типом. Создайте задачу с равными приоритетами, а затем попробуйте поиграть с этим числом. Наконец, запустите планировщик, как показано ниже.
void setup () { Serial.begin (9600); lcd.begin (16, 2); queue_1 = xQueueCreate (4, sizeof (число)); 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 = аналоговое чтение (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 интенсивность = 0; while (1) { Serial.println ("Задача2"); если (xQueueReceive (очередь_1, & интенсивность, portMAX_DELAY) == pdPASS) { lcd.clear (); lcd.setCursor (0, 0); lcd.print ("Интенсивность:"); lcd.setCursor (11, 0); lcd.print (яркость); } } }
Вот и все. Мы закончили кодирование части реализации очереди. Полный код с рабочим видео можно найти в конце.
Теперь подключите ЖК-дисплей и LDR к Arduino UNO в соответствии со схемой, загрузив код. Откройте серийный монитор и наблюдайте за задачами. Вы увидите, что задачи переключаются, а значения LDR меняются в зависимости от интенсивности света.
ПРИМЕЧАНИЕ. Большинство библиотек, созданных для различных датчиков, не поддерживаются ядром FreeRTOS из-за реализации функции задержки внутри библиотек. Задержка приводит к полной остановке ЦП, поэтому ядро FreeRTOS также перестает работать, и код больше не выполняется, и оно начинает некорректно работать. Итак, мы должны сделать библиотеки без задержек для работы с FreeRTOS.