- Что такое многозадачность?
- Зачем пропускать delay () в Arduino?
- Зачем использовать millis ()?
- Необходимые компоненты
- Принципиальная электрическая схема
- Программирование Arduino UNO для многозадачности
Многозадачность привела компьютеры к революции, где одна или несколько программ могут работать одновременно, что повышает эффективность, гибкость, адаптивность и продуктивность. Во встроенных системах микроконтроллеры также могут обрабатывать многозадачность и одновременно выполнять две или более задачи, не прерывая текущие инструкции.
Здесь, в этом руководстве, мы узнаем, как Arduino выполняет многозадачность с помощью функции Arduino millis. Обычно функция delay () используется в Arduino для периодической задачи, такой как мигание светодиода, но эта функция delay () останавливает программу на определенное время и не позволяет выполнять другие операции. Итак, в этой статье объясняется, как мы можем избежать использования функции delay () и заменить ее на millis () для одновременного выполнения нескольких задач и сделать Arduino контроллером многозадачности. Прежде чем вдаваться в подробности, давайте начнем с понимания многозадачности.
Что такое многозадачность?
Многозадачность просто означает одновременное выполнение нескольких задач или программ. Практически все операционные системы поддерживают многозадачность. Этот тип операционных систем известен как MOS (многозадачная операционная система). MOS может быть операционной системой мобильного или настольного ПК. Хороший пример многозадачности на компьютерах - это когда пользователи запускают приложение электронной почты, интернет-браузер, медиаплеер, игры одновременно, и если пользователи не хотят использовать приложение, оно запускается в фоновом режиме, если не закрыто. Конечный пользователь использует все эти приложения одновременно, но ОС использует эту концепцию несколько иначе. Давайте обсудим, как ОС справляется с многозадачностью.
Как видно на рисунке, ЦП делит время на три равные части и назначает каждую часть каждой задаче / приложению. Именно так выполняется многозадачность в большинстве систем. Концепция будет почти такой же для многозадачности Arduino, за исключением того, что распределение времени будет немного другим. Поскольку Arduino работает с низкой частотой и оперативной памятью по сравнению с ноутбуком / мобильным компьютером / ПК, время, отводимое на каждую задачу, также будет отличаться. В Arduino также есть функция delay (), которая широко используется. Но прежде чем начать, давайте обсудим, почему мы не должны использовать функцию delay () ни в одном проекте.
Зачем пропускать delay () в Arduino?
Если рассматривать справочную документацию Arduino, то существует два типа функций задержки: первая - это delay (), а вторая - delayMicroseconds (). Обе функции идентичны с точки зрения генерации задержки. Единственное отличие состоит в том, что в функции delay () переданное целое число параметра выражается в миллисекундах, т.е. если мы напишем delay (1000), то задержка будет равна 1000 миллисекунд, то есть 1 секунда. Аналогично в функции delayMicroseconds () передается параметр в микросекундах, т.е. если мы напишем delayMicroseconds (1000), то задержка будет составлять 1000 микросекунд, то есть 1 миллисекунду.
Вот и дело, обе функции приостанавливают программу на время, прошедшее в функции задержки. Таким образом, если мы даем задержку в 1 секунду, то процессор не может перейти к следующей инструкции, пока не пройдет 1 секунда. Аналогично, если задержка составляет 10 секунд, программа остановится на 10 секунд, и процессор не позволит перейти к следующим инструкциям, пока не пройдут 10 секунд. Это снижает производительность микроконтроллера с точки зрения скорости и выполнения инструкций.
Лучший пример, объясняющий недостаток функции задержки, - использование двух кнопок. Предположим, мы хотим переключить два светодиода с помощью двух кнопок. Таким образом, если нажать одну кнопку, соответствующий светодиод должен светиться в течение 2 секунд, аналогично, если нажата вторая кнопка, светодиод должен светиться в течение 4 секунд. Но когда мы используем delay (), если пользователь нажимает первую кнопку, программа останавливается на 2 секунды, а если пользователь нажимает вторую кнопку до задержки в 2 секунды, то микроконтроллер не принимает ввод, поскольку программа в стадии остановки.
Официальная документация Arduino четко упоминает об этом в своих примечаниях и предупреждениях о функции delay (). Вы можете пройти и проверить это, чтобы было понятнее.
Зачем использовать millis ()?
Чтобы преодолеть проблему, вызванную использованием задержки, разработчик должен использовать функцию millis (), которая проста в использовании, как только вы станете привычными, и она будет использовать 100% производительность процессора без каких-либо задержек при выполнении инструкций. millis () - это функция, которая просто возвращает количество миллисекунд, прошедших с тех пор, как плата Arduino начала запускать текущую программу без остановки программы. На этот раз число переполнится (т.е. вернется к нулю) примерно через 50 дней.
Так же, как в Arduino есть delayMicroseconds (), у него также есть микроверсия millis () как micros (). Разница между микросхемой и миллис состоит в том, что микросхема () будет переполняться примерно через 70 минут по сравнению с миллис (), которая составляет 50 дней. Итак, в зависимости от приложения вы можете использовать millis () или micros ().
Использование millis () вместо delay ():
Чтобы использовать millis () для измерения времени и задержки, вам необходимо записать и сохранить время, в которое произошло действие, чтобы начать отсчет времени, а затем через определенные промежутки времени проверять, прошло ли определенное время. Итак, как указано, сохраните текущее время в переменной.
беззнаковый длинный currentMillis = millis ();
Нам нужны еще две переменные, чтобы узнать, прошло ли необходимое время. Мы сохранили текущее время в переменной currentMillis, но нам также нужно знать, когда начался временной период и как долго этот период. Итак, объявляются Interval и previousMillis . Интервал сообщит нам время задержки, а previosMillis сохранит время последнего события.
беззнаковый длинный предыдущийМиллис; беззнаковый длинный период = 1000;
Чтобы понять это, давайте возьмем пример простого мигающего светодиода. Период = 1000 сообщит нам, что светодиод будет мигать в течение 1 секунды или 1000 мс.
const int ledPin = 4; // номер подключенного вывода светодиода int ledState = LOW; // используется для установки состояния светодиода unsigned long previousMillis = 0; // сохранит время последнего мигания светодиода const long period = 1000; // период мигания в мс void setup () { pinMode (ledPin, OUTPUT); // устанавливаем ledpin как выход } void loop () { unsigned long currentMillis = millis (); // сохраняем текущее время if (currentMillis - previousMillis> = period) {// проверяем, прошло ли 1000 мсек previousMillis = currentMillis; // сохраняем время последнего мигания светодиода if (ledState == LOW) {// если светодиод не горит, включаем его и наоборот ledState = HIGH; } еще { ledState = LOW; } digitalWrite (ledPin, ledState); // снова заставляем светодиод с ledState мигать } }
Здесь заявление
Прерывания в Arduino работают так же, как и в других микроконтроллерах. Плата Arduino UNO имеет два отдельных контакта для подключения прерываний к контактам 2 и 3 GPIO. Мы подробно рассмотрели это в учебнике по прерываниям Arduino, где вы можете узнать больше о прерываниях и их использовании.
Здесь мы покажем многозадачность Arduino, выполняя две задачи одновременно. Задачи будут включать в себя мигание двух светодиодов с разной задержкой времени, а также кнопку, которая будет использоваться для управления состоянием включения / выключения светодиода. Таким образом, три задачи будут выполняться одновременно.
Необходимые компоненты
- Arduino UNO
- Три светодиода (любого цвета)
- Сопротивления (470, 10к)
- Джемперы
- Макетная плата
Принципиальная электрическая схема
Принципиальная схема для демонстрации использования функции Arduino Millis () очень проста и не требует большого количества компонентов, как показано ниже.
Программирование Arduino UNO для многозадачности
Программирование Arduino UNO для многозадачности потребует только логики работы millis (), которая объясняется выше. Рекомендуется снова и снова попрактиковаться в мигании светодиода, используя миллис, чтобы прояснить логику и освоиться с миллис (), прежде чем начинать программировать Arduino UNO для многозадачности. В этом руководстве прерывание также используется одновременно с millis () для многозадачности. Кнопка будет прерыванием. Таким образом, всякий раз, когда генерируется прерывание, т.е. нажимается кнопка, светодиод переключается в состояние ВКЛ или ВЫКЛ.Программирование начинается с объявления номеров контактов, к которым подключены светодиоды и кнопка.
int led1 = 6; int led2 = 7; int toggleLed = 5; int pushButton = 2;
Затем мы пишем переменную для хранения состояния светодиодов для будущего использования.
int ledState1 = LOW; int ledState2 = LOW;
Как объяснено выше в примере мигания, переменные для периода и предыдущего милли объявлены для сравнения и создания задержки для светодиодов. Первый светодиод мигает через каждые 1 секунду, а другой светодиод мигает через 200 мс.
беззнаковый длинный предыдущийMillis1 = 0; const long period1 = 1000; беззнаковый длинный предыдущийMillis2 = 0; const long period2 = 200;
Другая миллисекционная функция будет использоваться для генерации задержки дребезга, чтобы избежать многократного нажатия кнопки. Будет такой же подход, как и выше.
int debouncePeriod = 20; int debounceMillis = 0;
Эти три переменными будет использоваться для хранения состояния кнопки, как прерывание, переключения светодиода и кнопочного состояния.
bool buttonPushing = false; int ledChange = LOW; int lastState = HIGH;
Определите действие контакта, который будет работать как ВХОД или ВЫХОД.
pinMode (led1, ВЫХОД); pinMode (led2, ВЫХОД); pinMode (toggleLed, ВЫХОД); pinMode (кнопка, ВХОД);
Теперь определите вывод прерывания, добавив прерывание с определением ISR и режима прерывания. Обратите внимание, что при объявлении функции attachInterrupt () рекомендуется использовать digitalPinToInterrupt (pin_number), чтобы преобразовать фактический цифровой вывод в конкретный номер прерывания.
attachInterrupt (digitalPinToInterrupt (pushButton), pushButton_ISR, CHANGE);
Написана подпрограмма прерывания, и она изменит только флаг buttonPushing. Обратите внимание, что подпрограмма прерывания должна быть как можно короче, поэтому постарайтесь написать ее и свести к минимуму дополнительные инструкции.
недействительным pushButton_ISR () { buttonPushing = истина; }
Цикл начинается с сохранения значения millis в переменной currentMillis, которая будет хранить значение времени, прошедшего каждый раз, когда цикл повторяется.
беззнаковый длинный currentMillis = millis ();
Всего в многозадачности есть три функции: мигание одного светодиода с интервалом 1 секунду, мигание второго светодиода с частотой 200 мсек и включение / выключение светодиода при нажатии кнопки. Итак, мы напишем три части для выполнения этой задачи.
Первым является переключение светодиодов состояния после каждой 1 секунды, сравнивая Миллис прошло.
если (текущийМиллис - предыдущийМиллис1> = период1) { предыдущийМиллис1 = текущийМиллис; если (ledState1 == LOW) { ledState1 = HIGH; } еще { ledState1 = LOW; } digitalWrite (led1, ledState1); }
Аналогичным образом, во- вторых, она включает светодиод через каждые 200 мс, сравнивая прошедшее с начала Миллис. Объяснение уже объяснялось ранее в этой статье.
если (текущийМиллис - предыдущийМиллис2> = период2) { предыдущийМиллис2 = текущийМиллис; если (ledState2 == LOW) { ledState2 = HIGH; } еще { ledState2 = LOW; } digitalWrite (led2, ledState2); }
Наконец, контролируется флаг buttonPushing, и после создания задержки устранения дребезга в 20 мс он просто переключает состояние светодиода, соответствующее кнопке, прикрепленной как прерывание.
if (buttonPushing = true) // проверяем, вызывается ли ISR { if ((currentMillis - debounceMillis)> debouncePeriod && buttonPushing) // генерируем задержку на 20 мс, чтобы избежать многократных нажатий { debounceMillis = currentMillis; // сохраняем время задержки последнего устранения дребезга if (digitalRead (pushButton) == LOW && lastState == HIGH) // меняем светодиод после нажатия кнопки { ledChange =! ledChange; digitalWrite (toggleLed, ledChange); lastState = LOW; } else if (digitalRead (pushButton) == HIGH && lastState == LOW) { lastState = HIGH; } buttonPushing = false; } }
На этом мы завершаем обучение Arduino millis (). Обратите внимание, что для того, чтобы привыкнуть к millis (), просто потренируйтесь реализовать эту логику в некоторых других приложениях. Вы также можете расширить его, чтобы использовать двигатели, серводвигатели, датчики и другие периферийные устройства. В случае сомнений напишите на наш форум или оставьте комментарий ниже.
Полный код и видео для демонстрации использования функции millis в Arduino приведены ниже.