Программирование AVR Многозадачность FreeRTOS на платформе Arduino Fri, December 06 2024  

Поделиться

Нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.


Многозадачность FreeRTOS на платформе Arduino Печать
Добавил(а) microsin   

Операционная система реального времени (ОСРВ) FreeRTOS получила порт, оптимизированный под среду разработки Arduino IDE. В это трудно поверить, но разработчики обещают полный доступ ко всем возможностям FreeRTOS в классическом рабочем окружении Arduino.

Уже довольно давно Arduino IDE получила драйверы и библиотеки, позволяющие писать программы для довольно мощных процессоров ARM (не только для AVR). Однако стандартное приложение Arduino было ограничено простыми примитивами функций setup (первоначальная настройка) и loop (бесконечный цикл выполняющегося алгоритма), что не давало способа эффективно реализовать многозадачность.

В этой статье (перевод [1]) дан краткий обзор простой и удобной реализации FreeRTOS, которая достаточно компактна, чтобы работать в среде Arduino IDE как её библиотека, и позволить пользователям прозрачно использовать как функции Arduino, так и FreeRTOS. Все непонятные термины, касающиеся систем RTOS, см. в статье [10] (раздел "Словарик" в конце этой статьи).

[Немного теории]

Большинство операционных систем спроектированы таким образом, что позволяют сразу нескольким программам или потокам работать одновременно (даже на одном процессоре). Эта функция операционной системы называется многозадачностью (multi-tasking). Пользователю кажется, что все запущенные задачи работают одновременно, но если заглянуть глубже, то каждое процессорное ядро в любой момент времени реально обрабатывает только одну задачу (программу или поток). Специальная часть операционной системы, которая называется планировщиком (scheduler), отвечает за распределение процессорного времени между задачами. Т. е. планировщик решает, какая программа и в какой момент должна переключиться на выполнение своей задачи. Такое переключение процессорного ядра между программами (потоками) может происходить достаточно быстро, чтобы создать иллюзию одновременной работы сразу нескольких программ.

Планировщик в Операционной Системе Реального Времени (ОСРВ; соответствующий английский термин Real Time Operating System, RTOS) разработан так, чтобы изначально предсказуемо распределить процессорное время между задачами, со строго определенным временем реакции всей системы на внешние события [2]. Обычно такое свойство описывают как детерминистский механизм поведения (deterministic execution pattern). В частности это свойство затребовано и в малых встраиваемых системах, таких как устройства Arduino, поскольку к таким устройствам предъявляются требования обработки алгоритмов в реальном времени.

Традиционные планировщики реального времени (real time scheduler), такие как шедулер FreeRTOS, реализуют свой детерминизм, назначая определенный приоритет каждому выполняемому потоку. Тогда шедулер использует этот приоритет, чтобы узнать, какому потоку в какой момент времени следует передать процессорное время (т. е. какой поток и когда должен продолжить выполнять свой алгоритм). В системе FreeRTOS выполняемый поток обозначается термином Task (задача).

[Быстрый старт]

Проще всего начать с использования Codebender [3], где Arduino FreeRTOS доступна как библиотека [4].

Другой способ, процесс по шагам:

1. Откройте менеджер библиотек Arduino IDE (Arduino IDE Library, доступный с версии Arduino 1.6.8), найдите в списке FreeRTOS Library (тип библиотеки Type: "Contributed" и тема Topic: "Timing").

Поиск библиотеки FreeRTOS в Arduino Library Manager

2. Убедитесь, что установлена самая свежая версия FreeRTOS Library Release. На момент написания статьи [1] это была версия v9.0.0-1.

Библиотека FreeRTOS v8.2.3 Release 6 установлена

3. Убедитесь с помощью меню Sketch -> Include Library, что библиотека FreeRTOS подключена к Вашему скетчу. Новый, пустой скетч будет выглядеть примерно так:

Пустой скетч с подключенной FreeRTOS

4. Скомпилируйте (Compile) и выгрузите (Upload) этот пустой скетч в свою плату Arduino (Arduino Uno, Arduino Yun, Arduino Leonardo, Arduino Mega 2560, Goldilocks 1284p и т. п.). Это покажет Вам, сколько памяти процессора Arduino уйдет на шедулер FreeRTOS. Ниже для примера приведена информация по использованию памяти на разных платах (код был скомпилирован под управлением Arduino v1.6.9 на Windows 10).

Плата Ардуино:   loop() -> FreeRTOS | Использовано памяти программ
Uno                 444 ->   7340   |     21%
Goldilocks          502 ->   7408   |      6%
Leonardo           3624 ->  10508   |     24%
Yun                3618 ->  10502   |     24%
Mega 2560           656 ->  24108   |      9%

На этом шаге FreeRTOS уже работает на Вашем устройстве Arduino.

[Что дальше?]

Теперь выгрузите в плату и протестируйте скетч Blink с нижележащей RTOS путем вставки #include < Arduino_FreeRTOS.h > в начало скетча. Это все, что нужно для запуска FreeRTOS в Ваших скетчах.

Следующий шаг - рассмотреть возможности профессиональной RTOS в среде Arduino IDE.

Скетч Blink_AnalogRead.ino (см. врезку ниже) это хороший способ начать комбинировать в одном скетче базовые примеры Arduino, такие как Blink и AnalogRead, с распределением их функционала на разные задачи (Tasks). Обе эти задачи будут выполнять свои функции, под управлением шедулера FreeRTOS. Этот скетч можно найти в папке Examples каталога установки Arduino IDE.

В этом примере два базовых скетча из встроенных в Arduino IDE примеров (Examples) скомбинированы в один многозадачный скетч, в котором работают 2 задачи FreeRTOS.

#include <Arduino_FreeRTOS.h>

// Определим две задачи для алгоритмов Blink и AnalogRead:
void TaskBlink( void *pvParameters );
void TaskAnalogRead( void *pvParameters );

// Функция setup запустится один раз, когда Вы нажмете кнопку
// сброса или подадите питание на плату.
void setup() {

   // Теперь создадим две задачи, чтобы они работали независимо
   // друг от друга:
   xTaskCreate(
      TaskBlink
      ,  (const portCHAR *)"Blink"  // Это просто любое имя, удобное
                                    // для чтения человеком.
      ,  128                        // Размер стека задачи
      ,  NULL
      ,  2                          // Приоритет задачи.
      ,  NULL );

   xTaskCreate(
      TaskAnalogRead
      ,  (const portCHAR *) "AnalogRead"
      ,  128                        // Этот размер стека может быть проверен
                                    // и подстроен путем чтения Highwater.
      ,  NULL
      ,  1                          // Приоритет задачи.
      ,  NULL );

   // Теперь автоматически и неявно для пользователя запустится scheduler,
   // который возьмет на себя управление планированием запуска отдельных задач.
}

void loop()
{
   // Эта функция пуста. Вся полезная работа теперь осуществляется
   // в запущенных задачах (Tasks). Эти задачи реализованы функциями
   // TaskBlink и TaskAnalogRead.
}

/*--------------------------------------------------*/
/*---------------------- Tasks ---------------------*/
/*--------------------------------------------------*/

void TaskBlink(void *pvParameters)
{
  (void) pvParameters;

   // Инициализация цифрового вывода 13 в режиме выхода.
   pinMode(13, OUTPUT);

   for (;;) // A Task shall never return or exit.
   {
      digitalWrite(13, HIGH);    // включение светодиода LED
      vTaskDelay( 1000 / portTICK_PERIOD_MS ); // ожидание в 1 секунду
      digitalWrite(13, LOW);     // выключение светодиода LED
      vTaskDelay( 1000 / portTICK_PERIOD_MS ); // ожидание в 1 секунду
   }
}

void TaskAnalogRead(void *pvParameters)
{
  (void) pvParameters;

   // Инициализация последовательного обмена данными на скорости
   // 9600 бит в секунду:
   Serial.begin(9600);

   for (;;)
   {
      // Чтение входа аналогового вывода 0:
      int sensorValue = analogRead(A0);
      // Вывод на печать прочитанного значения:
      Serial.println(sensorValue);
      // Задержка в 1 тик (15 мс) между чтениями, для стабильности:
      vTaskDelay(1);
   }
}

Если Вас интересуют приложения с низким энергопотреблением или с питанием от батарей, то можно просто использовать FreeRTOS для поддержки режимов пониженного энергопотребления микроконтроллеров AVR ATmegaXXXX (AVR ATmega power reduction modes). Подробнее см. [5].

Другие статьи рассматривают использование семафоров (Semaphore) для защиты аппаратных ресурсов [6] (наподобие последовательного порта Serial), очередей (Queue) для обмена данными между задачами (Tasks), или таймеров (Timer), чтобы генерировать задержки и отслеживать таймауты. Страничка с руководством для быстрого освоения FreeRTOS [7] даст Вам дополнительную информацию по продвинутым функциям, и предоставит демонстрационные примеры [8]. Также есть много примеров кода AVR FreeRTOS на сайте GitHub, которые могут быть легко портированы в среду разработки Arduino.

[Ссылки]

1. Using FreeRTOS multi-tasking in Arduino site:arduino.cc.
2. FreeRTOS: практическое применение, часть 1 (управление задачами).
3. We are codebender site:codebender.cc.
4. Arduino_FreeRTOS site:codebender.cc.
5. Arduino FreeRTOS для устройств с питанием от батарей.
6. Использование семафоров FreeRTOS в Arduino IDE.
7. FreeRTOS Quick Start Guide site:freertos.org.
8. FreeRTOS Demo Applications site:freertos.org.
9. AVR ATmega port of freeRTOS site:github.com.
10FreeRTOS: практическое применение, дополнения, словарик.

 

Добавить комментарий


Защитный код
Обновить

Top of Page