Arduino не перезагружается с включенным watchdog

У меня есть специальная плата, которая переключает индуктивную нагрузку через реле переменного тока 10A@230V. Из-за плохой конструкции скачки напряжения заставляют плату время от времени перезагружаться (коричневый, сторожевой пес и "неизвестная причина").

Проблема здесь в том, что время от времени микроконтроллер зависает и больше не выполняет код (или, по крайней мере, так кажется), даже если сторожевой пес должен быть запущен. Я запрограммировал предохранители WDTON и BROWN-OUT, так что сторожевой пес должен быть всегда включен.

Как только микроконтроллер зависает, внешний сброс заставляет его снова работать.

Мои вопросы:

Есть ли какое-нибудь программное обеспечение, гарантирующее, что устройство никогда не зависнет? Неправильно ли я использую сторожевой пес?

Я прочитал в спецификации Atmega 328P об ограничении на 4 цикла между записью WDE и записью битов прескалера (стр. 44), но мне неясно, применяется ли это ограничение при программировании предохранителя WDON. Кроме того, я не мог найти, что делает инструкция "wdt_enable (WDTO_2S);" под капотом.

Возможно ли, что сторожевой генератор останавливается из - за скачка напряжения?

Это мой код (часть, иллюстрирующая проблему сторожевого пса)

Оборудование: Изготовленная на заказ плата

микроконтроллер: ATmega 328P

Коричневый предохранитель: запрограммирован

Предохранитель WDTON: запрограммирован


#include <avr/wdt.h>

#define R0          3 // Relay output 16A @ 230Vac (10A @ 230Vac)
#define R1          4 // Relay output 10A @ 230Vac (10A @ 230Vac)
#define R2          5 // Relay output 10A @ 230Vac (10A @ 230Vac)

byte outputs;

//-------------------------------------------------------------------------

void test() {

#define TEST_SWITCHING_INTERVAL  500

  static unsigned long previousMillisTest;
  static int count;

  unsigned long actualTemp = millis();
  if ((actualTemp - previousMillisTest) >= TEST_SWITCHING_INTERVAL) {
    bitWrite (outputs, 0, !bitRead (outputs, 0)); Serial.print ("R0 = ");    Serial.print (bitRead (outputs, 0));
    bitWrite (outputs, 1, !bitRead (outputs, 1)); Serial.print ("   R1 = "); Serial.print (bitRead (outputs, 1));
    bitWrite (outputs, 2, !bitRead (outputs, 2)); Serial.print ("   R2 = "); Serial.print (bitRead (outputs, 2));
    Serial.print ("   count: "); Serial.print (count++);
    Serial.print ("  millis: "); Serial.println (previousMillisTest);
    previousMillisTest = actualTemp;
  }
  digitalWrite (R0, bitRead (outputs, 0));
  digitalWrite (R1, bitRead (outputs, 1));
  digitalWrite (R2, bitRead (outputs, 2));
}

//-----------------------------------------------------------------------

void setup() {

  uint8_t MCUSR_save;

  MCUSR_save = MCUSR;
  MCUSR = 0;
  wdt_enable (WDTO_2S);

  Serial.begin (9600);

  if      (MCUSR_save & 0x01)  Serial.println ("*********Restarting . Power-on Reset*********");
  else if (MCUSR_save & 0x02)  Serial.println ("*********Restarting . External Reset*********");
  else if (MCUSR_save & 0x04)  Serial.println ("*********Restarting . Brown-out Reset*********");
  else if (MCUSR_save & 0x08)  Serial.println ("*********Restarting . Watchdog Reset*********");
  else if (MCUSR_save & 0x10)  Serial.println ("*********Restarting . JTAG Reset*********");
  else                         Serial.println ("*********Restarting . Unknown reason*********");
  Serial.print ("MCUSR = 0x"); Serial.println (MCUSR_save, HEX);

  initPins();
  initDevice();

  if (DEBUG) showConfig();

  // prevents flooding the RS485 bus if the device is continuously restarting
  delay (100);

  sendFrame (RESTARTED, 0); // sends the "restarted" event through the RS485 port

}

//---------------------------------------------------------------------------

void loop() {
  wdt_reset();
  test();
}

, 👍2


1 ответ


3

Вы можете выбрать либо сброс процессора, либо выполнение процедуры прерывания. Пример кода:

#include <avr/wdt.h>

// сторожевые интервалы
// битовые паттерны сна для WDTCSR
enum
{
  WDT_16_MS  =  0b000000,
  WDT_32_MS  =  0b000001,
  WDT_64_MS  =  0b000010,
  WDT_128_MS =  0b000011,
  WDT_256_MS =  0b000100,
  WDT_512_MS =  0b000101,
  WDT_1_SEC  =  0b000110,
  WDT_2_SEC  =  0b000111,
  WDT_4_SEC  =  0b100000,
  WDT_8_SEC  =  0b100001,
 };  // end of WDT intervals enum
 
void setup()
  {
  wdt_disable ();
  Serial.begin(115200);
  Serial.println ();
  Serial.println("After reset");
  watchdogSetup(WDT_4_SEC);
  }

volatile bool watchdogFired = false;

ISR(WDT_vect) // Watchdog timer interrupt.
  {
  watchdogFired = true;
  }
 
void watchdogSetup(byte interval)
  {
  cli(); // disable all interrupts

  // Enter Watchdog Configuration mode:
  WDTCSR = bit (WDCE) | bit (WDE);
  // Set Watchdog settings:
  //  WDTCSR = bit (WDIE) | interval;   // set WDIE, and requested delay - for an INTERRUPT
  WDTCSR = bit (WDE) | interval;    // set WDIE, and requested delay - for a RESET
  wdt_reset(); // reset the WDT timer
  sei();  // enable interrupts again
  }
  
void loop()
  {

  if (watchdogFired)
    {
    watchdogFired = false;
    Serial.println ("Watchdog fired.");
    }  

  // wdt_reset();  // if you want to reset the watchdog
  
  } // end of loop

Этот пример сбрасывает процессор, однако если вы раскомментируете строку WDTCSR = bit (WDIE) | interval; и прокомментируете строку ниже WDTCSR = bit (WDE) | interval; то вместо этого она прерывается. Вы можете видеть это в примере вывода.

Если вам нужно прерывание, вы должны предоставить подпрограмму прерывания (ISR).


Ответы на комментарии:

Но существует мало документации о том, как адекватно восстановиться после сброса, когда предохранитель запрограммирован.

Процессор сбрасывается, а вы этого хотите, верно? Вам не нужно "восстанавливаться" после сброса. Это все равно что сказать, как вы восстанавливаетесь после его включения.

Нужно ли мне снова включать сторожевой пес, даже если он должен быть включен предохранителем?.

Сомневаюсь. В моем коде сторожевой пес продолжает стрелять даже после того, как вы получаете сообщение в первый раз (прерывание).

Однако вы можете изменить интервал на тот, который вам нужен. И вы можете сбросить сторожевой пес (wdt_reset ()), если все идет нормально.

Кроме того, при включенном предохранителе WDTON режим ISR недействителен (только режим сброса).

Ну, для режима ISR не обязательно существует ISR (пока, если вообще существует). В таблице данных конкретно говорится, что вы получаете режим сброса, а не режим ISR.

Из таблицы данных неясно, каков интервал времени, когда запрограммирован предохранитель WDTON.

,

Спасибо вам за ваш ответ. Я стараюсь следить за тем, чтобы сторожевой пес ВСЕГДА работал, чтобы предотвратить замерзание. В вашем примере сторожевой пес отключен в функции настройки. Вот почему я решил запрограммировать предохранитель WDTON. Но существует мало документации о том, как адекватно восстановиться после сброса, когда предохранитель запрограммирован. Нужно ли мне снова включать сторожевой пес, даже если он должен быть включен предохранителем?. Кроме того, при включенном предохранителе WDTON режим ISR недействителен (только режим сброса)., @Jvila

См.Мой исправленный ответ, который отвечает на ваши вопросы., @Nick Gammon

"Процессор сбрасывается, чего вы и хотите, верно?" -> Дело в том, что процессор ИНОГДА НЕ сбрасывается. Он замораживается даже с запрограммированным WDTON. 99,9% времени он работает нормально, но все же у меня есть несколько случаев замораживания. Моя предыдущая версия была очень похожа на ваш код, но я предполагал: скачок напряжения -> Сброс uC -> выполнение функции установки -> отключение сторожевого пса -> счетчик программ прыгает в другом месте кода из-за EMI - > программа зависает, потому что сторожевой пес отключен. Я знаю, может быть, это слишком много догадок, но я перешел на WDTON, и это улучшилось, @Jvila

Теперь я думаю, что, возможно, один скачок напряжения останавливает сторожевой генератор, а также осциллятор uC. Вы не знаете, возможно ли это?. Дело в том, что только при внешнем перезапуске uC снова оживает, @Jvila

Скачок напряжения мог бы перевести эту штуку в режим высоковольтного программирования. Если вы можете дотянуться до этой штуки достаточно, чтобы нажать Сброс, разве вы не можете подключить защиту от шипов? Например, стабилитрон, который перенаправляет высокое напряжение на землю., @Nick Gammon

Я уже пробовал разместить стабилитрон между Vcc (на разъеме ISP) и GND. С этим не повезло. Шипы уже есть (я вижу их с помощью прицела), а также перезагрузки. Я также пробовал использовать демпферы, фильтры emi, оптопарные реле и т. Д. Единственное, что действительно работало, - это ССР. Но все же я хотел бы избежать их установки. Но это кажется мне единственным вариантом, так как кажется невозможным решить эту проблему только с помощью изменения программного обеспечения. Во всяком случае, спасибо вам за помощь., @Jvila