| /* USER CODE BEGIN Header */ |
| /** |
| ****************************************************************************** |
| * @file hw_timerserver.c |
| * @author MCD Application Team |
| * @brief Hardware timerserver source file for STM32WPAN Middleware. |
| ****************************************************************************** |
| * @attention |
| * |
| * Copyright (c) 2021 STMicroelectronics. |
| * All rights reserved. |
| * |
| * This software is licensed under terms that can be found in the LICENSE file |
| * in the root directory of this software component. |
| * If no LICENSE file comes with this software, it is provided AS-IS. |
| * |
| ****************************************************************************** |
| */ |
| /* USER CODE END Header */ |
| |
| /* Includes ------------------------------------------------------------------*/ |
| #include "app_common.h" |
| #include "hw_conf.h" |
| |
| /* Private typedef -----------------------------------------------------------*/ |
| typedef enum |
| { |
| TimerID_Free, |
| TimerID_Created, |
| TimerID_Running |
| } TimerIDStatus_t; |
| |
| typedef enum |
| { |
| SSR_Read_Requested, |
| SSR_Read_Not_Requested |
| } RequestReadSSR_t; |
| |
| typedef enum |
| { |
| WakeupTimerValue_Overpassed, |
| WakeupTimerValue_LargeEnough |
| } WakeupTimerLimitation_Status_t; |
| |
| typedef struct |
| { |
| HW_TS_pTimerCb_t pTimerCallBack; |
| uint32_t CounterInit; |
| uint32_t CountLeft; |
| TimerIDStatus_t TimerIDStatus; |
| HW_TS_Mode_t TimerMode; |
| uint32_t TimerProcessID; |
| uint8_t PreviousID; |
| uint8_t NextID; |
| } TimerContext_t; |
| |
| /* Private defines -----------------------------------------------------------*/ |
| #define SSR_FORBIDDEN_VALUE 0xFFFFFFFF |
| #define TIMER_LIST_EMPTY 0xFFFF |
| |
| /* Private macros ------------------------------------------------------------*/ |
| /* Private variables ---------------------------------------------------------*/ |
| |
| /** |
| * START of Section TIMERSERVER_CONTEXT |
| */ |
| |
| PLACE_IN_SECTION("TIMERSERVER_CONTEXT") static volatile TimerContext_t aTimerContext[CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER]; |
| PLACE_IN_SECTION("TIMERSERVER_CONTEXT") static volatile uint8_t CurrentRunningTimerID; |
| PLACE_IN_SECTION("TIMERSERVER_CONTEXT") static volatile uint8_t PreviousRunningTimerID; |
| PLACE_IN_SECTION("TIMERSERVER_CONTEXT") static volatile uint32_t SSRValueOnLastSetup; |
| PLACE_IN_SECTION("TIMERSERVER_CONTEXT") static volatile WakeupTimerLimitation_Status_t WakeupTimerLimitation; |
| |
| /** |
| * END of Section TIMERSERVER_CONTEXT |
| */ |
| |
| static RTC_HandleTypeDef * phrtc; /**< RTC handle */ |
| static uint8_t WakeupTimerDivider; |
| static uint8_t AsynchPrescalerUserConfig; |
| static uint16_t SynchPrescalerUserConfig; |
| static volatile uint16_t MaxWakeupTimerSetup; |
| |
| /* Global variables ----------------------------------------------------------*/ |
| /* Private function prototypes -----------------------------------------------*/ |
| static void RestartWakeupCounter(uint16_t Value); |
| static uint16_t ReturnTimeElapsed(void); |
| static void RescheduleTimerList(void); |
| static void UnlinkTimer(uint8_t TimerID, RequestReadSSR_t RequestReadSSR); |
| static void LinkTimerBefore(uint8_t TimerID, uint8_t RefTimerID); |
| static void LinkTimerAfter(uint8_t TimerID, uint8_t RefTimerID); |
| static uint16_t linkTimer(uint8_t TimerID); |
| static uint32_t ReadRtcSsrValue(void); |
| |
| __weak void HW_TS_RTC_CountUpdated_AppNot(void); |
| |
| /* Functions Definition ------------------------------------------------------*/ |
| |
| /** |
| * @brief Read the RTC_SSR value |
| * As described in the reference manual, the RTC_SSR shall be read twice to ensure |
| * reliability of the value |
| * @param None |
| * @retval SSR value read |
| */ |
| static uint32_t ReadRtcSsrValue(void) |
| { |
| uint32_t first_read; |
| uint32_t second_read; |
| |
| first_read = (uint32_t) (READ_BIT(RTC->SSR, RTC_SSR_SS)); |
| |
| second_read = (uint32_t) (READ_BIT(RTC->SSR, RTC_SSR_SS)); |
| |
| while (first_read != second_read) |
| { |
| first_read = second_read; |
| |
| second_read = (uint32_t) (READ_BIT(RTC->SSR, RTC_SSR_SS)); |
| } |
| |
| return second_read; |
| } |
| |
| /** |
| * @brief Insert a Timer in the list after the Timer ID specified |
| * @param TimerID: The ID of the Timer |
| * @param RefTimerID: The ID of the Timer to be linked after |
| * @retval None |
| */ |
| static void LinkTimerAfter(uint8_t TimerID, uint8_t RefTimerID) |
| { |
| uint8_t next_id; |
| |
| next_id = aTimerContext[RefTimerID].NextID; |
| |
| if (next_id != CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER) |
| { |
| aTimerContext[next_id].PreviousID = TimerID; |
| } |
| aTimerContext[TimerID].NextID = next_id; |
| aTimerContext[TimerID].PreviousID = RefTimerID; |
| aTimerContext[RefTimerID].NextID = TimerID; |
| |
| return; |
| } |
| |
| /** |
| * @brief Insert a Timer in the list before the ID specified |
| * @param TimerID: The ID of the Timer |
| * @param RefTimerID: The ID of the Timer to be linked before |
| * @retval None |
| */ |
| static void LinkTimerBefore(uint8_t TimerID, uint8_t RefTimerID) |
| { |
| uint8_t previous_id; |
| |
| if (RefTimerID != CurrentRunningTimerID) |
| { |
| previous_id = aTimerContext[RefTimerID].PreviousID; |
| |
| aTimerContext[previous_id].NextID = TimerID; |
| aTimerContext[TimerID].NextID = RefTimerID; |
| aTimerContext[TimerID].PreviousID = previous_id; |
| aTimerContext[RefTimerID].PreviousID = TimerID; |
| } |
| else |
| { |
| aTimerContext[TimerID].NextID = RefTimerID; |
| aTimerContext[RefTimerID].PreviousID = TimerID; |
| } |
| |
| return; |
| } |
| |
| /** |
| * @brief Insert a Timer in the list |
| * @param TimerID: The ID of the Timer |
| * @retval None |
| */ |
| static uint16_t linkTimer(uint8_t TimerID) |
| { |
| uint32_t time_left; |
| uint16_t time_elapsed; |
| uint8_t timer_id_lookup; |
| uint8_t next_id; |
| |
| if (CurrentRunningTimerID == CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER) |
| { |
| /** |
| * No timer in the list |
| */ |
| PreviousRunningTimerID = CurrentRunningTimerID; |
| CurrentRunningTimerID = TimerID; |
| aTimerContext[TimerID].NextID = CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER; |
| |
| SSRValueOnLastSetup = SSR_FORBIDDEN_VALUE; |
| time_elapsed = 0; |
| } |
| else |
| { |
| time_elapsed = ReturnTimeElapsed(); |
| |
| /** |
| * update count of the timer to be linked |
| */ |
| aTimerContext[TimerID].CountLeft += time_elapsed; |
| time_left = aTimerContext[TimerID].CountLeft; |
| |
| /** |
| * Search for index where the new timer shall be linked |
| */ |
| if (aTimerContext[CurrentRunningTimerID].CountLeft <= time_left) |
| { |
| /** |
| * Search for the ID after the first one |
| */ |
| timer_id_lookup = CurrentRunningTimerID; |
| next_id = aTimerContext[timer_id_lookup].NextID; |
| while ((next_id != CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER) && (aTimerContext[next_id].CountLeft <= time_left)) |
| { |
| timer_id_lookup = aTimerContext[timer_id_lookup].NextID; |
| next_id = aTimerContext[timer_id_lookup].NextID; |
| } |
| |
| /** |
| * Link after the ID |
| */ |
| LinkTimerAfter(TimerID, timer_id_lookup); |
| } |
| else |
| { |
| /** |
| * Link before the first ID |
| */ |
| LinkTimerBefore(TimerID, CurrentRunningTimerID); |
| PreviousRunningTimerID = CurrentRunningTimerID; |
| CurrentRunningTimerID = TimerID; |
| } |
| } |
| |
| return time_elapsed; |
| } |
| |
| /** |
| * @brief Remove a Timer from the list |
| * @param TimerID: The ID of the Timer |
| * @param RequestReadSSR: Request to read the SSR register or not |
| * @retval None |
| */ |
| static void UnlinkTimer(uint8_t TimerID, RequestReadSSR_t RequestReadSSR) |
| { |
| uint8_t previous_id; |
| uint8_t next_id; |
| |
| if (TimerID == CurrentRunningTimerID) |
| { |
| PreviousRunningTimerID = CurrentRunningTimerID; |
| CurrentRunningTimerID = aTimerContext[TimerID].NextID; |
| } |
| else |
| { |
| previous_id = aTimerContext[TimerID].PreviousID; |
| next_id = aTimerContext[TimerID].NextID; |
| |
| aTimerContext[previous_id].NextID = aTimerContext[TimerID].NextID; |
| if (next_id != CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER) |
| { |
| aTimerContext[next_id].PreviousID = aTimerContext[TimerID].PreviousID; |
| } |
| } |
| |
| /** |
| * Timer is out of the list |
| */ |
| aTimerContext[TimerID].TimerIDStatus = TimerID_Created; |
| |
| if ((CurrentRunningTimerID == CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER) && (RequestReadSSR == SSR_Read_Requested)) |
| { |
| SSRValueOnLastSetup = SSR_FORBIDDEN_VALUE; |
| } |
| |
| return; |
| } |
| |
| /** |
| * @brief Return the number of ticks counted by the wakeuptimer since it has been started |
| * @note The API is reading the SSR register to get how many ticks have been counted |
| * since the time the timer has been started |
| * @param None |
| * @retval Time expired in Ticks |
| */ |
| static uint16_t ReturnTimeElapsed(void) |
| { |
| uint32_t return_value; |
| uint32_t wrap_counter; |
| |
| if (SSRValueOnLastSetup != SSR_FORBIDDEN_VALUE) |
| { |
| return_value = ReadRtcSsrValue(); /**< Read SSR register first */ |
| |
| if (SSRValueOnLastSetup >= return_value) |
| { |
| return_value = SSRValueOnLastSetup - return_value; |
| } |
| else |
| { |
| wrap_counter = SynchPrescalerUserConfig - return_value; |
| return_value = SSRValueOnLastSetup + wrap_counter; |
| } |
| |
| /** |
| * At this stage, ReturnValue holds the number of ticks counted by SSR |
| * Need to translate in number of ticks counted by the Wakeuptimer |
| */ |
| return_value = return_value * AsynchPrescalerUserConfig; |
| return_value = return_value >> WakeupTimerDivider; |
| } |
| else |
| { |
| return_value = 0; |
| } |
| |
| return (uint16_t) return_value; |
| } |
| |
| /** |
| * @brief Set the wakeup counter |
| * @note The API is writing the counter value so that the value is decreased by one to cope with the fact |
| * the interrupt is generated with 1 extra clock cycle (See RefManuel) |
| * It assumes all condition are met to be allowed to write the wakeup counter |
| * @param Value: Value to be written in the counter |
| * @retval None |
| */ |
| static void RestartWakeupCounter(uint16_t Value) |
| { |
| /** |
| * The wakeuptimer has been disabled in the calling function to reduce the time to poll the WUTWF |
| * FLAG when the new value will have to be written |
| * __HAL_RTC_WAKEUPTIMER_DISABLE(phrtc); |
| */ |
| |
| if (Value == 0) |
| { |
| SSRValueOnLastSetup = ReadRtcSsrValue(); |
| |
| /** |
| * Simulate that the Timer expired |
| */ |
| HAL_NVIC_SetPendingIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID); |
| } |
| else |
| { |
| if ((Value > 1) || (WakeupTimerDivider != 1)) |
| { |
| Value -= 1; |
| } |
| |
| while (__HAL_RTC_WAKEUPTIMER_GET_FLAG(phrtc, RTC_FLAG_WUTWF) == RESET) |
| ; |
| |
| /** |
| * make sure to clear the flags after checking the WUTWF. |
| * It takes 2 RTCCLK between the time the WUTE bit is disabled and the |
| * time the timer is disabled. The WUTWF bit somehow guarantee the system is stable |
| * Otherwise, when the timer is periodic with 1 Tick, it may generate an extra interrupt in between |
| * due to the autoreload feature |
| */ |
| __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(phrtc, RTC_FLAG_WUTF); /**< Clear flag in RTC module */ |
| __HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG(); /**< Clear flag in EXTI module */ |
| HAL_NVIC_ClearPendingIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID); /**< Clear pending bit in NVIC */ |
| |
| MODIFY_REG(RTC->WUTR, RTC_WUTR_WUT, Value); |
| |
| /** |
| * Update the value here after the WUTWF polling that may take some time |
| */ |
| SSRValueOnLastSetup = ReadRtcSsrValue(); |
| |
| __HAL_RTC_WAKEUPTIMER_ENABLE(phrtc); /**< Enable the Wakeup Timer */ |
| |
| HW_TS_RTC_CountUpdated_AppNot(); |
| } |
| |
| return; |
| } |
| |
| /** |
| * @brief Reschedule the list of timer |
| * @note 1) Update the count left for each timer in the list |
| * 2) Setup the wakeuptimer |
| * @param None |
| * @retval None |
| */ |
| static void RescheduleTimerList(void) |
| { |
| uint8_t localTimerID; |
| uint32_t timecountleft; |
| uint16_t wakeup_timer_value; |
| uint16_t time_elapsed; |
| |
| /** |
| * The wakeuptimer is disabled now to reduce the time to poll the WUTWF |
| * FLAG when the new value will have to be written |
| */ |
| if ((READ_BIT(RTC->CR, RTC_CR_WUTE) == (RTC_CR_WUTE)) == SET) |
| { |
| /** |
| * Wait for the flag to be back to 0 when the wakeup timer is enabled |
| */ |
| while (__HAL_RTC_WAKEUPTIMER_GET_FLAG(phrtc, RTC_FLAG_WUTWF) == SET) |
| ; |
| } |
| __HAL_RTC_WAKEUPTIMER_DISABLE(phrtc); /**< Disable the Wakeup Timer */ |
| |
| localTimerID = CurrentRunningTimerID; |
| |
| /** |
| * Calculate what will be the value to write in the wakeuptimer |
| */ |
| timecountleft = aTimerContext[localTimerID].CountLeft; |
| |
| /** |
| * Read how much has been counted |
| */ |
| time_elapsed = ReturnTimeElapsed(); |
| |
| if (timecountleft < time_elapsed) |
| { |
| /** |
| * There is no tick left to count |
| */ |
| wakeup_timer_value = 0; |
| WakeupTimerLimitation = WakeupTimerValue_LargeEnough; |
| } |
| else |
| { |
| if (timecountleft > (time_elapsed + MaxWakeupTimerSetup)) |
| { |
| /** |
| * The number of tick left is greater than the Wakeuptimer maximum value |
| */ |
| wakeup_timer_value = MaxWakeupTimerSetup; |
| |
| WakeupTimerLimitation = WakeupTimerValue_Overpassed; |
| } |
| else |
| { |
| wakeup_timer_value = timecountleft - time_elapsed; |
| WakeupTimerLimitation = WakeupTimerValue_LargeEnough; |
| } |
| } |
| |
| /** |
| * update ticks left to be counted for each timer |
| */ |
| while (localTimerID != CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER) |
| { |
| if (aTimerContext[localTimerID].CountLeft < time_elapsed) |
| { |
| aTimerContext[localTimerID].CountLeft = 0; |
| } |
| else |
| { |
| aTimerContext[localTimerID].CountLeft -= time_elapsed; |
| } |
| localTimerID = aTimerContext[localTimerID].NextID; |
| } |
| |
| /** |
| * Write next count |
| */ |
| RestartWakeupCounter(wakeup_timer_value); |
| |
| return; |
| } |
| |
| /* Public functions ----------------------------------------------------------*/ |
| |
| /** |
| * For all public interface except that may need write access to the RTC, the RTC |
| * shall be unlock at the beginning and locked at the output |
| * In order to ease maintainability, the unlock is done at the top and the lock at then end |
| * in case some new implementation is coming in the future |
| */ |
| |
| void HW_TS_RTC_Wakeup_Handler(void) |
| { |
| HW_TS_pTimerCb_t ptimer_callback; |
| uint32_t timer_process_id; |
| uint8_t local_current_running_timer_id; |
| #if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1) |
| uint32_t primask_bit; |
| #endif |
| |
| #if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1) |
| primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */ |
| __disable_irq(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/ |
| #endif |
| |
| /* Disable the write protection for RTC registers */ |
| __HAL_RTC_WRITEPROTECTION_DISABLE(phrtc); |
| |
| /** |
| * Disable the Wakeup Timer |
| * This may speed up a bit the processing to wait the timer to be disabled |
| * The timer is still counting 2 RTCCLK |
| */ |
| __HAL_RTC_WAKEUPTIMER_DISABLE(phrtc); |
| |
| local_current_running_timer_id = CurrentRunningTimerID; |
| |
| if (aTimerContext[local_current_running_timer_id].TimerIDStatus == TimerID_Running) |
| { |
| ptimer_callback = aTimerContext[local_current_running_timer_id].pTimerCallBack; |
| timer_process_id = aTimerContext[local_current_running_timer_id].TimerProcessID; |
| |
| /** |
| * It should be good to check whether the TimeElapsed is greater or not than the tick left to be counted |
| * However, due to the inaccuracy of the reading of the time elapsed, it may return there is 1 tick |
| * to be left whereas the count is over |
| * A more secure implementation has been done with a flag to state whereas the full count has been written |
| * in the wakeuptimer or not |
| */ |
| if (WakeupTimerLimitation != WakeupTimerValue_Overpassed) |
| { |
| if (aTimerContext[local_current_running_timer_id].TimerMode == hw_ts_Repeated) |
| { |
| UnlinkTimer(local_current_running_timer_id, SSR_Read_Not_Requested); |
| #if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1) |
| __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ |
| #endif |
| HW_TS_Start(local_current_running_timer_id, aTimerContext[local_current_running_timer_id].CounterInit); |
| |
| /* Disable the write protection for RTC registers */ |
| __HAL_RTC_WRITEPROTECTION_DISABLE(phrtc); |
| } |
| else |
| { |
| #if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1) |
| __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ |
| #endif |
| HW_TS_Stop(local_current_running_timer_id); |
| |
| /* Disable the write protection for RTC registers */ |
| __HAL_RTC_WRITEPROTECTION_DISABLE(phrtc); |
| } |
| |
| HW_TS_RTC_Int_AppNot(timer_process_id, local_current_running_timer_id, ptimer_callback); |
| } |
| else |
| { |
| RescheduleTimerList(); |
| #if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1) |
| __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ |
| #endif |
| } |
| } |
| else |
| { |
| /** |
| * We should never end up in this case |
| * However, if due to any bug in the timer server this is the case, the mistake may not impact the user. |
| * We could just clean the interrupt flag and get out from this unexpected interrupt |
| */ |
| while (__HAL_RTC_WAKEUPTIMER_GET_FLAG(phrtc, RTC_FLAG_WUTWF) == RESET) |
| ; |
| |
| /** |
| * make sure to clear the flags after checking the WUTWF. |
| * It takes 2 RTCCLK between the time the WUTE bit is disabled and the |
| * time the timer is disabled. The WUTWF bit somehow guarantee the system is stable |
| * Otherwise, when the timer is periodic with 1 Tick, it may generate an extra interrupt in between |
| * due to the autoreload feature |
| */ |
| __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(phrtc, RTC_FLAG_WUTF); /**< Clear flag in RTC module */ |
| __HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG(); /**< Clear flag in EXTI module */ |
| |
| #if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1) |
| __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ |
| #endif |
| } |
| |
| /* Enable the write protection for RTC registers */ |
| __HAL_RTC_WRITEPROTECTION_ENABLE(phrtc); |
| |
| return; |
| } |
| |
| void HW_TS_Init(HW_TS_InitMode_t TimerInitMode, RTC_HandleTypeDef * hrtc) |
| { |
| uint8_t loop; |
| uint32_t localmaxwakeuptimersetup; |
| |
| /** |
| * Get RTC handler |
| */ |
| phrtc = hrtc; |
| |
| /* Disable the write protection for RTC registers */ |
| __HAL_RTC_WRITEPROTECTION_DISABLE(phrtc); |
| |
| SET_BIT(RTC->CR, RTC_CR_BYPSHAD); |
| |
| /** |
| * Readout the user config |
| */ |
| WakeupTimerDivider = (4 - ((uint32_t) (READ_BIT(RTC->CR, RTC_CR_WUCKSEL)))); |
| |
| AsynchPrescalerUserConfig = |
| (uint8_t) (READ_BIT(RTC->PRER, RTC_PRER_PREDIV_A) >> (uint32_t) POSITION_VAL(RTC_PRER_PREDIV_A)) + 1; |
| |
| SynchPrescalerUserConfig = (uint16_t) (READ_BIT(RTC->PRER, RTC_PRER_PREDIV_S)) + 1; |
| |
| /** |
| * Margin is taken to avoid wrong calculation when the wrap around is there and some |
| * application interrupts may have delayed the reading |
| */ |
| localmaxwakeuptimersetup = |
| ((((SynchPrescalerUserConfig - 1) * AsynchPrescalerUserConfig) - CFG_HW_TS_RTC_HANDLER_MAX_DELAY) >> WakeupTimerDivider); |
| |
| if (localmaxwakeuptimersetup >= 0xFFFF) |
| { |
| MaxWakeupTimerSetup = 0xFFFF; |
| } |
| else |
| { |
| MaxWakeupTimerSetup = (uint16_t) localmaxwakeuptimersetup; |
| } |
| |
| /** |
| * Configure EXTI module |
| */ |
| LL_EXTI_EnableRisingTrig_0_31(RTC_EXTI_LINE_WAKEUPTIMER_EVENT); |
| LL_EXTI_EnableIT_0_31(RTC_EXTI_LINE_WAKEUPTIMER_EVENT); |
| |
| if (TimerInitMode == hw_ts_InitMode_Full) |
| { |
| WakeupTimerLimitation = WakeupTimerValue_LargeEnough; |
| SSRValueOnLastSetup = SSR_FORBIDDEN_VALUE; |
| |
| /** |
| * Initialize the timer server |
| */ |
| for (loop = 0; loop < CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER; loop++) |
| { |
| aTimerContext[loop].TimerIDStatus = TimerID_Free; |
| } |
| |
| CurrentRunningTimerID = CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER; /**< Set ID to non valid value */ |
| |
| __HAL_RTC_WAKEUPTIMER_DISABLE(phrtc); /**< Disable the Wakeup Timer */ |
| __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(phrtc, RTC_FLAG_WUTF); /**< Clear flag in RTC module */ |
| __HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG(); /**< Clear flag in EXTI module */ |
| HAL_NVIC_ClearPendingIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID); /**< Clear pending bit in NVIC */ |
| __HAL_RTC_WAKEUPTIMER_ENABLE_IT(phrtc, RTC_IT_WUT); /**< Enable interrupt in RTC module */ |
| } |
| else |
| { |
| if (__HAL_RTC_WAKEUPTIMER_GET_FLAG(phrtc, RTC_FLAG_WUTF) != RESET) |
| { |
| /** |
| * Simulate that the Timer expired |
| */ |
| HAL_NVIC_SetPendingIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID); |
| } |
| } |
| |
| /* Enable the write protection for RTC registers */ |
| __HAL_RTC_WRITEPROTECTION_ENABLE(phrtc); |
| |
| HAL_NVIC_SetPriority(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID, CFG_HW_TS_NVIC_RTC_WAKEUP_IT_PREEMPTPRIO, |
| CFG_HW_TS_NVIC_RTC_WAKEUP_IT_SUBPRIO); /**< Set NVIC priority */ |
| HAL_NVIC_EnableIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID); /**< Enable NVIC */ |
| |
| return; |
| } |
| |
| HW_TS_ReturnStatus_t HW_TS_Create(uint32_t TimerProcessID, uint8_t * pTimerId, HW_TS_Mode_t TimerMode, |
| HW_TS_pTimerCb_t pftimeout_handler) |
| { |
| HW_TS_ReturnStatus_t localreturnstatus; |
| uint8_t loop = 0; |
| #if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1) |
| uint32_t primask_bit; |
| #endif |
| |
| #if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1) |
| primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */ |
| __disable_irq(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/ |
| #endif |
| |
| while ((loop < CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER) && (aTimerContext[loop].TimerIDStatus != TimerID_Free)) |
| { |
| loop++; |
| } |
| |
| if (loop != CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER) |
| { |
| aTimerContext[loop].TimerIDStatus = TimerID_Created; |
| |
| #if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1) |
| __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ |
| #endif |
| |
| aTimerContext[loop].TimerProcessID = TimerProcessID; |
| aTimerContext[loop].TimerMode = TimerMode; |
| aTimerContext[loop].pTimerCallBack = pftimeout_handler; |
| *pTimerId = loop; |
| |
| localreturnstatus = hw_ts_Successful; |
| } |
| else |
| { |
| #if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1) |
| __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ |
| #endif |
| |
| localreturnstatus = hw_ts_Failed; |
| } |
| |
| return (localreturnstatus); |
| } |
| |
| void HW_TS_Delete(uint8_t timer_id) |
| { |
| HW_TS_Stop(timer_id); |
| |
| aTimerContext[timer_id].TimerIDStatus = TimerID_Free; /**< release ID */ |
| |
| return; |
| } |
| |
| void HW_TS_Stop(uint8_t timer_id) |
| { |
| uint8_t localcurrentrunningtimerid; |
| |
| #if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1) |
| uint32_t primask_bit; |
| #endif |
| |
| #if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1) |
| primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */ |
| __disable_irq(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/ |
| #endif |
| |
| HAL_NVIC_DisableIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID); /**< Disable NVIC */ |
| |
| /* Disable the write protection for RTC registers */ |
| __HAL_RTC_WRITEPROTECTION_DISABLE(phrtc); |
| |
| if (aTimerContext[timer_id].TimerIDStatus == TimerID_Running) |
| { |
| UnlinkTimer(timer_id, SSR_Read_Requested); |
| localcurrentrunningtimerid = CurrentRunningTimerID; |
| |
| if (localcurrentrunningtimerid == CFG_HW_TS_MAX_NBR_CONCURRENT_TIMER) |
| { |
| /** |
| * List is empty |
| */ |
| |
| /** |
| * Disable the timer |
| */ |
| if ((READ_BIT(RTC->CR, RTC_CR_WUTE) == (RTC_CR_WUTE)) == SET) |
| { |
| /** |
| * Wait for the flag to be back to 0 when the wakeup timer is enabled |
| */ |
| while (__HAL_RTC_WAKEUPTIMER_GET_FLAG(phrtc, RTC_FLAG_WUTWF) == SET) |
| ; |
| } |
| __HAL_RTC_WAKEUPTIMER_DISABLE(phrtc); /**< Disable the Wakeup Timer */ |
| |
| while (__HAL_RTC_WAKEUPTIMER_GET_FLAG(phrtc, RTC_FLAG_WUTWF) == RESET) |
| ; |
| |
| /** |
| * make sure to clear the flags after checking the WUTWF. |
| * It takes 2 RTCCLK between the time the WUTE bit is disabled and the |
| * time the timer is disabled. The WUTWF bit somehow guarantee the system is stable |
| * Otherwise, when the timer is periodic with 1 Tick, it may generate an extra interrupt in between |
| * due to the autoreload feature |
| */ |
| __HAL_RTC_WAKEUPTIMER_CLEAR_FLAG(phrtc, RTC_FLAG_WUTF); /**< Clear flag in RTC module */ |
| __HAL_RTC_WAKEUPTIMER_EXTI_CLEAR_FLAG(); /**< Clear flag in EXTI module */ |
| HAL_NVIC_ClearPendingIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID); /**< Clear pending bit in NVIC */ |
| } |
| else if (PreviousRunningTimerID != localcurrentrunningtimerid) |
| { |
| RescheduleTimerList(); |
| } |
| } |
| |
| /* Enable the write protection for RTC registers */ |
| __HAL_RTC_WRITEPROTECTION_ENABLE(phrtc); |
| |
| HAL_NVIC_EnableIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID); /**< Enable NVIC */ |
| |
| #if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1) |
| __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ |
| #endif |
| |
| return; |
| } |
| |
| void HW_TS_Start(uint8_t timer_id, uint32_t timeout_ticks) |
| { |
| uint16_t time_elapsed; |
| uint8_t localcurrentrunningtimerid; |
| |
| #if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1) |
| uint32_t primask_bit; |
| #endif |
| |
| if (aTimerContext[timer_id].TimerIDStatus == TimerID_Running) |
| { |
| HW_TS_Stop(timer_id); |
| } |
| |
| #if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1) |
| primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */ |
| __disable_irq(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/ |
| #endif |
| |
| HAL_NVIC_DisableIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID); /**< Disable NVIC */ |
| |
| /* Disable the write protection for RTC registers */ |
| __HAL_RTC_WRITEPROTECTION_DISABLE(phrtc); |
| |
| aTimerContext[timer_id].TimerIDStatus = TimerID_Running; |
| |
| aTimerContext[timer_id].CountLeft = timeout_ticks; |
| aTimerContext[timer_id].CounterInit = timeout_ticks; |
| |
| time_elapsed = linkTimer(timer_id); |
| |
| localcurrentrunningtimerid = CurrentRunningTimerID; |
| |
| if (PreviousRunningTimerID != localcurrentrunningtimerid) |
| { |
| RescheduleTimerList(); |
| } |
| else |
| { |
| aTimerContext[timer_id].CountLeft -= time_elapsed; |
| } |
| |
| /* Enable the write protection for RTC registers */ |
| __HAL_RTC_WRITEPROTECTION_ENABLE(phrtc); |
| |
| HAL_NVIC_EnableIRQ(CFG_HW_TS_RTC_WAKEUP_HANDLER_ID); /**< Enable NVIC */ |
| |
| #if (CFG_HW_TS_USE_PRIMASK_AS_CRITICAL_SECTION == 1) |
| __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ |
| #endif |
| |
| return; |
| } |
| |
| uint16_t HW_TS_RTC_ReadLeftTicksToCount(void) |
| { |
| uint32_t primask_bit; |
| uint16_t return_value, auro_reload_value, elapsed_time_value; |
| |
| primask_bit = __get_PRIMASK(); /**< backup PRIMASK bit */ |
| __disable_irq(); /**< Disable all interrupts by setting PRIMASK bit on Cortex*/ |
| |
| if ((READ_BIT(RTC->CR, RTC_CR_WUTE) == (RTC_CR_WUTE)) == SET) |
| { |
| auro_reload_value = (uint32_t) (READ_BIT(RTC->WUTR, RTC_WUTR_WUT)); |
| |
| elapsed_time_value = ReturnTimeElapsed(); |
| |
| if (auro_reload_value > elapsed_time_value) |
| { |
| return_value = auro_reload_value - elapsed_time_value; |
| } |
| else |
| { |
| return_value = 0; |
| } |
| } |
| else |
| { |
| return_value = TIMER_LIST_EMPTY; |
| } |
| |
| __set_PRIMASK(primask_bit); /**< Restore PRIMASK bit*/ |
| |
| return (return_value); |
| } |
| |
| __weak void HW_TS_RTC_Int_AppNot(uint32_t TimerProcessID, uint8_t TimerID, HW_TS_pTimerCb_t pTimerCallBack) |
| { |
| pTimerCallBack(); |
| |
| return; |
| } |