2023-12-02 07:05 AM - edited 2023-12-02 07:20 AM
Hi,
as I understand it:
I can activate the low power manager in CubeIDE. This will automatically add code, so each time the Sequencer has nothing to do, the system enters low power mode.
So far, this works. I can see it in the current consumption.
What I don´t understand: Is the HSE deactivated during sleep? I think it has to be, otherwise the current consumption should be much higher. But I cannot confirm it, when I use a scope and set test point in the code.
If I go to the file stm32_lpm_if.c and edit:
static void EnterLowPower(void)
{
/**
* This function is called from CRITICAL SECTION
*/
HAL_GPIO_WritePin(testpin_GPIO_Port, testpin_Pin, 0);
...
and
static void ExitLowPower(void)
{
HAL_GPIO_WritePin(testpin_GPIO_Port, testpin_Pin, 1);
/* Release ENTRY_STOP_MODE semaphore */
...
then I can see that each 100 ms (advertising interval), the microcontroller wakes up (current spike and test pin goes high)
and then goes back to sleep (test pin goes low).
static void Switch_On_HSI(void)
{
HAL_GPIO_WritePin(testpin_GPIO_Port, testpin_Pin, 0);
...
and here
static void ExitLowPower(void)
{
/* Release ENTRY_STOP_MODE semaphore */
LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID));
if(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_HSI)
{
/* Restore the clock configuration of the application in this user section */
/* USER CODE BEGIN ExitLowPower_1 */
HAL_GPIO_WritePin(testpin_GPIO_Port, testpin_Pin, 1);
LL_RCC_HSE_Enable( );
__HAL_FLASH_SET_LATENCY(FLASH_LATENCY_1);
while(!LL_RCC_HSE_IsReady( ));
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSE);
while (LL_RCC_GetSysClkSource( ) != LL_RCC_SYS_CLKSOURCE_STATUS_HSE);
/* USER CODE END ExitLowPower_1 */
}
...
then I observe, that the HSE is only turned on each 2,4 seconds.
I thought the procedure is:
Switch to HSI, go to sleep,
wakeup, turn on HSE, wait till it is ready, send advertisement package
swtich to HSI, go to sleep and so on.
From the readings on the oscilloscope, I cannot confirm that the HSE is turned on each 100 ms, as I would suppose it would be.
Also, when I monitor the MCO output (I output the HSE to a pin), it shows the HSI clock during sleep, but during the advertising there seems to be no clock signal on MCO pin (with prescaler = 16).
Btw. all these measurements are done during debugging.
Sorry for the bad quality of the oscilloscope, I am using relatively simple equipment and an improvised test setup.
Kind regards
René
2023-12-02 09:25 AM
That low-power mode code is broken, because it doesn't disable interrupts on a CPU, when it goes into low-power mode. As a result after wake-up before the clock configuration is complete any interrupt can run it's service routine and do things that probably should not be done in that MCU state. A proper example is there:
https://community.st.com/t5/stm32-mcus-motor-control/mc-sdk-not-work-with-mcu-stop-mode/m-p/94522
2023-12-04 01:20 AM
Most of the code is automatically generated by CubeIDE. So you are saying that ST is generating "broken" code as default in their development environment?
The device I´m measuring is working in the field for about a year now, with optimal current consumption (~3 uA in sleep, ~13 mA peak during advertising).
I am just wondering, why I cannot see the HSE signal on the MCO pin during debug. I am pretty sure the microcontroller switches correctly, as BLE functionality is working without problems.
2023-12-05 04:59 PM
Yes, all (HAL library, CubeMX generated and examples) of ST's low-power related (and most of other) code is broken. It is explained in the links I gave. Read those!
And you are talking about "sleep mode", but most likely are using a Stop mode. Anyway all of the modes, including what exactly they do, are explained in the reference manual.
2023-12-07 08:25 AM
Hello,
On STM32WB in BLE example, depending on low power mode used, HSE clock is disable or not. In sleep mode, HSE clock still enable and in stop mode and deeper mode, HSE clock is disable.
During advertising, CPU1 has nothing to do, CPU1 is in CSTOP, only CPU2 wakes up to manage the advertising.
When debugger is enabled, DBG_SLEEP and DBG_STOP bits of DBGMCU_CR register are set. When DBG_STOP bit is set: "Automatic clock stop disabled. All active clocks and oscillators continue to run during STOP mode, allowing full CPU1 debug capability. On exit from STOP mode, the clock settings are set to the STOP mode exit state."
In your case, you can disable the debugger and output HSE clock on MCO to see the right behavior.
Best Regards
2023-12-10 09:03 AM
@Remy ISSALYS wrote:During advertising, CPU1 has nothing to do, CPU1 is in CSTOP, only CPU2 wakes up to manage the advertising.
What do you mean? From my understanding, CPU1 and CPU2 have to wake up during advertising? CPU1 has to turn on the accurate 32 MHz clock and CPU2 has to do the rf stuff. Is that not correct?
2023-12-10 09:22 AM - edited 2023-12-10 10:03 AM
Some code, so you see where I inserted printf:
EnterStopMode:
void PWR_EnterStopMode(void)
{
/* USER CODE BEGIN PWR_EnterStopMode_1 */
/* USER CODE END PWR_EnterStopMode_1 */
/**
* When HAL_DBGMCU_EnableDBGStopMode() is called to keep the debugger active in Stop Mode,
* the systick shall be disabled otherwise the cpu may crash when moving out from stop mode
*
* When in production, the HAL_DBGMCU_EnableDBGStopMode() is not called so that the device can reach best power consumption
* However, the systick should be disabled anyway to avoid the case when it is about to expire at the same time the device enters
* stop mode (this will abort the Stop Mode entry).
*/
HAL_SuspendTick();
/**
* This function is called from CRITICAL SECTION
*/
EnterLowPower();
/************************************************************************************
* ENTER STOP MODE
***********************************************************************************/
LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2);
LL_LPM_EnableDeepSleep(); /**< Set SLEEPDEEP bit of Cortex System Control Register */
/**
* This option is used to ensure that store operations are completed
*/
#if defined (__CC_ARM)
__force_stores();
#endif
printf("sleep ");
__WFI();
printf("wakeUp ");
/* USER CODE BEGIN PWR_EnterStopMode_2 */
/* USER CODE END PWR_EnterStopMode_2 */
return;
}
EnterLowPower:
static void EnterLowPower(void)
{
/**
* This function is called from CRITICAL SECTION
*/
printf("start ");
printf("enterLowPower ");
while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID));
if (! LL_HSEM_1StepLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID))
{
printf("stepLockSuccess ");
if(LL_PWR_IsActiveFlag_C2DS() || LL_PWR_IsActiveFlag_C2SB())
{
printf("CPU2_isWasDeepSleep ");
/* Release ENTRY_STOP_MODE semaphore */
LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
printf("releasingStepLock ");
Switch_On_HSI();
__HAL_FLASH_SET_LATENCY(FLASH_LATENCY_0);
} else
{
printf("CPU2_wasNotDeepSleep ");
}
}
else
{
printf("stepLockFail ");
Switch_On_HSI();
__HAL_FLASH_SET_LATENCY(FLASH_LATENCY_0);
}
/* Release RCC semaphore */
LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
return;
}
ExitLowPower:
static void ExitLowPower(void)
{
printf("exitLowPower ");
/* Release ENTRY_STOP_MODE semaphore */
LL_HSEM_ReleaseLock(HSEM, CFG_HW_ENTRY_STOP_MODE_SEMID, 0);
while(LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID));
if(LL_RCC_GetSysClkSource() == LL_RCC_SYS_CLKSOURCE_STATUS_HSI)
{
/* Restore the clock configuration of the application in this user section */
/* USER CODE BEGIN ExitLowPower_1 */
printf("systemClockIsHSI ");
printf("turnOnHSE ");
LL_RCC_HSE_Enable( );
__HAL_FLASH_SET_LATENCY(FLASH_LATENCY_1);
while(!LL_RCC_HSE_IsReady( ));
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSE);
//LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSE);
//LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_SYSCLK);
while (LL_RCC_GetSysClkSource( ) != LL_RCC_SYS_CLKSOURCE_STATUS_HSE);
/* USER CODE END ExitLowPower_1 */
}
else
{
/* If the application is not running on HSE restore the clock configuration in this user section */
/* USER CODE BEGIN ExitLowPower_2 */
printf("systemClockIsAlreadyHSE ");
/* USER CODE END ExitLowPower_2 */
}
printf("end\r\n");
/* Release RCC semaphore */
LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, 0);
return;
}
Turn on HSI:
static void Switch_On_HSI(void)
{
printf("turnOnHSI ");
LL_RCC_HSI_Enable();
while(!LL_RCC_HSI_IsReady());
LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI);
LL_RCC_SetSMPSClockSource(LL_RCC_SMPS_CLKSOURCE_HSI);
while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI);
return;
}
When advertising, the console outputs (with the advertising interval time):
It seems, the HSE was still activated during sleep? Or at least the program thinks it is. Average current is ~300 uA, so I find it hard to believe that it is still turned on.
However, if I use a software timer, like
HW_TS_Start(Custom_App_Context.watchdog_timer_id, WATCHDOG_TIMESPAN);
Then, with the specified time, the console outputs:
So, to sum up:
When advertising, after waking up, the code says that the system clock is HSE, even though the HSI has been turned on and selected as system clock before entering Stop mode (_WFI).
When there is a wake up, that is not related to the advertising interval (e.g. a software timer), the system acts as expected: before sleep, system clock is switched to HSI. After wakeup, the system switches from HSI to HSE.
I have tested this during debugging, with the serial monitor, as well as with testPins and an oscilloscope. My observation is the same.
I use 95 % of the code, that is generated by CubeIDE.
I imagine the system somehow works, power consumption is as one would expect, Bluetooth communication works without problems with a range of about 25 meters.
I would, still, like to understand why the code does what it does and if there is a bug or if I´m just misnderstanding things.
The reason, why I started this investigation in the first place, is that I would like to switch from an external oscillator to an external clock that will be activated and deactivated by a dedicated pin. Therefore, I need to know where in the code I have to turn it on/off. Therefore, I need to know when the HSE has to be turned on and when the HSI has to be turned on.
2023-12-10 10:02 AM
Here´s a test with an oscilloscope:
Channel 1 (yellow) shows a spike each time the HSI is started.
Channel 2 (green) shows a spike each time the HSE is started.
Advertising interval is ~1,7 s. A software timer triggers a task each 2,5 s.
You can clearly see, that during advertising, there is no spike for the HSE, only for the HSI.
For the task, however, first there is a spike for the HSE (microcontroller wakes up and executes a task), directly followed by the activation of the HSI (for sleep).
I can observe this behavior with and without the debugger connected.
2023-12-11 04:06 AM
Ok, I did some more testing.
I think I observed that the HSE is activated before CPU1 wakes up from sleep mode.
CPU1 does wake up from sleep mode after advertising.
I set a test pin (LED_FLASH) high and low directly after _WFI(), so I can see when the microcontroller wakes up.
void PWR_EnterStopMode(void)
{
/* USER CODE BEGIN PWR_EnterStopMode_1 */
/* USER CODE END PWR_EnterStopMode_1 */
/**
* When HAL_DBGMCU_EnableDBGStopMode() is called to keep the debugger active in Stop Mode,
* the systick shall be disabled otherwise the cpu may crash when moving out from stop mode
*
* When in production, the HAL_DBGMCU_EnableDBGStopMode() is not called so that the device can reach best power consumption
* However, the systick should be disabled anyway to avoid the case when it is about to expire at the same time the device enters
* stop mode (this will abort the Stop Mode entry).
*/
HAL_SuspendTick();
/**
* This function is called from CRITICAL SECTION
*/
EnterLowPower();
/************************************************************************************
* ENTER STOP MODE
***********************************************************************************/
LL_PWR_SetPowerMode(LL_PWR_MODE_STOP2);
LL_LPM_EnableDeepSleep(); /**< Set SLEEPDEEP bit of Cortex System Control Register */
/**
* This option is used to ensure that store operations are completed
*/
#if defined (__CC_ARM)
__force_stores();
#endif
// LED_FLASH_GPIO_Port->BRR = (uint32_t)LED_FLASH_Pin;
__WFI();
/* USER CODE BEGIN PWR_EnterStopMode_2 */
LED_FLASH_GPIO_Port->BSRR = (uint32_t)LED_FLASH_Pin;
for(uint32_t i; i<32; i++)
{
__asm__("nop");
}
LED_FLASH_GPIO_Port->BRR = (uint32_t)LED_FLASH_Pin;
/* USER CODE END PWR_EnterStopMode_2 */
return;
}
I also measured the current consumption, so I can see when the microcontroller is advertising.
Interesting to see is that the microcontroller seems to stars advertising before CPU1 wakes up.
I also connected CH1 to the MCO to observe the clock change:
How does it do that? I thought the HSE is "actuated" by the CPU1? Here we clearly see, that the HSE is running before CPU1 wakes up, which confirms my findings from previous posts.
If I wake up the microcontroller with a timer task, then it first wakes up and then starts the HSE:
2023-12-11 04:12 AM
Would someone please confirm my findings?
Why is the wake up handler for CPU1 triggered after all, when CPU2 can do the advertising (and activation of HSE) by itself?
Does that mean that I cannot use an external clock (like the ECS-3225MV) if I want to turn it on and off during sleep? I thought about: When entering low power and switching to HSI, I turn off the external clock, go to sleep, when advertising is triggered, I activate the external clock again, wait till it is ready, then advertise.
With regard to my observation this does not seem to be possible. So is the Bypass function for OSC_IN just in case you want to connect an external clock and keep it turned on?