Сторожевой таймер застрял в цикле перезапуска? (мигает зеленый светодиод)

Я пытаюсь настроить способ перезагрузки Arduino по команде. Код ниже должен это сделать, но кажется, что мой Arduino просто застрял в каком-то цикле, из-за которого я не могу загрузить или получить какой-либо последовательный вывод. Зеленый светодиод (контакт 13) мигает очень быстро. Единственный способ остановить это — отключить питание устройства, даже кнопка сброса не работает. Это происходит только тогда, когда «R» получено через последовательный порт или если функция wdt_reset() закомментирована.

#include <avr/io.h>
#include <avr/wdt.h>

int ledPin = 3;

void setup()
{
  MCUSR=0;
  wdt_disable();
  Serial.begin(57600);
  Serial.println("BOOT!");
  pinMode(ledPin, OUTPUT);
  digitalWrite(ledPin, LOW);
  delay(500);
}

void loop(){
  if (Serial.available() > 0){
    char cmd = Serial.read();
    if (cmd == 'R'){
      Serial.println("R received!");
      wdt_enable(WDTO_1S);
      delay(2000);
      Serial.println("1 SEC.");
    }
  }
  wdt_reset();
  digitalWrite(ledPin, HIGH);
}

Что я делаю не так?

, 👍7

Обсуждение

Кажется, я столкнулся с этим некоторое время назад: чтобы загрузить новый эскиз (без сторожевого таймера), я удерживал кнопку сброса до тех пор, пока IDE не перешла в состояние «загрузки»., @sachleen

Также см. примечание внизу этого сообщения: http://ariverpad.wordpress.com/2012/02/26/resetting-the-arduino-through-software-for-fun-and-profit/., @sachleen

@sachleen да, я следил за этой записью, но немного не понимаю, куда идет этот код. Я так понимаю в скетч это не входит? Я бы предпочел не редактировать исходные файлы, поскольку это повлияет на каждую программу, которую я программирую на Arduino., @DominicM

Да, это изменит загрузчик и повлияет также на другие эскизы, но только если вы используете сторожевой таймер., @sachleen

@sachleen Означает ли это, что загрузчик нужно будет перепрошить?, @DominicM


4 ответа


Лучший ответ:

4

Одной из самых очевидных проблем здесь является то, что вы включаете сторожевой таймер с задержкой в 1 с, а затем просите микроконтроллер заснуть на 2 с, печатаете что-то и затем возвращаетесь в цикл. Теоретически это должно быть нормально (ваш wdt должен запустить перезагрузку в течение 2 секунд ожидания), но я видел, как это вызывало проблемы.

Кроме того, некоторые загрузчики (особенно загрузчик Optiboot на платах Uno и более новых) могут иметь проблемы с таймерами-сторожами, из-за чего wdt остается включенным после сброса, даже если цикл настройки отключает его. Для начала попробуйте увеличить время wdt до значения, большего 2S (вы должны иметь возможность сделать WDTO_8S) и посмотрите, сохранится ли проблема.

ИЗМЕНИТЬ - OP использует клон Arduino Pro Mini. Включенный загрузчик на Arduino Pro Mini не поддерживает перезагрузки системы с помощью WDT. По сути, на устройстве с загрузчиком, который не поддерживает перезагрузки WDT, плата перезагрузится, но таймер/сброс не будет приводить к постоянному сбросу платы при перезагрузке. Существуют альтернативные загрузчики для плат Arduino, которые могут решить эту проблему. Другое решение - использовать другой метод перезагрузки платы.

,

Итак, если это может вызвать проблемы, какой метод не вызывает проблем? Я только что попробовал 8 секунд с 9-секундной задержкой с тем же результатом. 8 секунд в любом случае было бы слишком долго, так как мне на самом деле не нужна задержка после последовательной команды., @DominicM

Один из первых вопросов - что это за плата и какой загрузчик вы на ней запускаете? Кроме того - что происходит с более простой версией кода сторожевого таймера? Например, такой, которая не считывает входные данные, а скорее выполняет цикл печати n раз и затем сбрасывается? Вот (http://www.megunolink.com/how-to-detect-lockups-using-the-arduino-watchdog/) пример очень простого использования сторожевого таймера - что происходит, когда вы запускаете что-то вроде этого?, @Nahkki

Я использую клон arduino pro mini с загрузчиком по умолчанию (не уверен, какой именно). С скопированным образцом из ссылки он точно так же зависает через пару секунд после включения., @DominicM

Быстрый поиск в Google по вашему продукту показывает, что у других есть такая же проблема. Загрузчик на Arduino Pro Mini не поддерживает перезагрузки системы, поддерживаемые сторожевым таймером. По сути, они перезапускают систему, но не перезапускают таймер, из-за чего плата постоянно сбрасывается при перезагрузке. Я не использовал Arduino Pro mini, и мне не очень везло с сторонними производителями (с ними все в порядке, просто мне не повезло), поэтому я не могу с ходу порекомендовать другой загрузчик, но быстрый поиск в Google показывает, что есть люди, которые нашли работающий загрузчик для устройства, поддерживающего WDT., @Nahkki

Нужен ли вообще загрузчик для watchdog? Могу ли я вообще избавиться от загрузчика, если я смогу программировать его по bluetooth, это будет для меня лучшим вариантом., @DominicM

Это, вероятно, не решит вашу проблему. Проблема не в загрузчике, а в реализации WDT в загрузчике и в том, как загрузчик обрабатывает события WDT. Избавление от загрузчика не избавляет от проблемы, а просто изменяет ее так, что вам нужно реализовать обработку WDT самостоятельно. Конечно, можно записать скетч на arduino без загрузчика ([Здесь](http://www.arduino.cc/en/Hacking/Programmer) есть некоторая документация по этому поводу.) Но без загрузчика вы не сможете загрузить скетч через bluetooth., @Nahkki

Есть ли конкретная причина, по которой вы используете WDT для сброса Arduino? Есть несколько других методов ([здесь](http://www.instructables.com/id/two-ways-to-reset-arduino-in-software/?ALLSTEPS) есть инструкция, подробно описывающая эти методы), которые вы могли бы использовать, чтобы обойти это., @Nahkki

WDT — единственное программное решение, но я думаю, что воспользуюсь одним из контактов для запуска сброса., @DominicM


-1

Вы можете перезапустить Arduino с помощью этой команды:

asm volatile ("  jmp 0");
,

Это позволит выполнить только мягкий сброс и не вернет настройки к значениям по умолчанию полностью., @DominicM


2

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

Однако, при использовании сброса системы сторожевого таймера необходимо соблюдать некоторые меры предосторожности, поскольку на всех AVR, которые также имеют прерывание сторожевого таймера, сторожевой таймер с предделителем, установленным на 0000 = 16 мс, остается активным после состояния сброса системы сторожевого таймера. Поэтому программист несет ответственность за очистку MCUSR и отключение таймера сторожевого таймера как можно скорее после возникновения такого состояния. Поскольку процедуры LPM для инициализации разделов .data и .bss могут длиться дольше, чем интервал тайм-аута сторожевого таймера, код для отключения сторожевого таймера должен быть помещен в раздел .init3, а содержимое MCUSR может быть дополнительно сохранено для последующего изучения источника сброса. Вот код, который выполняет описанное:

uint8_t mcusr_copy __attribute__ ((section (".noinit")));
void disable_wdt(void) \
     __attribute__((naked)) \
     __attribute__((section(".init3")));
void disable_wdt(void) {
  mcusr_copy = MCUSR;
  MCUSR = 0x00;
  wdt_disable();
}

Однако проблема может возникнуть при использовании загрузчика, который не учитывает возможность состояния сброса системы сторожевого таймера. Если это так, тайм-аут сторожевого таймера произойдет внутри загрузчика (который выполняется первым) до того, как указанный выше код сможет отключить сторожевой таймер, и микроконтроллер застрянет в бесконечном цикле сброса. Поскольку сброс микроконтроллера не очистит флаг WDRF в регистре MCUSR (его можно очистить только программно или с помощью сброса при включении питания), единственным способом будет выключение и включение питания и загрузка нового кода, когда сторожевой таймер все еще не активирован предыдущим кодом. WDRF в регистре MCUSR должен быть очищен до wdt_disable(), поскольку WDE не может быть очищен, если WDRF все еще установлен - поэтому порядок инструкций в приведенном выше коде очень важен. Функция, размещенная в разделе .init3, не должна вызываться из более позднего кода.

,

0

Удалить флаг сторожевого таймера в инициализации — одно из решений. Я использую optiboot 8.0, и он работает отлично.

// Прототип функции
void wdt_init(void) __attribute__ ((naked, used, section(".init3")));

// Реализация функции
void wdt_init(void)
{
    MCUSR = 0;
    wdt_disable();
    return;
}
,