Во многих программах требуется выработка временных интервалов. Это бывает необходимо для задержек, периодических опросов внешних устройств, обновления информации на индикаторах и дисплеях и т. п. Использование циклов ожиданий крайне непродуктивно, т. к. в это время процессор ничем полезным не занят.
Здесь будет показан пример использования прерывания таймера Watchdog конструктора Arduino UNO для генерации временных интервалов.
Перед тем, как приступить к работе, желательно иметь в наличии принципиальную схему контроллера Arduino UNO и описание на собственно контроллер ATmega328, т. к. описание работы будет базироваться на этих документах. Естественно предполагается, что на компьютере установлена среда программирования ArduinoIDE.
Попробуем запустить какую-нибудь индикацию того, что контроллер "живет". Для этого заставим мигать расположенный на плате светодиод примерно раз в секунду. Обычно для отсчета временных интервалов используется таймер. В нашем случае не будем занимать один из трех штатных таймеров, а воспользуемся таймером Watchdog. Он, конечно, не имеет хорошей точности, т. к. работает от RC-генератора, но для многих применений этого вполне достаточно.
Настраивается таймер через установку битов в регистре WDTCSR (Watchdog Timer Control Register). Адрес регистра — 0x60. Расположение бит регистра приведено в табл. 1.
Возможные режимы работы таймера приведены в табл. 2.
Бит WDTON управляет переводом таймера в режим системного сброса. Этот бит находится в специальных конфигурационных байтах и по умолчанию установлен в "1", поэтому его программирование рассматривать не будем.
Бит WDCE (Watchdog Change Enable) регистра WDTCSR управляет возможностью изменения состояния остальных битов этого регистра и должен быть установлен в "1", если необходимо изменить остальные биты регистра. Если этого не сделать, то изменения не будут записаны в регистр.
В процессе отладки программы (а также проработки примеров программ из инструкции к контроллеру Atmega328) было обнаружено следующее. Если установить в "1" только бит WDCE, например, командой:
WDTCSR = (1 << WDCE);
то установить делитель не удастся. Следует одновременно установить в "1" и бит WDE, т. е. выполнить команду:
WDTCSR = (1 << WDCE) | (1 << WDE);
а затем уже устанавливать биты делителя. Сбрасывать биты WDCE и WDE не требуется — они сбрасываются аппаратно, как написано в инструкции на контроллер — через 4 такта. Именно такая последовательность установки предделителя таймера приведена в примере инструкции на контроллер.
Установка бит WDP0-WDP3 регистра WDTCSR для изменения делителя таймера приведена в табл. 3.
После этих предварительных замечаний можно привести вариант программы, дополненной работой с Watchdog таймером. Подпрограмма SetWatchdogTimer() производит настройку таймера, а ISR(WDT_vect) — обрабатывает его прерывания.
void setup() {
pinMode(LED_BUILTIN, OUTPUT); // настройка светодиода индикации
cli(); // запрещаем прерывания перед настройкой
SetWatchdogTimer();
sei(); // разрешаем прерывания после настройки
}
void loop() { }
// Подпрограмма настройки прерываний сторожевого таймера
void SetWatchdogTimer(){
WDTCSR = (1 << WDCE) | (1 << WDE);
// установка делителя 0,5 сек.
WDTCSR = (0 << WDP3) | (1 << WDP2) | (0 << WDP1) | (1 << WDP0);
// разрешение прерываний таймера
WDTCSR |= (1 << WDIE);
}
// Программа обработки прерывания сторожевого таймера
ISR(WDT_vect){
// мигаем светодиодом раз в секунду
digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); }
И, загрузив программу в контроллер, увидеть, что контроллер "живет", подмигивая желтым глазком.