2016-09-06 14:01:19 +00:00
|
|
|
/* mbed Microcontroller Library
|
|
|
|
*******************************************************************************
|
2018-03-28 13:00:53 +00:00
|
|
|
* Copyright (c) 2018, STMicroelectronics
|
2016-09-06 14:01:19 +00:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
*
|
|
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
|
|
* and/or other materials provided with the distribution.
|
|
|
|
* 3. Neither the name of STMicroelectronics nor the names of its contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
|
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
|
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*******************************************************************************
|
|
|
|
*/
|
|
|
|
|
2018-03-13 17:11:18 +00:00
|
|
|
#if DEVICE_LPTICKER
|
2016-09-06 14:01:19 +00:00
|
|
|
|
2018-03-28 13:00:53 +00:00
|
|
|
/***********************************************************************/
|
|
|
|
/* lpticker_lptim config is 1 in json config file */
|
|
|
|
/* LPTICKER is based on LPTIM feature from ST drivers. RTC is not used */
|
2018-03-13 17:11:18 +00:00
|
|
|
#if MBED_CONF_TARGET_LPTICKER_LPTIM
|
2018-01-09 13:10:14 +00:00
|
|
|
|
2018-03-28 13:00:53 +00:00
|
|
|
#include "lp_ticker_api.h"
|
|
|
|
#include "mbed_error.h"
|
2019-05-06 09:56:15 +00:00
|
|
|
#include "mbed_power_mgmt.h"
|
|
|
|
#include "platform/mbed_critical.h"
|
|
|
|
#include <stdbool.h>
|
2018-03-28 13:00:53 +00:00
|
|
|
|
2019-06-04 08:29:06 +00:00
|
|
|
/* lpticker delay is for using C++ Low Power Ticker wrapper,
|
|
|
|
* which introduces extra delays. We rather want to use the
|
|
|
|
* low level implementation from this file */
|
|
|
|
#if defined(LPTICKER_DELAY_TICKS) && (LPTICKER_DELAY_TICKS > 0)
|
|
|
|
#warning "lpticker_delay_ticks usage not recommended"
|
2018-11-15 13:43:26 +00:00
|
|
|
#endif
|
|
|
|
|
2019-05-06 09:56:15 +00:00
|
|
|
#define LP_TIMER_WRAP(val) (val & 0xFFFF)
|
|
|
|
/* Safe guard is the number of ticks between the current tick and the next
|
|
|
|
* tick we want to program an interrupt for. Programing an interrupt in
|
|
|
|
* between is unreliable */
|
|
|
|
#define LP_TIMER_SAFE_GUARD 5
|
|
|
|
|
2019-10-14 16:02:57 +00:00
|
|
|
|
|
|
|
#if defined(DUAL_CORE)
|
|
|
|
#if defined(CORE_CM7)
|
|
|
|
#define LPTIM_MST_BASE LPTIM4_BASE
|
|
|
|
#define LPTIM_MST ((LPTIM_TypeDef *)LPTIM_MST_BASE)
|
|
|
|
|
|
|
|
#define RCC_PERIPHCLK_LPTIM RCC_PERIPHCLK_LPTIM4
|
|
|
|
#define RCC_LPTIMCLKSOURCE_LSE RCC_LPTIM4CLKSOURCE_LSE
|
|
|
|
#define RCC_LPTIMCLKSOURCE_LSI RCC_LPTIM4CLKSOURCE_LSI
|
|
|
|
|
|
|
|
#define LPTIM_MST_IRQ LPTIM4_IRQn
|
|
|
|
#define LPTIM_MST_RCC __HAL_RCC_LPTIM4_CLK_ENABLE
|
|
|
|
|
2019-11-18 17:13:09 +00:00
|
|
|
#define LPTIM_MST_RCC_CLKAM __HAL_RCC_LPTIM4_CLKAM_ENABLE
|
|
|
|
|
|
|
|
/* Enable LPTIM wakeup source but only for current core, and disable it for the other core */
|
|
|
|
#define LPTIM_MST_EXTI_LPTIM_WAKEUP_CONFIG() {\
|
|
|
|
HAL_EXTI_D1_EventInputConfig(EXTI_LINE52, EXTI_MODE_IT, ENABLE);\
|
|
|
|
HAL_EXTI_D2_EventInputConfig(EXTI_LINE52, EXTI_MODE_IT, DISABLE);\
|
|
|
|
}
|
2019-10-14 16:02:57 +00:00
|
|
|
#define LPTIM_MST_RESET_ON __HAL_RCC_LPTIM4_FORCE_RESET
|
|
|
|
#define LPTIM_MST_RESET_OFF __HAL_RCC_LPTIM4_RELEASE_RESET
|
|
|
|
|
|
|
|
//#define LPTIM_MST_BIT_WIDTH 32 // 16 or 32
|
|
|
|
|
|
|
|
//#define LPTIM_MST_PCLK 1 // Select the peripheral clock number (1 or 2)
|
|
|
|
|
|
|
|
#elif defined(CORE_CM4)
|
|
|
|
#define LPTIM_MST_BASE LPTIM5_BASE
|
|
|
|
#define LPTIM_MST ((LPTIM_TypeDef *)LPTIM_MST_BASE)
|
|
|
|
|
|
|
|
#define RCC_PERIPHCLK_LPTIM RCC_PERIPHCLK_LPTIM5
|
|
|
|
#define RCC_LPTIMCLKSOURCE_LSE RCC_LPTIM5CLKSOURCE_LSE
|
|
|
|
#define RCC_LPTIMCLKSOURCE_LSI RCC_LPTIM5CLKSOURCE_LSI
|
|
|
|
|
|
|
|
#define LPTIM_MST_IRQ LPTIM5_IRQn
|
|
|
|
#define LPTIM_MST_RCC __HAL_RCC_LPTIM5_CLK_ENABLE
|
|
|
|
|
2019-11-18 17:13:09 +00:00
|
|
|
#define LPTIM_MST_RCC_CLKAM __HAL_RCC_LPTIM5_CLKAM_ENABLE
|
|
|
|
|
|
|
|
/* Enable LPTIM wakeup source but only for current core, and disable it for the other core */
|
|
|
|
#define LPTIM_MST_EXTI_LPTIM_WAKEUP_CONFIG() {\
|
|
|
|
HAL_EXTI_D2_EventInputConfig(EXTI_LINE53, EXTI_MODE_IT, ENABLE);\
|
|
|
|
HAL_EXTI_D1_EventInputConfig(EXTI_LINE53, EXTI_MODE_IT, DISABLE);\
|
|
|
|
}
|
2019-10-14 16:02:57 +00:00
|
|
|
#define LPTIM_MST_RESET_ON __HAL_RCC_LPTIM5_FORCE_RESET
|
|
|
|
#define LPTIM_MST_RESET_OFF __HAL_RCC_LPTIM5_RELEASE_RESET
|
|
|
|
#else
|
|
|
|
#error "Core not supported"
|
|
|
|
#endif
|
|
|
|
#else
|
|
|
|
#define LPTIM_MST_BASE LPTIM1_BASE
|
|
|
|
#define LPTIM_MST ((LPTIM_TypeDef *)LPTIM_MST_BASE)
|
|
|
|
|
|
|
|
#define RCC_PERIPHCLK_LPTIM RCC_PERIPHCLK_LPTIM1
|
|
|
|
#define RCC_LPTIMCLKSOURCE_LSE RCC_LPTIM1CLKSOURCE_LSE
|
|
|
|
#define RCC_LPTIMCLKSOURCE_LSI RCC_LPTIM1CLKSOURCE_LSI
|
|
|
|
|
2020-05-22 03:04:13 +00:00
|
|
|
#if defined(STM32G071xx)
|
2019-12-06 15:24:36 +00:00
|
|
|
#define LPTIM_MST_IRQ TIM6_DAC_LPTIM1_IRQn
|
|
|
|
#else
|
2019-10-14 16:02:57 +00:00
|
|
|
#define LPTIM_MST_IRQ LPTIM1_IRQn
|
2019-12-06 15:24:36 +00:00
|
|
|
#endif
|
2019-10-14 16:02:57 +00:00
|
|
|
#define LPTIM_MST_RCC __HAL_RCC_LPTIM1_CLK_ENABLE
|
|
|
|
|
|
|
|
#define LPTIM_MST_RESET_ON __HAL_RCC_LPTIM1_FORCE_RESET
|
|
|
|
#define LPTIM_MST_RESET_OFF __HAL_RCC_LPTIM1_RELEASE_RESET
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
|
2018-01-09 13:10:14 +00:00
|
|
|
LPTIM_HandleTypeDef LptimHandle;
|
|
|
|
|
2018-06-27 12:21:07 +00:00
|
|
|
const ticker_info_t *lp_ticker_get_info()
|
2018-03-27 09:31:11 +00:00
|
|
|
{
|
|
|
|
static const ticker_info_t info = {
|
2018-03-28 13:00:53 +00:00
|
|
|
#if MBED_CONF_TARGET_LSE_AVAILABLE
|
2018-11-16 12:20:12 +00:00
|
|
|
LSE_VALUE / MBED_CONF_TARGET_LPTICKER_LPTIM_CLOCK,
|
2018-03-28 13:00:53 +00:00
|
|
|
#else
|
2018-11-16 12:20:12 +00:00
|
|
|
LSI_VALUE / MBED_CONF_TARGET_LPTICKER_LPTIM_CLOCK,
|
2018-03-28 13:00:53 +00:00
|
|
|
#endif
|
2018-03-27 09:31:11 +00:00
|
|
|
16
|
|
|
|
};
|
|
|
|
return &info;
|
|
|
|
}
|
|
|
|
|
2018-01-09 13:10:14 +00:00
|
|
|
volatile uint8_t lp_Fired = 0;
|
2019-05-06 09:56:15 +00:00
|
|
|
/* Flag and stored counter to handle delayed programing at low level */
|
|
|
|
volatile bool lp_delayed_prog = false;
|
2019-08-27 11:25:46 +00:00
|
|
|
|
|
|
|
volatile bool future_event_flag = false;
|
|
|
|
volatile bool roll_over_flag = false;
|
|
|
|
|
2019-05-06 09:56:15 +00:00
|
|
|
volatile bool lp_cmpok = false;
|
|
|
|
volatile timestamp_t lp_delayed_counter = 0;
|
|
|
|
volatile bool sleep_manager_locked = false;
|
2018-01-09 13:10:14 +00:00
|
|
|
|
2019-02-21 15:25:20 +00:00
|
|
|
static int LPTICKER_inited = 0;
|
2019-10-14 16:02:57 +00:00
|
|
|
static void LPTIM_IRQHandler(void);
|
2018-01-09 13:10:14 +00:00
|
|
|
|
|
|
|
void lp_ticker_init(void)
|
|
|
|
{
|
|
|
|
/* Check if LPTIM is already configured */
|
2019-02-21 15:25:20 +00:00
|
|
|
if (LPTICKER_inited) {
|
2018-07-12 15:34:16 +00:00
|
|
|
lp_ticker_disable_interrupt();
|
2018-01-09 13:10:14 +00:00
|
|
|
return;
|
|
|
|
}
|
2019-02-21 15:25:20 +00:00
|
|
|
LPTICKER_inited = 1;
|
2018-01-09 13:10:14 +00:00
|
|
|
|
|
|
|
RCC_PeriphCLKInitTypeDef RCC_PeriphCLKInitStruct = {0};
|
|
|
|
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
|
|
|
|
|
|
|
|
#if MBED_CONF_TARGET_LSE_AVAILABLE
|
|
|
|
|
|
|
|
/* Enable LSE clock */
|
|
|
|
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSE;
|
|
|
|
RCC_OscInitStruct.LSEState = RCC_LSE_ON;
|
|
|
|
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
|
|
|
|
|
|
|
|
/* Select the LSE clock as LPTIM peripheral clock */
|
2019-10-14 16:02:57 +00:00
|
|
|
RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM;
|
2018-01-09 13:10:14 +00:00
|
|
|
#if (TARGET_STM32L0)
|
2019-10-14 16:02:57 +00:00
|
|
|
RCC_PeriphCLKInitStruct.LptimClockSelection = RCC_LPTIMCLKSOURCE_LSE;
|
2018-01-09 13:10:14 +00:00
|
|
|
#else
|
2019-10-14 16:02:57 +00:00
|
|
|
#if (LPTIM_MST_BASE == LPTIM1_BASE)
|
|
|
|
RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIMCLKSOURCE_LSE;
|
|
|
|
#elif (LPTIM_MST_BASE == LPTIM3_BASE) || (LPTIM_MST_BASE == LPTIM4_BASE) || (LPTIM_MST_BASE == LPTIM5_BASE)
|
|
|
|
RCC_PeriphCLKInitStruct.Lptim345ClockSelection = RCC_LPTIMCLKSOURCE_LSE;
|
|
|
|
#endif /* LPTIM_MST_BASE == LPTIM1 */
|
|
|
|
#endif /* TARGET_STM32L0 */
|
2018-01-09 13:10:14 +00:00
|
|
|
#else /* MBED_CONF_TARGET_LSE_AVAILABLE */
|
|
|
|
|
|
|
|
/* Enable LSI clock */
|
2019-09-19 11:06:11 +00:00
|
|
|
#if TARGET_STM32WB
|
|
|
|
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI1;
|
|
|
|
#else
|
2018-01-09 13:10:14 +00:00
|
|
|
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI;
|
2019-09-19 11:06:11 +00:00
|
|
|
#endif
|
2018-01-09 13:10:14 +00:00
|
|
|
RCC_OscInitStruct.LSIState = RCC_LSI_ON;
|
|
|
|
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
|
|
|
|
|
|
|
|
/* Select the LSI clock as LPTIM peripheral clock */
|
2019-10-14 16:02:57 +00:00
|
|
|
RCC_PeriphCLKInitStruct.PeriphClockSelection = RCC_PERIPHCLK_LPTIM;
|
2018-01-09 13:10:14 +00:00
|
|
|
#if (TARGET_STM32L0)
|
2019-10-14 16:02:57 +00:00
|
|
|
RCC_PeriphCLKInitStruct.LptimClockSelection = RCC_LPTIMCLKSOURCE_LSI;
|
2018-01-09 13:10:14 +00:00
|
|
|
#else
|
2019-10-14 16:02:57 +00:00
|
|
|
#if (LPTIM_MST_BASE == LPTIM1_BASE)
|
|
|
|
RCC_PeriphCLKInitStruct.Lptim1ClockSelection = RCC_LPTIMCLKSOURCE_LSI;
|
|
|
|
#elif (LPTIM_MST_BASE == LPTIM3_BASE) || (LPTIM_MST_BASE == LPTIM4_BASE) || (LPTIM_MST_BASE == LPTIM5_BASE)
|
|
|
|
RCC_PeriphCLKInitStruct.Lptim345ClockSelection = RCC_LPTIMCLKSOURCE_LSI;
|
|
|
|
#endif /* LPTIM_MST_BASE == LPTIM1 */
|
|
|
|
#endif /* TARGET_STM32L0 */
|
2018-01-09 13:10:14 +00:00
|
|
|
|
|
|
|
#endif /* MBED_CONF_TARGET_LSE_AVAILABLE */
|
2019-10-14 16:02:57 +00:00
|
|
|
#if defined(DUAL_CORE)
|
2019-11-19 15:16:17 +00:00
|
|
|
while (LL_HSEM_1StepLock(HSEM, CFG_HW_RCC_SEMID)) {
|
2019-10-14 16:02:57 +00:00
|
|
|
}
|
|
|
|
#endif /* DUAL_CORE */
|
2018-01-09 13:10:14 +00:00
|
|
|
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) {
|
|
|
|
error("HAL_RCC_OscConfig ERROR\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphCLKInitStruct) != HAL_OK) {
|
|
|
|
error("HAL_RCCEx_PeriphCLKConfig ERROR\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-10-14 16:02:57 +00:00
|
|
|
LPTIM_MST_RCC();
|
|
|
|
LPTIM_MST_RESET_ON();
|
|
|
|
LPTIM_MST_RESET_OFF();
|
|
|
|
#if defined(DUAL_CORE)
|
2019-11-18 17:13:09 +00:00
|
|
|
/* Configure EXTI wakeup and configure autonomous mode */
|
|
|
|
LPTIM_MST_RCC_CLKAM();
|
|
|
|
LPTIM_MST_EXTI_LPTIM_WAKEUP_CONFIG();
|
|
|
|
|
2019-10-14 16:02:57 +00:00
|
|
|
LL_HSEM_ReleaseLock(HSEM, CFG_HW_RCC_SEMID, HSEM_CR_COREID_CURRENT);
|
|
|
|
#endif /* DUAL_CORE */
|
2018-01-09 13:10:14 +00:00
|
|
|
|
|
|
|
/* Initialize the LPTIM peripheral */
|
2019-10-14 16:02:57 +00:00
|
|
|
LptimHandle.Instance = LPTIM_MST;
|
2018-01-09 13:10:14 +00:00
|
|
|
LptimHandle.State = HAL_LPTIM_STATE_RESET;
|
|
|
|
LptimHandle.Init.Clock.Source = LPTIM_CLOCKSOURCE_APBCLOCK_LPOSC;
|
2018-11-16 12:20:12 +00:00
|
|
|
#if defined(MBED_CONF_TARGET_LPTICKER_LPTIM_CLOCK)
|
|
|
|
#if (MBED_CONF_TARGET_LPTICKER_LPTIM_CLOCK == 4)
|
|
|
|
LptimHandle.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV4;
|
|
|
|
#elif (MBED_CONF_TARGET_LPTICKER_LPTIM_CLOCK == 2)
|
|
|
|
LptimHandle.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV2;
|
|
|
|
#else
|
|
|
|
LptimHandle.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV1;
|
|
|
|
#endif
|
|
|
|
#else
|
2018-03-27 09:31:11 +00:00
|
|
|
LptimHandle.Init.Clock.Prescaler = LPTIM_PRESCALER_DIV1;
|
2018-11-16 12:20:12 +00:00
|
|
|
#endif /* MBED_CONF_TARGET_LPTICKER_LPTIM_CLOCK */
|
2018-01-09 13:10:14 +00:00
|
|
|
|
|
|
|
LptimHandle.Init.Trigger.Source = LPTIM_TRIGSOURCE_SOFTWARE;
|
2019-02-08 12:30:10 +00:00
|
|
|
#if defined (LPTIM_ACTIVEEDGE_FALLING)
|
|
|
|
LptimHandle.Init.Trigger.ActiveEdge = LPTIM_ACTIVEEDGE_FALLING;
|
|
|
|
#endif
|
|
|
|
#if defined (LPTIM_TRIGSAMPLETIME_DIRECTTRANSITION)
|
|
|
|
LptimHandle.Init.Trigger.SampleTime = LPTIM_TRIGSAMPLETIME_DIRECTTRANSITION;
|
|
|
|
#endif
|
|
|
|
|
2020-01-08 14:45:08 +00:00
|
|
|
LptimHandle.Init.UltraLowPowerClock.SampleTime = LPTIM_TRIGSAMPLETIME_DIRECTTRANSITION; // L5 ?
|
|
|
|
|
2018-01-09 13:10:14 +00:00
|
|
|
LptimHandle.Init.OutputPolarity = LPTIM_OUTPUTPOLARITY_HIGH;
|
|
|
|
LptimHandle.Init.UpdateMode = LPTIM_UPDATE_IMMEDIATE;
|
|
|
|
LptimHandle.Init.CounterSource = LPTIM_COUNTERSOURCE_INTERNAL;
|
2020-01-08 14:45:08 +00:00
|
|
|
#if defined (LPTIM_INPUT1SOURCE_GPIO) /* STM32L4 / STM32L5 */
|
2018-01-09 13:10:14 +00:00
|
|
|
LptimHandle.Init.Input1Source = LPTIM_INPUT1SOURCE_GPIO;
|
|
|
|
LptimHandle.Init.Input2Source = LPTIM_INPUT2SOURCE_GPIO;
|
2018-11-23 17:04:23 +00:00
|
|
|
#endif /* LPTIM_INPUT1SOURCE_GPIO */
|
2018-01-09 13:10:14 +00:00
|
|
|
|
2020-01-08 14:45:08 +00:00
|
|
|
#if defined(LPTIM_RCR_REP) /* STM32L4 / STM32L5 */
|
|
|
|
LptimHandle.Init.RepetitionCounter = 0;
|
|
|
|
#endif /* LPTIM_RCR_REP */
|
|
|
|
|
|
|
|
|
2018-01-09 13:10:14 +00:00
|
|
|
if (HAL_LPTIM_Init(&LptimHandle) != HAL_OK) {
|
|
|
|
error("HAL_LPTIM_Init ERROR\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2019-10-14 16:02:57 +00:00
|
|
|
NVIC_SetVector(LPTIM_MST_IRQ, (uint32_t)LPTIM_IRQHandler);
|
2018-01-09 13:10:14 +00:00
|
|
|
|
2018-11-23 17:04:23 +00:00
|
|
|
#if defined (__HAL_LPTIM_WAKEUPTIMER_EXTI_ENABLE_IT)
|
2018-01-09 13:10:14 +00:00
|
|
|
/* EXTI lines are not configured by default */
|
|
|
|
__HAL_LPTIM_WAKEUPTIMER_EXTI_ENABLE_IT();
|
2019-07-18 08:20:59 +00:00
|
|
|
#endif
|
|
|
|
#if defined (__HAL_LPTIM_WAKEUPTIMER_EXTI_ENABLE_RISING_EDGE)
|
2018-01-09 13:10:14 +00:00
|
|
|
__HAL_LPTIM_WAKEUPTIMER_EXTI_ENABLE_RISING_EDGE();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
__HAL_LPTIM_ENABLE_IT(&LptimHandle, LPTIM_IT_CMPM);
|
2019-05-06 09:56:15 +00:00
|
|
|
__HAL_LPTIM_ENABLE_IT(&LptimHandle, LPTIM_IT_CMPOK);
|
2018-01-09 13:10:14 +00:00
|
|
|
HAL_LPTIM_Counter_Start(&LptimHandle, 0xFFFF);
|
2018-11-15 13:43:26 +00:00
|
|
|
|
|
|
|
/* Need to write a compare value in order to get LPTIM_FLAG_CMPOK in set_interrupt */
|
|
|
|
__HAL_LPTIM_CLEAR_FLAG(&LptimHandle, LPTIM_FLAG_CMPOK);
|
|
|
|
__HAL_LPTIM_COMPARE_SET(&LptimHandle, 0);
|
|
|
|
while (__HAL_LPTIM_GET_FLAG(&LptimHandle, LPTIM_FLAG_CMPOK) == RESET) {
|
|
|
|
}
|
2019-05-06 09:56:15 +00:00
|
|
|
__HAL_LPTIM_CLEAR_FLAG(&LptimHandle, LPTIM_FLAG_CMPOK);
|
|
|
|
|
|
|
|
/* Init is called with Interrupts disabled, so the CMPOK interrupt
|
2019-06-04 09:26:34 +00:00
|
|
|
* will not be handled. Let's mark it is now safe to write to LP counter */
|
2019-05-06 09:56:15 +00:00
|
|
|
lp_cmpok = true;
|
2018-01-09 13:10:14 +00:00
|
|
|
}
|
|
|
|
|
2019-10-14 16:02:57 +00:00
|
|
|
static void LPTIM_IRQHandler(void)
|
2018-01-09 13:10:14 +00:00
|
|
|
{
|
2019-05-06 09:56:15 +00:00
|
|
|
core_util_critical_section_enter();
|
|
|
|
|
2018-01-09 13:10:14 +00:00
|
|
|
if (lp_Fired) {
|
|
|
|
lp_Fired = 0;
|
2019-05-06 09:56:15 +00:00
|
|
|
/* We're already in handler and interrupt might be pending,
|
|
|
|
* so clear the flag, to avoid calling irq_handler twice */
|
|
|
|
__HAL_LPTIM_CLEAR_FLAG(&LptimHandle, LPTIM_FLAG_CMPM);
|
2019-06-04 14:22:19 +00:00
|
|
|
lp_ticker_irq_handler();
|
2018-01-09 13:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Compare match interrupt */
|
|
|
|
if (__HAL_LPTIM_GET_FLAG(&LptimHandle, LPTIM_FLAG_CMPM) != RESET) {
|
|
|
|
if (__HAL_LPTIM_GET_IT_SOURCE(&LptimHandle, LPTIM_IT_CMPM) != RESET) {
|
|
|
|
/* Clear Compare match flag */
|
|
|
|
__HAL_LPTIM_CLEAR_FLAG(&LptimHandle, LPTIM_FLAG_CMPM);
|
2019-06-04 14:22:19 +00:00
|
|
|
lp_ticker_irq_handler();
|
2018-01-09 13:10:14 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-06 09:56:15 +00:00
|
|
|
if (__HAL_LPTIM_GET_FLAG(&LptimHandle, LPTIM_FLAG_CMPOK) != RESET) {
|
|
|
|
if (__HAL_LPTIM_GET_IT_SOURCE(&LptimHandle, LPTIM_IT_CMPOK) != RESET) {
|
|
|
|
__HAL_LPTIM_CLEAR_FLAG(&LptimHandle, LPTIM_FLAG_CMPOK);
|
|
|
|
lp_cmpok = true;
|
2019-09-02 08:05:22 +00:00
|
|
|
if (sleep_manager_locked) {
|
2019-05-06 09:56:15 +00:00
|
|
|
sleep_manager_unlock_deep_sleep();
|
|
|
|
sleep_manager_locked = false;
|
|
|
|
}
|
2019-09-02 08:05:22 +00:00
|
|
|
if (lp_delayed_prog) {
|
|
|
|
if (roll_over_flag) {
|
|
|
|
/* If we were close to the roll over of the ticker counter
|
|
|
|
* change current tick so it can be compared with buffer.
|
2019-08-27 11:25:46 +00:00
|
|
|
* If this event got outdated fire interrupt right now,
|
|
|
|
* else schedule it normally. */
|
2019-09-02 08:05:22 +00:00
|
|
|
if (lp_delayed_counter <= ((lp_ticker_read() + LP_TIMER_SAFE_GUARD + 1) & 0xFFFF)) {
|
2019-08-27 11:25:46 +00:00
|
|
|
lp_ticker_fire_interrupt();
|
|
|
|
} else {
|
|
|
|
lp_ticker_set_interrupt((lp_delayed_counter - LP_TIMER_SAFE_GUARD - 1) & 0xFFFF);
|
|
|
|
}
|
|
|
|
roll_over_flag = false;
|
|
|
|
} else {
|
2019-09-02 08:05:22 +00:00
|
|
|
if (future_event_flag && (lp_delayed_counter <= lp_ticker_read())) {
|
2019-08-27 11:25:46 +00:00
|
|
|
/* If this event got outdated fire interrupt right now,
|
|
|
|
* else schedule it normally. */
|
|
|
|
lp_ticker_fire_interrupt();
|
|
|
|
future_event_flag = false;
|
|
|
|
} else {
|
|
|
|
lp_ticker_set_interrupt(lp_delayed_counter);
|
|
|
|
}
|
|
|
|
}
|
2019-09-02 08:05:22 +00:00
|
|
|
|
2019-05-06 09:56:15 +00:00
|
|
|
lp_delayed_prog = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-11-23 17:04:23 +00:00
|
|
|
#if defined (__HAL_LPTIM_WAKEUPTIMER_EXTI_CLEAR_FLAG)
|
|
|
|
/* EXTI lines are not configured by default */
|
2018-01-09 13:10:14 +00:00
|
|
|
__HAL_LPTIM_WAKEUPTIMER_EXTI_CLEAR_FLAG();
|
|
|
|
#endif
|
2019-05-06 09:56:15 +00:00
|
|
|
core_util_critical_section_exit();
|
2018-01-09 13:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t lp_ticker_read(void)
|
|
|
|
{
|
2019-10-14 16:02:57 +00:00
|
|
|
uint32_t lp_time = LPTIM_MST->CNT;
|
2018-04-17 09:27:43 +00:00
|
|
|
/* Reading the LPTIM_CNT register may return unreliable values.
|
|
|
|
It is necessary to perform two consecutive read accesses and verify that the two returned values are identical */
|
2019-10-14 16:02:57 +00:00
|
|
|
while (lp_time != LPTIM_MST->CNT) {
|
|
|
|
lp_time = LPTIM_MST->CNT;
|
2018-04-17 09:27:43 +00:00
|
|
|
}
|
|
|
|
return lp_time;
|
2018-01-09 13:10:14 +00:00
|
|
|
}
|
|
|
|
|
2019-05-06 09:56:15 +00:00
|
|
|
/* This function should always be called from critical section */
|
2018-01-09 13:10:14 +00:00
|
|
|
void lp_ticker_set_interrupt(timestamp_t timestamp)
|
|
|
|
{
|
2019-05-06 09:56:15 +00:00
|
|
|
core_util_critical_section_enter();
|
2019-09-02 08:05:22 +00:00
|
|
|
|
2019-08-27 11:25:46 +00:00
|
|
|
timestamp_t last_read_counter = lp_ticker_read();
|
2018-01-09 13:10:14 +00:00
|
|
|
|
2019-05-06 09:56:15 +00:00
|
|
|
/* Always store the last requested timestamp */
|
|
|
|
lp_delayed_counter = timestamp;
|
2019-10-14 16:02:57 +00:00
|
|
|
NVIC_EnableIRQ(LPTIM_MST_IRQ);
|
2019-03-29 13:51:14 +00:00
|
|
|
|
2019-05-06 09:56:15 +00:00
|
|
|
/* CMPOK is set by hardware to inform application that the APB bus write operation to the
|
|
|
|
* LPTIM_CMP register has been successfully completed.
|
|
|
|
* Any successive write before the CMPOK flag be set, will lead to unpredictable results
|
|
|
|
* We need to prevent to set a new comparator value before CMPOK flag is set by HW */
|
|
|
|
if (lp_cmpok == false) {
|
|
|
|
/* if this is not safe to write, then delay the programing to the
|
|
|
|
* time when CMPOK interrupt will trigger */
|
2019-09-02 08:05:22 +00:00
|
|
|
|
2019-08-27 11:25:46 +00:00
|
|
|
/* If this target timestamp is close to the roll over of the ticker counter
|
|
|
|
* and current tick is also close to the roll over, then we are in danger zone.*/
|
2019-09-02 08:05:22 +00:00
|
|
|
if (((0xFFFF - LP_TIMER_SAFE_GUARD < timestamp) || (timestamp < LP_TIMER_SAFE_GUARD)) && (0xFFFA < last_read_counter)) {
|
2019-08-27 11:25:46 +00:00
|
|
|
roll_over_flag = true;
|
|
|
|
/* Change the lp_delayed_counter buffer in that way so the value of (0xFFFF - LP_TIMER_SAFE_GUARD) is equal to 0.
|
|
|
|
* By doing this it is easy to check if the value of timestamp get outdated by delaying its programming
|
|
|
|
* For example if LP_TIMER_SAFE_GUARD is set to 5
|
|
|
|
* (0xFFFA + LP_TIMER_SAFE_GUARD + 1) & 0xFFFF = 0
|
|
|
|
* (0xFFFF + LP_TIMER_SAFE_GUARD + 1) & 0xFFFF = 5
|
|
|
|
* (0x0000 + LP_TIMER_SAFE_GUARD + 1) & 0xFFFF = 6
|
|
|
|
* (0x0005 + LP_TIMER_SAFE_GUARD + 1) & 0xFFFF = 11*/
|
|
|
|
lp_delayed_counter = (timestamp + LP_TIMER_SAFE_GUARD + 1) & 0xFFFF;
|
|
|
|
} else {
|
|
|
|
roll_over_flag = false;
|
|
|
|
/* Check if event was meant to be in the past. */
|
2019-09-02 08:05:22 +00:00
|
|
|
if (lp_delayed_counter >= last_read_counter) {
|
2019-08-27 11:25:46 +00:00
|
|
|
future_event_flag = true;
|
|
|
|
} else {
|
|
|
|
future_event_flag = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-05-06 09:56:15 +00:00
|
|
|
lp_delayed_prog = true;
|
2019-03-29 13:51:14 +00:00
|
|
|
} else {
|
2019-09-02 08:05:22 +00:00
|
|
|
|
2019-05-06 09:56:15 +00:00
|
|
|
lp_ticker_clear_interrupt();
|
|
|
|
|
|
|
|
/* HW is not able to trig a very short term interrupt, that is
|
|
|
|
* not less than few ticks away (LP_TIMER_SAFE_GUARD). So let's make sure it'
|
|
|
|
* s at least current tick + LP_TIMER_SAFE_GUARD */
|
2019-09-02 08:05:22 +00:00
|
|
|
for (uint8_t i = 0; i < LP_TIMER_SAFE_GUARD; i++) {
|
2019-07-18 08:20:59 +00:00
|
|
|
if (LP_TIMER_WRAP((last_read_counter + i)) == timestamp) {
|
|
|
|
timestamp = LP_TIMER_WRAP((timestamp + LP_TIMER_SAFE_GUARD));
|
2019-05-06 09:56:15 +00:00
|
|
|
}
|
|
|
|
}
|
2019-09-02 08:05:22 +00:00
|
|
|
|
2019-06-04 09:26:34 +00:00
|
|
|
/* Then check if this target timestamp is not in the past, or close to wrap-around
|
|
|
|
* Let's assume last_read_counter = 0xFFFC, and we want to program timestamp = 0x100
|
|
|
|
* The interrupt will not fire before the CMPOK flag is OK, so there are 2 cases:
|
|
|
|
* in case CMPOK flag is set by HW after or at wrap-around, then this will fire only @0x100
|
|
|
|
* in case CMPOK flag is set before, it will indeed fire early, as for the wrap-around case.
|
|
|
|
* But that will take at least 3 cycles and the interrupt fires at the end of a cycle.
|
|
|
|
* In our case 0xFFFC + 3 => at the transition between 0xFFFF and 0.
|
|
|
|
* If last_read_counter was 0xFFFB, it should be at the transition between 0xFFFE and 0xFFFF.
|
|
|
|
* There might be crossing cases where it would also fire @ 0xFFFE, but by the time we read the counter,
|
|
|
|
* it may already have moved to the next one, so for now we've taken this as margin of error.
|
|
|
|
*/
|
2019-09-02 08:05:22 +00:00
|
|
|
if ((timestamp < last_read_counter) && (last_read_counter <= (0xFFFF - LP_TIMER_SAFE_GUARD))) {
|
2019-05-06 09:56:15 +00:00
|
|
|
/* Workaround, because limitation */
|
|
|
|
__HAL_LPTIM_COMPARE_SET(&LptimHandle, ~0);
|
|
|
|
} else {
|
|
|
|
/* It is safe to write */
|
|
|
|
__HAL_LPTIM_COMPARE_SET(&LptimHandle, timestamp);
|
|
|
|
}
|
2018-01-09 13:10:14 +00:00
|
|
|
|
2019-05-06 09:56:15 +00:00
|
|
|
/* We just programed the CMP so we'll need to wait for cmpok before
|
|
|
|
* next programing */
|
|
|
|
lp_cmpok = false;
|
|
|
|
/* Prevent from sleeping after compare register was set as we need CMPOK
|
|
|
|
* interrupt to fire (in ~3x30us cycles) before we can safely enter deep sleep mode */
|
2019-09-02 08:05:22 +00:00
|
|
|
if (!sleep_manager_locked) {
|
2019-05-06 09:56:15 +00:00
|
|
|
sleep_manager_lock_deep_sleep();
|
|
|
|
sleep_manager_locked = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
core_util_critical_section_exit();
|
2018-01-09 13:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void lp_ticker_fire_interrupt(void)
|
|
|
|
{
|
2019-05-06 09:56:15 +00:00
|
|
|
core_util_critical_section_enter();
|
2018-01-09 13:10:14 +00:00
|
|
|
lp_Fired = 1;
|
2019-05-06 09:56:15 +00:00
|
|
|
/* In case we fire interrupt now, then cancel pending programing */
|
|
|
|
lp_delayed_prog = false;
|
2019-10-14 16:02:57 +00:00
|
|
|
NVIC_SetPendingIRQ(LPTIM_MST_IRQ);
|
|
|
|
NVIC_EnableIRQ(LPTIM_MST_IRQ);
|
2019-05-06 09:56:15 +00:00
|
|
|
core_util_critical_section_exit();
|
2018-01-09 13:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void lp_ticker_disable_interrupt(void)
|
|
|
|
{
|
2019-05-06 09:56:15 +00:00
|
|
|
core_util_critical_section_enter();
|
|
|
|
|
2019-09-02 08:05:22 +00:00
|
|
|
if (!lp_cmpok) {
|
2019-05-06 09:56:15 +00:00
|
|
|
while (__HAL_LPTIM_GET_FLAG(&LptimHandle, LPTIM_FLAG_CMPOK) == RESET) {
|
|
|
|
}
|
|
|
|
__HAL_LPTIM_CLEAR_FLAG(&LptimHandle, LPTIM_FLAG_CMPOK);
|
|
|
|
lp_cmpok = true;
|
|
|
|
}
|
|
|
|
/* now that CMPOK is set, allow deep sleep again */
|
2019-09-02 08:05:22 +00:00
|
|
|
if (sleep_manager_locked) {
|
2019-05-06 09:56:15 +00:00
|
|
|
sleep_manager_unlock_deep_sleep();
|
|
|
|
sleep_manager_locked = false;
|
|
|
|
}
|
|
|
|
lp_delayed_prog = false;
|
2019-06-04 14:22:19 +00:00
|
|
|
lp_Fired = 0;
|
2019-10-14 16:02:57 +00:00
|
|
|
NVIC_DisableIRQ(LPTIM_MST_IRQ);
|
|
|
|
NVIC_ClearPendingIRQ(LPTIM_MST_IRQ);
|
2019-05-06 09:56:15 +00:00
|
|
|
|
|
|
|
core_util_critical_section_exit();
|
2018-01-09 13:10:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void lp_ticker_clear_interrupt(void)
|
|
|
|
{
|
2019-05-06 09:56:15 +00:00
|
|
|
core_util_critical_section_enter();
|
2018-01-09 13:10:14 +00:00
|
|
|
__HAL_LPTIM_CLEAR_FLAG(&LptimHandle, LPTIM_FLAG_CMPM);
|
2019-10-14 16:02:57 +00:00
|
|
|
NVIC_ClearPendingIRQ(LPTIM_MST_IRQ);
|
2019-05-06 09:56:15 +00:00
|
|
|
core_util_critical_section_exit();
|
2018-01-09 13:10:14 +00:00
|
|
|
}
|
|
|
|
|
2018-07-25 06:40:30 +00:00
|
|
|
void lp_ticker_free(void)
|
|
|
|
{
|
2018-07-20 06:58:11 +00:00
|
|
|
lp_ticker_disable_interrupt();
|
2018-07-25 06:40:30 +00:00
|
|
|
}
|
2018-03-28 13:00:53 +00:00
|
|
|
|
|
|
|
/*****************************************************************/
|
|
|
|
/* lpticker_lptim config is 0 or not defined in json config file */
|
|
|
|
/* LPTICKER is based on RTC wake up feature from ST drivers */
|
2018-03-13 17:11:18 +00:00
|
|
|
#else /* MBED_CONF_TARGET_LPTICKER_LPTIM */
|
2018-01-09 13:10:14 +00:00
|
|
|
|
2018-03-28 13:00:53 +00:00
|
|
|
#include "rtc_api_hal.h"
|
2018-04-24 12:06:57 +00:00
|
|
|
|
2018-06-27 12:21:07 +00:00
|
|
|
const ticker_info_t *lp_ticker_get_info()
|
2018-04-24 12:06:57 +00:00
|
|
|
{
|
|
|
|
static const ticker_info_t info = {
|
2018-06-27 12:21:07 +00:00
|
|
|
RTC_CLOCK / 4, // RTC_WAKEUPCLOCK_RTCCLK_DIV4
|
2018-04-24 12:06:57 +00:00
|
|
|
32
|
|
|
|
};
|
|
|
|
return &info;
|
|
|
|
}
|
|
|
|
|
2016-09-06 14:01:19 +00:00
|
|
|
void lp_ticker_init(void)
|
|
|
|
{
|
|
|
|
rtc_init();
|
2018-04-24 12:06:57 +00:00
|
|
|
lp_ticker_disable_interrupt();
|
2016-09-06 14:01:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t lp_ticker_read(void)
|
|
|
|
{
|
2018-04-24 12:06:57 +00:00
|
|
|
return rtc_read_lp();
|
2016-09-06 14:01:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void lp_ticker_set_interrupt(timestamp_t timestamp)
|
|
|
|
{
|
2018-04-24 12:06:57 +00:00
|
|
|
rtc_set_wake_up_timer(timestamp);
|
2016-09-06 14:01:19 +00:00
|
|
|
}
|
|
|
|
|
Ticker: add fire interrupt now function
fire_interrupt function should be used for events in the past. As we have now
64bit timestamp, we can figure out what is in the past, and ask a target to invoke
an interrupt immediately. The previous attemps in the target HAL tickers were not ideal, as it can wrap around easily (16 or 32 bit counters). This new
functionality should solve this problem.
set_interrupt for tickers in HAL code should not handle anything but the next match interrupt. If it was in the past is handled by the upper layer.
It is possible that we are setting next event to the close future, so once it is set it is already in the past. Therefore we add a check after set interrupt to verify it is in future.
If it is not, we fire interrupt immediately. This results in
two events - first one immediate, correct one. The second one might be scheduled in far future (almost entire ticker range),
that should be discarded.
The specification for the fire_interrupts are:
- should set pending bit for the ticker interrupt (as soon as possible),
the event we are scheduling is already in the past, and we do not want to skip
any events
- no arguments are provided, neither return value, not needed
- ticker should be initialized prior calling this function (no need to check if it is already initialized)
All our targets provide this new functionality, removing old misleading if (timestamp is in the past) checks.
2017-06-27 11:18:59 +00:00
|
|
|
void lp_ticker_fire_interrupt(void)
|
|
|
|
{
|
2018-04-24 12:06:57 +00:00
|
|
|
rtc_fire_interrupt();
|
Ticker: add fire interrupt now function
fire_interrupt function should be used for events in the past. As we have now
64bit timestamp, we can figure out what is in the past, and ask a target to invoke
an interrupt immediately. The previous attemps in the target HAL tickers were not ideal, as it can wrap around easily (16 or 32 bit counters). This new
functionality should solve this problem.
set_interrupt for tickers in HAL code should not handle anything but the next match interrupt. If it was in the past is handled by the upper layer.
It is possible that we are setting next event to the close future, so once it is set it is already in the past. Therefore we add a check after set interrupt to verify it is in future.
If it is not, we fire interrupt immediately. This results in
two events - first one immediate, correct one. The second one might be scheduled in far future (almost entire ticker range),
that should be discarded.
The specification for the fire_interrupts are:
- should set pending bit for the ticker interrupt (as soon as possible),
the event we are scheduling is already in the past, and we do not want to skip
any events
- no arguments are provided, neither return value, not needed
- ticker should be initialized prior calling this function (no need to check if it is already initialized)
All our targets provide this new functionality, removing old misleading if (timestamp is in the past) checks.
2017-06-27 11:18:59 +00:00
|
|
|
}
|
|
|
|
|
2016-09-06 14:01:19 +00:00
|
|
|
void lp_ticker_disable_interrupt(void)
|
|
|
|
{
|
|
|
|
rtc_deactivate_wake_up_timer();
|
|
|
|
}
|
|
|
|
|
|
|
|
void lp_ticker_clear_interrupt(void)
|
|
|
|
{
|
2018-07-20 06:58:11 +00:00
|
|
|
lp_ticker_disable_interrupt();
|
2016-09-06 14:01:19 +00:00
|
|
|
}
|
|
|
|
|
2018-07-25 06:40:30 +00:00
|
|
|
void lp_ticker_free(void)
|
|
|
|
{
|
2018-07-20 06:58:11 +00:00
|
|
|
lp_ticker_disable_interrupt();
|
2018-07-25 06:40:30 +00:00
|
|
|
}
|
|
|
|
|
2018-03-13 17:11:18 +00:00
|
|
|
#endif /* MBED_CONF_TARGET_LPTICKER_LPTIM */
|
2018-01-09 13:10:14 +00:00
|
|
|
|
2018-03-13 17:11:18 +00:00
|
|
|
#endif /* DEVICE_LPTICKER */
|