Универсальный сторожевой таймер на 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 решил не трогать) распределились следующим образом:

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

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

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

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

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

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

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


  • МК Attiny13-SSU ~ $0.3 (при покупке 10 шт)
  • MOSFET P-канал IRLML2502 — $0.03 (заказ 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. Разбираться, что там накосячила наизменяла дружная команда arduino.cc желания не возникло )))



Тиньку настроил на работу от внутреннего резонатора с частотой 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://habr.com/ru/post/447004/


Интересные статьи

Интересные статьи

Поведение планировщика Windows значительно изменилось в Windows 10 2004 без каких-либо предупреждений и изменения документации. Вероятно, это поломает несколько приложений. Такое прои...
Если попытаться в двух словах описать, в чем заключается функция роутинга на фронтэнде веб-приложений, то можно придти к выводу, что каждый популярный фреймоворк совершенно по-разному представляе...
C++ — язык запутанный, и существенным его недостатком является сложность создания изолированных блоков кода. В типовом проекте всё зависит от всего. Эта статья показывает, как писать высокоизолир...
Есть статьи о недостатках Битрикса, которые написаны программистами. Недостатки, описанные в них рядовому пользователю безразличны, ведь он не собирается ничего программировать.
Эта статья посвящена одному из способов сделать в 1с-Битрикс форму в всплывающем окне. Достоинства метода: - можно использовать любые формы 1с-Битрикс, которые выводятся компонентом. Например, добавле...