0
\$\begingroup\$

I have AVR Atmega328p which I want to blink for 5 secs every 16 secs (to test the watchdog interrupts).

I have the following code, which keeps restarting, and the last line is:

test_blink(2);

Which I know because the led on PC2 is always on. Where did I wrong? I re-checked the WD settings for 10s times already..

#define F_CPU 128000

// LED Definitions
#define LED_PORT PORTD
#define LED_PIN PD6
#define LED_DDR DDRD
#define LED_PWM_PCNT 50

// Watchdog definitions
// counter = 2*8 = 16 seconds
#define WD_COUNTER_MAX 2

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/sleep.h>
#include <stdint.h>
#include <util/delay.h>
#include <avr/wdt.h>

// Global watchdog counter (for wake-up counts)
volatile uint8_t watchdog_counter;

// Function declarations [may split to files]
void reset_pins();
void led_blink();
void sleep_me();
void setup_init();
void test_blink(int pin);
void setup_watchdog(uint8_t state);

void led_blink()
{
    LED_DDR = (1<<LED_PIN);
    LED_PORT = (1<<LED_PIN);
    _delay_ms(5000);
}

void test_blink(int pin)
{
    reset_pins();
    DDRC |= (1<<pin);
    PORTC |= (1<<pin);
    _delay_ms(300);
    reset_pins();
}

void reset_pins()
{
    DDRB = 0;
    DDRC = 0;
    DDRD = 0;
    PORTB = 0;
    PORTC = 0;
    PORTD = 0;
}

void setup_watchdog(uint8_t state)
{
    // Watchdog:
    // Clear WD reset flag
    MCUSR = 0;

    WDTCSR = (1 << WDCE) | (1 << WDE);
    if (state == 0) {
        WDTCSR = 0;
    } else {
        WDTCSR = (1<<WDP3) | (1<<WDP0) | (1<<WDIE);
    }
    wdt_reset();
    test_blink(2);
}

void sleep_me()
{
    ADCSRA = 0;
    reset_pins();
    PRR = 0xff;

    // Set sleep mode to POWER DOWN
    SMCR |= (1 << SM1);
    // Enable SLEEP
    SMCR |= (1<<SE);

    // Enable global interrupts.
    sei();

    // Disable pull-up resistors
    SMCR |= (1<<PUD);

    test_blink(2);
    setup_watchdog(1);
  sleep_cpu();
}

void setup_init()
{
    watchdog_counter = 0;
    setup_watchdog(1);
}

// watchdog interrupt
ISR(WDT_vect) {
    setup_watchdog(0);
}

int main(void)
{
    cli();
    setup_init();
    test_blink(4);
  while(1)
  {
        cli();
        // Disable SLEEP
        SMCR &= ~(1<<SE);

        if (watchdog_counter >= WD_COUNTER_MAX) led_blink();
        else sleep_me();

        watchdog_counter++;
  }
}

I am using the following fuses, and avrdude command:

avrdude -B 250 -c usbtiny -P usb -p m328p -U lfuse:w:0xD3:m -U hfuse:w:0xDF:m -U efuse:w:0x07:m -U flash:w:watchdog.hex:a
\$\endgroup\$
1
\$\begingroup\$

From what I can see you are not enabling global interrupts in the status register.

From the datasheet:

Bit 6 – WDIE: Watchdog Interrupt Enable When this bit is written to '1' and the I-bit in the Status Register is set, the Watchdog Interrupt is enabled. If WDE is cleared in combination with this setting, the Watchdog Timer is in Interrupt Mode, and the corresponding interrupt is executed if timeout in the Watchdog Timer occurs. If WDE is set, the Watchdog Timer is in Interrupt and System Reset Mode. The first timeout in the Watchdog Timer will set WDIF. Executing the corresponding interrupt vector will clear WDIE and WDIF automatically by hardware (the Watchdog goes to System Reset Mode)

Search the datasheet for "SREG" and see bit 7-I. This bit needs to be set for any interrupts to work properly.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.