Author Topic: ARM CM0: MCU appears to crash on the second wakeup from standby, help?  (Read 2851 times)

0 Members and 1 Guest are viewing this topic.

Offline XFDDesignTopic starter

  • Frequent Contributor
  • **
  • Posts: 442
  • Country: us
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):
Code: [Select]
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();


    }

}


 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11260
  • Country: us
    • Personal site
Re: ARM CM0: MCU appears to crash on the second wakeup from standby, help?
« Reply #1 on: September 01, 2017, 05:39:46 pm »
Please, use defines from header files. It is impossible to read this code with all those magic numbers.

And where this "__attribute__((interrupt(14))) " even come from? This is nonsense for ARM, and compiler should probably complain about this.

You can't (or should not) call deepSleep() from the IRQ handler itself. Let it exit the interrupt normally and your main while (1) loop will take care of the sleep.

Now your code blocks because the same interrupt handler can't be called twice without extra messing around.
Alex
 

Offline XFDDesignTopic starter

  • Frequent Contributor
  • **
  • Posts: 442
  • Country: us
Re: ARM CM0: MCU appears to crash on the second wakeup from standby, help?
« Reply #2 on: September 01, 2017, 06:03:25 pm »
Please, use defines from header files. It is impossible to read this code with all those magic numbers.

The structures are from the included Atmel source, and thus I omitted them. I'm not sure what else you're trying to get to.

Quote
And where this "__attribute__((interrupt(14))) " even come from? This is nonsense for ARM, and compiler should probably complain about this.

It appears to be compliant from the examples in source I've found, and ISR 14 is Timer2's interrupt vector.

Quote
You can't (or should not) call deepSleep() from the IRQ handler itself. Let it exit the interrupt normally and your main while (1) loop will take care of the sleep.

Since posting this, I'm able to skip that function call and have instead simply set SCB.SLEEPONEXIT. Still get the exact same behavior though.

Quote
Now your code blocks because the same interrupt handler can't be called twice without extra messing around.
It shouldn't be called twice, all things considered, unless something else goes seriously wrong. It's the _only_ ISR used, and given the lag developed by the timer (typ >1s at least) should not be a problem.

----------------------------------------------------------------------------------


One thing I found since the original post, is that if I don't set SCB.DEEPSLEEP and let it just 'sleep,' the sleep current is the same 358uA as when the device crashes. I'm unable to determine any correlation to the root issue, but it was an interesting observation. As mentioned above, digging into the SCB reg, there is a "SLEEPONEXIT" bit, which upon leaving the ISR returns to sleep mode, essentially allowing me to do all my business within the timer ISR (just had to make sure to turn *off* the Interrupt Service once within it). I do still get the same unwanted outcome, however. One deep sleep cycle, one wake up, a second sleep cycle, and then 'unknown' state.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11260
  • Country: us
    • Personal site
Re: ARM CM0: MCU appears to crash on the second wakeup from standby, help?
« Reply #3 on: September 01, 2017, 06:07:42 pm »
They are standard CMSIS headers (made by Atmel, obviously, who else will make them?). If you refuse to use them, then I personally won't be able to help you, sorry.

The main problem is that you are calling issuing WFI from within the interrupt handler.

Attribute stuff is not need on Cortex devices, it all works with plain C.
Alex
 

Offline XFDDesignTopic starter

  • Frequent Contributor
  • **
  • Posts: 442
  • Country: us
Re: ARM CM0: MCU appears to crash on the second wakeup from standby, help?
« Reply #4 on: September 01, 2017, 06:17:24 pm »
The main problem is that you are calling issuing WFI from within the interrupt handler.
As mentioned, I no longer need to call WFI within the handler, and it still fails, so it follows that WFI from within the handler is not the problem.

Quote
Attribute stuff is not need on Cortex devices, it all works with plain C.
Omitting the __attribute__ factor, as used in Atmel's own code produces the following error:

"Error      expected declaration specifiers or '...' before numeric constant"

Just for completeness' sake, here is an excerpt from the startup_samd09.c as generated by Atmel:
Code: [Select]
/* Cortex-M0+ core handlers */
void NMI_Handler             ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void HardFault_Handler       ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void SVC_Handler             ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void PendSV_Handler          ( void ) __attribute__ ((weak, alias("Dummy_Handler")));
void SysTick_Handler         ( void ) __attribute__ ((weak, alias("Dummy_Handler")));

Quote
They are standard CMSIS headers (made by Atmel, obviously, who else will make them?). If you refuse to use them, then I personally won't be able to help you, sorry.

I'm not even understanding what or where your complaint is and by the discrepancies above, I too do not think your efforts will be purposeful as well.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11260
  • Country: us
    • Personal site
Re: ARM CM0: MCU appears to crash on the second wakeup from standby, help?
« Reply #5 on: September 01, 2017, 06:23:18 pm »
As mentioned, I no longer need to call WFI within the handler, and it still fails, so it follows that WFI from within the handler is not the problem.
Then you should probably post up to date code. It is hard to guess what changes you have made.

Omitting the __attribute__ factor, as used in Atmel's own code produces the following error:
you need to remove the whole thing. Just leave the function alone. I can guarantee that this construct is never used in Atmel code for ARM, since it is an AVR thing.

Just for completeness' sake, here is an excerpt from the startup_samd09.c as generated by Atmel:
Yes, I know how Atmel code looks like.


I'm not even understanding what or where your complaint is and by the discrepancies above, I too do not think your efforts will be purposeful as well.
I'm complaing about magic numbers everywhere.  Here is how reasonable written GCLK initialization looks like, IMO:
Code: [Select]
  GCLK->CLKCTRL.reg = GCLK_CLKCTRL_ID(TC1_GCLK_ID) | GCLK_CLKCTRL_CLKEN | GCLK_CLKCTRL_GEN(0);

Alex
 

Offline XFDDesignTopic starter

  • Frequent Contributor
  • **
  • Posts: 442
  • Country: us
Re: ARM CM0: MCU appears to crash on the second wakeup from standby, help?
« Reply #6 on: September 01, 2017, 06:33:44 pm »
Then you should probably post up to date code. It is hard to guess what changes you have made.

This much is reasonable.

Changed items:
Code: [Select]
void __attribute__((interrupt(14))) TC2_Handler(void) {
// do nothing!
pTC2->INTFLAG = 0x01; // clear the ISR
wakeUp();             // Restore all of the clock configurations.
for(uint32_t x=0;x<10;x++){
pPort->OUTTGL=(1<<10);    // perform a simple toggle
for(uint32_t q=0;q<100;q++); // perform a bit of time delay
}
resetTimer();  // stuff the timer with appropriate reset information and start it
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
}
and
Code: [Select]
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 |= 0x06; // DEEEP SLEEP, but now use SLEEPONEXIT. When an ISR finishes,
// the uC will resume sleeping rather than continuing with the
// program
asm volatile("WFI");

}
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11260
  • Country: us
    • Personal site
Re: ARM CM0: MCU appears to crash on the second wakeup from standby, help?
« Reply #7 on: September 01, 2017, 06:51:07 pm »
There is no need to enable/disable clocks, just configure them once and let the MCU handle the rest. If you don't want some clocks to run when MCU is sleeping, just don't set RUNSTDBY bit.

If for whatever reason you need to do clock control manually, you have to wait for synchronization before going to sleep (or exiting the IRQ handler in this case).

But I do suspect that the problems come from this manual clock management without waiting for synchronization or any of the status bits.

And just use
Code: [Select]
void TC2_Handler(void) {  This attribute is meaningless on ARM.
Alex
 

Offline XFDDesignTopic starter

  • Frequent Contributor
  • **
  • Posts: 442
  • Country: us
Re: ARM CM0: MCU appears to crash on the second wakeup from standby, help?
« Reply #8 on: September 01, 2017, 07:00:11 pm »
There is no need to enable/disable clocks, just configure them once and let the MCU handle the rest. If you don't want some clocks to run when MCU is sleeping, just don't set RUNSTDBY bit.

If for whatever reason you need to do clock control manually, you have to wait for synchronization before going to sleep (or exiting the IRQ handler in this case).

But I do suspect that the problems come from this manual clock management without waiting for synchronization or any of the status bits.

And just use
Code: [Select]
void TC2_Handler(void) {  This attribute is meaningless on ARM.

I'll go through and correct the code to release the manual control from the code and follow up.

On the 'magic numbers,' for what it's worth, those terms you used are not present in the datasheet ( http://www.atmel.com/Images/Atmel-42414-SAM-D09_Datasheet.pdf ) . What I get are bit locations, not   pseudonyms (not sure the right term for it) as you used. My efforts in finding good documentation for ARM has been largely unfruitful.
 

Online ataradov

  • Super Contributor
  • ***
  • Posts: 11260
  • Country: us
    • Personal site
Re: ARM CM0: MCU appears to crash on the second wakeup from standby, help?
« Reply #9 on: September 01, 2017, 07:13:44 pm »
Those defines have very well defined structure. It is always possible to infer a name from a register name (which you already use in your code) and a bit name mentioned in the datasheet. And you can just look in the corresponding header file, of course.

I have some examples for Atmel ARM MCUs on my GitHub https://github.com/ataradov/mcu-starter-projects

I can come up with the demo code you want, but im on vacation this week, away from any hardware.
Alex
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4199
  • Country: us
Re: ARM CM0: MCU appears to crash on the second wakeup from standby, help?
« Reply #10 on: September 02, 2017, 01:26:59 am »
Code: [Select]
pTC2->INTENSET=0x01; // enable "OVERFLOW INTERRUPT"
pTC2->CTRLA = 0x0F02; // just a simple counter. 32,000Hz / 1024 = 31.25Hz / 32mS tick

We want that to look like:

Code: [Select]
pTC2->INTENSET= TC_INTENSET_OVF; // enable "OVERFLOW INTERRUPT"
pTC2->CTRLA = TC_CTRLA_PRESCALER_DIV1024 | TC_CTRLA_RUNSTDNY | TC_CTRLA_MODE_COUNT16 | TC_CTRLA_ENABLE;

Otherwise we have to open up the datasheet and stare a long time to see if your constants really do what your comment says they do (at least, you DID have comments!)

 

Offline XFDDesignTopic starter

  • Frequent Contributor
  • **
  • Posts: 442
  • Country: us
Re: ARM CM0: MCU appears to crash on the second wakeup from standby, help?
« Reply #11 on: September 05, 2017, 06:23:17 pm »
Code: [Select]
pTC2->INTENSET=0x01; // enable "OVERFLOW INTERRUPT"
pTC2->CTRLA = 0x0F02; // just a simple counter. 32,000Hz / 1024 = 31.25Hz / 32mS tick

We want that to look like:

Code: [Select]
pTC2->INTENSET= TC_INTENSET_OVF; // enable "OVERFLOW INTERRUPT"
pTC2->CTRLA = TC_CTRLA_PRESCALER_DIV1024 | TC_CTRLA_RUNSTDNY | TC_CTRLA_MODE_COUNT16 | TC_CTRLA_ENABLE;

Otherwise we have to open up the datasheet and stare a long time to see if your constants really do what your comment says they do (at least, you DID have comments!)

Fair enough, but without a reliable and consistent source book to derive this kind of context from, this is asking a lot. What we get are the Datasheets. When I first got into these parts, I tried with the mess that is ASF. No documentation. Half the programs didn't even compile correctly. Everything from Atmel in terms of header files showed itself to be without credit.

As such, would it be a reasonable compromise to then do up a mess of #defines, so it might not be "TC_CTRLA_PRESCALAR_DIV1024" but would be "PRESCALARDIV1024"?
 

Offline westfw

  • Super Contributor
  • ***
  • Posts: 4199
  • Country: us
Re: ARM CM0: MCU appears to crash on the second wakeup from standby, help?
« Reply #12 on: September 06, 2017, 08:43:55 am »
Quote
without a reliable and consistent source book to derive this kind of context from
There is, though as you point out it's not so easy to find, nor well documented.
You want the CMSIS-Atmel/<version>/CMSIS/Device/ATMEL/<device>/include/component/<peripheral>.h
(For example "CMSIS-Atmel/1.1.0/CMSIS/Device/ATMEL/samd10/include/component/tc.h")
It's the same file you got (or that should be used for) the structure definitions needed to use "pTC2->INTENSET" and such.

Quote
As such, would it be a reasonable compromise to then do up a mess of #defines, so it might not be "TC_CTRLA_PRESCALAR_DIV1024" but would be "PRESCALARDIV1024"?
It's all machine-generated standard format stuff, so it should be predictable once you get used to it.

Code: [Select]
TC_CTRLA_PRESCALAR_DIV1024
xx                         Peripheral type
   xxxxx                   Register name
         xxxxxxxxx         Field name
                   xxxxxxx Value
(Those names are all exactly as they are documented in the datasheet.)

(Personally, I'm still at the "open the .h file in another window" stage.  I guess that since the registers are strongly typed, the "right" IDE will have autocomplete and context-sensitive help?)

It's also true that the .h file is obfuscated with a bunch of other, less useful, and occasionally misleading symbol definitions.  Things like TC_CTRLA_PRESCALER_Pos, TC_CTRLA_PRESCALER_Msk, TC_CTRLA_PRESCALER(value)...

You might find https://github.com/WestfW/SAMD10-experiments useful.  It's where I whine about things that annoyed me in my experiments with a SAMD10 Xplained Mini board, and some of the code I ended up with...  It hasn't gotten updated as much as I would have liked, but it's a start.

Philosophically, I've been told (but never 100% convinced, I guess), that it would be worthwhile to have your own set of symbols defined, even if you don't use the Atmel-provided .h files, and even if the definitions are subject to your own errors.  Ie:

Code: [Select]
#define TC_DIV1024  0x0700
#define TC_RUNSTDBY 0x0800
#define TC_16BIT    0x0000
#define TC_RUN      0x0002
pTC2->CTRLA = TC_DIV1024 | TC_RUNSTDBY | TC_16BIT | TC_RUN;


 
The following users thanked this post: XFDDesign


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf