Универсальный сторожевой таймер на ATtiny13

в 9:01, , рубрики: attiny13, diy или сделай сам, wdt, программирование микроконтроллеров, Разработка под Arduino

Универсальный сторожевой таймер на ATtiny13 - 1
Внешний сторожевой таймер это костыль для плохих разработчиков, которые не могут разработать нормально работающую программу для микроконтроллеров или стабильно работающую схему.

Тем более встроенный WDT имеется у большинства современных микроконтроллеров.

Но бывают случаи, когда приходится иметь дело с готовой платой или модулем с определенными проблемами. Свой первый WDT я сделал для борьбы с редкими, но все же иногда происходящими зависаниями ESP8266. Причем софтовый ресет тогда не спасал и ESP-шка не хотела переподключаться к WiFi. Передергивание питания внешним WDT решило проблему.

Вторая проблема возникла с GSM контроллером Elecrow ATMEGA 32u4 A9G. Здесь имели место быть очень редко случающиеся зависание SIM-карты. (Кстати эту же проблема бывает и с USB-модемами 3G и 4G). Для борьбы с таким зависанием нужно передернуть питание на SIM-ке. И вроде даже вывод у GSM модема для этого есть, но в схемотехнику устройства данная возможность не заложена. И для достижения максимальной надежность пришлось снова обращаться к внешней сторожевой собаке.

Схему на таймере 555 я не стал повторять. Слишком много недостатков у нее выявилось:

  • Большие габариты и довольно много обвязки
  • Неудобная установка времени срабатывания подстроечным резистором
  • Довольно длительное время сброса (необходима разрядка конденсатора)
  • Ну и потенциальное зависание МК с низким уровнем на выходе таймера, когда таймер просто перестает срабатывать.
  • А проектов OpenSource в интернете, полностью соответствующих моим требованиям, я не нашел.

Требования к новому WDT

  • Низкая цена устройства, простота изготовления и малые габариты
  • Управление периодической сменой логического уровня 0/1 на входе
  • Простая настройка времени срабатывания (как вариант выбор из предустановленных интервалов)

Разработка железа

В качестве основной микросхемы выбрал микроконтроллер ATtiny13. Его возможностей оказалось более чем достаточно для моей задачи. А цена, с учетом уменьшения элементов обвязки — практически такая же как у 555 микросхемы.
Универсальный сторожевой таймер на ATtiny13 - 2

Пять выводов МК (RESET решил не трогать) распределились следующим образом:

  1. Выход таймера
  2. Вход для сброса
  3. Три оставшихся вывода — задания времени срабатывания

Для коммутации питания используется P-канальный MOSFET. Подойдет любой совместимый по корпусу, но желательно брать с так называемым «логическим уровнем управления» — то есть полностью открывающийся от низкого напряжения 3-5В: IRLML2502, AO3415 и т.п. Несмотря на малые размеры, данный транзистор способен управлять нагрузкой в 4А. Если нужно коммутировать что-то другое, к этому выходу можно напрямую подключить реле на 5В.

Светодиод загорается в момент срабатывания таймера и отключения основного устройства.

Основной разъем для подключения к плате микроконтроллера имеет четыре вывода

  1. Общая шина
  2. Вход — сброс таймера
  3. Выход +5В (управляется таймером)
  4. Вход +5В

Два разъема — ICSP программатор и джамперы питания можно не устанавливать на плате. Микроконтроллер прошить в программаторе заранее, а время срабатывания задать постоянной перемычкой.

Список комплектующих

Изготовление

Платы получились маленькие — 18×22 мм. Я развел два варианта:

Для одностороннего изготовления ЛУТом
Универсальный сторожевой таймер на ATtiny13 - 3
Универсальный сторожевой таймер на ATtiny13 - 4
И для заказа на заводе с улучшенным дизайном и переходами меж сторонами. (Закажу у китайцев, при случае)
Универсальный сторожевой таймер на ATtiny13 - 5
Универсальный сторожевой таймер на ATtiny13 - 6
Домашние технологии дают примерно такой прототип.
Универсальный сторожевой таймер на ATtiny13 - 7
Универсальный сторожевой таймер на ATtiny13 - 8
Универсальный сторожевой таймер на ATtiny13 - 9

Прошивка

Для прошивки использовал самодельный программатор на баз Arduino Nano
Универсальный сторожевой таймер на ATtiny13 - 10
Программировал я в среде Arduino IDE с установленной поддержкой Attiny13 — MicroCore. В последней версии IDE были проблемы программатора ArduinoISP, но нормально заработало в версии Arduino IDE 1.6.13. Разбираться, что там накосячила наизменяла дружная команда arduino.cc желания не возникло )))

Универсальный сторожевой таймер на ATtiny13 - 11
Тиньку настроил на работу от внутреннего резонатора с частотой 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а
https://github.com/samopal-pro/wdt-tiny13

Автор: sav13

Источник

* - обязательные к заполнению поля