Ардуино мультизадачность: выполняем несколько задач одновременно

Использование мультизадачности FreeRTOS на Arduino

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

Рассмотрим простую, легкую в использовании и надежную реализацию FreeRTOS, которую можно вставить в Arduino IDE в виде библиотеки, и которая позволяет беспрепятственно использовать лучшие части обоих сред.

Использование мультизадачности FreeRTOS в Arduino

Происхождение

Большинство операционных систем позволяют одновременно выполнять несколько программ или потоков. Это называется многозадачностью.

На самом деле, каждое ядро процессора в любой заданный момент времени может запускать только одну программу.

Часть операционной системы, называемая планировщиком, отвечает за принятие решения о том, какая программа должна выполняться, и обеспечивает иллюзию одновременного выполнения путем быстрого переключения между программами.

Планировщик в операционный системе реального времени (RTOS) предназначен для обеспечения предсказуемого (обычно описываемого как детерминированного) шаблона выполнения. Это особенно интересно для встраиваемых систем, таких как устройства Arduino, поскольку к встраиваемым системам часто предъявляются требования реального времени.

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

Давайте начнем

Во-первых, в менеджере библиотек Arduino IDE, начиная с версии 1.6.8, найдите библиотеку FreeRTOS под типом «Contributed» («Внесены») и темой «Timing» («Расчет времени»).

Поиск в менеджере библиотек Arduino

Убедитесь, что установлен последний релиз библиотеке FreeRTOS. В данном случае это v10.1.0-1.

FreeRTOS v8.2.3 Release 6 истановлена

Затем подключите библиотеку FreeRTOS либо через меню Скетч → Подключить библиотеку, либо добавьте следующую строку в начале скетча:

#include

Скомпилируйте и загрузите этот пустой скетч (с FreeRTOS) на устройство Arduino Uno/Yun/Leonardo/Mega. Это покажет вам, сколько флэш памяти потребляется планировщиком FreeRTOS. При тестировании с Arduino IDE v1.6.9 на Windows 10 было получено следующее:

Размер флэш-памяти, занимаемой FreeRTOS на разных платах Arduino

УстройствоПустой скетч только с loop()Пустой скетч с FreeRTOSДополнительный занимаемый объем памяти программ
Uno 444 7018 20%
Goldilocks 502 7086 5%
Leonardo 3618 10166 23%
Yun 3612 10160 23%
Mega 656 7086 2%

На этом этапе FreeRTOS уже запущена на вашем устройстве.

Следующие шаги

Теперь загрузите и протестируйте скетч Blink, используя операционную систему реального времени, просто вставив #include в начало скетча. Это всё, что нужно, чтобы FreeRTOS работала в ваших скетчах.

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

Blink_AnalogRead.ino – это хороший способ начать, поскольку он объединяет два базовых примера Arduino, Blink и AnalogRead, в один скетч с двумя отдельными задачами Task. Обе задачи выполняют свою работу, управляемые планировщиком FreeRTOS. Данный скетч может быть найден через меню Файл → Примеры → FreeRTOS.

#include // объявить две задачи для мигания Blink и чтения аналогового входа AnalogRead void TaskBlink( void *pvParameters ); void TaskAnalogRead( void *pvParameters ); // функция setup запускается единожды, когда вы нажимаете reset или включаете плату void setup() { // Теперь устанавливаем задачи для независимого запуска. xTaskCreate( TaskBlink , (const portCHAR *)”Blink” // Название только для людей , 128 // размер стека , NULL , 2 // приоритет , NULL ); xTaskCreate( TaskAnalogRead , (const portCHAR *) “AnalogRead” , 128 // этот размер стека может быть провере и подстроен с помощью чтения Highwater , NULL , 1 // приоритет , NULL ); // Теперь планировщик задач, берущий управление над планированием отдельных задач, // запускается автоматически. } void loop() { // Пусто. Всё выполняется в задачах. } /*—————————————————*/ /*———————- Задачи ———————*/ /*—————————————————*/ void TaskBlink(void *pvParameters) // Это задача. { (void) pvParameters; // настроить цифровой вывод 13 на выход. pinMode(13, OUTPUT); for (;;) // задача должна никогда не прекращаться или прерываться { digitalWrite(13, HIGH); // включить светодиод (HIGH – это уровень напряжения) vTaskDelay( 1000 / portTICK_PERIOD_MS ); // ждать одну секунду digitalWrite(13, LOW); // выключить светодиод, установив напряжение LOW vTaskDelay( 1000 / portTICK_PERIOD_MS ); // ждать одну секунду } } void TaskAnalogRead(void *pvParameters) // Это задача. { (void) pvParameters; // инициализировать последовательный порт на скорость 9600 бит/секунда: Serial.begin(9600); for (;;) { // прочитать вход на аналоговом выводе 0: int sensorValue = analogRead(A0); // напечатать прочитанное значение: Serial.println(sensorValue); vTaskDelay(1); // задержка один тик (15 мс) между чтениями (для стабильности) } }

Если вас интересуют устройства с малым энергопотреблением или работающие от аккумуляторов, то FreeRTOS легко использовать для поддержки режимов снижения энергопотребления AVR ATmega.

Следующие статьи буду посвящены использованию семафоров (Semaphore) для защиты аппаратных ресурсов (например, последовательного порта), очередей (Queue) для передачи данных между задачами (Task) или таймеров (Timer) для управления точными задержками и таймаутами.

Оригинал статьи:

  • Using FreeRTOS multi-tasking in Arduino

Источник: https://radioprog.ru/post/543

Делаем несколько дел одновременно-периодически

продолжение статьи МИГАЕМ СВЕТОДИОДОМ БЕЗ DELAY() или всегда ли официальный примеру учат “хорошему”.

Сразу оговорюсь, в рамках 8-ми битных контроллеров настоящей одновременности добится невозможно (только если какое-то действие будет выполняется независимым аппаратным блоком, например – генерация PWM). Но для подавляющего большинства задач – хватает “псевдо-одновременности”.

Когда контроллер по очереди “хватается” то за одно дело, то за другое. Если на каждом “деле” он не задерживается слишком на долго, то с точки зрения человека это выглядит как “одновременно”.

Типичный пример чем может одновременно заниматься контроллер:

  • мигать диодом (периодически переключать его состояние)
  • читать датчик температуры
  • слушать приходящие команды из Serial
  • сообщать о текущем состоянии переменных в Serial

Так как каждое из этих действий выполняется контроллером за микросекунды (миллисекунды в худшем случае), то пробегая раз за разом по этому списку – мы создадим иллюзию одновременности. Главное – не задерживаться на одном месте не долго. Скажем при мигании диодом – не использовать функцию delay()  (она полностью останавливает выполнение скетча на какое-то время).

В принципе, как обходится без delay() – мы уже умеем благодаря примеру [src=”blink…”]. Так же в прошлой статье [src….] мы детально разобрали стилистические ошибки этого примера и постарались его улучшить.

Так что сделать, по образу и подобию этого примера два одновременных периодических действия – не должно составить проблемы.

Просто заводим для каждого действия заводим свою переменную previousTime  (естественно имя чуть-чуть меняем) и оборачиваем действий в if(millis()-previousTime)

Скажем мы хотим мигать диодом раз в пол-секунды и, при этом выводить, раз в секунду, в Serial текущие значение millis()  (что-бы даже не видя ардуины физически иметь уверенность что скетч не завис).

Полностью по аналогии со скетчем из предыдущей статьи/* Blink And Print Without Delay 2013 by alxarduino@gmail.com http://alxarduino.blogspot.com/2013/09/BlinkAndPrintWithoutDelay.

html */ #define LED_PIN 13 // номер выхода,подключенного к светодиоду
#define BLINK_INTERVAL 5000UL // интервал между включение/выключением светодиода (5 секунд)
#define PRINT_INTERVAL 1000UL // периодичность вывода времени в Serial (1 cсекунда)
#define SERIAL_SPEED 9600 // скорость работы Serial void setup() { // задаем режим выхода для порта, подключенного к светодиоду pinMode(LED_PIN, OUTPUT); // задаем скорость работы ком-порта Serial.begin(SERIAL_SPEED); } void loop()
{ // мигаем диодом (периодически переключаем его состояние) static unsigned long prevBlinkTime = 0; // время когда последний раз переключали диод if(millis() – prevBlinkTime > BLINK_INTERVAL) { prevBlinkTime = millis(); // digitalWrite(LED_PIN,!digitalRead(LED_PIN)); } // периодически выводим millis() в Serial static unsigned long prevPrintTime=0; if(millis()-prevPrintTime>PRINT_INTERVAL){ prevPrintTime=millis(); Serial.print(“Current time:”); Serial.println(millis()); } }

Причесываем код

Но подобный, сильно большой loop() – признак плохого тона. Не хорошо когда функция имеет слишком много обязанностей. Пока у нас только два периодических действия, а если будет 10-ть? Получится “простыня” в которой трудно ориентироваться. Поэтому мы каждую “смысловую единицу” (мигание диодом и вывод времени) вынесем в отдельные функции и будем вызвать их из loop()

/* Blink And Print Without Delay 2013 by alxarduino@gmail.com http://alxarduino.blogspot.com/2013/09/BlinkAndPrintWithoutDelay.html */ #define LED_PIN 13 // номер выхода,подключенного к светодиоду
#define BLINK_INTERVAL 5000UL // интервал между включение/выключением светодиода (5 секунд)
#define PRINT_INTERVAL 1000UL // периодичность вывода времени в Serial (1 cекунда)
#define SERIAL_SPEED 9600 // скорость работы Serial void setup() { // задаем режим выхода для порта, подключенного к светодиоду pinMode(LED_PIN, OUTPUT); // задаем скорость работы ком-порта Serial.begin(SERIAL_SPEED); } void loop()
{ blinkLed(BLINK_INTERVAL); // мигаем printTime(PRINT_INTERVAL); // выводим время
} // мигает диодом с периодичностью interval
void blinkLed(unsigned long interval ){ static unsigned long prevTime = 0; // время когда последний раз переключали диод if(millis() – prevTime > interval) { prevTime = millis(); // digitalWrite(LED_PIN,!digitalRead(LED_PIN)); }
} // выводит в Serial время с периодичностью interval
void printTime(unsigned long interval){ static unsigned long prevTime=0; if(millis()-prevTime>interval){ prevTime=millis(); Serial.print(“Current time:”); Serial.println(millis()); }
}
Как видите, благодаря тому что мы, с помощью ключевого слово static, “спрятали” объявление prevTime внутрь функции (но при этом сохранили ее способность сохранять значение при выходе из функции подобно глобальной переменной) мы смогли внутри обеих функций (blinkLed и printTime) использовать одно и тоже имя переменной prevTime. Нам теперь не нужно хмурить мозг что-бы каждый раз придумывать новое уникальное имя переменное. Более того, у нас получилась как-бы “универсальная заготовка периодической функции”.// “заготовка/шаблон функции” которая периодически выполняет КАКОЕ-ТО-ДЕЙСТВИЕ
void somePeriodical(unsigned long interval){ static unsigned long prevTime=0; if(millis()-prevTime>interval){ prevTime=millis(); КАКОЕ-ТО-ДЕЙСТВИЕ; }
}
Теперь, когда нам понадобится занятся “еще чем-нибудь” время от времени. Мы возмем этот шаблон. Вместо имени somePeriodical дадим более осмысленное имя, впишем действие которое нам нужно, и вызовем эту функцию из loop()

Так же, если вы заметили, этот шаблон не содержит жестко прописанного интервала времени. Мы его “подаем снаружи” с помощью параметра функции. Это дает нам возможность, примеру, сам интервал мигания положить в какую-то переменную и менять его по мере надобности (ну скажем в зависимости от температуры). 

Или, мигать с разной частотой в зависимости от того нажата кнопка или нет. Примерно так:

void loop(){ if(digitalRead(PIN_BUTTON)) blinkLed(BLINK_INTERVAL) else blinkLed(BLINK_INTERVAL*2); // в два раза медленее …..
}
Тут мы мигаем либо с нормальной, либо с половинной частотой в зависимости от того HIGH или LOW уровень сейчас на пине PIN_BUTTON (нажата или нет кнопка) На сегодня все. В следующий раз рассмотрим попробуем “сделать себе удобно”. Посмотрим как можно сделать что-бы ради мелочи (например нам нужно периодически выводить значение какой-то переменной, причем код нужне “на 2 минуты”, в отладочных целях, потом его выкинем) не заводить целую специальную функцию, не выписывать “прицеп” из if-а и milis() и т.п.
P.S. Задать вопросы и почитать обсуждение вы можете либо в комментариях ниже, либо на сайте arduino.ru в ветке Еще раз мигаем светодиодом без Delay | Аппаратная платформа Arduino

UPD: Оформил этот прием в виде библиотеки. Про это следующая статья:  Мигаем без delay() с комфортом

Источник: http://alxarduino.blogspot.com/2013/09/BlinkAndPrintWithoutDelay.html

Ардуино: параллельное выполнение задач по таймеру

Множество уроков на Ардуино сводятся к последовательному выполнению каких-то действий. Например, чтобы заставить контроллер мигать светодиодом или периодически пищать зуммером, достаточно будет такой простой программы:

const int ledPin = 2; void setup() { pinMode(ledPin, OUTPUT); } void loop() { digitalWrite(ledPin, HIGH); delay(500); digitalWrite(ledPin, LOW); delay(500); }

Включаем светодиод, ждем пол секунды, затем выключаем его и снова ждем. Поскольку задача у нас одна, и кроме мигания мы ничего не собираемся делать, то в промежутках между включением и выключением используем функцию delay. Во время работы delay наш контроллер не делает ничего полезного. Он просто тратит время.

А что, если теперь мы захотим мигать одновременно двумя светодиодами с разным периодом? Допустим, первый светодиод должен включаться и выключаться каждую секунду, а второй каждые 300 миллисекунд.

Чтобы решить эту задачу, нам придется отказаться от функции delay. Это может быть тяжелым ударом для каждого новичка, но в серьезных программах delay никто не использует

Читайте также:  Карманный источник питания на 12 вольт своими руками

Источник: http://robotclass.ru/tutorials/arduino-parallel-events/

Arduino и переполнение счетчиков – мигаем светодиодом без delay

Новички, которые начинают изучать программирование микроконтроллеров Arduino (язык программирования Ардуино — Wiring, очень упрощенная версия C++), скорее всего, видели примеры, как помигать встроенным в микроконтроллер светодиодом.

Обычно, в этих примерах используется функция delay(), которая как бы “замораживает” микроконтроллер на время своего выполнения. И если Arduino должен обрабатывать не только задержку при мигании светодиодом (например с задержкой в 5 сек.

), а еще и нажатие на кнопку, то в момент выполнения функции delay() микроконтроллер может попросту “не заметить” нажатие на кнопку. Поскольку, обычно, в скетчах для Ардуино обрабатывается более 2 событий, использовать функцию delay() весьма не желательно.

Чем и как правильно заменить delay, рассказано ниже.

Все примеры, опубликованные в этой статье протестированы на микроконтроллере (далее МК) Arduino Mega 2560.

Arduino – мигаем светодиодом

// Включение светодиода на 1 секунду и отключение на тот же интервал в цикле   void setup()   {                 // Инициализируем цифровой вход/выход в режиме выхода.   // Выход 13 на большинстве плат Arduino подключен к светодиоду на плате.   pinMode(13, OUTPUT);     }   void loop()   {   digitalWrite(13, HIGH);   // Подаем напряжение на светодиод   delay(1000);              // Ожидаем 1 секунду   digitalWrite(13, LOW);    // Отключаем напряжение со светодиода   delay(1000);              // Ожидаем 1 секунду   }

Этот пример служит только для быстрого старта, чтобы бы можно было сразу увидеть результат программирования микроконтроллера, и не сильно вникать в тонкости программирования.

Проблемы начинаются тогда, когда от Arduino требуется выполнить несколько действий одновременно – функция delay() полностью “зависает” микроконтроллер на время своего выполнения, не позволяя параллельно обрабатывать, например, нажатия кнопок, показания датчиков и т.п.

Многозадачность Arduino

Для того, чтобы можно было выполнять несколько действий на Ардуино одновременно – следует воспользоваться таймерами. Пример вывода информации в Serial Monitor и параллельного мигания светодиодом без использования функции delay():

// http://petrenco.com/arduino.php #define LED_MK_PIN 13      // Номер выхода, к которому подключен внутренний светодиод #define LED_MK_INTERVAL 1000UL  // Интервал между включениея и выключения LED #define SERIAL_SPEED 115200 // Скорость работы порта Serial – то же число должно быть выбрано и в мониторе порта #define SERIAL_INTERVAL 3000UL  // Периодичность вывода времени в Serial (1 cсекунда) void setup()   {   pinMode(LED_MK_PIN, OUTPUT);             Serial.begin(SERIAL_SPEED);   }   void loop()   {   // Включение/отключение светодиода   static unsigned long led_mk_timer = 0;   if (millis() – led_mk_timer > LED_MK_INTERVAL)     {     led_mk_timer = millis();  //     digitalWrite(LED_MK_PIN, !digitalRead(LED_MK_PIN));     }       // Вывод данных в Serial с заданным интервалом   static unsigned long serial_timer = 0;   static byte test_value_1 = 5;   static byte test_value_2 = random(0, 255);   static byte test_value_result;   if (millis() – serial_timer > SERIAL_INTERVAL)     {     serial_timer = millis();           test_value_2 = random(0, 255);     test_value_result = test_value_1 – test_value_2;           Serial.print(“Current time:”);     Serial.println(millis());     Serial.print(“test_value_1: “);     Serial.print(test_value_1);     Serial.print(“; test_value_2: “);     Serial.print(test_value_2);     Serial.print(“; test_value_result (test_value_1 – test_value_2): “);     Serial.print(test_value_result);     Serial.println();     Serial.println();     }

  }

На самом деле, МК не имеет функций многозадачности, и не может параллельно/одновременно выполнять несколько различных действий.

Но благодаря тому, что каждое последовательное действие в примере выше происходит очень быстро, за несколько микросекунд (1 секунда = 1 000 000 микросекунд), с использованием таймеров можно добиться того, что будет казаться, как будто бы Ардуино выполняет несколько различных операций одновременно.

Если внимательно изучить код, то на первый взгляд может сложиться впечатление, что данные будут выводиться только в течении 4 294 967 295 миллисекунд, что приблизительно равно 50 дням. Ведь самый вместительный тип переменной unsigned long ограничен именно этим числом.

При достижении в функции mills() значения 4 294 967 295 (в ней так же используется тип unsigned long), на следующем шаге её значение будет сброшено в 0, и далее снова начнет увеличиваться от нуля.

Если применить всем известные математические правила к случаю, когда в serial_timer будет, например, через 50 дней записано значение 4 294 967 000, то после сброса mills() в ноль, строка if (millis() – serial_timer > SERIAL_INTERVAL) (например 5000 – 4 294 967 000 > 3000) может никогда не стать верной, соответственно, через 50 дней сообщения в Serial Monitor выводиться более не будут. Но то что кажется – не всегда верно, и вот почему.

Специально для того, чтобы объяснить, как будет работать таймер после сброса значения в ноль, в скрипте добавлены переменные: test_value_1, test_value_2, test_value_result.

Все они объявлены, как тип byte, в котором могут храниться только положительные числа от 0 до 255 (byte является всегда только unsigned, поэтому писать unsigned byte будет ошибкой).

Переменные объявлены как byte исключительно для облегчения восприятия, но то же самое будет происходить и с типом unsigned long. Теперь рассмотрим вывод данных и разберем, как так может получиться, что 5 минус 19 будет равно 242?

Current time:3001 test_value_1: 5; test_value_2: 19; test_value_result (test_value_1 – test_value_2): 242 Current time:6002 test_value_1: 5; test_value_2: 158; test_value_result (test_value_1 – test_value_2): 103 Current time:9003 test_value_1: 5; test_value_2: 38; test_value_result (test_value_1 – test_value_2): 223

Такая “странная”, на первый взгляд математика происходит из за того, что в переменной не могут содержаться отрицательные значения из-за её типа.

МК считает так (образно): поскольку 19 более 5, то от числа пять сначала отнимается пятерка, её же отнимаем от 19: 5 – 5 = 0, в остатке от 19 остается 14 (19 – 5 = 14).

Далее, от 0 отнимается единица, что дает максимальное значение этого типа переменной (byte) 255, в остатке остается 13. Отняв 13 от 255 (255 – 13) и получается 242!

Теперь разберем следующий пример, вычтем из трех восемь (3 – 8). Цифру восемь раскладываем на 3 + 1 + 4. Теперь считаем: 3 – 3 = 0, 0 – 1 = 255, 255 – 4 = 251.

Работа с таймерами, приведена для примера, существуют более красивые, в плане читаемости кода, решения: классы, функции, сторонние библиотеки и др., но принципы их работы основываются на том, что все переменные, используемые в таймерах, объявляются как unsigned.

Если после прочтения статьи останется что-то не понятно – спрашивайте в комментариях и смотрите статьи по ссылкам ниже.

Источник: https://petrenco.com/arduino.php?txt=193

Многозадачность FreeRTOS на платформе Arduino | avr | programming

Операционная система реального времени (ОСРВ) 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”).

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

3. Убедитесь с помощью меню Sketch -> Include Library, что библиотека 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 // Определим две задачи для алгоритмов 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); } }

Читайте также:  Подключаем к attiny85 питание от солнечной батареи

Источник: http://microsin.net/programming/avr/using-freertos-multi-tasking-in-arduino.html

Arduino delay millis и micros для организации задержки в скетче

Задержки в Ардуино играют очень большую роль. Без них не сможет работать даже самый простой пример Blink, который моргает светодиодом через заданный промежуток времени.

Но большинство начинающих программистов мало знают о временных задержках и используют только Arduino delay, не зная побочных эффектов этой команды.

В этой статье я подробно расскажу о временных функциях и особенностях их использования в среде разработки Arduino IDE.

Функции времени в Ардуино

В Arduino cуществует несколько различных команд, которые отвечают за работу со временем и паузы:

  • delay()
  • delayMicroseconds()
  • millis()
  • micros()

Они отличаются по точности и имеют свои особенности, которые стоит учитывать при написании кода.

Использование функции arduino delay

Синтаксис

Ардуино delay является самой простой командой и её чаще всего используют новички.

По сути она является задержкой, которая приостанавливает работу программы, на указанное в скобках число миллисекунд. (В одной секунде 1000 миллисекунд.

)  Максимальное значение может быть 4294967295 мс, что примерно ровняется 50 суткам. Давайте рассмотрим простой пример, наглядно показывающий работу этой команды.

void setup() { pinMode(13, OUTPUT);
} void loop() { digitalWrite(13, HIGH); // подаем высокий сигнал на 13 пин delay(10000); // пауза 10000мс или 10 секунд digitalWrite13, LOW); // подаем низкий сигнал на 13 пин delay(10000); // пауза 10000мс или 10 секунд
}

В методе setup прописываем, что пин 13 будет использоваться, как выход. В основной части программы сначала на пин подается высокий сигнал, затем делаем задержку в 10 секунд. На это время программа как бы приостанавливается. Дальше подается низкий сигнал и опять задержка и все начинается сначала. В итоге мы получаем, что на пин поочередно подается, то 5 В, то 0.

Нужно отчетливо понимать, что на время паузы с помощью delay работа программы приостанавливается, приложение не будет получать никаких данных с датчиков. Это является самым большим недостатком использования функции delay в Arduino. Обойти это ограничения можно с помощью прерываний, но об этом мы поговорим в отельной статье.

Пример delay с миганием светодиодом

Пример схемы для иллюстрации работы функции delay.
Можно построить схему со светодиодом и резистором. Тогда у нас получится стандартный пример – мигание светодиодом. Для этого на пин, который мы обозначили как выходной, необходимо подключить светодиод плюсовым контактом.

Свободную ногу светодиода через резистор приблизительно на 220 Ом (можно немного больше) подключаем на землю.  Определить полярность можно, если посмотреть на его внутренности. Большая чашечка внутри соединена с минусом, а маленькая ножка с плюсом.

Если ваш светодиод новый, то определить полярность можно по длине выводов: длинная ножка — плюс, короткая – минус.

Функция delayMicroseconds

Данная функция является полным аналогом delay за исключением того, что единицы измерения у нее не миллисекунды, а микросекунды (в 1 секунде – 1000000 микросекунд). Максимальное значение будет 16383, что равно 16 миллисекундам. Разрешение равно 4, то есть число будет всегда кратно четырем. Кусочек примера будет выглядеть следующим образом:

digitalWrite(2, HIGH); // подаем высокий сигнал на 2 пин delayMicroseconds(16383); // пауза 16383мкс digitalWrite(2, LOW); // подаем низкий сигнал на 2 пин delayMicroseconds(16383); // пауза 16383мкс

Проблема с delayMicroseconds точно такая же, как у delay — эти функции полностью «вешают» программу и она на некоторое время буквально замирает.

В это время невозможна работа с портами, считывание информации с датчиков и произведение математических операций.

 Для мигалок данный вариант подходит, но опытные пользователи не используют её для больших проектов, так как там не нужны такие сбои. Поэтому, гораздо лучше использовать функции, описанные ниже.

Функция millis вместо delay

Функция millis() позволит выполнить задержку без delay на ардуино, тем самым обойти недостатки предыдущих способов. Максимальное значение параметра millis такое же, как и у функции delay (4294967295мс или 50 суток). При переполнении значение просто сбрасывается в 0, не забывайте об этом.

С помощью millis мы не останавливаем выполнение всего скетча, а просто указываем, сколько времени ардуино должна просто «обходить» именно тот блок кода, который мы хотим приостановить. В отличие от delay millis сама по себе ничего не останавливает.

Данная команда просто возвращает нам от встроенного таймера микроконтроллера количество миллисекунд, прошедших с момента запуска. При каждом вызове loop Мы сами измеряем время, прошедшее с последнего вызова нашего кода и если разница времени меньше желаемой паузы, то игнорируем код.

Как только разница станет больше нужной паузы, мы выполняем код, получаем текущее время с помощью той же millis и запоминаем его — это время будет новой точкой отсчета.

В следующем цикле отсчет уже будет от новой точки и мы опять будем игнорировать код, пока новая разница millis и нашего сохраненного прежде значения не достигнет вновь желаемой паузы.

Задержка без delay с помощью millis требует большего кода, но с ее помощью можно моргать светодиодом и ставить на паузу скетч, не останавливая при этом систему. 

Вот пример, наглядно иллюстрирующий работу команды:

unsigned long timing; // Переменная для хранения точки отсчета
void setup() { Serial.begin(9600);
} void loop() {
/* В этом месте начинается выполнение аналога delay() Вычисляем разницу между текущим моментом и ранее сохраненной точкой отсчета. Если разница больше нужного значения, то выполняем код. Если нет – ничего не делаем */ if (millis() – timing > 10000){ // Вместо 10000 подставьте нужное вам значение паузы timing = millis(); Serial.println (“10 seconds”); }
}

Сначала мы вводим переменную timing, в ней будет храниться количество миллисекунд. По умолчанию значение переменной равно 0.

В основной части программы проверяем условие: если количество миллисекунд с запуска микроконтроллера минус число, записанное в переменную timing больше, чем 10000, то выполняется действие по выводу сообщения в монитор порта и в переменную записывается текущее значение времени.

В результате работы программы каждые 10 секунд в монитор порта будет выводиться надпись 10 seconds. Данный способ позволяет моргать светодиодом без delay.

Функция micros вместо delay

Данная функция так же может выполнить задержку, не используя команду delay. Она работает точно так же, как и millis, но считает не миллисекунды, а микросекунды с разрешением в 4мкс. Её максимальное значение 4294967295 микросекунд или 70 минут. При переполнении значение просто сбрасывается в 0, не забывайте об этом.

Резюме

Платформа Arduino предоставляет нам несколько способов выполнения задержки в своем проекте. С помощью delay вы можете быстро поставить на паузу выполнение скетча, но при этом заблокируете работу микроконтроллера.

Использование команды millis позволяет обойтись в ардуино без delay, но для этого потребуется чуть больше программировать. Выбирайте лучший способ в зависимости от сложности вашего проекта. Как правило, в простых скетчах и при задержке меньше 10 секунд используют delay.

Если логика работы сложнее и требуется большая задержка, то вместо delay лучше использовать millis.

Источник: https://ArduinoMaster.ru/program/arduino-delay-millis/

Таймеры и многозадачность на Ардуино

Сегодня мы поговорим о такой актуальной теме, как таймеры и организация многозадачности на Arduino. Поводом для написания этой статьи послужили лекции Олега Артамонова @olartamonov для студентов МИРЭА в рамках IoT Академии Samsung, а точнее, высказывание Олега, цитата (2-я лекция, 1:13:08):

Судя по высказываниям Олега, у него весьма превратное представление об Arduino вообще и об «ардуинщиках» в частности.

Мигание пятью светодиодами в означенных им режимах это абсолютно тривиальная задача для Arduino, а для Arduino Mega Server это вообще не задача, а сущее недоразумение — его штатными средствами организуется многозадачность, которая легко управляет сотнями различных сущностей (светодиодов, сервоприводов, шаговых моторов и т. д.) в реальном времени.

Давайте вместе разберёмся как организовать многозадачность на Arduino, а заодно поможем студентам МИРЭА избавится от навязанных им стереотипов восприятия по отношению к социо-культурному и технологическому феномену нашего времени под названием Arduino.

Лекции Олега Артамонова

Нужно отдать должное, сами лекции Олега хороши — в них даётся много полезной и хорошо структурированной информации о микроконтроллерах и я бы рекомендовал всем заинтересованным в этом вопросе с ними ознакомиться. Единственным недостатком этих лекций мне показался неприкрытый техно-снобизм в отношении Arduino, которая выступает в них в роли «мальчика для битья».

В частности, на протяжении всех лекций Олегом делаются безапелляционные заявления о непригодности Arduino для построения сложных многозадачных систем, что просто противоречит истине и реальной практике.

На Arduino можно делать потрясающие многозадачные системы в которых в (псевдо, естественно) многозадачном режиме одновременно работают десятки и сотни сущностей (светодиодов, датчиков, актуаторов, сервоприводов, шаговых моторов, беспроводных и проводных интерфейсов и т. д.).

Не будем далеко ходить за примерами. Вот проект Зимнего сада («Умной теплицы») в котором в реальном времени в многозадачном режиме работают следующие сущности:

Топология распределённого nRF24 контроллера с огромным числом подключённого и работающего в реальном времени оборудования. Пользователь имеет дело только с «базой», работа nRF24 партнёра полностью прозрачна для него. И, да, это Arduino.

На «базе»:

— 7 сервоприводов — 9 шаговых моторов — 6 реле — 3 датчика влажности почвы — 2 датчика освещённости — Датчик уровня воды

— Датчик влажности и температуры воздуха

На nRF24 удалённой части:

— 12 датчиков влажности почвы — 12 реле — 3 шаговых мотора — 2 датчика освещённости

— Датчик уровня воды

Кроме этого, в реальном времени функционирует собственно сама nRF24 связь между двумя распределёнными частями системы и Ethernet интерфейс сервера и серверный движок, обеспечивающий веб-интерфейс пользователя системы.

Итого, в реальном времени, в многозадачном режиме на 8-битной Меге функционирует как минимум 60 сущностей (и это не считая множества сервисов самой операционной системы AMS, с ними число сущностей приблизится к сотне). Что очевидным образом никак не согласуется с высказыванием о том, что «на Arduino невозможна настоящая многозадачность и мигать даже пятью светодиодами на ней проблематично».

Пара слов в защиту Arduino

(Хотя очевидно, что Arduino как социо-культурный и технологический феномен с многомиллионной армией поклонников и многими тысячами потрясающих проектов в защите не нуждается.)

Я много раз говорил и ещё раз повторю, что Arduino в своей софтверной составляющей это, по сути, просто один из возможных уровней абстракции (как и любой другой) со своими достоинствами и недостатками.

И пользователю нет абсолютно никакой разницы, что «крутится» внутри его маленького кусочка кремния — «чистая» Arduino, RTOS, RIOT OS, AMS или какая-то другая математическая абстракция представления и управления железными ресурсами контроллера.

Читайте также:  Ардуино почтовый ящик: отправляем e-mail, когда положили письмо

Пользователю важно решение его проблем — чтобы контроллер поливал растения, включал свет, управлял шторами и т. д. И основная проблема не в инструментах, используемых в разработке, а в умении ими пользоваться и, банально, в воображении и инженерном видинии самого разработчика.

Как это работает?

Сама по себе многозадачность на микроконтроллерах может быть организована разными способами, в данном случае речь пойдёт о самом простом — процессы по очереди получают управление и добровольно отдают его после использования своего кванта времени.

Этот способ, конечно, не лишён очевидных недостатков, но, как говорится, практика — критерий истины и он прекрасно зарекомендовал себя в реальных условиях: он используется как в стандартных дистрибутивах Arduino Mega Server, так и во множестве проектов на AMS Pro.

И эти системы работают в режиме 24/7 и имеют подтверждённые аптаймы во многие месяцы беспроблемной работы.

Это индикация около сотни сущностей распределённой nRF24 системы, управляемых независимо друг от друга в реальном времени. Обратите внимание на два последних индикатора «CPU» — при этом даже на 8-битной Меге загрузка процессора ровна нулю (то есть система полностью свободна).

Немного о таймерах

Для организации управления сложными системами недостаточно просто передавать по очереди управление между процессами и наряду с автоматической передачей управления в AMS используются различные виды таймеров: циклические, циклические с заданным количеством повторений (пакетные), одиночные, рандомные, смешанные и т. д. Всё это организовано нативными средствами Arduino и не использует прерывания или прямое программирование таймеров микроконтроллера (но прерывания, конечно же, использоваться системой «по их прямому назначению»).

Что опять же вступает в прямое противоречие с высказыванием «На 3 светодиода железных таймеров хватит, с дальше у ардуинщиков начнутся проблемы». Не начнутся. Нам доступны любые типы таймеров в любом количестве. И, при желании, мы можем наделать себе ещё сколько угодно новых и сколь угодно экзотических.

Основной кейс

Основной кейс при данном типе организации многозадачности — это создавать так называемый «неблокирующий» код, то есть код, который не использует функцию delay(), которая просто приостанавливает выполнение программы на заданное время.

Реальное время

Описываемый способ реализации многозадачности можно охарактеризовать как «soft-realtime», типовое время задержки в системе составляет 10 мс (но пиковые задержки могут быть значительно больше и не нормируются). Это накладывает известные ограничения на спектр применения данного решения, но для большинства «бытовых» задач (и не только) он прекрасно подходит, см. пример выше.

Если требуется управление в более жёстком реальном времени, то это требует специальной оптимизации кода под конкретную задачу, перестройки архитектуры или, в совсем крайних случаях, выделения отдельного контроллера под специфические функции. Как пример, выделение отдельного контроллера эффектов умной светодиодной ленты.

Это общее теоретическое описание работы многозадачности в Arduino вообще и в AMS в частности, теперь перейдём к рассмотрению практических примеров.

Циклические таймеры

Рассмотрим реализацию самых простых циклических таймеров. Это таймеры (в терминологии AMS «cycles»), которые включаются через определённые, заранее заданные промежутки времени и используются для активации циклических процессов.

Вообще, таймеры программно лучше оформлять в виде объектов, но в стандартной поставке Arduino Mega Server эти таймеры реализованы в виде функций, поэтому, для начала, рассмотрим их в этой ипостаси.

Использовать циклические таймеры очень просто: достаточно поместить код, который нужно периодически выполнять, между скобками оператора if.

Если нужно использовать другой интервал срабатывания, то просто используем нужную переменную вместо cycle1s.

Различных циклов можно сделать сколько угодно — система даже на 8-битной Меге без проблем потянет обслуживание буквально сотен таких таймеров (только, естественно, нужно не забывать чтобы вызываемый код не был блокирующим).

if (cycle1s) { // Код, который необходимо выполнять, например, каждую секунду }

Теперь организация работы таймеров. Определение управляющих переменных в главном файле:

// Cycles bool cycle1s = false; bool cycle5s = false; bool cycle20s = false; bool cycle30s = false; bool cycle1m = false; bool cycle3m = false; bool cycle5m = false;

Набор интервалов может быть расширен любыми нужными значениями от десятков миллисекунд до суток и более.

Модуль «Timers»:

/* Module Timers part of Arduino Mega Server project */ // Cycles unsigned long timeSec; unsigned long timer1s; unsigned long timer5s; unsigned long timer20s; unsigned long timer30s; unsigned long timer1m; unsigned long timer3m; unsigned long timer5m; void timersInit() { unsigned long uptimeSec = millis() / 1000; timeSec = uptimeSec; timer1s = uptimeSec; timer5s = uptimeSec; timer20s = uptimeSec; timer30s = uptimeSec; timer1m = uptimeSec; timer3m = uptimeSec; timer5m = uptimeSec; } void timersWorks() { timeSec = millis() / 1000; if (timeSec – timer1s >= 1) { timer1s = timeSec; cycle1s = true; if (timeSec – timer5s >= 5) {timer5s = timeSec; cycle5s = true;} if (timeSec – timer20s >= 20) {timer20s = timeSec; cycle20s = true;} if (timeSec – timer30s >= 30) {timer30s = timeSec; cycle30s = true;} if (timeSec – timer1m >= 60) {timer1m = timeSec; cycle1m = true;} if (timeSec – timer3m >= 180) {timer3m = timeSec; cycle3m = true;} if (timeSec – timer5m >= 300) {timer5m = timeSec; cycle5m = true;} } } void eraseCycles() { cycle1s = false; cycle5s = false; cycle20s = false; cycle30s = false; cycle1m = false; cycle3m = false; cycle5m = false; }

И далее код подключения этих таймеров в основном цикле. В начале главного цикла проверяется не сработал ли какой-либо из таймеров, а в конце все поднятые флаги таймеров сбрасываются. Любой системный код находится между этими двумя функциями и, что наиболее важно, любой из таймеров можно использовать многократно в любых частях кода системы.

void loop() { timersWorks(); // Код системных процессов eraseCycles(); }

Циклические таймеры в виде объектной библиотеки

Теперь рассмотрим организацию тех же таймеров, но в более правильном объектном виде, оформленном в готовую библиотеку. Назовём её myCycle.

Заголовочный файл в котором представлены объявления класса, методов и некоторых предопределённых констант:

/* myCycle Library part of Arduino Mega Server project */ #ifndef _MYCYCLE_H #define _MYCYCLE_H #define MS_500 500 #define MS_01S 1000 #define MS_02S 2000 #define MS_05S 5000 #define MS_10S 10000 #define MS_13S 13000 #define MS_17S 17000 #define MS_20S 20000 #define MS_30S 30000 #define MS_01M 60000 #define MS_03M 180000 #define MS_05M 300000 #define MS_01H 3600000 #define MS_06H 21600000 #define MS_12H 43200000 #define MS_01D 86400000 class myCycle { private: bool _go; bool _active; unsigned long _start; unsigned long _period; public: myCycle(unsigned long per, bool act); void reInit(unsigned long per, bool act); void reStart(); bool check(); bool go(); void clear(); // active bool active(); void setActive(bool act); // period unsigned long period(); void setPeriod(unsigned long per); }; // class myCycle #endif // _MYCYCLE_H

И файл реализации в котором находится код библиотеки:

/* myCycle Library part of Arduino Mega Server project */ #include “myCycle.h” #include myCycle::myCycle(unsigned long per, bool act) { _go = false; _active = act; _period = per; _start = millis(); } // Methods void myCycle::reInit(unsigned long per, bool act) { _go = false; _active = act; _period = per; _start = millis(); } void myCycle::reStart() { _start = millis(); } bool myCycle::check() { if (millis() – _start >= _period) { _start = millis(); if (_active) { _go = true; } } return _go; } bool myCycle::go() { return _go; } void myCycle::clear() { _go = false; } // Active bool myCycle::active() { return _active; } void myCycle::setActive(bool act) { _active = act; } // Period unsigned long myCycle::period() { return _period; } void myCycle::setPeriod(unsigned long per) { _period = per; }

Использование этого варианта тоже просто и имеет некоторые преимущества перед «функциональным» вариантом: тут можно очень легко объявлять таймеры с нужными интервалами и не нужно заранее создавать множество таймеров «на всякий случай».

В главном файле:

Подключаем библиотеку:

#include “myCycle.h”

Создаём объекты:

// Cycles myCycle cycle500(MS_500, true); myCycle cycle2s(MS_02S, true); myCycle cycle5s(MS_05S, true);

Добавляем функции обслуживания работы таймеров:

void timersWorks() { cycle500.check(); cycle2s.check(); cycle5s.check(); } void eraseCycles() { cycle500.clear(); cycle2s.clear(); cycle5s.clear(); }

В главном цикле используем объявленные таймеры в любом нужном месте кода:

void loop() { timersWorks(); // Код системных процессов if (cycle5s.go()) { Serial.println(F(“cycle5s!”)); } eraseCycles(); }

Библиотека имеет несколько более широкий функционал, чем код, приведённый в первом варианте. Например, она содержит методы активации/дезактивации таймеров, установки и получения текущего значения, рестарта и реинициализации таймеров, что ещё больше расширяет возможности использования их в практических задачах.

Другие виды таймеров на Arduino

Чтобы не загромождать статью, я не буду здесь приводить код и разбирать работу всех возможных типов таймеров — все они строятся по одним и тем же принципам. Если эта тема будет интересна, то можно будет написать отдельную статью об этом. Здесь я только дам общее описание таймеров, которые используются в AMS и прекрасно себя зарекомендовали на практике.

Циклические с заданным количеством повторений (пакетные)

Это таймеры, которые срабатывают заранее определённое количество раз. Например, вам нужно делать 3 попытки отправки сообщения по беспроводному каналу nRF24. Таймер активируется только 3 раза и соответственное количество раз делаются попытки отправки сообщений.

Тут же возможны различные расширения функциональности типа активации/дезактивации таймера в зависимости от определённых условий и т. п.

Одиночные

Это различные вариации на тему «автозагрузки», когда какое-либо действие выполняется через определённый интервал времени после старта контроллера или какого-либо события.

Рандомные

Иногда требуется не строгая периодичность выполнения каких-то действий, а наоборот некий разброс срабатываний возле некоторого среднего значения, для этих целей применяются так называемые рандомные таймеры.

Например, у вас есть два распределённых контроллера, которые связаны друг с другом по беспроводному каналу.

Если одна система будет посылать свои сообщения другой, согласуясь со срабатыванием обычного таймера, например, раз в 20 секунд, то эти сообщения будут приходить на вторую систему в строго определённой «фазе», которая может попадать в «проблемный» интервал работы цикла второго контроллера, в результате его работа может быть нестабильной. Если в этом случае использовать рандомный таймер, то он «размажет» по времени моменты прихода сообщений от первого контроллера и проблемы удастся избежать.

Это только абстрактный пример для понимания того, что собой представляют рандомные таймеры. И вы можете ознакомиться с их реализацией — стандартный дистрибутив Arduino Mega Server содержит код такого таймера.

Смешанные

Тут полная свобода действий, можно как угодно комбинировать работу различных типов таймеров, используя запуск одних таймеров от других и их встроенную логику типа активации/дезактивации по условиям, изменение периода срабатывания «на лету» и т. п.

Нет практически никаких ограничений на количество таймеров и логику работы — их может быть сотни, даже на 8-битном контроллере.

Межпроцессное взаимодействие

Межпроцессное взаимодействие, семафоры, почтовые ящики и прочие атрибуты многозадачных систем организуются на Arduino тоже без каких-либо проблем — их можно организовать любым удобным для вас способом — начиная от передачи параметров через статические переменные и заканчивая упаковкой логики в любые классы и объекты, тут нет абсолютно никаких ограничений и проблем.

Подробное описание межпроцессного взаимодействия выходит за рамки этой статьи, но вы можете скачать себе дистрибутив Arduino Mega Server для любой платформы и самостоятельно ознакомиться с его устройством и работой.

Заключение

В общем, можно сказать, что средствами Arduino можно создать любые виды таймеров, запускать любое их количество и использовать любое их сочетание, другими словами, Arduino может удовлетворить любой каприз по таймерному (псевдо) многозадачному управлению сложными микроконтроллерными системами.

И это не должно быть секретом для студентов МИРЭА, как будущих инженеров микропроцессорных систем, ведь эти принципы можно применять на любой платформе.

P.S.
Недавно вышла новая, 0.17 версия Arduino Mega Server для Arduino Mega, Due, 101 и M0, которую вы можете скачать на официальной странице загрузки. Код таймеров, описанный в этой статье, взят из этих дистрибутивов.

Добавить в избранное

Источник: http://othermedia.info/?p=6321

Ссылка на основную публикацию
Adblock
detector