Программный сброс AVR без ватчдога
Я написал обычную программу-оболочку avr, которая уже использует сторожевой таймер для перезапуска, а после сброса сторожевого таймера - восстанавливает плохую ситуацию.
После этого процесса восстановления я должен перезапустить avr. Все, что я нашел в теме «программный сброс arduino», устанавливает кратчайший тайм-аут сторожевого таймера (15 мс), а затем вращает процессор (for(;;);), пока не вызовет сброс сторожевого таймера.
Но это приводит к входу в процесс восстановления, который уже выполнен на этом этапе.
Можно ли добиться этого элегантным способом?
Какой-то не очень или менее элегантный способ:
Я могу привязать цифровой пин к сбросу и подтянуть резистором.
Я могу записать на фиксированный адрес во флэш-памяти или в EEPROM (но это может происходить часто) перед программным перезапуском, чтобы предотвратить вход в режим восстановления.
Есть ли какие-либо регистры, которые "выживают" при программном сбросе?
Изменить:
Я добавляю фиктивный пример кода, чтобы намекнуть, что я пытаюсь сделать.
В реальном коде при восстановлении микроконтроллер подключается к последовательной шине, ожидает команды, затем перезагружается, чтобы вернуться в режим приложения.
/*************************** Оболочка приложения **************** ****************/
// определяем call_application и call_recover как слабые пустые функции
//чтобы их можно было переписать.
bool isWatchdogReset()
{
return MCUSR == 8;
}
void softwareReset()
{
// wdt_enable(WDTO_15MS);for(;;);// (таким образом входит в режим восстановления)
//или
// asm("jmp 0x0");// (таким образом регистры функции не сбрасываются)
}
bool isSoftwareReset()
{
//???
}
int main()
{
bool needRecover = isWatchdogReset() && !isSoftwareReset();
MCUSR = 0;
init_board();
wdt_enable(WDTO_1S);
while(1)
{
if(!needRecover)
{
call_application();
}
else
{
call_recover();
}
wdt_reset();
}
}
/********************************* Application ********************************/
void call_application()
{
if(randomTrue())
{
//зависание более 1 секунды, что приводит к сбросу WDT
}
else
{
//правильная работа
}
}
/********************************** Recover ***********************************/
void call_recover()
{
// инициализируем UART, подключаем прерывания, записываем flash и EEPROM, модифицируем
//регистры функций.
//восстановление завершено, возврат к исходному приложению вроде как
// ничего не произошло.
softwareReset();
}
Изменить: Я пытаюсь объяснить кратко, чтобы понять суть:
1) Я уже использую сторожевой таймер для обнаружения ошибок в коде приложения.
2) программный сброс - неподходящее решение, потому что он не сбрасывает регистры. Так что только программа будет сброшена, состояние микроконтроллера нет.
3) Чтобы правильно выполнить сброс, нужно установить тайм-аут сторожевого таймера. Но смотрите пункт 1), он уже используется, так как же вы можете отличить запрошенный сброс от сброса для восстановления ошибочного кода?
@Dankó Dávid, 👍2
Обсуждение2 ответа
Сброс в основном выполняется по адресу вектора сброса 0x0000
(если у вас нет загрузчика или вы не хотите его запускать).
Есть также регистр MCUSR
, указывающий, какой источник сброса вызвал сброс. Он не будет установлен, если вы сделаете простой переход на адрес сброса.
Память также сохраняется, но код инициализации C/C++ имеет место и инициализирует ваши глобальные переменные. Этого можно избежать, поместив переменную/переменные в раздел .noinit
. Но не определяется, что там после сброса. В сочетании с регистром MCUSR
вы можете инициализировать переменные для обычных источников сброса и оставить их неизменными, если вы перейдете к адресу вектора сброса.
Кроме того, обычные источники сброса переходят к загрузчику, поэтому он может использовать те же области памяти, что и ваши переменные .noinit
, и уничтожать их.
Я проверяю регистр MCUSR сразу после запуска, чтобы определить источник сброса/включения питания. Теперь я попробовал этот способ сброса (void sw_reset(){asm("jmp 0x0");}). Это работает, но, как я разобрал, он просто сбрасывает указатель стека, что приводит к повторному запуску программы из main(), но настройки порта, таймеров и UART остаются., @Dankó Dávid
@DankóDávid Так ты не хочешь перезапускать его? Может быть, это [проблема XY](https://meta.stackexchange.com/a/66378)?, @KIIV
Также обратите внимание, что аппаратный сброс устанавливает все регистры ввода-вывода в исходное значение, чего не происходит при переходе на 0x0000. Лишь очень немногие регистры, такие как MCUSR, не имеют определенного начального значения., @Edgar Bonet
@EdgarBonet да, это может быть проблемой в процессе связи TWI или около того. Но код инициализации надо писать, так как что-то уже настроено. Определенно в этом случае., @KIIV
Я отредактировал вопрос и добавил пример того, какую именно проблему я пытаюсь решить. 1) Я уже использую сторожевой таймер для обнаружения неисправности. 2) Я хотел бы полностью сбросить AVR (сбросить все регистры функций, к которым прикасалась функция восстановления, и я не могу отследить), но единственный способ, который я нашел, - это использовать тайм-аут WDT, что приводит не только к перезапуску, но и к распознать как «неисправность перезапуска». Более упрощенно: мне нужен сброс неисправности (WDT) и намеренный программный сброс, которые не используют один и тот же механизм сброса. Или различайте два способа сброса AVR., @Dankó Dávid
Логика ваших приложений неверна. Вы устанавливаете needRecover
в начале main
, но проверяете его в бесконечном цикле while (1)
. Значение needRecover
не изменится в цикле.
Сначала выполните инициализацию, независимо от того, будет ли обычное включение питания или перезапуск сторожевого таймера, а затем выполните действия в бесконечном цикле.
Вы можете поместить инициализацию в функцию с именем setup
, а бесконечный цикл — в функцию с именем loop
. И есть такая функция main
:
int main( void )
{
setup();
for (;;)
{
loop();
}
return 0;
}
подождите, где-то это уже видели :-)
Процесс восстановления должен перезапустить микроконтроллер. Но теперь для этого используется сторожевой таймер. Что приводит к входу снова в режим рекавери, а не в режим приложений., @Dankó Dávid
@DankóDávid, используйте прерывание сторожевого таймера вместо сторожевого таймера. настройте прерывание, а затем выполните сброс с помощью wdt_enable(WDTO_15MS);
мой здесь: https://github.com/jandrassy/Regulator/blob/b6ef120c6f8963979268d8d17b56e5632336f317/Regulator/Watchdog.ino#L24, @Juraj
вы можете использовать эту библиотеку https://github.com/nadavmatalon/WatchDog. Я планирую заменить ею свою обработку сторожевого таймера., @Juraj
- AsyncWebServer дает сброс wdt
- Сброс Arduino с помощью ПО (каждый день)
- Трассировка стека сброса ESP12E Soft WDT ведет к библиотекам
- Как выйти из загрузочного цикла?
- Barebones AVR — подключение вывода RESET к Vcc
- Сброс Arduino Uno в коде
- Как сбросить или отформатировать Arduino?
- В чем разница/связь между Arduino и AVR?
а почему со вторым сбросом wdt проблема? или почему нельзя продолжить без перезагрузки?, @Juraj
Я прочитал ваш вопрос три раза, но до сих пор не понимаю, что вы описываете. О каком «восстановительном процессе» вы говорите? «Восстанавливают» данные в процедуре прерывания WDT., @Gerben