Внешний сторожевой таймер это костыль для плохих разработчиков, которые не могут разработать нормально работающую программу для микроконтроллеров или стабильно работающую схему.
Тем более встроенный 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В: IRLML5203, 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 мм. Я развел два варианта:
Для одностороннего изготовления ЛУТом
И для заказа на заводе с улучшенным дизайном и переходами меж сторонами. (Закажу у китайцев, при случае)
Домашние технологии дают примерно такой прототип.
Прошивка
Программировал я в среде ArduinoIDE с установленной поддержкой Attiny13 — MicroCore. В последней версии IDE были проблемы программатора ArduinoISP, но нормально заработало в версии Arduino IDE 1.6.13. Разбираться, что там накосячила наизменяла дружная команда arduino.cc желания не возникло )))
Тиньку настроил на работу от внутреннего резонатора с частотой 1.2МГц. Программа простая — настраиваем входы/выходы, считываем PB2 — PB4 и определяем время срабатывания, настраиваем таймер и переходим в режим IDLE. По прерыванию по таймеру определяем состояние контрольного входа. Если состояние изменилось на противоположное, сбрасываем счетчик. Если показания счетчика превысило установленное время срабатывания — передергиваем питание на выходе.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 |
#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 ) static const uint16_t AT[] PROGMEM ={4,8,20,40,80,120,240,480}; uint16_t TM=AT[((PINB & 0x1C)>>2)]; 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 в основной программе следующее
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#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а
На всякий случай, IRLML2502 — N-канальный. 6402, как вариант.
Спасибо. Перепутал.
День добрый! Какой посоветуете взять мосфет, IRLML6402 или как у вас IRLML5203?
Лучше всего использовать мосфеты с логическим уровнем управления, который полностью открываются при достаточно низким напряжении на затворе:
IRLML5103, IRLML5203, IRLML6302, IRLML6402 (как у вас)
Подскажите-ток потребления девайса какой?
Так как дисплей светодиодный и не отключается — то 10-20мА. Сам контроллер потребляет меньше 1мА
а почему сам тинка13 а лучше 13v … не защищен от зависаний ???
то есть не защишен сам WDT !!!
Что за дичь!? ПИШИ В VSCODE+PLATFORMIO!!!
Принципиально что-то изменится?
Онлайн отладка появится?
Сильно размер кода сократится?
Alexey, на схеме и печатке ошибка!
Резистор R3 на СБРОС должен быть Pull-Up (подтянут к + питания) а он у тебя — Pull-Down (притянут к GND). Контроллер будет в бесконечном СБРОСе…
А так — схема очень полезная, спасибо!
Да. Конечно подтяжка к VCC
Спасибо
Офигенная штука! Доработал все свои устройства. Работает как часы, автору большое спасибо!