XMC1000 TIP of the day: Deep Sleep Mode

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
Not applicable
Power consumption is always been a concern to most developer when design a new product.
In XMC1000 device, there is deep sleep mode where MCLK and PCLK is switch to standby clock.
Peripherals that continue to operate will run using the slow standby clock and can eventually generate an event to wake-up the CPU.
To enter this mode, application should:

1. Enable access to protected registers
2. Setup VDDC monitoring timer
3. Set PPB.SCR.SLEEPDEEP = 1
4. Put the ACMP into low power mode AND/OR Disable the ACMPs
5. Gate off any unused peripherals and wait for VDDC to stabilize
6. Slow down the MCLK, PCLK and RTC
7. Power Off the flash (via PWRSVCR.FPD)
8. Execute a WFE or WFI instruction


Here's the example code for enter deep sleep mode:

void DeepSleep(void)
{
volatile u32 idiv = 0;

__disable_irq(); //disable all interrupts

SCU_GENERAL->PASSWD = CLK002_DIRECT_ACCESS_ALLOW; // Open the lock that protects privileged bits.
while(((SCU_GENERAL->PASSWD)&SCU_GENERAL_PASSWD_PROTS_Msk)); // Loop until protection is removed.

SCU_CLK->CLKCR |= 0x3ff00000UL; // CNTADJ = 0x3ff, For VDDC monitoring

PPB->SCR |= PPB_SCR_SLEEPDEEP_Msk; // Setup for Deep Sleep instead of normal sleep

COMPARATOR->ANACMP0 |= COMPARATOR_ANACMP0_CMP_LPWR_Msk; // Put Comparator into low power mode

COMPARATOR->ANACMP0 &= ~(COMPARATOR_ANACMP0_CMP_EN_Msk); // Disable ACMP0
COMPARATOR->ANACMP1 &= ~(COMPARATOR_ANACMP1_CMP_EN_Msk); // Disable ACMP1
COMPARATOR->ANACMP2 &= ~(COMPARATOR_ANACMP2_CMP_EN_Msk); // Disable ACMP2

SCU_CLK->CGATSET0 = 0x07FFUL; // Gate Off all peripherals
WR_REG(SCU_CLK->CLKCR, SCU_CLK_CLKCR_CNTADJ_Msk, SCU_CLK_CLKCR_CNTADJ_Pos,(0x3FFU));
while ((SCU_CLK->CLKCR) & 0xC0000000UL); // Wait for VDDC to stabilize

/* Ramp down Frequency */
idiv = (SCU_CLK->CLKCR & SCU_CLK_CLKCR_IDIV_Msk)>>SCU_CLK_CLKCR_IDIV_Pos;
while (idiv < 255)
{
idiv = idiv << 2;
WR_REG(SCU_CLK->CLKCR, SCU_CLK_CLKCR_IDIV_Msk, SCU_CLK_CLKCR_IDIV_Pos, idiv);
WR_REG(SCU_CLK->CLKCR, SCU_CLK_CLKCR_CNTADJ_Msk, SCU_CLK_CLKCR_CNTADJ_Pos,(0x3FFU));
while ((SCU_CLK->CLKCR) & 0xC0000000UL); // Wait for VDDC to stabilize
if (idiv > 255)
idiv = 255;
}

/* Step Down PCLK to equal MCKL, set RTC to internal clock */
SCU_CLK->CLKCR = 0x3ff0FFFFUL; // CNTADJ = 0x3ff, MCLK = PCLK = 125kHz
while ((SCU_CLK->CLKCR) & 0xC0000000UL); // Wait for VDDC to stabilize

SCU_CLK->PWRSVCR |= SCU_CLK_PWRSVCR_FPD_Msk; // make sure flash powers down during deep sleep

SCU_GENERAL->PASSWD = CLK002_DIRECT_ACCESS_DISALLOW; // Close the lock opened above.

__WFI(); // Go To Deep Sleep
}
0 Likes
7 Replies
Not applicable
Hi Jackson,

base on this code, MCU won't waked up by any interrupt event right?

How to create software event to awake MCU?

Regards,
iamten
0 Likes
User7804
Level 4
Level 4
IMO there are other issues:

The "while (idiv < 255)" can loop until idiv = 254, then idiv is multiplied by four (idiv = idiv << 2), causing an overflow since idiv is an 8 bit value.

The "if (idiv > 255) idiv = 255;" check after writing the value is useless.

I don't see why idiv needs to be declared volatile.

CNTADJ seems to be written twice (using different methods) - why?

Oliver
0 Likes
Not applicable
iamten wrote:
Hi Jackson,

base on this code, MCU won't waked up by any interrupt event right?

How to create software event to awake MCU?

Regards,
iamten


Hi iamten,

It is possible to wake up from interrupt as long as the peripheral did not turn off and the interrupt is enabled.
You can use the RTC to created an interrupt to wake up the MCU.

obetz wrote:
IMO there are other issues:

The "while (idiv < 255)" can loop until idiv = 254, then idiv is multiplied by four (idiv = idiv << 2), causing an overflow since idiv is an 8 bit value.

The "if (idiv > 255) idiv = 255;" check after writing the value is useless.

I don't see why idiv needs to be declared volatile.

CNTADJ seems to be written twice (using different methods) - why?

Oliver


Hi Oliver,

Basically the idea is to ramp down the SCU_CLK->CLKCR.IDIV in the step of 4 (e.g.: 0x01 -> 0x04 -> 0x08...)
So it doesn't really matters how you code it as long as the clock is ramp down slowly.
0 Likes
User7804
Level 4
Level 4
Hello Jackson,

I did understand what you intended basically, but I'm afraid the code you posted doesn't work and can end with a very high clock and an illegal transition. Let me know if my error report was not clear enough, I can try to explain in other words.

Besides the error, I described also two more things I don't understand (volatile idiv, CNTADJ written twice) likely doing no harm. Nevertheless it would be interesting to know your intention behind this or whether I simply overlooked the reason.

Oliver
0 Likes
Not applicable
Agree with Oliver's comments about the use of keyword volatile and multiple writes to CNTADJ (including in the while loop), which seem to be redundant.

Ditto about the check for idiv > 255. This should come before the write to IDIV bit field, otherwise divider is bypassed just before exiting the while loop?

while (idiv < 255)
{
idiv = idiv << 2;
if (idiv > 255)
idiv = 255;
WR_REG(SCU_CLK->CLKCR, SCU_CLK_CLKCR_IDIV_Msk, SCU_CLK_CLKCR_IDIV_Pos, idiv);
WR_REG(SCU_CLK->CLKCR, SCU_CLK_CLKCR_CNTADJ_Msk, SCU_CLK_CLKCR_CNTADJ_Pos,(0x3FFU));
while ((SCU_CLK->CLKCR) & 0xC0000000UL); // Wait for VDDC to stabilize

}
0 Likes
User7804
Level 4
Level 4
Even better would be to use the correct condition for the while() loop:

As I wrote, the "idiv < 255" condition would pass until idiv=254.

Since we want to avoid idiv being larger than 255 after the multiplication, we have to guarantee that idiv*4 ("<<2" is useless obfuscation) will not overflow.

Therefore the max. value before the *4 operation is 63. 64 will overflow.

"while (idiv < 64)" or "while (idiv <= 63)" will do the job, no further test is needed.

The original code will overflow and write zero to CLKCR.IDIV with most starting values. For example, starting with idiv=1 produces 4-16-64-256. The latter will be masked to zero and MCLK will jump from 0,5MHz to 32MHz, far beyond the spec.

BTW: The XMC1100 documentation is rather vague about the influence of IDIV steps, there is still a "(TBC)" comment in 12.3.7.

Disclaimer: I didn't test what I wrote, so there is a certain chance of being wrong.
0 Likes
Not applicable
Hi,

As far as I understand, when going to deep sleep mode the DCO1(96MHz oscillator) is disabled and then MCLK/PCLK is running from the standby clock (32kHz oscillator DCO2).
When the device wakes up from the deep sleep (e.g. via SysTick interrupt) I see that the MCLK/PCLK is switched back automatically to DCO1.

Is there an option to configure the device in such a way that after wake-up form deep sleep it:
- keeps DCO1(96MHz) off
AND
- runs from the standby clock (32kHz DCO2)?

Thanks.

PS: We are using a XMC1400 device
0 Likes