продолжение статьи МИГАЕМ СВЕТОДИОДОМ БЕЗ DELAY() или всегда ли официальный примеру учат "хорошему".
Сразу оговорюсь, в рамках 8-ми битных контроллеров настоящей одновременности добится невозможно (только если какое-то действие будет выполняется независимым аппаратным блоком, например - генерация PWM). Но для подавляющего большинства задач - хватает "псевдо-одновременности". Когда контроллер по очереди "хватается" то за одно дело, то за другое. Если на каждом "деле" он не задерживается слишком на долго, то с точки зрения человека это выглядит как "одновременно".
Типичный пример чем может одновременно заниматься контроллер:
Сразу оговорюсь, в рамках 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. Нам теперь не нужно хмурить мозг что-бы каждый раз придумывать новое уникальное имя переменное. Более того, у нас получилась как-бы "универсальная заготовка периодической функции".
Теперь, когда нам понадобится занятся "еще чем-нибудь" время от времени. Мы возмем этот шаблон. Вместо имени somePeriodical дадим более осмысленное имя, впишем действие которое нам нужно, и вызовем эту функцию из loop()
P.S. Задать вопросы и почитать обсуждение вы можете либо в комментариях ниже, либо на сайте arduino.ru в ветке Еще раз мигаем светодиодом без Delay | Аппаратная платформа Arduino
UPD: Оформил этот прием в виде библиотеки. Про это следующая статья: Мигаем без delay() с комфортом
// "заготовка/шаблон функции" которая периодически выполняет КАКОЕ-ТО-ДЕЙСТВИЕ 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() и т.п.
На сегодня все. В следующий раз рассмотрим попробуем "сделать себе удобно". Посмотрим как можно сделать что-бы ради мелочи (например нам нужно периодически выводить значение какой-то переменной, причем код нужне "на 2 минуты", в отладочных целях, потом его выкинем) не заводить целую специальную функцию, не выписывать "прицеп" из if-а и milis() и т.п.
P.S. Задать вопросы и почитать обсуждение вы можете либо в комментариях ниже, либо на сайте arduino.ru в ветке Еще раз мигаем светодиодом без Delay | Аппаратная платформа Arduino
UPD: Оформил этот прием в виде библиотеки. Про это следующая статья: Мигаем без delay() с комфортом
Этот комментарий был удален автором.
ОтветитьУдалитьа каким образом можно организовать два ШИМ сигнала?
ОтветитьУдалитьна blinkLed(BLINK_INTERVAL); ошибка!!
ОтветитьУдалитьСкетч отлично работает на Nano Версия Ide 1.0.5
ОтветитьУдалитьЕсли попытаемся параллельно измерять температуру датчиком DS18B20? Ему на измерение требуется 750 мс. Стало быть быстрее чем раз в 750 мс остальные операции делать не получится?
ОтветитьУдалить