Универсальный сторожевой таймер на ATtiny13
Внешний сторожевой таймер это костыль для плохих разработчиков, которые не могут разработать нормально работающую программу для микроконтроллеров или стабильно работающую схему.
Тем более встроенный WDT имеется у большинства современных микроконтроллеров.
Но бывают случаи, когда приходится иметь дело с готовой платой или модулем с определенными проблемами. Свой первый WDT я сделал для борьбы с редкими, но все же иногда происходящими зависаниями ESP8266. Причем софтовый ресет тогда не спасал и ESP-шка не хотела переподключаться к WiFi. Передергивание питания внешним WDT решило проблему.
Вторая проблема возникла с GSM контроллером Elecrow ATMEGA 32u4 A9G. Здесь имели место быть очень редко случающиеся зависание SIM-карты. (Кстати эту же проблема бывает и с USB-модемами 3G и 4G). Для борьбы с таким зависанием нужно передернуть питание на SIM-ке. И вроде даже вывод у GSM модема для этого есть, но в схемотехнику устройства данная возможность не заложена. И для достижения максимальной надежность пришлось снова обращаться к внешней сторожевой собаке.
Схему на таймере 555 я не стал повторять. Слишком много недостатков у нее выявилось:
- Большие габариты и довольно много обвязки
- Неудобная установка времени срабатывания подстроечным резистором
- Довольно длительное время сброса (необходима разрядка конденсатора)
- Ну и потенциальное зависание МК с низким уровнем на выходе таймера, когда таймер просто перестает срабатывать.
- А проектов OpenSource в интернете, полностью соответствующих моим требованиям, я не нашел.
Требования к новому WDT
- Низкая цена устройства, простота изготовления и малые габариты
- Управление периодической сменой логического уровня 0/1 на входе
- Простая настройка времени срабатывания (как вариант выбор из предустановленных интервалов)
Разработка железа
В качестве основной микросхемы выбрал микроконтроллер ATtiny13. Его возможностей оказалось более чем достаточно для моей задачи. А цена, с учетом уменьшения элементов обвязки — практически такая же как у 555 микросхемы.
Пять выводов МК (RESET решил не трогать) распределились следующим образом:
- Выход таймера
- Вход для сброса
- Три оставшихся вывода — задания времени срабатывания
Для коммутации питания используется P-канальный MOSFET. Подойдет любой совместимый по корпусу, но желательно брать с так называемым «логическим уровнем управления» — то есть полностью открывающийся от низкого напряжения 3–5В: IRLML2502, AO3415 и т.п. Несмотря на малые размеры, данный транзистор способен управлять нагрузкой в 4А. Если нужно коммутировать что-то другое, к этому выходу можно напрямую подключить реле на 5В.
Светодиод загорается в момент срабатывания таймера и отключения основного устройства.
Основной разъем для подключения к плате микроконтроллера имеет четыре вывода
- Общая шина
- Вход — сброс таймера
- Выход +5В (управляется таймером)
- Вход +5В
Два разъема — ICSP программатор и джамперы питания можно не устанавливать на плате. Микроконтроллер прошить в программаторе заранее, а время срабатывания задать постоянной перемычкой.
Список комплектующих
Изготовление
Платы получились маленькие — 18×22 мм. Я развел два варианта:
Для одностороннего изготовления ЛУТом
И для заказа на заводе с улучшенным дизайном и переходами меж сторонами. (Закажу у китайцев, при случае)
Домашние технологии дают примерно такой прототип.
Прошивка
Для прошивки использовал самодельный программатор на баз Arduino Nano
Программировал я в среде Arduino IDE с установленной поддержкой Attiny13 — MicroCore. В последней версии IDE были проблемы программатора ArduinoISP, но нормально заработало в версии Arduino IDE 1.6.13. Разбираться, что там накосячила наизменяла дружная команда arduino.cc желания не возникло)))
Тиньку настроил на работу от внутреннего резонатора с частотой 1.2МГц. Программа простая — настраиваем входы/выходы, считываем PB2 -PB4 и определяем время срабатывания, настраиваем таймер и переходим в режим IDLE. По прерыванию по таймеру определяем состояние контрольного входа. Если состояние изменилось на противоположное, сбрасываем счетчик. Если показания счетчика превысило установленное время срабатывания — передергиваем питание на выходе.
#define F_CPU 1200000UL
#include
#include
#include
boolean pb1_state;
volatile uint16_t pb1_count;
// Оброботчик прерывания по таймеру TIMER0
ISR(TIM0_OVF_vect){
pb1_count++;
}
int main(){
// Устанавливаем выход PB0
DDRB |= (1 << PB0); // pinMode(PB0, OUTPUT);
PORTB &= ~(1 << PB0); // digitalWrite(PB0, LOW);}
// Устанавливаем вход PB1 с подтягиванием
DDRB &= ~(1 << PB1); // pinMode(PB1, INPUT_PULLUP);
PORTB |= (1 << PB1);
// Устанавливаем вход PB2 с подтягиванием
DDRB &= ~(1 << PB2); // pinMode(PB2, INPUT_PULLUP);
PORTB |= (1 << PB2);
// Устанавливаем входы PB3 с подтягиванием
DDRB &= ~(1 << PB3); // pinMode(PB3, INPUT_PULLUP);
PORTB |= (1 << PB3);
// Устанавливаем входы PB4 с подтягиванием
DDRB &= ~(1 << PB4); // pinMode(PB4, INPUT_PULLUP);
PORTB |= (1 << PB4);
// Определяам время срабатывание таймера по входам PB2,PB3,PB4 (перемычки подтягивают к земле) (период, сек = TM/4 )
uint16_t TM = 0;
bool pb2 = false;
bool pb3 = false;
bool pb4 = false;
if( PINB & (1 << PINB2) )pb2 = true;
if( PINB & (1 << PINB3) )pb3 = true;
if( PINB & (1 << PINB4) )pb4 = true;
if( pb2 == true && pb3 == true && pb4 == true )TM = 4; //Таймаут 1 сек
else if( pb2 == false && pb3 == true && pb4 == true )TM = 8; //Таймаут 2 сек
else if( pb2 == true && pb3 == false && pb4 == true )TM = 20; //Таймаут 5 сек
else if( pb2 == false && pb3 == false && pb4 == true )TM = 40; //Таймаут 10 сек
else if( pb2 == true && pb3 == true && pb4 == false )TM = 80; //Таймаут 20 сек
else if( pb2 == false && pb3 == true && pb4 == false )TM = 120; //Таймаут 30 сек
else if( pb2 == true && pb3 == false && pb4 == false )TM = 240; //Таймаут 60 сек
else if( pb2 == false && pb3 == false && pb4 == false )TM = 480; //Таймаут 120 сек
pb1_count = 0;
pb1_state = false;
// Отключаем ADC
PRR = (1<= TM ){
PORTB |= (1 << PB0); // digitalWrite(PB0, HIGH);}
_delay_ms(1000); // Ждем секунду
PORTB &= ~(1 << PB0); // digitalWrite(PB0, LOW);}
pb1_count = 0; // Сбрасываем счетчик
}
TIMSK0 = (1<
Весь код уместился в 340 байт — ровно треть от килобайта памяти тиньки. Работа таймера проверяется просто — в зависимости от времени установки — периодически загорается светодиод на 1 сек. В это время на выходе Vвых напряжение 5В пропадает. Если контакт «вход» с периодичностью 1 сек замыкать на землю — сброс не производится и светодиод не загорается.
Управление WDT в основной программе следующее
#define PIN_WDT 5 //GPIO контроллера, куда подключен WDT
bool WDT_flag = false;
// Инициализация порта таймера
void WDT_begin(){
pinMode(PIN_WDT,OUTPUT);
digitalWrite(PIN_WDT,WDT_FLAG);
}
// Сброс таймера (не реже чем 1 на время срабатывания WDT, установленное перемычкой)
void WDT_reset(){
if( WDT_flag)WDT_flag = false;
else WDT_flag = true;
digitalWrite(PIN_WDT,WDT_FLAG);
}
Вот собственно, а все. Все исходные файлы, схемы и печатные платы можно скачать с GITHUBа
https://github.com/samopal-pro/wdt-tiny13