Продолжаем развивать нашу программу. С этой статье мы рассмотрим работу программных таймеров, задача которых заменить выполнение функции delay(), обеспечивая задержку выполнения части кода, при этом позволяя контроллеру продолжить выполнение основной программы.
Основой для работы таймера является функция unsigned long millis(), которая возвращает количество миллисекунд с момента начала выполнения текущей программы на плате Arduino.
На картинке показан алгоритм работы основного цикла программы, обращающегося к таймерам.
Рассмотрим работу таймера. Пока мы не будем использовать структуры и классы (эх….), т.к. мы их ещё не проходили, да и смысл работы таймера, от этого, не меняется.
Таймер имеет две основные переменные: bStat и ulTime.
bool bStat – состояние таймера – включён / выключен. Состояние таймера возвращает функция getStat().
unsigned long ulTime – время срабатывания таймера. Задание интервала задержки задается функцией setTimer(dT), которой в качестве параметра передается значение переменной dT – время задержки в миллисекундах. Т.е. если нам нужно включить таймер на 1 секунду, мы вызовем функцию setTimer(1000).
unsigned long setTimer(unsigned long dT){ //dT - длительность задержки if(ulTime == 0){ //если таймер не включен ulTime = millis() + dT; //задаем значение - сумма текущего времени } //и заданного времени ожидания return ulTime; }
В функции setTimer мы получаем текущее значение счетчика времени контроллера и прибавляем к нему значение длительности задержки (т.е. определяем в будущем точку, переход через которую будет признаком окончания работы таймера).
bool getTimer(){
unsigned long nMillis = millis();
if(ulTime <= nMillis){ //проверяем состояние таймера
ulTime = 0; //если время истекло,
// сбрасываем заданное значение, выключаем таймер.
}
return ulTime != 0; //возвращаем логическое состояние таймера, если включен - true
}
Состояние таймера определяется значением, возвращаемым функцией getTimer(), в которой сравнивается значение глобальной переменной ulTime и текущего значения счетчика времени, возвращаемого функцией millis(). Функция getTimer() возвращает логическое true/false, в зависимости от состояния таймера.
В программе, которую мы разбирали на занятии используется несколько таймеров, два из которых создают асинхронные (относительно друг друга) события, для включения виртуального индикатора.
Виртуальный индикатор – часть программы, запускаемая событием, выполняющая мигание светодиодов заданное количество раз с программно заданной длительностью. В жизни это могло бы так: если проехал грузовик – мигни 4 раза, а если легковушка, то 2 раза.
#define RED 9
#define GREEN 10
#define BLUE 11
#define TIME_X 200
#define SHORT_TIME 4530
#define LONG_TIME 7550
int StatTimer = 0;
bool arStatTimers[] = {1, 0, 1};
unsigned long arTimers[] = {0, 0, 0};
//***************************** установка времени задержки таймера
bool setTimer(unsigned long dt, int nTimer = 0){ //dt - длительность задержки, nTimer - номер таймера
bool bActive = false;
if(arTimers[nTimer] == 0){ //если таймер не включен
arTimers[nTimer] = millis() + dt; //задаем значение - сумма текущего времени
bActive = true;
} //и заданного времни ожидания
return bActive;
}
//*****************************
bool getTimer(int nTimer = 0){
if(arTimers[nTimer] <= millis()){ //проверяем состояние таймера
arTimers[nTimer] = 0; //если время истекло, сбрасываем заданное значение, выключаем таймер.
}
return arTimers[nTimer] != 0; //возвращаем логическое состояние таймера, если включен - true
}
//************************************
void setup() {
pinMode(GREEN, OUTPUT);
setTimer(SHORT_TIME, 1); //запускаем оба таймера
setTimer(LONG_TIME, 2);
}
//*******************************
void loop() {
if(arStatTimers[1]){ //если произошло 1 событие
arStatTimers[1] = 0;//сбрасываем флаг события
StatTimer += 8; //запускаем индикацию - добавляем 4 мигания к счетчику индикации
}
if(arStatTimers[2]){ //если произошло 1 событие
arStatTimers[2] = 0;//сбрасываем флаг события
StatTimer += 4; //запускаем индикацию - добавляем 2 мигания к счетчику индикации
}
if(StatTimer){ //если индикация включена
if(!getTimer()){ //определяем состояние 0-го таймера и если выключился,
setTimer(TIME_X); //запускаем заново
int st = (StatTimer & 1) == 0; //состояние светодиода определяется младшим разрядом счетчика индикатора
digitalWrite(GREEN, st);
StatTimer--; //уменьшаем счетчик индикатора
}
}
if(!getTimer(2)){
arStatTimers[2] = 1;
setTimer(LONG_TIME, 2);
}
if(!getTimer(1)){
arStatTimers[1] = 1;
setTimer(SHORT_TIME, 1);
}
}
На что стоит обратить внимание:
- В программе используется логическая операция “И”. st = (StatTimer & 1) == 0; Единица маскирует значение переменной StatTimer, оставляя значимым только младший разряд, который определяет состояние светодиода.
Задание. Прошу предложить решение следующей задачи: допишите код так, что бы по каждому событию загорался “свой” светодиод. arStatTimers[1] – RED, arStatTimers[2] – GREEN.