Типичный пример чем может одновременно заниматься контроллер:
мигать диодом (периодически переключать его состояние)
читать датчик температуры
слушать приходящие команды из Serial
сообщать о текущем состоянии переменных в Serial
Так как каждое из этих действий выполняется контроллером за микросекунды (миллисекунды в худшем случае), то пробегая раз за разом по этому списку - мы создадим иллюзию одновременности. Главное - не задерживаться на одном месте не долго. Скажем при мигании диодом - не использовать функцию delay() (она полностью останавливает выполнение скетча на какое-то время).
Заводим для каждого действия свою переменную previousTime (естественно имя чуть-чуть меняем) и оборачиваем действий в
if(millis()-previousTime)
Два дела сразу
Скажем мы хотим мигать диодом раз в пол-секунды и, при этом выводить, раз в секунду, в Serial текущие значение millis() (что-бы даже не видя ардуины физически иметь уверенность что скетч не завис).
Код: Выделить всё
#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()
Код: Выделить всё
#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 (нажата или нет кнопка)