Внешний сторожевой таймер это костыль для плохих разработчиков, которые не могут разработать нормально работающую программу для микроконтроллеров или стабильно работающую схему.
Тем более встроенный 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 программатор и джамперы питания можно не устанавливать на плате. Микроконтроллер прошить в программаторе заранее, а время срабатывания задать постоянной перемычкой.
Список комплектующих
- МК Attiny13-SSU ~ $0.3 (при покупке 10 шт)
- MOSFET P-канал IRLML5203 — $0.09 (заказ 50шт) или MOSFET AO3415 — $0.05
- Резистор 1К SMD1206
- Резистор 470 SMD1206
- Светодиод 1206 любого цвета
- Разъемы PLS-6, PLS-3 и PLS-4R (PLD-3 и PLS-4R) — нормально отрезаются от длинных гребенок
Изготовление
Платы получились маленькие — 18?22 мм. Я развел два варианта:
Для одностороннего изготовления ЛУТом:
И для заказа на заводе с улучшенным дизайном и переходами меж сторонами. (Закажу у китайцев, при случае)
Домашние технологии дают примерно такой прототип.
Прошивка
Для прошивки использовал самодельный программатор на баз Arduino Nano
Программировал я в среде Arduino IDE с установленной поддержкой Attiny13 — MicroCore. В последней версии IDE были проблемы программатора ArduinoISP, но нормально заработало в версии Arduino IDE 1.6.13. Разбираться, что там
Тиньку настроил на работу от внутреннего резонатора с частотой 1.2МГц. Программа простая — настраиваем входы/выходы, считываем PB2 -PB4 и определяем время срабатывания, настраиваем таймер и переходим в режим IDLE. По прерыванию по таймеру определяем состояние контрольного входа. Если состояние изменилось на противоположное, сбрасываем счетчик. Если показания счетчика превысило установленное время срабатывания — передергиваем питание на выходе.
#define F_CPU 1200000UL
#include <avr/io.h>
#include <util/delay.h>
#include <avr/interrupt.h>
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<<PRADC); // shut down ADC
// Настраиваем таймер
TIMSK0 = (1<<TOIE0); // Включаем таймер TIMER0
TCCR0B = (1<<CS02) | (1<<CS00); // Пределитель таймера на 1/1024
// Задаем режим сна
MCUCR &= ~(1<<SM1); // idle mode
MCUCR &= ~(1<<SM0); // idle mode
MCUCR |= (1<<SE);
sei();
while(1) {
// Зпсываем до прерывания по таймеру
asm("sleep");
// Таймер сработал
TIMSK0 &= ~ (1<<TOIE0); // Останавливаем TIMER0
// Считываем состояние PB1
bool pb1 = false;
if( PINB & (1 << PINB1) )pb1 = true;
// Если состояние входа инвертировалось, сбрасываем время
if( pb1 != pb1_state )pb1_count = 0;
pb1_state = pb1;
// Если превышено время установки таймера
if( pb1_count >= TM ){
PORTB |= (1 << PB0); // digitalWrite(PB0, HIGH);}
_delay_ms(1000); // Ждем секунду
PORTB &= ~(1 << PB0); // digitalWrite(PB0, LOW);}
pb1_count = 0; // Сбрасываем счетчик
}
TIMSK0 = (1<<TOIE0); // Включаем таймер TIMER0
sei();
}
return 0;
}
Весь код уместился в 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
Комментарии (45)
jaiprakash
06.04.2019 12:40А что не так со специализированными сторожевиками, типа ADM6316, ADM823, ADM824?
olartamonov
06.04.2019 12:44А STWD100 и прочие аппаратные вотчдоги, коих выпускается полно, благородному дону чем не угодили?..
Ну и если уж делать свой, то зачем такой гроб, как ATTiny13? Есть же PIC10, есть ATTiny4/5/9/10 в SOT23.sav13 Автор
06.04.2019 13:28Ни вижу, почему бы благородному дону не собрать WDT на тиньке 13 )))
На самом деле вы правы, руководствовался принципом — слепила из того что было.
Тиньки были в наличии, а остальное покупать нужно.
neitri
06.04.2019 14:25Предложу свой вариант программы#define F_CPU 1200000UL #include <avr/io.h> #include <util/delay.h> #include <avr/interrupt.h> volatile uint16_t pb1_count; // Оброботчик прерывания по таймеру TIMER0 ISR(TIM0_OVF_vect){ pb1_count++; } int main() { // Устанавливаем выход PB0 и вход PB1 PB2 PB3 PB4 с подтягиванием DDRB = (1 << PB0)| (~(1 << PB1)) | (~(1 << PB2)) | (~(1 << PB4)); PORTB = ~(1 << PB0) | (1 << PB1) | (1 << PB2) | (1 << PB3) | (1 << PB4); // Определяам время срабатывание таймера по входам PB2,PB3,PB4 (перемычки подтягивают к земле) (период, сек = TM/4 ) byte i=0; if( PINB & (1 << PINB2) ) i+=1; if( PINB & (1 << PINB3) ) i+=2; if( PINB & (1 << PINB4) ) i+=4; uint16_t TM; switch (i){ case 0: TM=4; break; case 1: TM=8; break; case 2: TM=20; break; case 3: TM=40; break; case 4: TM=80; break; case 5: TM=120; break; case 6: TM=240; break; default: TM=480; } pb1_count = 0; boolean pb1_state = false; // Отключаем ADC PRR = (1<<PRADC); // shut down ADC // Настраиваем таймер TIMSK0 = (1<<TOIE0); // Включаем таймер TIMER0 TCCR0B = (1<<CS02) | (1<<CS00); // Пределитель таймера на 1/1024 // Задаем режим сна MCUCR &= ~(1<<SM1); // idle mode MCUCR &= ~(1<<SM0); // idle mode MCUCR |= (1<<SE); sei(); while(1) { // Зпсываем до прерывания по таймеру asm("sleep"); // Таймер сработал // Считываем состояние PB1 bool pb1 = false; if( PINB & (1 << PINB1) )pb1 = true; // Если состояние входа инвертировалось, сбрасываем время if( pb1 != pb1_state )pb1_count = 0; pb1_state = pb1; // Если превышено время установки таймера if( pb1_count >= TM ){ PORTB |= (1 << PB0); // digitalWrite(PB0, HIGH);} pb1_count = 0; //_delay_ms(1000); // Ждем секунду while (pb1_count<4){}; PORTB &= ~(1 << PB0); // digitalWrite(PB0, LOW);} pb1_count = 0; // Сбрасываем счетчик } } }
sav13 Автор
06.04.2019 15:38Согласен. Над оптимизацией кода еще работать и работать.
Только вот ваш код у меня меньше не получается. Может быть LTO оптимизатор компилятор мою конструкцию преобразует в что-то еще более компактное?
neitri
06.04.2019 16:23
Program Memory Usage: 320 bytes 31,3 % Fullconst uint16_t AT[] PROGMEM ={4,8,20,40,80,120,240,480}; TM=AT[i];
Data Memory Usage: 18 bytes 28,1 % Full
static const uint16_t AT[] PROGMEM ={4,8,20,40,80,120,240,480}; TM=AT[i];
Program Memory Usage: 266 bytes 26,0 % Full
Data Memory Usage: 2 bytes 3,1 % Full
Andy_Big
06.04.2019 18:36byte i=0;
if( PINB & (1 << PINB2) ) i+=1;
if( PINB & (1 << PINB3) ) i+=2;
if( PINB & (1 << PINB4) ) i+=4;
uint16_t TM; switch ((PINB & 0x1C)){ case 0: TM=4; break; case 1<<2: TM=8; break; case 2<<2: TM=20; break; case 3<<2: TM=40; break; case 4<<2: TM=80; break; case 5<<2: TM=120; break; case 6<<2: TM=240; break; default: TM=480; }
Andy_Big
06.04.2019 19:15Кстати, если условие свича изменить на такое:
switch ((PINB & 0x1C)>>2){ case 0: TM=4; break; case 1: TM=8; break; case 2: TM=20; break; case 3: TM=40; break; case 4: TM=80; break; case 5: TM=120; break; case 6: TM=240; break; default: TM=480; }
То компилятор оптимизирует его еще лучше.
Но наиболее компактный вариант — у neitri
AKudinov
06.04.2019 14:34+1Внешний сторожевой таймер это костыль для плохих разработчиков, которые не могут разработать нормально работающую программу для микроконтроллеров или стабильно работающую схему.
Вот это да-а-а…
Статью с таким начальным посылом можно дальше не читать?Sun-ami
06.04.2019 16:18Да, посыл в корне неверный. Сторожевой таймер — это защита прежде всего от аппаратных сбоев, которые невозможно предотвратить на 100% — всегда есть естественная радиация, и возможны внешние электромагнитные воздействия, от которых не спасут все применяемые на практике меры противодействия им. И применяются они не только в микроконтроллерах, но и в промышленных компьютерах и серверах.
olartamonov
06.04.2019 15:15// Сброс таймера (не реже чем 1 на время срабатывания WDT, установленное перемычкой)
И, кстати, да, но нет.
Таймеры на встроенных в контроллер RC-генераторах довольно нестабильны по температуре, поэтому надо смотреть не номинальное время срабатывания у вас на столе (оно же «установленное перемычкой»), а минимально возможное в температурном диапазоне эксплуатации устройства. Оно запросто может отличаться на десятки процентов.sav13 Автор
06.04.2019 17:34В ATtiny13 4-е поколение встроенного генератора, имеющее неплохую внутреннюю калибровку. Как правила, настройка калибровочных значений идет уже с завода. При необходимости можно произвести самостоятельную температурную калибровку с достижением 1% точности, что для моих задач более чем достаточно
olartamonov
06.04.2019 18:58+2При необходимости можно произвести самостоятельную температурную калибровку с достижением 1% точности
Вы планируете к своему вотчдогу ещё и высокоточный термодатчик добавлять?..
RamirezRodrigues
06.04.2019 17:26+2Удивлен, что в устройстве, которое делается «для достижения максимальной надежности», нет резистора 10K для ноги reset и конденсатора 100 nF на питание.
easyelectronics.ru/podklyuchenie-mikrokontrollera-likbez.htmlsav13 Автор
06.04.2019 17:28На ресет, как и на другие входы, есть встроенная подияжка
0.1мкф по питанию — в даташите такого нету. А если учесть, что высокочастотной нагрузки нету, то в принципе можно и обойтись
sav13 Автор
07.04.2019 15:28Почему бы не обратиться по подтяжке к даташиту на AVR?
When designing a system where debugWIRE will be used, the following must be observed:
• Pull-Up resistor on the dW/(RESET) line must be in the range of 10k to 20 k?. However, the
pull-up resistor is optional.
Andy_Big
06.04.2019 18:46Только я что-то не понял плюсов подобного принципа работы вачдога — строго периодичное изменение сигнала. Вроде вачдоги всегда строятся на превышении времени между сбросами, а не на строгой синхронности с отслеживаемым девайсом, которому нельзя ни шага влево-вправо от заданного периода сброса.
apple01
06.04.2019 18:50
Мой вариант watchdog на ATTINY13 для ESP8266 в формате шилда Wemos D1. Питание подается через разъем USB, программирование задержек осуществляется перемычками на оборотной стороне. В случае зависания ESP8266 ей отключается питание на выбранный интервал времени.
slog2
06.04.2019 21:48+2Я на схеме не увидел ни одного конденсатора, шунтирующего питание. Сомневаюсь что этот «костыль для плохих разработчиков» будет надёжнее самого защищаемого устройства.
KN_Dima
07.04.2019 06:57Повесить МК можно не только по питанию, но и по… выходу, а они здесь выведены напрямую, так что да — вы правы.
amartology
07.04.2019 09:55+2Внешний сторожевой таймер это костыль для плохих разработчиков, которые не могут разработать нормально работающую программу для микроконтроллеров или стабильно работающую схему.
Вот где-то тут я начал подозревать, что автор не понимает в вочдогах примерно ничего.
Тем более встроенный WDT имеется у большинства современных микроконтроллеров.
И второе предложение усугубило это подозрение.
Вот что пишет про вочдоги автор, которому действительно можно доверять (Ганссл):
WDTs are not emergency outs, but integral parts of our systems. The WDT is as important as main() or the runtime library, it's an asset that is likely to be used, and maybe used a lot.
AKudinov
07.04.2019 10:22Окончательно закрепляет впечатление комментарий выше про подтяжки на входах и фильтрацию питания.
sav13 Автор
07.04.2019 15:18Подскажите литературу про обязательную внешнюю подтяжку?
В даташите на ATtiny13 сказано, что встроенной подтяжки вполне достаточно
When designing a system where debugWIRE will be used, the following must be observed:
• Pull-Up resistor on the dW/(RESET) line must be in the range of 10k to 20 k?. However, the
pull-up resistor is optional.jaiprakash
07.04.2019 16:23Это правило хорошего тона, основанное на практике. Если вы можете абсолютно гарантировать что все ваши устройства никогда не попадут в условия наводок, или возможный слёт прошивки для вас неважен — необязательно подтягивать.
AKudinov
07.04.2019 16:58Прямо вот с языка сняли про правило хорошего тона.
В даташите на ATtiny13 сазано, что подтяжка на входе Reset имеет сопротивление 30..80 кОм. Да, «на столе» и в «тепличных условиях» этого достаточно, но в более жёстких условиях сопротивление хочется иметь поменьше, чтобы амплитуда импульсов напряжения, наводимых помехами на входы МК, была поменьше, потому и нужен внешний резистор. Я бы его вообще намертво к питанию приколотил, но через Reset ещё debugWire ходит.
Ну, а ёмкость 0.1 мкФ рядом с выводами питания — просто классика жанра. С одной стороны, улучшает стабильность работы самой микросхемы (контур, через который текут высокочастотные токи помех, замыкается через ёмкость, его площадь и индуктивность снижаются, уменьшая вероятность возникновения бросков напряжения, способных завесить микросхему; мы же стабильно работающую схему проектируем), с другой стороны, защищает цепи питания от внешнего мира (опять же, токи помех замыкаются через ёмкость).olartamonov
07.04.2019 19:37Вообще говоря, как минимум в STM32 и EFM32 на ресете есть фильтр, который гарантированно отсекает помехи длительностью до 50 нс, в результате чего встроенной подтяжки вполне достаточно — при отсутствии длинной дорожки до ресета и электроэрозионного станка в радиусе нескольких метров.
В тиньке они тоже есть, но их параметры в явной форме даташит не сообщает.
jaiprakash
К сожалению, в сложной электромагнитной обстановке AVR-ка прекрасно зависает вместе с её сторожевиком.
sav13 Автор
Так можно практически про любую маломощную схему сказать. 24 вольтовое клацкающее реле с током обмотки по 0.5А тут намного надежнее )))
Делал WDT под свою задачу — удаленные устройства сбора информации на GSM с небольшим бюджетом, когда выезд на место перегрузить зависший модем очень затратное мероприятие
jaiprakash
Против вашего решения ничего не имею, даже неиспользование специальных микросхем можно объяснить тем, что нужно было срочно, а тиньки валялись россыпью.
Я скорее пишу как предупреждение тем, кто попытается применить это в других условиях.
Чисто умозрительно, чем проще микросхема, тем лучше справится с помехами, а МК не просты.
olartamonov
В тиньке вообще свой вотчдог есть. Стоит его использовать.
Ну и вообще вся программа написана странно. Заводим таймер, по изменению состояния на входе сбрасываем его счётчик, по переполнению — сбрасываем контролируемое устройство. Всё.
Hint: и да, в качестве таймера можно использовать таймер собственного вотчдога тиньки ;)
jaiprakash
У меня как-то AVR стабильно зависал вместе с вочдогом. Из-за наводок. Так что «глупый» внешний вочдог имхо надёжнее.
sav13 Автор
ХЗ. У меня AVR-ки сами по себе не зависали. Вот всякие там ESP-шки это да. Хотя в области сильных электромагнитных помех я их не ставил. Там как то уже промышленные решения нужны.
olartamonov
Это всегда.
sim31r
WDT не должен висеть в прерывании и еще есть несколько нюансов. Если помеховая обстановка такая что «ужас-ужас», можно проверять значение всех переменных и чуть что не так перезагружать микроконтроллер. В итоге, как бы не изменилось содержание RAM, микроконтроллер просто не сможет зависнуть.
Умный ватчдог может кроме простого зависания отслеживать сбой в передаче данных, не корректные данные с модема, изменение потребления тока схемой, не корректное питание, защищать от высоких и низких температур, ограничивать количество перезагрузок если они не помогают и еще по мелочам.
jaiprakash
Я наверное непонятно выразился.
Происходила полная остановка всего, включая внутренний wdt. Как проверять ram, если тактирование зависло?
Чем умнее внешний вочдог, тем он более подвержен воздействию. И вот нам уже нужны надзиратели за надзирателями.
Andy_Big
Да, тактирование вачдога от общего клока — слабое место. Хорошо когда в контроллере есть IWDT — совершенно независимый вачдог, тактирующийся от собственного внутреннего генератора :)
jaiprakash
Так в AVR независимый генератор для вочдога. Останавливались оба.
Andy_Big
Тогда фиг знает… Это какие наводки должны быть чтобы низкочастотный неуправляемый RC-генератор завис :)
jaiprakash
Достаточно в схеме индуктивности с высоковольтными выбросами. Да, можно всё развязать гальванически и заэкранировать, но в прототипе было по-простому. Хоть и не на висящих проводах, плата была вытравлена, но не сильно помогло.
sav13 Автор
По INT0 думал, но решил, что может всякий «мусор» ловить и сбрасываться
Переполнение счетчика можно настроить на 30 секунд?
olartamonov
а) Подавления дребезга и фильтрации мусора у вас в коде нет, так что ваше решение лучше только тем, что на мусор будет реагировать только при его прилёте в момент опроса, что не есть надёжная защита
б) Ничто не мешает на прерываниях сделать не просто ловлю импульса сброса, а определение его ширины и сравнение с минимально допустимой — но тогда будьте добры со стороны контролируемого устройства обеспечить формирование импульсов сброса заданной ширины (и это всё не имеет практического смысла в 99,999 % случаев)
Вы у меня спрашиваете? Тиньки вы программируете, я их и в руках-то не знаю сколько лет не держал. Это в даташите написано.