From 8007b1df7c592722b6387d5f347bf5b0a73e895f Mon Sep 17 00:00:00 2001 From: Laurent MEUNIER Date: Wed, 28 Mar 2018 15:37:45 +0200 Subject: [PATCH 1/3] Use temporarily MSI or HSI when exiting Deep Sleep There are cases where HW registers are found in unpexcepted state when exiting Deep Sleep only few micro-seconds after it was entered. By using an internal clock that does not depend on anythin and clocking the system without using PLL, this allows SetSysClock default configuration to run fine whatever possible configuration we find the HW in when exiting Deep Sleep. Also we shall restore interrupts only after all cloks are back to expected running state. --- targets/TARGET_STM/sleep.c | 89 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 86 insertions(+), 3 deletions(-) diff --git a/targets/TARGET_STM/sleep.c b/targets/TARGET_STM/sleep.c index b4e36c892d..4237eca2f6 100644 --- a/targets/TARGET_STM/sleep.c +++ b/targets/TARGET_STM/sleep.c @@ -35,6 +35,88 @@ extern void HAL_SuspendTick(void); extern void HAL_ResumeTick(void); +// On L4 platforms we've seen unstable PLL CLK configuraiton +// when DEEP SLEEP exits just few µs after being entered +// So we need to force MSI usage before setting clocks again +static void ForceClockOutofDeepSleep(void) { + RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; + RCC_OscInitTypeDef RCC_OscInitStruct = {0}; + uint32_t pFLatency = 0; + + /* Enable Power Control clock */ + __HAL_RCC_PWR_CLK_ENABLE(); + +#ifdef PWR_FLAG_VOS + /* Poll VOSF bit of in PWR_CSR. Wait until it is reset to 0 */ + //while (__HAL_PWR_GET_FLAG(PWR_FLAG_VOS) != RESET) {}; +#endif + + /* Get the Oscillators configuration according to the internal RCC registers */ + HAL_RCC_GetOscConfig(&RCC_OscInitStruct); + +#if (TARGET_STM32L4 || TARGET_STM32L1) /* MSI used for L4 */ + /**Initializes the CPU, AHB and APB busses clocks + */ + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_MSI; + RCC_OscInitStruct.MSIState = RCC_MSI_ON; + RCC_OscInitStruct.MSICalibrationValue = RCC_MSICALIBRATION_DEFAULT; + RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_4; // Intermediate freq, 1MHz range + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; + if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { + error("clock issue\r\n"); + } + + /* Get the Clocks configuration according to the internal RCC registers */ + HAL_RCC_GetClockConfig(&RCC_ClkInitStruct, &pFLatency); + + // Select HSI ss system clock source as a first step +#ifdef RCC_CLOCKTYPE_PCLK2 + RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK + | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2); + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; +#else + RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK + | RCC_CLOCKTYPE_PCLK1); +#endif + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_MSI; + RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1; + if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, pFLatency) != HAL_OK) { + error("clock issue\r\n"); + } +#else /* HSI used on others */ + /**Initializes the CPU, AHB and APB busses clocks + */ + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI; + RCC_OscInitStruct.HSIState = RCC_HSI_ON; + RCC_OscInitStruct.HSICalibrationValue = 16; + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; + if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { + error("clock issue"); + } + + /* Get the Clocks configuration according to the internal RCC registers */ + HAL_RCC_GetClockConfig(&RCC_ClkInitStruct, &pFLatency); + + /**Initializes the CPU, AHB and APB busses clocks + */ +#ifdef RCC_CLOCKTYPE_PCLK2 + RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK + |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2); + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1; +#else + RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK + |RCC_CLOCKTYPE_PCLK1); +#endif + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_HSI; + RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2; + if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, pFLatency) != HAL_OK) { + error("clock issue"); + } +#endif // TARGET_STM32L4 +} + void hal_sleep(void) { // Disable IRQs @@ -83,13 +165,12 @@ void hal_deepsleep(void) #else /* TARGET_STM32L4 */ HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); #endif /* TARGET_STM32L4 */ + // Verify Clock Out of Deep Sleep + ForceClockOutofDeepSleep(); // Restart HAL tick HAL_ResumeTick(); - // Enable IRQs - core_util_critical_section_exit(); - // After wake-up from STOP reconfigure the PLL SetSysClock(); @@ -107,6 +188,8 @@ void hal_deepsleep(void) rtc_synchronize(); } #endif + // Enable IRQs + core_util_critical_section_exit(); } #endif From 3d92af50ce2289ee83d5abc5344091cd01ec5677 Mon Sep 17 00:00:00 2001 From: Laurent MEUNIER Date: Fri, 6 Apr 2018 15:18:16 +0200 Subject: [PATCH 2/3] Add delay to let clock stabilize when out of deep sleep Tests have shown that there is hich-up on MSI clock during the setup phase. If this stabilization phase happens when application has restarted again this can have side effects, like grambled UART characters typically. So we're adding a delay before hading-over back to application. With this modification, on NCULEO_L476RG, the wake-up time is increased from 2ms to 2,5ms. If possible this should be improved in the future to save 500 microseconds of wak-up time. See TODO --- targets/TARGET_STM/sleep.c | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/targets/TARGET_STM/sleep.c b/targets/TARGET_STM/sleep.c index 4237eca2f6..2f6b41d113 100644 --- a/targets/TARGET_STM/sleep.c +++ b/targets/TARGET_STM/sleep.c @@ -35,6 +35,18 @@ extern void HAL_SuspendTick(void); extern void HAL_ResumeTick(void); +/* Wait loop - assuming tick is 1 us */ +static void wait_loop(uint32_t timeout) +{ + uint32_t t1, t2, elapsed = 0; + t1 = us_ticker_read(); + do { + t2 = us_ticker_read(); + elapsed = (t2 > t1) ? (t2 - t1) : ((uint64_t)t2 + 0xFFFFFFFF - t1 + 1); + } while (elapsed < timeout); + return; +} + // On L4 platforms we've seen unstable PLL CLK configuraiton // when DEEP SLEEP exits just few µs after being entered // So we need to force MSI usage before setting clocks again @@ -174,6 +186,12 @@ void hal_deepsleep(void) // After wake-up from STOP reconfigure the PLL SetSysClock(); + /* Wait for clock to be stabilized. + * TO DO: a better way of doing this, would be to rely on + * HW Flag. At least this ensures proper operation out of + * deep sleep */ + wait_loop(500); + TIM_HandleTypeDef TimMasterHandle; TimMasterHandle.Instance = TIM_MST; __HAL_TIM_SET_COUNTER(&TimMasterHandle, EnterTimeUS); From ad4a250292f34cb664adf3218d1daa25f3919373 Mon Sep 17 00:00:00 2001 From: Laurent MEUNIER Date: Fri, 6 Apr 2018 17:03:53 +0200 Subject: [PATCH 3/3] Style fix --- targets/TARGET_STM/sleep.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/targets/TARGET_STM/sleep.c b/targets/TARGET_STM/sleep.c index 2f6b41d113..10c3cefde5 100644 --- a/targets/TARGET_STM/sleep.c +++ b/targets/TARGET_STM/sleep.c @@ -50,7 +50,8 @@ static void wait_loop(uint32_t timeout) // On L4 platforms we've seen unstable PLL CLK configuraiton // when DEEP SLEEP exits just few µs after being entered // So we need to force MSI usage before setting clocks again -static void ForceClockOutofDeepSleep(void) { +static void ForceClockOutofDeepSleep(void) +{ RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; RCC_OscInitTypeDef RCC_OscInitStruct = {0}; uint32_t pFLatency = 0;