I have an ATSAMD09 CortexM0 I've been developing my understanding of, and am presently sorting out the standby feature.
I have a simple program which configures some clocks, mainly setting up the timer and the core clock. Once they're setup, I configure the timer to run on the ULP32k clock and turn on the interrupts (timer2 overflow interrupt). I set the 'standby' bit on SCR, and then send "WFI" in my deepSleep() routine. Once I call it, boom. The part goes into standby as intended. After N counts of the ULP32k timer, the interrupt trips. The system wakes up into the interrupt routine, I clear the interrupt flag. I rerun my GCLK and SYSCTRL startup routines and toggle a pin a few times to signal that it's back in operation, and then it goes back to sleep. To here, it's doing fine. While in standby, it's 2.5uA. When active, it's about 750uA. Now the second time it's supposed to wake up.... It stalls, and burns 384uA.
The external oscillator doesn't fire up, so I've had it run on the OSC8M. No change in current.
I figured that maybe it's falling into a trap state, and since the Debug (Atmel-ICE) stops working once you go into standby, I'd have it just toggle a pin from the default interrupt handler. It never gets called.
Do any of you experienced ARM folk have any idea what I'm doing wrong? Is there anything I can look for?
Here is my source (not in total, but covers the program aspect):
void deepSleep(void){
pGCLK->GENCTRL=0x00290300; // turn on GEN0 and drive it from OSC32ULP
pGCLK->GENCTRL=0x00290303; // turn on GEN3 and drive it from OSC32ULP (needed for timer)
pSysCtrl->XOSC = 0xAB44; // turn off XOSC
pSysCtrl->OSC8M = 0x880e0380; // turn off OSC8M
*pSCR |= 0x4; // DEEEP SLEEP!
asm volatile("WFI");
}
void wakeUp(void){
volatile uint32_t testResult;
pSysCtrl->OSC8M = 0x880e0382; // turn on OSC8M
while(1){ // wait until OSC8M is ready...
testResult = pSysCtrl->INTFLAG&0x08;
if(0!=testResult)
break;
}
pPort->DIRSET = 0xFFFFFFFF; // set to OUTPUT
pPort->OUTCLR = 0xFFFFFFFF; // turn all the outputs to 0 - try and reduce current/power
pPort->OUTSET = (1 << 10);
/*
pPort->PINCFG[10]=0x01; // setup the pin to be multiplexed.
pPort->PMUX[5]=0x77; // set PA10 and PA11 to be muxed to peripheral H
pPort->PINCFG[6]=0x01;
pPort->PMUX[3]=0x22; // set PA06 (SERCOMPAD0) to output and mux it.
*/
pSysCtrl->XOSC = 0xAB46; // turn on XOSC
while(1){ // wait until XOSC is ready...
testResult = pSysCtrl->INTFLAG&0x01;
if(0!=testResult)
break;
}
pGCLK->GENCTRL=0x00290001; // turn on GEN1 and drive it from XOSC
pGCLK->GENCTRL=0x00090204; // now drive GEN4 from GEN1
pGCLK->GENCTRL=0x00290600; // turn on GEN0 and drive it from GCLK1 (OSC8M for now, again)
// pGCLK->GENDIV =0x00000A00; // divide by 10 for 1.356MHz operation
pGCLK->GENCTRL=0x00290303; // turn on GEN3 and drive it from OSC32ULP (needed for timer)
pPM->APBCMASK = 0x84;
setupTimer(65535-350);
/*
pGCLK->CLKCTRL = 0x400E; // route the clock to the core.
pCOM->CTRLA = 0x40120004; // setup the serial port
pCOM->BAUD = 64793; // 9600 bps
pCOM->CTRLA = 0x40120006; // setup the serial port
pCOM->CTRLB = 0x00010000; // turn on the transmitter
*/
}
void setupTimer(uint16_t count){
//route clock.
pGCLK->GENCTRL = 0x00290303; // route the 32kHz ULP osc to GEN3. Run while in standby
pGCLK->CLKCTRL = 0x4312; // route Gen3 to TC2 (for some reason there is no tie-in to TC1?)
pPM->APBCMASK = 0x84; // enable the clock on the power map
// now setup the timer, but don't turn it on
pTC2->CTRLA = 0x0F00; // just a simple counter. 32,000Hz / 1024 = 31.25Hz / 32mS tick
pTC2->INTFLAG = 0x01; // make sure the flag is cleared.
pTC2->INTENSET=0x01; // enable "OVERFLOW INTERRUPT"
pTC2->COUNT = count; // load up the value
pNVIC->NVIC_ISER = (1 << 14); // set the interrupt for timer 14
resetCount = count;
}
void startTimer(void)
{
pTC2->CTRLA = 0x0F02; // turn on the timer.
}
void resetTimer(void){
//route clock.
pGCLK->GENCTRL = 0x00290303; // route the 32kHz ULP osc to GEN3. Run while in standby
pGCLK->CLKCTRL = 0x4312; // route Gen3 to TC2 (for some reason there is no tie-in to TC1?)
pPM->APBCMASK = 0x84; // enable the clock on the power map
// now setup the timer, but don't turn it on
pTC2->INTFLAG = 0x39; // make sure the flag is cleared.
pTC2->INTENSET=0x01; // enable "OVERFLOW INTERRUPT"
pTC2->COUNT = resetCount; // load up the value
pTC2->CTRLA = 0x0F02; // just a simple counter. 32,000Hz / 1024 = 31.25Hz / 32mS tick
pTC2->COUNT = resetCount; // load up the value
// turn it on
}
void __attribute__((interrupt(14))) TC2_Handler(void) {
// do nothing!
pTC2->INTFLAG = 0x01;
wakeUp();
for(uint32_t x=0;x<10;x++){
pPort->OUTTGL=(1<<10);
for(uint32_t q=0;q<100;q++);
}
resetTimer();
deepSleep();
}
int main(void)
{
wakeUp();
startTimer(); // trigger it.
while (1)
{
deepSleep();
}
}