Чому мій AVR скидається, коли я дзвоню wdt_disable (), щоб спробувати вимкнути таймер сторожового собаки?


34

У мене виникає проблема, коли виконання послідовності відключення сторожової собаки на AVR ATtiny84A насправді скидає чіп, хоча у таймера має залишитися багато часу на ньому. Це відбувається непослідовно і при запуску одного і того ж коду на багатьох фізичних частинах; деякі скидаються кожного разу, деякі інколи скидаються, а деякі ніколи.

Щоб продемонструвати проблему, я написав просту програму, яка ...

  1. Вмикає сторожову службу з тайм-аутом на 1 секунду
  2. Скидає сторожову сторону
  3. Блимає білий світлодіод увімкнутим протягом 0,1 секунди
  4. Вимикав білий світлодіод протягом 0,1 секунди
  5. Відключає сторожову службу

Загальний час між включенням та відключенням сторожової собаки становить менше 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.

    }
}

При запуску програма перевіряє, чи був попередній скидання викликаний таймаутом сторожової служби, і якщо так, то він світиться червоним світлодіодом і очищає прапор скидання сторожової собаки, щоб вказати, що відбулося скидання сторожової собаки. Я вважаю, що цей код ніколи не повинен виконуватись, і червоний світлодіод ніколи не повинен вмикатись, але це часто відбувається.

Що тут відбувається?


7
Якщо ви вирішили написати власний Q&A тут щодо цієї проблеми, я можу уявити біль і страждання, які були необхідні для її виявлення.
Володимир Крейвер

3
Будьте впевнені! 12 годин на цю помилку. На деякий час помилка буде ЛИШЕ траплятися поза сайтом. Якби я приніс дошки на робочий стіл, то помилка пішла б, швидше за все, від температурних впливів (моє місце холодне, що змушує осцилятор сторожового органу працювати трохи повільніше порівняно з системним годинником). Для її відтворення знадобилося 30+ випробувань та впіймання в акті на відео.
bigjosh

Я майже відчуваю біль. Я не старий і орієнтований на ЕЕ, але іноді опинявся в таких ситуаціях. Чудовий улов, випити пива та продовжувати вирішувати проблеми;)
Володимир Крейвер

Відповіді:


41

У програмі wdt_reset () є помилка.

Ось код ...

__asm__ __volatile__ ( \
   "in __tmp_reg__, __SREG__" "\n\t" \
   "cli" "\n\t" \
   "out %0, %1" "\n\t" \
   "out %0, __zero_reg__" "\n\t" \
   "out __SREG__,__tmp_reg__" "\n\t" \
   : /* no outputs */ \
   : "I" (_SFR_IO_ADDR(_WD_CONTROL_REG)), \
   "r" ((uint8_t)(_BV(_WD_CHANGE_BIT) | _BV(WDE))) \
   : "r0" \
)

Четвертий рядок розширюється до ...

out _WD_CONTROL_REG, _BV(_WD_CHANGE_BIT) | _BV(WDE)

Наміром цього рядка є записати 1 на WD_CHANGE_BIT, що дозволить наступному рядку записати 0 у біт включення сторожового собаки (WDE). З даних:

Для відключення увімкненого таймера сторожових доріжок необхідно дотримуватися наступної процедури: 1. У цій же операції запишіть логічний код у WDCE та WDE. Логічний має бути записаний у WDE незалежно від попереднього значення біта WDE. 2. Протягом наступних чотирьох тактових циклів в тій же операції запишіть біти WDE та WDP за бажанням, але з очищеним бітом WDCE.

На жаль, це призначення має побічний ефект також встановлення 3-х нижчих біт реєстру контролю сторожової собаки (WDCE) на 0. Це негайно встановлює prescaler на його найменше значення. Якщо новий доскакулер вже був звільнений в момент виконання цієї інструкції, процесор буде скинутий.

Оскільки на сторожовому таймері працює електричний незалежний генератор на 128 кГц, важко передбачити, яким буде стан нового доскалера стосовно запущеної програми. Це пояснює широкий діапазон спостережуваних способів поведінки, коли помилка може бути співвіднесена з напругою живлення, температурою та виробничою партією, оскільки все це може асиметрично впливати на швидкість охоронного осцилятора та системний годинник. Це було дуже важко знайти помилку!

Тут оновлений код, який дозволяє уникнути цієї проблеми ...

__asm__ __volatile__ ( \
   "in __tmp_reg__, __SREG__" "\n\t" \
   "cli" "\n\t" \
   "wdr" "\n\t" \
   "out %0, %1" "\n\t" \
   "out %0, __zero_reg__" "\n\t" \
   "out __SREG__,__tmp_reg__" "\n\t" \
   : /* no outputs */ \
   : "I" (_SFR_IO_ADDR(_WD_CONTROL_REG)), \
   "r" ((uint8_t)(_BV(_WD_CHANGE_BIT) | _BV(WDE))) \
   : "r0" \
)

Додаткова wdrінструкція скидає таймер сторожового собаки, тому коли наступний рядок потенційно переходить на інший доказовий апарат, гарантовано, що він ще не вийшов.

Це також можна виправити, встановивши біти WD_CHANGE_BIT і WDE у WD_CONTROL_REGISTER, як це пропонується у таблицях даних ...

; Write logical one to WDCE and WDE
; Keep old prescaler setting to prevent unintentional Watchdog Reset
in r16, WDTCR
ori r16, (1<<WDCE)|(1<<WDE)
out WDTCR, r16

... але для цього потрібно більше коду та додатковий регістр подряпин. Оскільки лічильник сторожових налаштувань скидається, коли його все одно вимкнено, додаткове скидання нічого не в'яже і не має випадкових побічних ефектів.


7
Я також хотів би надати вам реквізит, тому що, коли я пішов перевірити список
видань

1
ps "josh.com" справжній ... вражаючий
vicatcu
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.