cancel
Showing results for 
Search instead for 
Did you mean: 

Success STML4, FREERTOS, TICKLESS using LSI and LPTIMx, w SLEEP wfi, Need help with STOP1 wfi

Kent Swan
Senior

On a STM32L4 I've successfully developed a FreeRTOS tickless method using LSI driving LPTIMx at 32Khz with wfi Sleep mode in spite of the restrictions on the LP timers. Tick timing while not perfect is reasonably accurate and more than sufficient for my purposes. For those that are curious, I'm using the PWM mode with dynamic updating of the CMP register to maintain tick timing. This allows me to set longer tick delays within the tickless sleep mode without disrupting the underlying clock which is running continuously even while sleeping.

I've instrumented my platform with signaling outputs on gpio pins into a logic analyzer which allows me to verify timing as well FreeRTOS task switching performance and everything works perfectly until I try to use STOP1 sleep mode.

When I STOP1 mode it works... sort of... The documentation on STOP modes indicate that the clock tree needs to be restored from a default state. Calling SystemClock_Config() just after coming out of STOP1 either isn't restablishing my clock tree or if it is, seems to disrupt the LPTIMx. What I want is to recover the clock tree back to where I was before calling STOP1 but without disrupting the LPTIMx I'm using to keep track of things while sleeping.

Can anyone give me some hints here?

11 REPLIES 11
Jeff Tenney
Senior

Hi Kent, did you ever solve this problem? I just finished writing support for LPTIM ticks and no-drift tickless idle, and the STOP modes seem to be working. But I'm concerned about any pitfalls that may be lurking.

Unfortunately no. It's likely that my handling lacks the nuances required to enter and stay in STOP1 until either a regular interrupt or the LPTIMx expires. One of the issues is that the L4 LPTIMers don't have a reset command (or at least I haven't found it). What I was doing was running the LPTIMx off of the 32khz internal RC clock source which is supposed to keep running while in STOP1. I'd really like to see how you managed it.

https://gist.github.com/jefftenney/02b313fe649a14b4c75237f925872d72

I am very curious to know if it works in your application. If I understand your application correctly, you'll need to define configTICK_USES_LSI in FreeRTOSConfig.h.

Kent Swan
Senior

I'll give it a look and report back. I'm using a STM32L46x and, in this case, used CUBEMx for configuration. What stm32L4 processor, CubeMx L4 Library Version and/or FreeRTOS Version did you use in your solution?

Tested on L4Rx and L476. Destined for L486 in a couple of weeks when the hardware comes in. It's for FreeRTOS 10, but is definitely compatible with the last few years of versions. It should also be compatible with whatever cube mx generates (any version) except for defining configTICK_USES_LSI if needed. But I'm not a cube mx user.

Kent Swan
Senior

Read through your code. Generally makes sense even though some of the time keeping/correction nuances are a bit deep. A couple of questions. (a) I couldn't find where you slelected STOP1 as your sleep mode for wfi (which will turn off the main clock tree) otherwise wfi simply performs a normal sleep with the clock tree running and the cpu core consuming power and (b) How does this code handle a non tick peripheral interrupt waking it up?

Jeff Tenney
Senior

(a) The code to select STOP1 (or STOP2) would normally be in configPRE_SLEEP_PROCESSING(), and the code to re-enable the PLL or other clocks disabled by STOP mode would be in configPOST_SLEEP_PROCESSING(). Essentially the application needs to set PWR->CR1 (to choose STOP1 instead of STOP0 or STOP2) and set/clear the SLEEPDEEP bit in SCB->SCR based on current conditions. Function configPRE_SLEEP_PROCESSING() is available for that. You also need to reenable clocks after STOP mode, and configPOST_SLEEP_PROCESSING() is available for that.

(b) This code handles non-tick peripherals waking it up by rescheduling the next upcoming tick after the "unexpected" interrupt and by advancing the tick count by only the number that were actually suppressed. See lines 324 - 360.

Kent Swan
Senior

Re (b) okay I see what you're doing there. Makes sense. Humm... Since L4's don't have a reset function I have to figure out how you handled scheduling near the 16 bit counter's wrap point.

Re (a) I wonder if my preSleep and postSleep processes are not correct. Could you make your's available so I can compare.

Jeff Tenney
Senior

I don't use them because a task coordinates all uses of deep sleep in my application. But I can make examples (untested):

This belongs in your application code:

// These two functions are called from the same critical section.
 
static uint32_t rccCfgrSave;
static uint32_t rccCrSave;
 
void preSleepProcessing()
{
   // System-init code has already selected the level of deep sleep we use
   // whenever we use deep sleep.  That configuration is in PWR->CR1.  Init
   // code also already selected whether we wake up from STOP mode on the MSI
   // or the HSI.  That configuration is in the RCC_CFGR_STOPWUCK bit.
 
   if (isDeepSleepOkRightNow)
   {
      rccCrSave = RCC->CR;
      rccCfgrSave = RCC->CFGR;
      SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
   }
}
 
void postSleepProcessing()
{
   if (SCB->SCR & SCB_SCR_SLEEPDEEP_Msk)
   {
      // We may have been in deep sleep.  If we were, the hardware cleared
      // several enable bits in the CR, and it changed the selected system clock
      // in CFGR.  Restore them now.
      //
      RCC->CR = rccCrSave;
      RCC->CFGR = rccCfgrSave;
      SCB->SCR &= ~SCB_SCR_SLEEPDEEP_Msk;
   }
}

And this belongs in FreeRTOSConfig.h:

#define configPRE_SLEEP_PROCESSING( x ) preSleepProcessing()
#define configPOST_SLEEP_PROCESSING( x ) postSleepProcessing()