Power saving modes SAM3

Hi All,

I would like to implement power saving modes for SAM3 microcontrollers (Arduino due board) so that RIOT's idle thread can actually save power by entering sleep or wait modes.

I have a RIOT "user space" program successfully entering and exiting WAIT mode. My reference is Atmel Application Note AT03498.

However, when I put the same functionality into cpu/sam3/lpm_arch.c - lpm_arch_set(), I observe several restarts of the board after which the CPU stalls without any possibility of wakeup.

What am I doing wrong?

I attached the new lpm_arch_set() function and my main.c file for reference.

Thanks, Moritz

//=================== lpm_arch.c =================== enum lpm_mode lpm_arch_set(enum lpm_mode target) {     uint32_t j=0;     //configure WKUP0 (PA1, pin no 24, "CANRX0")     gpio_init(GPIO_PIN(PA, 1), GPIO_IN);     SUPC->SUPC_WUIR = SUPC_WUIR_WKUPEN0_ENABLE; //enable WKUP0 as wakeup source     SUPC->SUPC_WUIR |= SUPC_WUIR_WKUPT0_LOW_TO_HIGH; //low to high transition creates wakeup event     SUPC->SUPC_WUMR = SUPC_WUMR_WKUPDBC_32_SCLK; //set debounce period to 32 slow clk cycles

    //configure fast startup     PMC->PMC_FSPR = PMC_FSPR_FSTP0; //set polarity of WKUP0 to active high
    PMC->PMC_FSMR = PMC_FSMR_FSTT0; //enable fast startup input enable at WKUP0          switch(target) {         case LPM_IDLE:
            DEBUG("lpm_arch_set(): setting WAIT mode.\n");
            //enable fast RC oscillator but do not switch to RC now.
            PMC->CKGR_MOR = CKGR_MOR_MOSCSEL | (0x37 << 16) | CKGR_MOR_MOSCXTEN | CKGR_MOR_MOSCRCEN;
            //wait for fast RC oscillator to stabilize             while (!(PMC->PMC_SR & PMC_SR_MOSCRCS));                                  //switch from main xtal oscillator to Fast RC             PMC->CKGR_MOR = (0x37 << 16) | CKGR_MOR_MOSCRCEN | CKGR_MOR_MOSCXTEN;             //wait for main oscillator selection status bit MOSCSELS             while (!(PMC->PMC_SR & PMC_SR_MOSCSELS));                          REG_UART_BRGR = (4000000UL / (16 * 9600UL)); //adjust baudrate generator                                  //disable main xtal oscillator             PMC->CKGR_MOR = (0x37 << 16) | CKGR_MOR_MOSCRCEN;                                  //change frequency of fast rc oscillator             PMC->CKGR_MOR = (0x37 << 16) | PMC->CKGR_MOR | CKGR_MOR_MOSCRCF_4_MHz;             //wait for fast rc oscillator to stabilize             while (!(PMC->PMC_SR & PMC_SR_MOSCRCS));                                  //switch from PLL to main clock             PMC->PMC_MCKR = (PMC->PMC_MCKR & (uint32_t)~PMC_MCKR_CSS_Msk)| PMC_MCKR_CSS_MAIN_CLK;             while (!(PMC->PMC_SR & PMC_SR_MCKRDY));             PMC->PMC_MCKR = (PMC->PMC_MCKR & (uint32_t)~PMC_MCKR_PRES_Msk) | PMC_MCKR_PRES_CLK_1;             while (!(PMC->PMC_SR & PMC_SR_MCKRDY));

            PMC->CKGR_PLLAR = 0x2 << 14; // Stop PLL A, STMODE must be set at 2 when the PLLA is Off

            PMC->PMC_FSMR |= (PMC_FSMR_LPM); //Set LPM bit in PMC Fast Startup Mode Register (PMC_FSMR)             SCB->SCR &= (uint32_t)~SCB_SCR_SLEEPDEEP_Msk;

            __DSB(); // ensure all memory accesses have completed             __WFE(); // go to low power mode (issue wait-for-event instruction)                                  for(j=0;j<500;j++) {                 __NOP();             }             while (!(PMC->CKGR_MOR & CKGR_MOR_MOSCRCEN));
                         break;         case LPM_SLEEP:             DEBUG("lpm_arch_set(): setting SLEEP mode.\n");             break;         case LPM_POWERDOWN:             DEBUG("lpm_arch_set(): setting BACKUP mode.\n");             break;         default:             DEBUG("lpm_arch_set(): unhandled low-power mode.\n");             return 0;     }

    return 0; }

//=================== main.c =================== #include <stdio.h> #include "xtimer.h" #include "timex.h" #include "periph/gpio.h"

#include "thread.h" #include "msg.h"

#include "lpm.h" #include "arch/lpm_arch.h"

/* set interval to 200 millisecond */ #define INTERVAL (2000 * MS_IN_USEC) #define LED_RED GPIO_PIN(PB, 25) #define LED_GREEN GPIO_PIN(PC, 28)

char blink_stack[THREAD_STACKSIZE_MAIN]; xtimer_t xtimer;

int main(void) {     uint32_t i=0;     uint32_t last_wakeup = xtimer_now();     gpio_init(LED_RED, GPIO_OUT);     gpio_clear(LED_RED);
         kernel_pid_t me = thread_getpid();
    printf("thread started, pid: %" PRIkernel_pid "\r\n", me);

    while(1) {         printf("going to sleep...\r\n");
                 xtimer_periodic_wakeup(&last_wakeup, INTERVAL);                  printf("main(): slept until %" PRIu32 "\r\n", xtimer_now());
        if(i==1) {             gpio_set(LED_RED);             i=0;         } else {             gpio_clear(LED_RED);             i=1;         }     }

    return 0; }

// =============== terminal output (scheduler debug enabled) ================ 2016-11-23 12:51:15,104 - INFO # Created thread idle. PID: 1. Priority: 15. 2016-11-23 12:51:15,157 - INFO # sched_set_status: adding thread 1 to runqueue 15. 2016-11-23 12:51:15,202 - INFO # Created thread main. PID: 2. Priority: 7. 2016-11-23 12:51:15,255 - INFO # sched_set_status: adding thread 2 to runqueue 7. 2016-11-23 12:51:15,300 - INFO # sched_run: active thread: 0, next thread: 2 2016-11-23 12:51:15,349 - INFO # sched_run: done, changed sched_active_thread. 2016-11-23 12:51:15,444 - INFO # main(): This is RIOT! (Version: 2016.10-devel-407-gd1111-christina.ias.rwth-aachen.de) 2016-11-23 12:51:15,468 - INFO # thread started, pid: 2 2016-11-23 12:51:15,489 - INFO # going to sleep... 2016-11-23 12:51:15,546 - INFO # sched_set_status: removing thread 2 from runqueue 7. 2016-11-23 12:51:15,591 - INFO # sched_run: active thread: 2, next thread: 1 2016-11-23 12:51:15,640 - INFO # sched_run: done, changed sched_active_thread. 2016-11-23 12:51:15,652 - INFO # idle thread 2016-11-23 12:51:17,488 - INFO # Created thread idle. PID: 1. Priority: 15. 2016-11-23 12:51:17,541 - INFO # sched_set_status: adding thread 1 to runqueue 15. 2016-11-23 12:51:17,586 - INFO # Created thread main. PID: 2. Priority: 7. 2016-11-23 12:51:17,639 - INFO # sched_set_status: adding thread 2 to runqueue 7. 2016-11-23 12:51:17,684 - INFO # sched_run: active thread: 0, next thread: 2 2016-11-23 12:51:17,733 - INFO # sched_run: done, changed sched_active_thread. 2016-11-23 12:51:17,827 - INFO # main(): This is RIOT! (Version: 2016.10-devel-407-gd1111-christina.ias.rwth-aachen.de) 2016-11-23 12:51:17,852 - INFO # thread started, pid: 2 2016-11-23 12:51:17,872 - INFO # going to sleep... 2016-11-23 12:51:17,930 - INFO # sched_set_status: removing thread 2 from runqueue 7. 2016-11-23 12:51:17,975 - INFO # sched_run: active thread: 2, next thread: 1 2016-11-23 12:51:18,024 - INFO # sched_run: done, changed sched_active_thread. 2016-11-23 12:51:18,036 - INFO # idle thread 2016-11-23 12:51:19,871 - INFO # Created thread idle. PID: 1. Priority: 15. 2016-11-23 12:51:19,925 - INFO # sched_set_status: adding thread 1 to runqueue 15. 2016-11-23 12:51:19,970 - INFO # Created thread main. PID: 2. Priority: 7. 2016-11-23 12:51:20,023 - INFO # sched_set_status: adding thread 2 to runqueue 7. 2016-11-23 12:51:20,068 - INFO # sched_run: active thread: 0, next thread: 2 2016-11-23 12:51:20,117 - INFO # sched_run: done, changed sched_active_thread. 2016-11-23 12:51:20,211 - INFO # main(): This is RIOT! (Version: 2016.10-devel-407-gd1111-christina.ias.rwth-aachen.de) 2016-11-23 12:51:20,236 - INFO # thread started, pid: 2 2016-11-23 12:51:20,256 - INFO # going to sleep... 2016-11-23 12:51:20,314 - INFO # sched_set_status: removing thread 2 from runqueue 7. 2016-11-23 12:51:20,359 - INFO # sched_run: active thread: 2, next thread: 1 2016-11-23 12:51:20,408 - INFO # sched_run: done, changed sched_active_thread. 2016-11-23 12:51:20,420 - INFO # idle thread ...