У меня возникла проблема, когда выполнение последовательности сторожевого таймера отключения на AVR ATtiny84A фактически сбрасывает микросхему, даже несмотря на то, что на таймере должно быть достаточно времени. Это происходит непоследовательно и при запуске одного и того же кода на многих физических частях; некоторые сбрасываются каждый раз, некоторые сбрасываются иногда, а некоторые никогда.
Чтобы продемонстрировать проблему, я написал простую программу, которая ...
- Включает сторожевой таймер с задержкой в 1 секунду
- Сбрасывает сторожевой таймер
- Мигает белым светодиодом на 0,1 секунды
- Вспыхнул белый светодиод на 0,1 секунды
- Отключает сторожевой таймер
Общее время между включением и отключением сторожевого таймера составляет менее 0,3 секунды, однако иногда происходит сброс сторожевого таймера при выполнении последовательности отключения.
Вот код:
#define F_CPU 1000000 // Name used by delay.h. We are running 1Mhz (default fuses)
#include <avr/io.h>
#include <util/delay.h>
#include <avr/wdt.h>
// White LED connected to pin 8 - PA5
#define WHITE_LED_PORT PORTA
#define WHITE_LED_DDR DDRA
#define WHITE_LED_BIT 5
// Red LED connected to pin 7 - PA6
#define RED_LED_PORT PORTA
#define RED_LED_DDR DDRA
#define RED_LED_BIT 6
int main(void)
{
// Set LED pins to output mode
RED_LED_DDR |= _BV(RED_LED_BIT);
WHITE_LED_DDR |= _BV(WHITE_LED_BIT);
// Are we coming out of a watchdog reset?
// WDRF: Watchdog Reset Flag
// This bit is set if a watchdog reset occurs. The bit is reset by a Power-on Reset, or by writing a
// logic zero to the flag
if (MCUSR & _BV(WDRF) ) {
// We should never get here!
// Light the RED led to show it happened
RED_LED_PORT |= _BV(RED_LED_BIT);
MCUCR = 0; // Clear the flag for next time
}
while(1)
{
// Enable a 1 second watchdog
wdt_enable( WDTO_1S );
wdt_reset(); // Not necessary since the enable macro does it, but just to be 100% sure
// Flash white LED for 0.1 second just so we know it is running
WHITE_LED_PORT |= _BV(WHITE_LED_BIT);
_delay_ms(100);
WHITE_LED_PORT &= ~_BV(WHITE_LED_BIT);
_delay_ms(100);
// Ok, when we get here, it has only been about 0.2 seconds since we reset the watchdog.
wdt_disable(); // Turn off the watchdog with plenty of time to spare.
}
}
При запуске программа проверяет, был ли предыдущий сброс вызван тайм-аутом сторожевого таймера, и если это так, загорается красный светодиод и очищает флаг сброса сторожевого таймера, чтобы указать, что сброс сторожевого таймера произошел. Я считаю, что этот код никогда не должен выполняться, и красный светодиод никогда не должен загораться, но это часто происходит.
Что здесь происходит?
Ответы:
В процедуре библиотеки wdt_reset () есть ошибка.
Вот код ...
Четвертая строка расширяется до ...
Цель этой строки - записать 1 в WD_CHANGE_BIT, что позволит следующей строке записать 0 в бит включения сторожевого таймера (WDE). Из таблицы данных:
К сожалению, у этого назначения есть побочный эффект также установки младших 3 битов регистра управления сторожевым таймером (WDCE) в 0. Это сразу устанавливает прескалер к его кратчайшему значению. Если новый прескалер уже запущен в момент выполнения этой инструкции, процессор будет сброшен.
Поскольку сторожевой таймер работает от физически независимого генератора частотой 128 кГц, трудно предсказать, каким будет состояние нового прескалера относительно работающей программы. Это объясняет широкий диапазон наблюдаемого поведения, при котором ошибка может быть соотнесена с напряжением питания, температурой и производственной партией, поскольку все эти вещи могут асимметрично влиять на скорость сторожевого генератора и тактовых импульсов системы. Это было очень трудно найти ошибку!
Вот обновленный код, который позволяет избежать этой проблемы ...
Дополнительная
wdr
инструкция сбрасывает сторожевой таймер, поэтому, когда следующая строка потенциально переключается на другой прескалер, гарантированно еще не истекло время ожидания.Это также может быть исправлено с помощью OR, добавляя биты WD_CHANGE_BIT и WDE в WD_CONTROL_REGISTER, как предложено в таблицах данных ...
... но это требует больше кода и дополнительного регистра. Так как сторожевой счетчик сбрасывается, когда он все равно отключен, дополнительный сброс ничего не затирает и не имеет непреднамеренных побочных эффектов.
источник