Skip to content

STM32 Low Power Timer feature updates #5459

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Dec 12, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 3 additions & 14 deletions targets/TARGET_STM/lp_ticker.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* mbed Microcontroller Library
*******************************************************************************
* Copyright (c) 2016, STMicroelectronics
* Copyright (c) 2017, STMicroelectronics
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -31,29 +31,18 @@

#if DEVICE_LOWPOWERTIMER

#include "ticker_api.h"
#include "lp_ticker_api.h"
#include "rtc_api.h"
#include "rtc_api_hal.h"

static uint8_t lp_ticker_inited = 0;

void lp_ticker_init(void)
{
if (lp_ticker_inited) return;
lp_ticker_inited = 1;

rtc_init();
rtc_set_irq_handler((uint32_t) lp_ticker_irq_handler);
}

uint32_t lp_ticker_read(void)
{
uint32_t usecs = 0;
time_t time = 0;

lp_ticker_init();

do {
time = rtc_read();
usecs = rtc_read_subseconds();
Expand Down Expand Up @@ -82,7 +71,7 @@ void lp_ticker_disable_interrupt(void)

void lp_ticker_clear_interrupt(void)
{

NVIC_ClearPendingIRQ(RTC_WKUP_IRQn);
}

#endif
#endif /* DEVICE_LOWPOWERTIMER */
138 changes: 77 additions & 61 deletions targets/TARGET_STM/rtc_api.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* mbed Microcontroller Library
*******************************************************************************
* Copyright (c) 2016, STMicroelectronics
* Copyright (c) 2017, STMicroelectronics
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand All @@ -27,29 +27,15 @@
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*******************************************************************************
*/

#if DEVICE_RTC

#include "rtc_api.h"
#include "rtc_api_hal.h"
#include "mbed_error.h"
#include "mbed_mktime.h"

static RTC_HandleTypeDef RtcHandle;

#if RTC_LSI
#define RTC_CLOCK LSI_VALUE
#else
#define RTC_CLOCK LSE_VALUE
#endif

#if DEVICE_LOWPOWERTIMER
#define RTC_ASYNCH_PREDIV ((RTC_CLOCK - 1) / 0x8000)
#define RTC_SYNCH_PREDIV (RTC_CLOCK / (RTC_ASYNCH_PREDIV + 1) - 1)
#else
#define RTC_ASYNCH_PREDIV (0x007F)
#define RTC_SYNCH_PREDIV (RTC_CLOCK / (RTC_ASYNCH_PREDIV + 1) - 1)
#endif

#if DEVICE_LOWPOWERTIMER
static void (*irq_handler)(void);
static void RTC_IRQHandler(void);
Expand All @@ -61,38 +47,38 @@ void rtc_init(void)
RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0};

// Enable access to Backup domain
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWR_EnableBkUpAccess();

RtcHandle.Instance = RTC;
RtcHandle.State = HAL_RTC_STATE_RESET;
if (rtc_isenabled()) {
return;
}

#if !RTC_LSI
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
#if MBED_CONF_TARGET_LSE_AVAILABLE
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; // Mandatory, otherwise the PLL is reconfigured!
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
RCC_OscInitStruct.LSIState = RCC_LSI_OFF;

if (HAL_RCC_OscConfig(&RCC_OscInitStruct) == HAL_OK) {
__HAL_RCC_RTC_CLKPRESCALER(RCC_RTCCLKSOURCE_LSE);
__HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_LSE);
} else {
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
error("Cannot initialize RTC with LSE\n");
}

__HAL_RCC_RTC_CLKPRESCALER(RCC_RTCCLKSOURCE_LSE);
__HAL_RCC_RTC_CONFIG(RCC_RTCCLKSOURCE_LSE);

PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_RTC;
PeriphClkInitStruct.RTCClockSelection = RCC_RTCCLKSOURCE_LSE;
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) {
error("PeriphClkInitStruct RTC failed with LSE\n");
}
#else /* !RTC_LSI */
__HAL_RCC_PWR_CLK_ENABLE();

#else /* MBED_CONF_TARGET_LSE_AVAILABLE */
// Reset Backup domain
__HAL_RCC_BACKUPRESET_FORCE();
__HAL_RCC_BACKUPRESET_RELEASE();

// Enable LSI clock
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI;
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_LSE;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; // Mandatory, otherwise the PLL is reconfigured!
RCC_OscInitStruct.LSEState = RCC_LSE_OFF;
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
Expand All @@ -108,46 +94,50 @@ void rtc_init(void)
if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) {
error("PeriphClkInitStruct RTC failed with LSI\n");
}
#endif /* !RTC_LSI */
#endif /* MBED_CONF_TARGET_LSE_AVAILABLE */

// Enable RTC
__HAL_RCC_RTC_ENABLE();

RtcHandle.Instance = RTC;
RtcHandle.State = HAL_RTC_STATE_RESET;

#if TARGET_STM32F1
RtcHandle.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
#else /* TARGET_STM32F1 */
RtcHandle.Init.HourFormat = RTC_HOURFORMAT_24;
RtcHandle.Init.AsynchPrediv = RTC_ASYNCH_PREDIV;
RtcHandle.Init.SynchPrediv = RTC_SYNCH_PREDIV;

/* PREDIV_A : 7-bit asynchronous prescaler */
#if DEVICE_LOWPOWERTIMER
/* PREDIV_A is set to a small value to improve the SubSeconds resolution */
/* with a 32768Hz clock, PREDIV_A=7 gives a precision of 244us */
RtcHandle.Init.AsynchPrediv = 7;
#else
/* PREDIV_A is set to the maximum value to improve the consumption */
RtcHandle.Init.AsynchPrediv = 0x007F;
#endif
/* PREDIV_S : 15-bit synchronous prescaler */
/* PREDIV_S is set in order to get a 1 Hz clock */
RtcHandle.Init.SynchPrediv = RTC_CLOCK / (RtcHandle.Init.AsynchPrediv + 1) - 1;
RtcHandle.Init.OutPut = RTC_OUTPUT_DISABLE;
RtcHandle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
RtcHandle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
#endif /* TARGET_STM32F1 */

if (HAL_RTC_Init(&RtcHandle) != HAL_OK) {
error("RTC error: RTC initialization failed.");
error("RTC initialization failed");
}

#if DEVICE_LOWPOWERTIMER
rtc_synchronize(); // Wait for RSF

#if !RTC_LSI
if (!rtc_isenabled())
#endif /* !RTC_LSI */
{
if (!rtc_isenabled()) {
rtc_write(0);
}

NVIC_ClearPendingIRQ(RTC_WKUP_IRQn);
NVIC_DisableIRQ(RTC_WKUP_IRQn);
NVIC_SetVector(RTC_WKUP_IRQn, (uint32_t)RTC_IRQHandler);
NVIC_EnableIRQ(RTC_WKUP_IRQn);

#endif /* DEVICE_LOWPOWERTIMER */
}

void rtc_free(void)
{
#if RTC_LSI
#if !MBED_CONF_TARGET_LSE_AVAILABLE
// Enable Power clock
__HAL_RCC_PWR_CLK_ENABLE();

Expand Down Expand Up @@ -279,8 +269,12 @@ void rtc_write(time_t t)
#endif /* TARGET_STM32F1 */

// Change the RTC current date/time
HAL_RTC_SetDate(&RtcHandle, &dateStruct, RTC_FORMAT_BIN);
HAL_RTC_SetTime(&RtcHandle, &timeStruct, RTC_FORMAT_BIN);
if (HAL_RTC_SetDate(&RtcHandle, &dateStruct, RTC_FORMAT_BIN) != HAL_OK) {
error("HAL_RTC_SetDate error\n");
}
if (HAL_RTC_SetTime(&RtcHandle, &timeStruct, RTC_FORMAT_BIN) != HAL_OK) {
error("HAL_RTC_SetTime error\n");
}
}

int rtc_isenabled(void)
Expand All @@ -292,6 +286,13 @@ int rtc_isenabled(void)
#endif /* TARGET_STM32F1 */
}

void rtc_synchronize(void)
{
if (HAL_RTC_WaitForSynchro(&RtcHandle) != HAL_OK) {
error("rtc_synchronize error\n");
}
}

#if DEVICE_LOWPOWERTIMER

static void RTC_IRQHandler(void)
Expand All @@ -304,23 +305,42 @@ static void RTC_IRQHandler(void)
}
}

void rtc_set_irq_handler(uint32_t handler)
{
irq_handler = (void (*)(void))handler;
}

uint32_t rtc_read_subseconds(void)
{
return 1000000.f * ((double)(RTC_SYNCH_PREDIV - RTC->SSR) / (RTC_SYNCH_PREDIV + 1));
return 1000000.f * ((double)((RTC->PRER & RTC_PRER_PREDIV_S) - RTC->SSR) / ((RTC->PRER & RTC_PRER_PREDIV_S) + 1));
}

void rtc_set_wake_up_timer(uint32_t delta)
{
uint32_t wake_up_counter = delta / (2000000 / RTC_CLOCK);
/* Ex for Wakeup period resolution with RTCCLK=32768 Hz :
* RTCCLK_DIV2: ~122us < wakeup period < ~4s
* RTCCLK_DIV4: ~244us < wakeup period < ~8s
* RTCCLK_DIV8: ~488us < wakeup period < ~16s
* RTCCLK_DIV16: ~976us < wakeup period < ~32s
* CK_SPRE_16BITS: 1s < wakeup period < (0xFFFF+ 1) x 1 s = 65536 s (18 hours)
* CK_SPRE_17BITS: 18h+1s < wakeup period < (0x1FFFF+ 1) x 1 s = 131072 s (36 hours)
*/
uint32_t WakeUpClock[6] = {RTC_WAKEUPCLOCK_RTCCLK_DIV2, RTC_WAKEUPCLOCK_RTCCLK_DIV4, RTC_WAKEUPCLOCK_RTCCLK_DIV8, RTC_WAKEUPCLOCK_RTCCLK_DIV16, RTC_WAKEUPCLOCK_CK_SPRE_16BITS, RTC_WAKEUPCLOCK_CK_SPRE_17BITS};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think we can get more details in the commit message regarding this calculation of WakeUpClock ?I could think of how this is improving the time ( similar to what the comment says about, that this is choosing the best divider for RTC clock based on the period (if I read the code correctly)) ?

uint8_t ClockDiv[4] = {2, 4, 8, 16};
uint32_t WakeUpCounter;
uint8_t DivIndex = 0;

do {
WakeUpCounter = delta / (ClockDiv[DivIndex] * 1000000 / RTC_CLOCK);
DivIndex++;
} while ( (WakeUpCounter > 0xFFFF) && (DivIndex < 4) );

if (WakeUpCounter > 0xFFFF) {
WakeUpCounter = delta / 1000000;
DivIndex++;
}

if (HAL_RTCEx_SetWakeUpTimer_IT(&RtcHandle, wake_up_counter,
RTC_WAKEUPCLOCK_RTCCLK_DIV2) != HAL_OK) {
error("Set wake up timer failed\n");
irq_handler = (void (*)(void))lp_ticker_irq_handler;
NVIC_SetVector(RTC_WKUP_IRQn, (uint32_t)RTC_IRQHandler);
NVIC_EnableIRQ(RTC_WKUP_IRQn);

if (HAL_RTCEx_SetWakeUpTimer_IT(&RtcHandle, 0xFFFF & WakeUpCounter, WakeUpClock[DivIndex-1]) != HAL_OK) {
error("rtc_set_wake_up_timer init error (%d)\n", DivIndex);
}
}

Expand All @@ -329,10 +349,6 @@ void rtc_deactivate_wake_up_timer(void)
HAL_RTCEx_DeactivateWakeUpTimer(&RtcHandle);
}

void rtc_synchronize(void)
{
HAL_RTC_WaitForSynchro(&RtcHandle);
}
#endif /* DEVICE_LOWPOWERTIMER */

#endif /* DEVICE_RTC */
17 changes: 8 additions & 9 deletions targets/TARGET_STM/rtc_api_hal.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/* mbed Microcontroller Library
*******************************************************************************
* Copyright (c) 2016, STMicroelectronics
* Copyright (c) 2017, STMicroelectronics
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -33,19 +33,18 @@

#include <stdint.h>
#include "rtc_api.h"
#include "ticker_api.h"
#include "lp_ticker_api.h"

#ifdef __cplusplus
extern "C" {
#endif
/*
* Extend rtc_api.h
*/

/** Set the given function as handler of wakeup timer event.
*
* @param handler The function to set as handler
*/
void rtc_set_irq_handler(uint32_t handler);
#if MBED_CONF_TARGET_LSE_AVAILABLE
#define RTC_CLOCK LSE_VALUE
#else
#define RTC_CLOCK LSI_VALUE
#endif

/** Read the subsecond register.
*
Expand Down
17 changes: 12 additions & 5 deletions targets/TARGET_STM/sleep.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,16 +68,16 @@ void hal_deepsleep(void)
#if TARGET_STM32L4
int pwrClockEnabled = __HAL_RCC_PWR_IS_CLK_ENABLED();
int lowPowerModeEnabled = PWR->CR1 & PWR_CR1_LPR;

if (!pwrClockEnabled) {
__HAL_RCC_PWR_CLK_ENABLE();
}
if (lowPowerModeEnabled) {
HAL_PWREx_DisableLowPowerRunMode();
}

HAL_PWREx_EnterSTOP2Mode(PWR_STOPENTRY_WFI);

if (lowPowerModeEnabled) {
HAL_PWREx_EnableLowPowerRunMode();
}
Expand All @@ -101,8 +101,15 @@ void hal_deepsleep(void)
TimMasterHandle.Instance = TIM_MST;
__HAL_TIM_SET_COUNTER(&TimMasterHandle, EnterTimeUS);

#if DEVICE_LOWPOWERTIMER
rtc_synchronize();
#if DEVICE_RTC
/* Wait for RTC RSF bit synchro if RTC is configured */
#if (TARGET_STM32F2) || (TARGET_STM32F4) || (TARGET_STM32F7)
if (READ_BIT(RCC->BDCR, RCC_BDCR_RTCSEL)) {
#else /* (TARGET_STM32F2) || (TARGET_STM32F4) || (TARGET_STM32F7) */
if (__HAL_RCC_GET_RTC_SOURCE()) {
#endif /* (TARGET_STM32F2) || (TARGET_STM32F4) || (TARGET_STM32F7) */
rtc_synchronize();
}
#endif
}

Expand Down
Loading