Skip to content

STM32 LPTICKER update for targets supporting RTC #6739

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
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
49 changes: 35 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) 2017, STMicroelectronics
* Copyright (c) 2018, STMicroelectronics
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -30,16 +30,24 @@

#if DEVICE_LPTICKER

#include "rtc_api_hal.h"

/***********************************************************************/
/* lpticker_lptim config is 1 in json config file */
/* LPTICKER is based on LPTIM feature from ST drivers. RTC is not used */
#if MBED_CONF_TARGET_LPTICKER_LPTIM

#include "lp_ticker_api.h"
#include "mbed_error.h"

LPTIM_HandleTypeDef LptimHandle;

const ticker_info_t* lp_ticker_get_info()
{
static const ticker_info_t info = {
RTC_CLOCK,
#if MBED_CONF_TARGET_LSE_AVAILABLE
LSE_VALUE,
#else
LSI_VALUE,
#endif
16
};
return &info;
Expand Down Expand Up @@ -165,8 +173,8 @@ static void LPTIM1_IRQHandler(void)
/* Clear Compare match flag */
__HAL_LPTIM_CLEAR_FLAG(&LptimHandle, LPTIM_FLAG_CMPM);

if (irq_handler) {
irq_handler();
if (irq_handler) {
irq_handler();
}
}
}
Expand Down Expand Up @@ -224,30 +232,43 @@ void lp_ticker_clear_interrupt(void)
NVIC_ClearPendingIRQ(LPTIM1_IRQn);
}



/*****************************************************************/
/* lpticker_lptim config is 0 or not defined in json config file */
/* LPTICKER is based on RTC wake up feature from ST drivers */
#else /* MBED_CONF_TARGET_LPTICKER_LPTIM */

#include "rtc_api_hal.h"

const ticker_info_t* lp_ticker_get_info()
{
static const ticker_info_t info = {
RTC_CLOCK/4, // RTC_WAKEUPCLOCK_RTCCLK_DIV4
32
};
return &info;
}

void lp_ticker_init(void)
{
rtc_init();
lp_ticker_disable_interrupt();
}

uint32_t lp_ticker_read(void)
{
uint32_t usecs = rtc_read_us();
return usecs;
return rtc_read_lp();
}

void lp_ticker_set_interrupt(timestamp_t timestamp)
{
uint32_t delta;

delta = timestamp - lp_ticker_read();
rtc_set_wake_up_timer(delta);
rtc_set_wake_up_timer(timestamp);
}

void lp_ticker_fire_interrupt(void)
{
NVIC_SetPendingIRQ(RTC_WKUP_IRQn);
rtc_fire_interrupt();
}

void lp_ticker_disable_interrupt(void)
Expand All @@ -257,7 +278,7 @@ void lp_ticker_disable_interrupt(void)

void lp_ticker_clear_interrupt(void)
{
NVIC_ClearPendingIRQ(RTC_WKUP_IRQn);
NVIC_DisableIRQ(RTC_WKUP_IRQn);
}

#endif /* MBED_CONF_TARGET_LPTICKER_LPTIM */
Expand Down
136 changes: 70 additions & 66 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) 2017, STMicroelectronics
* Copyright (c) 2018, STMicroelectronics
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -32,18 +32,10 @@

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

static RTC_HandleTypeDef RtcHandle;

#if DEVICE_LPTICKER && !MBED_CONF_TARGET_LPTICKER_LPTIM

#define GET_TICK_PERIOD(VALUE) (2048 * 1000000 / VALUE) /* 1s / SynchPrediv value * 2^11 (value to get the maximum precision value with no u32 overflow) */

static void (*irq_handler)(void);
static void RTC_IRQHandler(void);
static uint32_t lp_TickPeriod_us = GET_TICK_PERIOD(4095); /* default SynchPrediv value = 4095 */
#endif /* DEVICE_LPTICKER && !MBED_CONF_TARGET_LPTICKER_LPTIM */

void rtc_init(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
Expand All @@ -53,7 +45,11 @@ void rtc_init(void)
__HAL_RCC_PWR_CLK_ENABLE();
HAL_PWR_EnableBkUpAccess();

#if DEVICE_LPTICKER
if ( (rtc_isenabled()) && ((RTC->PRER & RTC_PRER_PREDIV_S) == PREDIV_S_VALUE) ) {
#else /* DEVICE_LPTICKER */
if (rtc_isenabled()) {
#endif /* DEVICE_LPTICKER */
return;
}

Expand Down Expand Up @@ -107,28 +103,13 @@ void rtc_init(void)
RtcHandle.Init.AsynchPrediv = RTC_AUTO_1_SECOND;
#else /* TARGET_STM32F1 */
RtcHandle.Init.HourFormat = RTC_HOURFORMAT_24;

/* PREDIV_A : 7-bit asynchronous prescaler */
#if DEVICE_LPTICKER && !MBED_CONF_TARGET_LPTICKER_LPTIM
/* 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.AsynchPrediv = PREDIV_A_VALUE;
RtcHandle.Init.SynchPrediv = PREDIV_S_VALUE;
RtcHandle.Init.OutPut = RTC_OUTPUT_DISABLE;
RtcHandle.Init.OutPutPolarity = RTC_OUTPUT_POLARITY_HIGH;
RtcHandle.Init.OutPutType = RTC_OUTPUT_TYPE_OPENDRAIN;
#endif /* TARGET_STM32F1 */

#if DEVICE_LPTICKER && !MBED_CONF_TARGET_LPTICKER_LPTIM
lp_TickPeriod_us = GET_TICK_PERIOD(RtcHandle.Init.SynchPrediv);
#endif

if (HAL_RTC_Init(&RtcHandle) != HAL_OK) {
error("RTC initialization failed");
}
Expand Down Expand Up @@ -294,18 +275,41 @@ void rtc_synchronize(void)

#if DEVICE_LPTICKER && !MBED_CONF_TARGET_LPTICKER_LPTIM

static void RTC_IRQHandler(void);
static void (*irq_handler)(void);

volatile uint8_t lp_Fired = 0;
volatile uint32_t LP_continuous_time = 0;
volatile uint32_t LP_last_RTC_time = 0;

static void RTC_IRQHandler(void)
{
/* Update HAL state */
RtcHandle.Instance = RTC;
HAL_RTCEx_WakeUpTimerIRQHandler(&RtcHandle);
/* In case of registered handler, call it. */
if (irq_handler) {
irq_handler();
if(__HAL_RTC_WAKEUPTIMER_GET_IT(&RtcHandle, RTC_IT_WUT)) {
/* Get the status of the Interrupt */
if((uint32_t)(RTC->CR & RTC_IT_WUT) != (uint32_t)RESET) {
/* Clear the WAKEUPTIMER interrupt pending bit */
__HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(&RtcHandle, RTC_FLAG_WUTF);

lp_Fired = 0;
if (irq_handler) {
irq_handler();
}
}
}

if (lp_Fired) {
lp_Fired = 0;
if (irq_handler) {
irq_handler();
}
}

__HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG();
}

uint32_t rtc_read_us(void)
uint32_t rtc_read_lp(void)
{
RTC_TimeTypeDef timeStruct = {0};
RTC_DateTypeDef dateStruct = {0};
Expand All @@ -322,52 +326,52 @@ uint32_t rtc_read_us(void)
time/date is one second less than as indicated by RTC_TR/RTC_DR. */
timeStruct.Seconds -= 1;
}
uint32_t RTCTime = timeStruct.Seconds + timeStruct.Minutes * 60 + timeStruct.Hours * 60 * 60;
uint32_t Time_us = ((timeStruct.SecondFraction - timeStruct.SubSeconds) * lp_TickPeriod_us) >> 11;
uint32_t RTC_time_s = timeStruct.Seconds + timeStruct.Minutes * 60 + timeStruct.Hours * 60 * 60; // Max 0x0001-517F => * 8191 + 8191 = 0x2A2E-AE80

return (RTCTime * 1000000) + Time_us ;
if (LP_last_RTC_time <= RTC_time_s) {
LP_continuous_time += (RTC_time_s - LP_last_RTC_time);
} else {
LP_continuous_time += (24 * 60 * 60 + RTC_time_s - LP_last_RTC_time);
}
LP_last_RTC_time = RTC_time_s;

return LP_continuous_time * PREDIV_S_VALUE + timeStruct.SecondFraction - timeStruct.SubSeconds;
}

void rtc_set_wake_up_timer(uint32_t delta)
void rtc_set_wake_up_timer(timestamp_t timestamp)
{
#define RTC_CLOCK_US (((uint64_t)RTC_CLOCK << 32 ) / 1000000)

uint32_t WakeUpCounter;
uint32_t WakeUpClock;

/* 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)
*/
if (delta < (0x10000 * 2 / RTC_CLOCK * 1000000) ) { // (0xFFFF + 1) * RTCCLK_DIV2 / RTC_CLOCK * 1s
WakeUpCounter = (((uint64_t)delta * RTC_CLOCK_US) >> 32) >> 1 ;
WakeUpClock = RTC_WAKEUPCLOCK_RTCCLK_DIV2;
} else if (delta < (0x10000 * 4 / RTC_CLOCK * 1000000) ) {
WakeUpCounter = (((uint64_t)delta * RTC_CLOCK_US) >> 32) >> 2 ;
WakeUpClock = RTC_WAKEUPCLOCK_RTCCLK_DIV4;
} else if (delta < (0x10000 * 8 / RTC_CLOCK * 1000000) ) {
WakeUpCounter = (((uint64_t)delta * RTC_CLOCK_US) >> 32) >> 3 ;
WakeUpClock = RTC_WAKEUPCLOCK_RTCCLK_DIV8;
} else if (delta < (0x10000 * 16 / RTC_CLOCK * 1000000) ) {
WakeUpCounter = (((uint64_t)delta * RTC_CLOCK_US) >> 32) >> 4 ;
WakeUpClock = RTC_WAKEUPCLOCK_RTCCLK_DIV16;
uint32_t current_lp_time;

current_lp_time = rtc_read_lp();

if (timestamp < current_lp_time) {
WakeUpCounter = 0xFFFFFFFF - current_lp_time + timestamp;
} else {
WakeUpCounter = (delta / 1000000) ;
WakeUpClock = RTC_WAKEUPCLOCK_CK_SPRE_16BITS;
WakeUpCounter = timestamp - current_lp_time;
}

irq_handler = (void (*)(void))lp_ticker_irq_handler;
NVIC_SetVector(RTC_WKUP_IRQn, (uint32_t)RTC_IRQHandler);
NVIC_EnableIRQ(RTC_WKUP_IRQn);
if (WakeUpCounter > 0xFFFF) {
WakeUpCounter = 0xFFFF;
}

RtcHandle.Instance = RTC;
if (HAL_RTCEx_SetWakeUpTimer_IT(&RtcHandle, (uint32_t)WakeUpCounter, WakeUpClock) != HAL_OK) {
if (HAL_RTCEx_SetWakeUpTimer_IT(&RtcHandle, WakeUpCounter, RTC_WAKEUPCLOCK_RTCCLK_DIV4) != HAL_OK) {
error("rtc_set_wake_up_timer init error\n");
}

NVIC_SetVector(RTC_WKUP_IRQn, (uint32_t)RTC_IRQHandler);
irq_handler = (void (*)(void))lp_ticker_irq_handler;
NVIC_EnableIRQ(RTC_WKUP_IRQn);
}

void rtc_fire_interrupt(void)
{
lp_Fired = 1;
NVIC_SetVector(RTC_WKUP_IRQn, (uint32_t)RTC_IRQHandler);
irq_handler = (void (*)(void))lp_ticker_irq_handler;
NVIC_SetPendingIRQ(RTC_WKUP_IRQn);
NVIC_EnableIRQ(RTC_WKUP_IRQn);
}

void rtc_deactivate_wake_up_timer(void)
Expand Down
43 changes: 28 additions & 15 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) 2017, STMicroelectronics
* Copyright (c) 2018, STMicroelectronics
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -31,15 +31,8 @@
#ifndef MBED_RTC_API_HAL_H
#define MBED_RTC_API_HAL_H

#include <stdint.h>
#include "rtc_api.h"
#include "ticker_api.h"
#include "lp_ticker_api.h"
#include "us_ticker_api.h"
#include "hal_tick.h"
#include "mbed_critical.h"
#include "mbed_error.h"
#include "mbed_debug.h"

#ifdef __cplusplus
extern "C" {
Expand All @@ -51,31 +44,51 @@ extern "C" {
#define RTC_CLOCK LSI_VALUE
#endif

/** Read RTC time with subsecond precision.
#if DEVICE_LPTICKER && !MBED_CONF_TARGET_LPTICKER_LPTIM
/* PREDIV_A : 7-bit asynchronous prescaler */
/* PREDIV_A is set to set LPTICKER frequency to RTC_CLOCK/4 */
#define PREDIV_A_VALUE 3

/** Read RTC counter with sub second precision
*
* @return Time is microsecond
* @return LP ticker counter
*/
uint32_t rtc_read_us(void);
uint32_t rtc_read_lp(void);

/** Program a wake up timer event in delta microseconds.
/** Program a wake up timer event
*
* @param delta The time to wait
* @param timestamp: counter to set
*/
void rtc_set_wake_up_timer(timestamp_t timestamp);

/** Call RTC Wake Up IT
*/
void rtc_set_wake_up_timer(uint32_t delta);
void rtc_fire_interrupt(void);

/** Disable the wake up timer event.
*
* The wake up timer use auto reload, you have to deactivate it manually.
*/
void rtc_deactivate_wake_up_timer(void);

#else /* DEVICE_LPTICKER && !MBED_CONF_TARGET_LPTICKER_LPTIM */

/* PREDIV_A : 7-bit asynchronous prescaler */
/* PREDIV_A is set to the maximum value to improve the consumption */
#define PREDIV_A_VALUE 127

#endif /* DEVICE_LPTICKER && !MBED_CONF_TARGET_LPTICKER_LPTIM */

/* PREDIV_S : 15-bit synchronous prescaler */
/* PREDIV_S is set in order to get a 1 Hz clock */
#define PREDIV_S_VALUE RTC_CLOCK / (PREDIV_A_VALUE + 1) - 1

/** Synchronise the RTC shadow registers.
*
* Must be called after a deepsleep.
*/
void rtc_synchronize(void);


#ifdef __cplusplus
}
#endif
Expand Down
Loading