продолжение статьи МИГАЕМ СВЕТОДИОДОМ БЕЗ 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 мс остальные операции делать не получится?
ОтветитьУдалить