Search code examples
sleeppower-managementatmelcortex-msleep-mode

Cortex M0 doesn't enter sleep mode


Got a problem with the Atmel SAMB11 on an explained pro Devboard. I've loaded a quite simple example from Atmel, where a 32KHz Timer is initialized to wake up the µC from sleep and turn on a LED. Problem is, the controller doesn't sleep at all. It just activates the LED immediately and doesn't wait for an interrupt.

#include <asf.h>

// Callback Func to enable LED
static void aon_sleep_timer_callback(void)
{
    gpio_pin_set_output_level(LED_0_PIN, LED_0_ACTIVE);
}
//Configure LED
static void configure_gpio_pins(void)
{
    struct gpio_config config_gpio_pin;
    gpio_get_config_defaults(&config_gpio_pin);
    config_gpio_pin.direction = GPIO_PIN_DIR_OUTPUT;
    gpio_pin_set_config(LED_0_PIN, &config_gpio_pin);
    gpio_pin_set_output_level(LED_0_PIN, LED_0_INACTIVE);
}
// Configure Timer with 10sec to overflow
static void configure_aon_sleep_timer(void)
{
    struct aon_sleep_timer_config config_aon_sleep_timer;
    aon_sleep_timer_get_config_defaults(&config_aon_sleep_timer);
    config_aon_sleep_timer.counter = 320000; // Wait about 10sec
    aon_sleep_timer_init(&config_aon_sleep_timer);
}
// Configure Callback and enable Interrupt
static void configure_aon_sleep_timer_callback(void)
{
    aon_sleep_timer_register_callback(aon_sleep_timer_callback);
    NVIC_EnableIRQ(AON_SLEEP_TIMER_IRQn);
}

int main(void)
{
    // Setup Clock, LED and Timer
    system_clock_config(CLOCK_RESOURCE_XO_26_MHZ, CLOCK_FREQ_26_MHZ);
    configure_gpio_pins();
    configure_aon_sleep_timer();
    configure_aon_sleep_timer_callback();

    // wait for timer to be active
    while(!aon_sleep_timer_sleep_timer_active());
    // Go to sleep
    asm volatile ("wfi");
    asm volatile ("nop");
    // Enable LED immediately if sleep doesn't work
    gpio_pin_set_output_level(LED_0_PIN, LED_0_ACTIVE);
    while (true) {}
}

Code seems self-explanatory, but the WFI command doesn't work here. Anyone can help?


Solution

  • The WFI call works, it just receives an interrupt almost immediately after it is called, which causes the WFI call to stop blocking and then execution continues to the next line.

    You could safely remove everything below // Go to sleep which would allow the main function to return. The AON timer would still execute its callback. However, there are a couple of potential downsides to this approach:

    • This would not allow the SAMB11 to transition to a lower power mode.
    • This removes your while-loop at the end of main. In its current state, the while-loop isn't needed, but you might have plans to add code to it later.

    Here is an example that configures the AON, configures the SAMB11 to use low power modes, and then loops waiting for platform and/or BLE events. Currently there are no events for the loop to receive. If you want the loop to receive events then you could modify the AON callback to post an event with the at_ble_event_user_defined_post function or modify main() to configure the BLE module before entering the loop.

    Use the ASF Wizard to add any of the BLE modules to your project in order to compile this example.

    #include <asf.h>
    #include "platform.h"
    
    // Configure LED
    static void configure_gpio_pins(void)
    {
        struct gpio_config config_gpio_pin;
        gpio_get_config_defaults(&config_gpio_pin);
        config_gpio_pin.direction = GPIO_PIN_DIR_OUTPUT;
        gpio_pin_set_config(LED_0_PIN, &config_gpio_pin);
        gpio_pin_set_output_level(LED_0_PIN, LED_0_INACTIVE);
    }
    
    // Callback Func to toggle LED
    static bool led_is_on = false;
    static void aon_sleep_timer_callback(void)
    {
        configure_gpio_pins();
        if(led_is_on) {
            gpio_pin_set_output_level(LED_0_PIN, LED_0_INACTIVE);
            led_is_on = false;
        } else {
            gpio_pin_set_output_level(LED_0_PIN, LED_0_ACTIVE);
            led_is_on = true;
        }
    }
    
    // Configure Timer to fire periodically
    static void configure_aon_sleep_timer(void)
    {
        struct aon_sleep_timer_config config_aon_sleep_timer;
        aon_sleep_timer_get_config_defaults(&config_aon_sleep_timer);
        config_aon_sleep_timer.counter = 32000; // Wait about 1 sec
        config_aon_sleep_timer.mode = AON_SLEEP_TIMER_RELOAD_MODE;
        aon_sleep_timer_init(&config_aon_sleep_timer);
    }
    // Configure Callback and enable Interrupt
    static void configure_aon_sleep_timer_callback(void)
    {
        aon_sleep_timer_register_callback(aon_sleep_timer_callback);
        NVIC_EnableIRQ(AON_SLEEP_TIMER0_IRQn);
    }
    
    int main(void)
    {
        // Setup Clock
        system_clock_config(CLOCK_RESOURCE_XO_26_MHZ, CLOCK_FREQ_26_MHZ);
    
        plf_drv_status plf_status;
        if((plf_status = platform_driver_init()) == STATUS_SUCCESS) {
    
            // Setup LED and Timer
            configure_gpio_pins();
            configure_aon_sleep_timer();
            configure_aon_sleep_timer_callback();
    
            // wait for timer to be active
            while(!aon_sleep_timer_sleep_timer_active());
    
            // Go to sleep
            release_sleep_lock();
            while(true) {
                // Replace platform_event_wait with at_ble_event_get if you would like to read the received event.
                plf_status = platform_event_wait(0);
            }
        }
    }
    

    Regarding WFI, the following example shows how to turn off most of the interrupts and use WFI to block main(). The LED will toggle every time an interrupt is received. I don't recommend using this as I'm not sure why these interrupts are enabled initially. This is just intended to show how WFI can block on a SAMB11.

    #include <asf.h>
    #include "platform.h"
    
    // Configure LED
    static void configure_gpio_pins(void)
    {
        struct gpio_config config_gpio_pin;
        gpio_get_config_defaults(&config_gpio_pin);
        config_gpio_pin.direction = GPIO_PIN_DIR_OUTPUT;
        gpio_pin_set_config(LED_0_PIN, &config_gpio_pin);
        gpio_pin_set_output_level(LED_0_PIN, LED_0_INACTIVE);
    }
    
    // Callback Func to toggle LED
    static bool led_is_on = false;
    static void toggle_led(void)
    {
        configure_gpio_pins();
        if(led_is_on) {
            gpio_pin_set_output_level(LED_0_PIN, LED_0_INACTIVE);
            led_is_on = false;
        } else {
            gpio_pin_set_output_level(LED_0_PIN, LED_0_ACTIVE);
            led_is_on = true;
        }
    }
    
    int main(void)
    {
        // Setup Clock
        system_clock_config(CLOCK_RESOURCE_XO_26_MHZ, CLOCK_FREQ_26_MHZ);
    
        // Clear all interrupts.
        NVIC->ICER[0] = 0xFFFFFFFF;
    
        // During testing, interrupts were received about once per second; stopped receiving interrupts (LED stopped flashing) after about 2 minutes.
        int loop_count = 0;
        while(true) {
            __WFI();
            toggle_led();
        }
    }