buserror/simavr

WDIE is wronly cleared on interrupt in Interrupt Mode

WGH- opened this issue · 4 comments

WGH- commented

WDIE should be only cleared in "Interrupt and System Reset Mode".

The fix is to add && avr_regbit_get(avr, p->wde) condition.

static void avr_watchdog_irq_notify(
struct avr_irq_t * irq,
uint32_t value,
void * param)
{
avr_watchdog_t * p = (avr_watchdog_t *)param;
avr_t * avr = p->io.avr;
/* interrupt handling calls this twice...
* first when raised (during queuing), value = 1
* again when cleared (after servicing), value = 0
*/
if (!value && avr_regbit_get(avr, p->watchdog.raised)) {
avr_regbit_clear(avr, p->watchdog.enable);
}
}

I think I'll submit a PR (unless someone does that first) once I get access to hardware to double-check the tests on.

Hi @WGH-

I have applied your fix suggestion to my simavr fork and now I can use WDT and Arduino_freeRTOS without any problems.
I recommend you do a PR. Thanks for the info.

WGH- commented

@lcgamboa just to clarify, Arduino_freeRTOS depends on the correct behaviour of WDIE auto-clearing, doesn't work correctly on simavr, and my suggested change fixes it?

Yes, I tested some examples from the Arduino_freeRTOS library and they all worked after the fix you suggested.

I was having issues with the watchdog timer interrupts and I'm glad to find this issue. Here's a simple example code for testing purposes. it uses the watchdog timer to blink an LED.

The watchdog timer interrupt only works once and then stops. I updated the interrupt clearing line with the following line of code as an interim solution.

Current: WDTCSR |= (1 << WDIF);
Temporary Solution: WDTCSR |= (1 << WDIE) | (1 << WDIF);

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

ISR(WDT_vect)
{
    // Clear interrupt flag by writing 1 to it.
    WDTCSR |= (1 << WDIF);
    // Toggle LED.
    PORTB ^= _BV(PB5);
}

int main()
{
    // Set LED as output.
    DDRB |= _BV(DDB5);

    // Enable watchdog timer.
    WDTCSR |= (1 << WDCE) | (1 << WDE);
    WDTCSR = (1 << WDIE);

    // Toggle LED.
    PORTB ^= _BV(PB5);

    // Enable global interrupts.
    sei();

    // Loop forever.
    while (1)
    {
        _NOP();
    }
}