1 /**
2 ******************************************************************************
3 * @file stm32wb0x_hal_radio_timer.c
4 * @author GPM WBL Application Team
5 * @brief Virtual timer and Radio timer high level APIs
6 * @details This file implements the software layer that provides the virtualization of the
7 * resources of a single hardware timer in order to allocate many user virtual timers.
8 * The only constraint to the number of virtual timers is the memory.
9 * Each instance of a virtual timer is placed in an queue ordered by the expiration time
10 * and it can be linked to a callback.
11 * The timer tick is in charge to execute the callback linked to each virtual timer
12 * and to update the hardware timeout to guarantee the expiration of the next virtual
13 * timer in the queue.
14 * A special virtual timer called calibration/anti-wrapping timer is automatically armed
15 * by the software. This timer can address two tasks:
16 * - it is in charge to maintain the never wrapping virtual time base.
17 * - if the slow clock calibration is enabled, it starts the calibration procedure at each
18 * calibration interval specified during the initialization.
19 *
20 * A timer is intended as an event programmed in the future at a certain absolute expiration time
21 * on a time base. In this implementation the time base grows on 64 bits. Then, it never wraps.
22 * However, due to hardware timer finite length and in order to maintain the timing coherency, the time base
23 * must be maintained at least one time before the hardware timer wraps.
24 * Then even if the slow clock calibration is disabled, the calibration/anti-wrapping timer
25 * is always active with the only role to maintain the time base and it will expire
26 * at a rate that depends on the hardware timer capability.
27 * The time base unit is a STU that is the unit exposed to the user and it is equal to 625/256 us.
28 * The calibration/anti-wrapping mechanism is not managed by the user.
29 *
30 * This software layer also exposes the possibility to program a radio timer.
31 * A radio timer allows the user to trigger an already configured radio transaction.
32 * The duties of this library does not include the configuration of a radio transaction.
33 * This layer tries to exploit the last calibration values to program the radio activity
34 * in order to improve the accuracy. In this case, the radio event is not immediately programmed
35 * when it is requested, but only when the next calibration values are available.
36 * Since the calibration values are available inside the timer tick when the calibration is over,
37 * the application must ensure that the timer tick is called after the calibration timer
38 * expiration within a certain margin in order to avoid that the radio event is shifted in the
39 * past and cannot be anymore programmed.
40 *
41 ******************************************************************************
42 * @attention
43 *
44 * Copyright (c) 2024 STMicroelectronics.
45 * All rights reserved.
46 *
47 * This software is licensed under terms that can be found in the LICENSE file
48 * in the root directory of this software component.
49 * If no LICENSE file comes with this software, it is provided AS-IS.
50 *
51 ******************************************************************************
52 */
53
54 /* Includes ------------------------------------------------------------------*/
55 #include "stm32wb0x_hal.h"
56
57 /** @addtogroup STM32WB0x_HAL_Driver
58 * @{
59 */
60
61 /** @addtogroup RADIO_TIMER
62 * @brief HAL RADIO TIMER module driver
63 * @{
64 */
65
66 /* Private typedef -----------------------------------------------------------*/
67
68 /** @defgroup RADIO_TIMER_Private_Types RADIO TIMER Private Types
69 * @{
70 */
71 typedef struct
72 {
73 uint8_t periodicCalibration; /*!< Periodic calibration enable status */
74 uint32_t periodicCalibrationInterval; /*!< Periodic calibration interval in ms, to disable set to 0 */
75 bool calibration_in_progress; /*!< Flag to indicate that a periodic calibration has been started */
76 } CalibrationSettingsTypeDef;
77
78 typedef struct
79 {
80 uint32_t period; /** Number of 16 MHz clock cycles in (2*(SLOW_COUNT+1)) low speed oscillator periods */
81 uint32_t freq; /** 2^39/period */
82 int32_t freq1; /** Round(((freq/64)*0x753)/256) */
83 int32_t period1; /** Round (( ((period /256) * 0x8BCF6) + (((period % 256)* 0x8BCF6)/256)) / 32) */
84 int32_t last_period1; /** Period global in last calibration */
85 uint64_t last_calibration_time; /** Absolute system time when last calibration was performed */
86 uint32_t calibration_machine_interval; /** Calibration Interval MTU */
87 uint8_t calibration_data_available; /** Flag to signal if a new calibration data is available or not */
88 } CalibrationDataTypeDef;
89
90 typedef struct
91 {
92 uint8_t tx_cal_delay; /**time in MTU to be compensated if transmission and pll calibration are requested. The value in RAM must be initialized before the TIMER is initialized*/
93 uint8_t tx_no_cal_delay; /**time in MTU to be compensated if transmission is requested. The value in RAM must be initialized before the TIMER is initialized*/
94 uint8_t rx_cal_delay; /**time in MTU to be compensated if reception and pll calibration are requested. The value in RAM must be initialized before the TIMER is initialized*/
95 uint8_t rx_no_cal_delay; /**time in MTU to be compensated if reception is requested. The value in RAM must be initialized before the TIMER is initialized*/
96 uint8_t tx_cal_delay_st; /**time in STU to be compensated if transmission and pll calibration are requested. The value in RAM must be initialized before the TIMER is initialized*/
97 uint8_t tim12_delay_mt;
98 } TxRxDelayTypeDef;
99
100 typedef struct
101 {
102 uint64_t expiryTime;
103 bool cal_req;
104 bool active;
105 bool pending;
106 bool intTxRx_to_be_served;
107 bool event_type;
108 } RADIO_TIMER_RadioHandleTypeDef;
109
110 typedef struct
111 {
112 CalibrationSettingsTypeDef calibrationSettings;
113 CalibrationDataTypeDef calibrationData;
114 TxRxDelayTypeDef TxRxDelay;
115 VTIMER_HandleType calibrationTimer;
116 RADIO_TIMER_RadioHandleTypeDef radioTimer;
117 uint32_t hs_startup_time; /*!< HS startup time */
118 uint64_t cumulative_time; /** Absolute system time since power up */
119 uint64_t last_system_time; /** Last System Time */
120 uint32_t last_machine_time; /** Last machine time used to update cumulative time */
121 uint8_t last_setup_time; /**setup time of last timer programmed*/
122 uint32_t last_anchor_mt;
123 VTIMER_HandleType *rootNode; /*!< First timer of the host timer queue */
124 bool enableTimeBase; /*!< Internal flag. User can ignore it*/
125 uint8_t expired_count; /*!< Progressive number to indicate expired timers */
126 uint8_t served_count; /*!< Progressive number to indicate served expired timers */
127 uint8_t stop_notimer_action; /*!< Flag to indicate DEEPSTOP no timer action */
128 uint8_t wakeup_calibration; /*!< Flag to indicate if start a calibration after wakeup */
129 #if defined (STM32WB06) || defined (STM32WB07)
130 uint8_t hostIsRadioPending; /*!< If hostIsRadioPending is true, the virtual timer callback will be triggered when the wakeup timer triggers */
131 uint32_t hostMargin; /*!< It depends on the hs startup time. See HOST_MARGIN */
132 uint8_t waitCal; /*!< Wait the next calibration to get the latest values */
133 #endif
134 } RADIO_TIMER_ContextTypeDef;
135
136 /**
137 * @}
138 */
139
140 /* Private define ------------------------------------------------------------*/
141 /** @defgroup RADIO_TIMER_Private_Defines RADIO TIMER Private Defines
142 * @{
143 */
144 #define MULT64_THR_FREQ (806)
145 #define MULT64_THR_PERIOD (1589)
146
147 /* Margin to add to the calibration interval in order to guarantee
148 * enough time to program the radio timer after the calibration.
149 * It is expressed in STU. */
150 #define RADIO_ACTIVITY_MARGIN (204800)
151
152 /* Threshold to take into account the calibration duration. */
153 #define CALIB_SAFE_THR (370)
154
155 /* Minimum threshold to safely program the radio timer (expressed in STU) */
156 #define TIMER1_MARGIN (10)
157
158 /* Delay to program a radio timer in the worst case (in STU).
159 This is the sum of: 1st init dalay, 2 init delay and tx delay (118 + 65 + 2) */
160 #define TIMER1_INIT_DELAY (76)
161
162 /* Radio event types */
163 #define RX (0)
164 #define TX (1)
165
166 #define MARGIN_EXT (200)
167
168 /* Minimum threshold in STU to safely clear radio timers.
169 The wakeup timer in the worst case triggers about 30us in advance.
170 This must be considered when the radio timer is cleared.
171 Then a window of about 30 us is considered as critical, that is
172 it is not sure the timer can be cleared properly */
173 #define CLEAR_MIN_THR (15)
174
175 /* Extra margin to consider before going in low power mode.
176 This is the time (STU) needed for the system to go to sleep from the time the
177 HAL_RADIO_TIMER_PowerSaveLevelCheck() is called. */
178 #define LOW_POWER_THR (82) // Around 200 us.
179
180 #if defined (STM32WB06) || defined (STM32WB07)
181 /* HOST_MARGIN is the margin in STU needed to program a pending radio operation after the host timer is triggered */
182 #define HOST_MARGIN (200)
183
184 #define RADIO_TX_RX_EXCEPTION_NUMBER 18
185 #endif
186
187 /**
188 * @}
189 */
190
191 /* Private macros ------------------------------------------------------------*/
192
193 /** @defgroup RADIO_TIMER_Private_Macros RADIO TIMER Private Macros
194 * @{
195 */
196
197 #define ATOMIC_SECTION_BEGIN() uint32_t uwPRIMASK_Bit = __get_PRIMASK(); \
198 __disable_irq(); \
199 /* Must be called in the same scope of ATOMIC_SECTION_BEGIN */
200 #define ATOMIC_SECTION_END() __set_PRIMASK(uwPRIMASK_Bit)
201
202 #define MAX(a,b) ((a) < (b) )? (b) : (a)
203 #define MIN(a,b) ((a) < (b) )? (a) : (b)
204 #define DIFF8(a,b) ((a)>=(b) ? ((a)-(b)) : (256+((a)-(b))))
205 #define TIME_DIFF(a, b) (((int32_t)((a - b) << (32-TIMER_BITS))) >> (32-TIMER_BITS))
206 /* This define assumes that a is always greater than b */
207 #define TIME_ABSDIFF(a, b) ((a - b) & TIMER_MAX_VALUE)
208 #define INCREMENT_EXPIRE_COUNT_ISR (RADIO_TIMER_Context.expired_count\
209 = ((RADIO_TIMER_Context.expired_count + 1) == RADIO_TIMER_Context.served_count) ? RADIO_TIMER_Context.expired_count : (RADIO_TIMER_Context.expired_count + 1))
210 #define INCREMENT_EXPIRE_COUNT ATOMIC_SECTION_BEGIN(); INCREMENT_EXPIRE_COUNT_ISR ; ATOMIC_SECTION_END();
211
212 /**
213 * @}
214 */
215
216 /* Private variables ---------------------------------------------------------*/
217 /** @defgroup RADIO_TIMER_Private_Variables RADIO TIMER Private Variables
218 * @{
219 */
220 static RADIO_TIMER_ContextTypeDef RADIO_TIMER_Context;
221 /**
222 * @}
223 */
224
225 /* Private constants ---------------------------------------------------------*/
226 /** @defgroup RADIO_TIMER_Private_Constants RADIO TIMER Private Constants
227 * @{
228 */
229 #define TIMER_SYSTICK_PER_FIVE_SECONDS (2048000)
230 #define TIMER_SYSTICK_PER_SECOND (409600)
231 #define TIMER_SYSTICK_PER_10MS (4096)
232
233 /** Margin arging to take for long sleep to allow the
234 * system to avoid to have the counter wrapping. It is expressed in machine
235 * time, so it is variable when using internal RO
236 */
237 #define TIMER_WRAPPING_MARGIN (4096)
238 /** Number of significant bits in the radio timer */
239 #define TIMER_BITS (32)
240 #define TIMER_MAX_VALUE (0xFFFFFFFFU >> (32-TIMER_BITS))
241
242 #define WAKEUP_INIT_DELAY (27) /* about 65us in STU */
243 /**
244 * @}
245 */
246
247 /* Private function prototypes -----------------------------------------------*/
248 /** @addtogroup RADIO_TIMER_Private_Functions
249 * @{
250 */
251 /* Privati Calibration APIs */
252 static void _calibrationProcedure(void);
253 static void _timer_start_calibration(void);
254 static void _timer_calibrate(CalibrationDataTypeDef *calibrationData);
255 static void _get_calibration_data(CalibrationDataTypeDef *calibrationData);
256 static void _calibration_callback(void *handle);
257 static void _updateCalibrationData(void);
258
259 static uint32_t _us_to_systime(uint32_t time);
260 static uint32_t _us_to_machinetime(uint32_t time);
261 static void _configureTxRxDelay(RADIO_TIMER_ContextTypeDef *context, uint8_t calculate_st);
262 static void _update_xtal_startup_time(uint16_t hs_startup_time, int32_t freq1);
263 uint32_t blue_unit_conversion(uint32_t time, uint32_t period_freq,
264 uint32_t thr); /* Translate MTU to STU and vice-versa. It is implemented using integer operation. */
265 static uint64_t _get_system_time_and_machine(RADIO_TIMER_ContextTypeDef *context, uint32_t *current_machine_time);
266 static int32_t _start_timer(VTIMER_HandleType *timerHandle, uint64_t time);
267 static VTIMER_HandleType *_update_user_timeout(VTIMER_HandleType *rootNode, uint8_t *expired);
268 static VTIMER_HandleType *_insert_timer_in_queue(VTIMER_HandleType *rootNode, VTIMER_HandleType *handle);
269 static void _virtualTimeBaseEnable(FunctionalState state);
270 static VTIMER_HandleType *_remove_timer_in_queue(VTIMER_HandleType *rootNode, VTIMER_HandleType *handle);
271 static VTIMER_HandleType *_check_callbacks(VTIMER_HandleType *rootNode, VTIMER_HandleType **expiredList);
272 static void _update_system_time(RADIO_TIMER_ContextTypeDef *context);
273 static void _check_radio_activity(RADIO_TIMER_RadioHandleTypeDef *timerHandle, uint8_t *expired);
274 #if defined (STM32WB06) || defined (STM32WB07)
275 static uint32_t TIMER_SetRadioHostWakeupTime(uint32_t delay, bool *share);
276 static void _set_controller_as_host(void);
277 static void _check_host_activity(void);
278 #else
279 static uint32_t VTIMER_SetWakeupTime(uint32_t delay, bool allow_sleep);
280 #endif
281 static uint8_t TIMER_SetRadioTimerValue(uint32_t timeout, bool event_type, bool cal_req);
282 static uint64_t TIMER_GetPastSysTime(uint32_t time, uint64_t *current_system_time);
283 static bool TIMER_SleepCheck(void);
284 static uint8_t TIMER_GetRadioTimerValue(uint32_t *time);
285 /**
286 * @}
287 */
288
289 /* Exported functions --------------------------------------------------------*/
290
291 /** @defgroup RADIO_TIMER_Exported_Functions Radio Timer Exported Functions
292 * @{
293 */
294
295 /* ------------------------ RADIO Init APIs ----------------------------------*/
296 /**
297 * @brief Initialize the radio timer module. It must be placed in the initialization
298 * section of the application.
299 * @param RADIO_TIMER_InitStruct Radio Timer Initialization parameters
300 * @retval None
301 */
HAL_RADIO_TIMER_Init(RADIO_TIMER_InitTypeDef * RADIO_TIMER_InitStruct)302 void HAL_RADIO_TIMER_Init(RADIO_TIMER_InitTypeDef *RADIO_TIMER_InitStruct)
303 {
304 /* Interrupt Configuration */
305 LL_RADIO_TIMER_ClearFlag_CPUWakeup(WAKEUP);
306 LL_RADIO_TIMER_EnableCPUWakeupIT(WAKEUP);
307 NVIC_EnableIRQ(RADIO_TIMER_CPU_WKUP_IRQn);
308 NVIC_EnableIRQ(RADIO_TIMER_ERROR_IRQn);
309
310 #if defined (STM32WB06) || defined (STM32WB07)
311 LL_RADIO_TIMER_ClearFlag_BLEWakeup(WAKEUP);
312 LL_RADIO_TIMER_EnableBLEWakeupIT(WAKEUP);
313 NVIC_EnableIRQ(RADIO_TIMER_TXRX_WKUP_IRQn);
314 RADIO_TIMER_Context.hostMargin = MAX(HOST_MARGIN, RADIO_TIMER_InitStruct->XTAL_StartupTime);
315 #endif
316
317 /* Calibration Setting */
318 RADIO_TIMER_Context.calibrationSettings.periodicCalibration = (RADIO_TIMER_InitStruct->periodicCalibrationInterval != 0);
319 if (RADIO_TIMER_Context.calibrationSettings.periodicCalibration || RADIO_TIMER_InitStruct->enableInitialCalibration)
320 {
321 _calibrationProcedure();
322 }
323 else
324 {
325 /* Assume fix frequency at 32.768 kHz */
326 RADIO_TIMER_Context.calibrationData.last_period1 = 0x00190000;
327 RADIO_TIMER_Context.calibrationData.period1 = 0x00190000 ;
328 RADIO_TIMER_Context.calibrationData.freq1 = 0x0028F5C2 ;
329 RADIO_TIMER_Context.calibrationData.period = 23437;
330 RADIO_TIMER_Context.calibrationData.freq = 23456748;
331 }
332 if (RADIO_TIMER_InitStruct->periodicCalibrationInterval == 0)
333 {
334 RADIO_TIMER_Context.calibrationSettings.periodicCalibrationInterval = HAL_RADIO_TIMER_MachineTimeToSysTime(0x50000000);
335 }
336 else
337 {
338 RADIO_TIMER_Context.calibrationSettings.periodicCalibrationInterval = (TIMER_SYSTICK_PER_10MS * RADIO_TIMER_InitStruct->periodicCalibrationInterval) / 10;
339 RADIO_TIMER_Context.calibrationSettings.periodicCalibrationInterval = MIN(RADIO_TIMER_Context.calibrationSettings.periodicCalibrationInterval,
340 HAL_RADIO_TIMER_MachineTimeToSysTime(TIMER_MAX_VALUE - TIMER_WRAPPING_MARGIN));
341 }
342 RADIO_TIMER_Context.calibrationSettings.calibration_in_progress = FALSE;
343
344 /* XTAL startup time configuration */
345 RADIO_TIMER_Context.hs_startup_time = RADIO_TIMER_InitStruct->XTAL_StartupTime;
346 _update_xtal_startup_time(RADIO_TIMER_Context.hs_startup_time, RADIO_TIMER_Context.calibrationData.freq1);
347
348 /* Init Radio Timer Context */
349 RADIO_TIMER_Context.last_setup_time = 0;
350 RADIO_TIMER_Context.cumulative_time = 0;
351 RADIO_TIMER_Context.last_machine_time = LL_RADIO_TIMER_GetAbsoluteTime(WAKEUP);
352 RADIO_TIMER_Context.last_system_time = 0;
353 RADIO_TIMER_Context.calibrationData.last_calibration_time = 0;
354 RADIO_TIMER_Context.calibrationData.calibration_data_available = 0;
355 RADIO_TIMER_Context.calibrationData.calibration_machine_interval = blue_unit_conversion(RADIO_TIMER_Context.calibrationSettings.periodicCalibrationInterval,
356 RADIO_TIMER_Context.calibrationData.freq1, MULT64_THR_FREQ);
357 RADIO_TIMER_Context.wakeup_calibration = RADIO_TIMER_Context.calibrationSettings.periodicCalibration;
358
359 /* Init the Virtual Timer queue */
360 RADIO_TIMER_Context.rootNode = NULL;
361 RADIO_TIMER_Context.enableTimeBase = TRUE;
362 RADIO_TIMER_Context.stop_notimer_action = FALSE;
363 RADIO_TIMER_Context.expired_count = 0;
364 RADIO_TIMER_Context.served_count = 0;
365
366 /* Init Radio Timer queue */
367 RADIO_TIMER_Context.radioTimer.active = FALSE;
368 RADIO_TIMER_Context.radioTimer.pending = FALSE;
369 RADIO_TIMER_Context.radioTimer.intTxRx_to_be_served = FALSE;
370 RADIO_TIMER_Context.radioTimer.expiryTime = 0;
371
372 /* Configure the Calibration callback and schedule the next calibration */
373 RADIO_TIMER_Context.calibrationTimer.callback = _calibration_callback;
374 RADIO_TIMER_Context.calibrationTimer.userData = NULL;
375 _start_timer(&RADIO_TIMER_Context.calibrationTimer,
376 HAL_RADIO_TIMER_GetCurrentSysTime() + RADIO_TIMER_Context.calibrationSettings.periodicCalibrationInterval);
377
378 /* Tx & Rx delay configuration */
379 _configureTxRxDelay(&RADIO_TIMER_Context, TRUE);
380 }
381
382 /**
383 * @brief Timer module state machine. Check and schedule the calibration.
384 * Check expired timers and execute user callback.
385 * It must be placed inside the infinite loop.
386 * @retval None
387 */
HAL_RADIO_TIMER_Tick(void)388 void HAL_RADIO_TIMER_Tick(void)
389 {
390 uint8_t expired = 0;
391
392 ATOMIC_SECTION_BEGIN();
393 if (RADIO_TIMER_Context.radioTimer.active)
394 {
395 if (RADIO_TIMER_Context.radioTimer.expiryTime < HAL_RADIO_TIMER_GetCurrentSysTime())
396 {
397 RADIO_TIMER_Context.radioTimer.active = FALSE;
398 }
399 }
400 ATOMIC_SECTION_END();
401
402 /* Check for expired timers */
403 while (DIFF8(RADIO_TIMER_Context.expired_count, RADIO_TIMER_Context.served_count))
404 {
405 VTIMER_HandleType *expiredList, *curr;
406 uint8_t to_be_served = DIFF8(RADIO_TIMER_Context.expired_count, RADIO_TIMER_Context.served_count);
407
408 RADIO_TIMER_Context.rootNode = _check_callbacks(RADIO_TIMER_Context.rootNode, &expiredList);
409
410 /* Call all the user callbacks */
411 curr = expiredList;
412 while (curr != NULL)
413 {
414 /* Save next pointer, in case callback start the timer again */
415 VTIMER_HandleType *next = curr->next;
416 curr->active = FALSE;
417 if (curr->callback)
418 {
419 curr->callback(curr); /* we are sure a callback is set?*/
420 }
421 curr = next;
422 }
423
424 RADIO_TIMER_Context.rootNode = _update_user_timeout(RADIO_TIMER_Context.rootNode, &expired);
425 if (expired == 1)
426 {
427 /* A new root timer is already expired, mimic timer expire */
428 INCREMENT_EXPIRE_COUNT;
429 }
430 RADIO_TIMER_Context.served_count += to_be_served;
431 }
432
433 /* Check for periodic calibration */
434 if (RADIO_TIMER_Context.calibrationSettings.calibration_in_progress)
435 {
436 if (LL_RADIO_TIMER_IsActiveFlag_LSICalibrationEnded(RADIO_CTRL))
437 {
438 /* Calibration is completed */
439 RADIO_TIMER_Context.calibrationSettings.calibration_in_progress = FALSE;
440 if ((RADIO_TIMER_Context.wakeup_calibration == FALSE) && RADIO_TIMER_Context.stop_notimer_action)
441 {
442 RADIO_TIMER_Context.stop_notimer_action = FALSE;
443 }
444 else
445 {
446 /* Collect calibration data */
447 _updateCalibrationData();
448 }
449
450 #if defined (STM32WB06) || defined (STM32WB07)
451 if (RADIO_TIMER_Context.waitCal)
452 {
453 RADIO_TIMER_Context.waitCal = 0;
454 RADIO_TIMER_Context.radioTimer.pending = TRUE;
455 _check_radio_activity(&RADIO_TIMER_Context.radioTimer, &expired);
456 RADIO_TIMER_Context.rootNode = _update_user_timeout(RADIO_TIMER_Context.rootNode, &expired);
457 }
458 #else
459 _check_radio_activity(&RADIO_TIMER_Context.radioTimer, &expired);
460 #endif
461
462 HAL_RADIO_TIMER_StopVirtualTimer(&RADIO_TIMER_Context.calibrationTimer);
463 /* Schedule next calibration event */
464 _start_timer(&RADIO_TIMER_Context.calibrationTimer,
465 HAL_RADIO_TIMER_GetCurrentSysTime() + RADIO_TIMER_Context.calibrationSettings.periodicCalibrationInterval);
466 }
467 }
468 /* if there is a periodic calibration, start it in advance during the active phase */
469 else
470 {
471 if (RADIO_TIMER_Context.calibrationSettings.periodicCalibration)
472 {
473 if (HAL_RADIO_TIMER_GetCurrentSysTime() > (RADIO_TIMER_Context.calibrationData.last_calibration_time +
474 TIMER_SYSTICK_PER_FIVE_SECONDS))
475 {
476 _calibration_callback(&RADIO_TIMER_Context.calibrationTimer);
477 }
478 }
479 }
480 }
481
482 /**
483 * @brief Return the status of the radio timer.
484 * The timeout of the last radio timer activity taken into account by the Timer Module
485 * is saved in the variable passed as parameter.
486 * @param time: Pointer of the variable where the time of the last radio activity scheduled is stored
487 * The time is expressed in STU.
488 * @retval 0 if no radio timer is pending.
489 * @retval 1 if a radio timer is pending.
490 */
HAL_RADIO_TIMER_GetRadioTimerStatus(uint64_t * time)491 RADIO_TIMER_Status HAL_RADIO_TIMER_GetRadioTimerStatus(uint64_t *time)
492 {
493 RADIO_TIMER_Status status;
494
495 *time = RADIO_TIMER_Context.radioTimer.expiryTime;
496 if ((RADIO_TIMER_Context.radioTimer.pending) || (LL_RADIO_TIMER_IsEnabledBLEWakeupTimer(WAKEUP))
497 || (LL_RADIO_TIMER_IsEnabledTimer1(BLUE)))
498 {
499 status = RADIO_TIMER_PENDING;
500 }
501 else
502 {
503 status = RADIO_TIMER_OFF;
504 }
505 return status;
506 }
507
508 /**
509 * @brief Get the last anchorPoint in system time unit.
510 * @param current_system_time: Current System Time
511 * @return TimerCapture register in system time unit.
512 */
HAL_RADIO_TIMER_GetAnchorPoint(uint64_t * current_system_time)513 uint64_t HAL_RADIO_TIMER_GetAnchorPoint(uint64_t *current_system_time)
514 {
515 return TIMER_GetPastSysTime(BLUE->TIMERCAPTUREREG, current_system_time);
516 }
517
518 /**
519 * @brief Returns the admitted low power mode according to the next timer activity.
520 * @return Low Power mode
521 */
HAL_RADIO_TIMER_PowerSaveLevelCheck(void)522 PowerSaveLevels HAL_RADIO_TIMER_PowerSaveLevelCheck(void)
523 {
524 uint32_t nextRadioActivity;
525 uint8_t timerState;
526 uint64_t current_time;
527 PowerSaveLevels level;
528
529 if (TIMER_SleepCheck() == FALSE)
530 {
531 return POWER_SAVE_LEVEL_RUNNING;
532 }
533
534 level = POWER_SAVE_LEVEL_STOP;
535
536 current_time = HAL_RADIO_TIMER_GetCurrentSysTime();
537 timerState = TIMER_GetRadioTimerValue(&nextRadioActivity);
538
539 /*Timer1 and wakeup timer are programmed only through the timer module*/
540 if (((RADIO_TIMER_Context.radioTimer.active || RADIO_TIMER_Context.radioTimer.pending)
541 && !(timerState == RADIO_TIMER1_BUSY)) || RADIO_TIMER_Context.radioTimer.intTxRx_to_be_served)
542 {
543 if (RADIO_TIMER_Context.radioTimer.expiryTime < (current_time + \
544 RADIO_TIMER_Context.last_setup_time + \
545 RADIO_TIMER_Context.hs_startup_time + \
546 LOW_POWER_THR))
547 {
548 return POWER_SAVE_LEVEL_CPU_HALT;
549 }
550
551 level = POWER_SAVE_LEVEL_STOP_LS_CLOCK_ON;
552 }
553 else
554 {
555 if ((timerState == RADIO_TIMER2_BUSY) || (timerState == RADIO_TIMER1_BUSY))
556 {
557 return POWER_SAVE_LEVEL_CPU_HALT;
558 }
559 }
560
561 if (RADIO_TIMER_Context.rootNode != NULL && RADIO_TIMER_Context.rootNode->active)
562 {
563 if (RADIO_TIMER_Context.rootNode->expiryTime < (current_time + LOW_POWER_THR + RADIO_TIMER_Context.hs_startup_time))
564 {
565 return POWER_SAVE_LEVEL_CPU_HALT;
566 }
567
568 if (level == POWER_SAVE_LEVEL_STOP)
569 {
570 if ((RADIO_TIMER_Context.rootNode->next == NULL)
571 && (RADIO_TIMER_Context.rootNode == &RADIO_TIMER_Context.calibrationTimer))
572 {
573 RADIO_TIMER_Context.stop_notimer_action = TRUE;
574 _virtualTimeBaseEnable(DISABLE);
575 LL_RADIO_TIMER_DisableCPUWakeupTimer(WAKEUP);
576 return POWER_SAVE_LEVEL_STOP;
577 }
578 }
579 level = POWER_SAVE_LEVEL_STOP_LS_CLOCK_ON;
580 }
581
582 return level;
583 }
584
585 /* ---------------------- RADIO Activity APIs --------------------------------*/
586 /**
587 * @brief Schedules a radio activity for the given absolute timeout value expressed in STU.
588 * If the calibration of the low speed oscillator is needed, if it is possible,
589 * the radio timer will be programmed with the latest calibration data.
590 * @param time: Absolute time expressed in STU.
591 * @param event_type: Specify if it is a TX (1) or RX (0) event.
592 * @param cal_req: Specify if PLL calibration is requested (1) or not (0).
593 * @retval 0 if radio activity has been scheduled successfully.
594 * @retval 1 if radio activity has been rejected (it is too close or in the past).
595 */
HAL_RADIO_TIMER_SetRadioTimerValue(uint32_t time,uint8_t event_type,uint8_t cal_req)596 uint32_t HAL_RADIO_TIMER_SetRadioTimerValue(uint32_t time, uint8_t event_type, uint8_t cal_req)
597 {
598 uint8_t retVal = 0;
599 #if defined (STM32WB06) || defined (STM32WB07)
600 uint64_t current_time;
601 #endif
602
603 RADIO_TIMER_Context.radioTimer.event_type = event_type;
604 RADIO_TIMER_Context.radioTimer.cal_req = cal_req;
605 RADIO_TIMER_Context.radioTimer.expiryTime = RADIO_TIMER_Context.calibrationData.last_calibration_time + (uint32_t)(time - (uint32_t)RADIO_TIMER_Context.calibrationData.last_calibration_time);
606 RADIO_TIMER_Context.radioTimer.active = FALSE;
607 RADIO_TIMER_Context.radioTimer.intTxRx_to_be_served = FALSE;
608 RADIO_TIMER_Context.radioTimer.pending = TRUE;
609
610 #if defined (STM32WB06) || defined (STM32WB07)
611 current_time = HAL_RADIO_TIMER_GetCurrentSysTime();
612
613 if (RADIO_TIMER_Context.rootNode == NULL)
614 {
615 _check_radio_activity(&RADIO_TIMER_Context.radioTimer, &retVal);
616 }
617 else
618 {
619 if (RADIO_TIMER_Context.rootNode->expiryTime < current_time ||
620 ((RADIO_TIMER_Context.radioTimer.expiryTime < (RADIO_TIMER_Context.rootNode->expiryTime +
621 RADIO_TIMER_Context.hostMargin)) && RADIO_TIMER_Context.rootNode->active) || !RADIO_TIMER_Context.rootNode->active)
622 {
623 /* Program the radio timer */
624 _check_radio_activity(&RADIO_TIMER_Context.radioTimer, &retVal);
625 if ((RADIO_TIMER_Context.radioTimer.expiryTime >= RADIO_TIMER_Context.rootNode->expiryTime)
626 && RADIO_TIMER_Context.rootNode->active)
627 {
628 /*The radio operation is before or too close the host timeout*/
629 RADIO_TIMER_Context.hostIsRadioPending = 1;
630 }
631 }
632 else
633 {
634 /* If radio timer is not programmed, an emulated host timer is already programmed.
635 Make sure radio errors are disabled.
636 This call is not needed if radio errors are not enabled by the BLE stack. */
637 _set_controller_as_host();
638 }
639 }
640 #else
641 _check_radio_activity(&RADIO_TIMER_Context.radioTimer, &retVal);
642 #endif
643
644 _virtualTimeBaseEnable(ENABLE);
645
646 return retVal;
647 }
648
649 /**
650 * @brief Programs Timer1 with a relative timeout - expressed in us - wrt the previous radio event.
651 * @param rel_timeout_us: relative delay, in us, wrt the previous radio event.
652 * @param event_type: 1 Tx event.
653 0 Rx event
654 * @param cal_req: 1 PLL calibartion is requested.
655 0 PLL calibartion is not requested.
656 * @warning The API must be called with interrupts disabled to avoid programming the timer with a value in the past
657 * @retval 0 if a correct timeout has been programmed in the timeout register
658 * @retval 1 if a correct timeout cannot be programmed
659 */
HAL_RADIO_TIMER_SetRadioTimerRelativeUsValue(uint32_t rel_timeout_us,bool event_type,bool cal_req)660 uint32_t HAL_RADIO_TIMER_SetRadioTimerRelativeUsValue(uint32_t rel_timeout_us, bool event_type, bool cal_req)
661 {
662 uint32_t event_time;
663 uint32_t radio_init_delay;
664 uint32_t abs_timeout_mt;
665 uint32_t current_machine_time;
666
667 /*choose the 2nd init duration. Check the event_type and cal. request*/
668 if (event_type == TX)
669 {
670 if (cal_req)
671 {
672 radio_init_delay = RADIO_TIMER_Context.TxRxDelay.tx_cal_delay;
673 }
674 else
675 {
676 radio_init_delay = RADIO_TIMER_Context.TxRxDelay.tx_no_cal_delay;
677 }
678 }
679 else
680 {
681 if (cal_req)
682 {
683 radio_init_delay = RADIO_TIMER_Context.TxRxDelay.rx_cal_delay;
684 }
685 else
686 {
687 radio_init_delay = RADIO_TIMER_Context.TxRxDelay.rx_no_cal_delay;
688 }
689 }
690
691 abs_timeout_mt = RADIO_TIMER_Context.last_anchor_mt + _us_to_machinetime(rel_timeout_us);
692
693 event_time = (abs_timeout_mt - RADIO_TIMER_Context.TxRxDelay.tim12_delay_mt - radio_init_delay) & TIMER_MAX_VALUE;
694
695 current_machine_time = WAKEUP->ABSOLUTE_TIME;
696
697 if ((event_time - current_machine_time) > 0x80000000)
698 {
699 /* Requested time is in the past, return error */
700 return 1;
701 }
702
703 LL_RADIO_TIMER_SetTimeout(BLUE, event_time);
704 LL_RADIO_TIMER_EnableTimer1(BLUE);
705 LL_RADIO_TIMER_DisableBLEWakeupTimer(WAKEUP);
706
707 #if defined (STM32WB06) || defined (STM32WB07)
708 BLUEGLOB->BYTE4 |= 1 << 7;
709 BLUEGLOB->BYTE22 = 0xF0;
710 BLUEGLOB->BYTE23 = 0xFF;
711 #endif
712
713 RADIO_TIMER_Context.last_anchor_mt = abs_timeout_mt & TIMER_MAX_VALUE;
714
715 radio_init_delay += RADIO_TIMER_Context.TxRxDelay.tim12_delay_mt;
716 RADIO_TIMER_Context.last_setup_time = blue_unit_conversion(radio_init_delay, RADIO_TIMER_Context.calibrationData.period1, MULT64_THR_PERIOD);
717
718 return 0;
719 }
720
721 /**
722 * @brief Return the status of the Radio timers and the last value programmed in the register.
723 * @note When Timer2 is on schedule, the time is expressed in microseconds, otherwise in absolute machine time units.
724 * @param time: pointer to value which is going to have time value.
725 * @retval 0 if no timer has been programmed.
726 * @retval 1 if Timer1 has been programmed.
727 * @retval 2 if Timer2 has been programmed.
728 * @retval 3 if Wakeup Timer has been programmed.
729 */
HAL_RADIO_TIMER_GetRadioTimerValue(uint32_t * time)730 uint8_t HAL_RADIO_TIMER_GetRadioTimerValue(uint32_t *time)
731 {
732 return TIMER_GetRadioTimerValue(time);
733 }
734
735 /**
736 * @brief Clear the last radio activity scheduled disabling the radio timers too.
737 * Furthermore, it returns if the timeout is too close with respect the current time and
738 * the radio activity might not be cleared in time.
739 * @retval 0 if the radio activity has been cleared successfully.
740 * @retval 1 if it is too late to clear the last radio activity.
741 * @retval 2 if it might not be possible to clear the last radio activity.
742 */
HAL_RADIO_TIMER_ClearRadioTimerValue(void)743 uint32_t HAL_RADIO_TIMER_ClearRadioTimerValue(void)
744 {
745 int64_t time_diff;
746 uint8_t retVal;
747
748 /* Disable Radio Timer1/2 and BLE Wakeup Timer */
749 LL_RADIO_TIMER_DisableTimer1(BLUE);
750 LL_RADIO_TIMER_DisableTimer2(BLUE);
751 LL_RADIO_TIMER_DisableBLEWakeupTimer(WAKEUP);
752 RADIO_TIMER_Context.radioTimer.active = FALSE;
753 RADIO_TIMER_Context.radioTimer.pending = FALSE;
754 RADIO_TIMER_Context.radioTimer.intTxRx_to_be_served = FALSE;
755
756 /*The rfSetup is different if Timer1 or Wakeup timer is programmed*/
757 ATOMIC_SECTION_BEGIN();
758 time_diff = RADIO_TIMER_Context.radioTimer.expiryTime \
759 - HAL_RADIO_TIMER_GetCurrentSysTime() \
760 - RADIO_TIMER_Context.last_setup_time;
761 ATOMIC_SECTION_END();
762
763 #if defined (STM32WB06) || defined (STM32WB07)
764 /* Check if the routine is executed in the Tx/Rx interrupt handler or not */
765 if (((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) - 16) != RADIO_TX_RX_EXCEPTION_NUMBER)
766 {
767 _check_host_activity();
768 }
769 #endif
770
771 if (time_diff <= 0)
772 {
773 retVal = HAL_RADIO_TIMER_LATE;
774 }
775 else if (time_diff < CLEAR_MIN_THR)
776 {
777 retVal = HAL_RADIO_TIMER_CRITICAL;
778 }
779 else
780 {
781 retVal = HAL_RADIO_TIMER_SUCCESS;
782 }
783
784 return retVal;
785 }
786
787 /**
788 * @brief Program the radio timer (a.k.a Timer1) as close as possible.
789 * The current time is sampled and increased by two.
790 * It means that the timer is going to trigger in a timer interval that goes
791 * from one to two machine time units.
792 */
HAL_RADIO_TIMER_SetRadioCloseTimeout(void)793 void HAL_RADIO_TIMER_SetRadioCloseTimeout(void)
794 {
795 uint32_t current_time;
796
797 ATOMIC_SECTION_BEGIN();
798 current_time = LL_RADIO_TIMER_GetAbsoluteTime(WAKEUP);
799 LL_RADIO_TIMER_SetTimeout(BLUE, ((current_time + 2) & TIMER_MAX_VALUE));
800 LL_RADIO_TIMER_EnableTimer1(BLUE);
801 ATOMIC_SECTION_END();
802 }
803
804 /**
805 * @brief Radio activity finished.
806 * @retval None
807 */
HAL_RADIO_TIMER_RadioTimerIsr(void)808 void HAL_RADIO_TIMER_RadioTimerIsr(void)
809 {
810 #if defined (STM32WB06) || defined (STM32WB07)
811 if (!(LL_RADIO_TIMER_IsEnabledTimer1(BLUE) || LL_RADIO_TIMER_IsEnabledTimer2(BLUE)))
812 {
813 _check_host_activity();
814 }
815 #endif
816 }
817
818 /**
819 * @brief Timer State machine semaphore to signal the radio activity finished.
820 * @retval None
821 */
HAL_RADIO_TIMER_EndOfRadioActivityIsr(void)822 void HAL_RADIO_TIMER_EndOfRadioActivityIsr(void)
823 {
824 RADIO_TIMER_Context.radioTimer.intTxRx_to_be_served = FALSE;
825 }
826
827 /* ----------------------- Radio Timer time unit APIs ------------------------*/
828
829 /**
830 * @brief Translate time in microseconds into sys time units.
831 * @param time: Microseconds to be converted in STU
832 * @return STU value
833 */
HAL_RADIO_TIMER_UsToSystime(uint32_t time)834 uint32_t HAL_RADIO_TIMER_UsToSystime(uint32_t time)
835 {
836 return _us_to_systime(time);
837 }
838
839 /**
840 * @brief Returns the STU corresponding to the MTU passed as parameter.
841 * @param time: MTU amount to be converted in STU
842 * @warning This function is not re-entrant since it updates the context variable
843 * storing the system time. It should be called only in
844 * user context and not in interrupt context.
845 * @return STU value
846 */
HAL_RADIO_TIMER_MachineTimeToSysTime(uint32_t time)847 uint32_t HAL_RADIO_TIMER_MachineTimeToSysTime(uint32_t time)
848 {
849 return blue_unit_conversion(time, RADIO_TIMER_Context.calibrationData.period1, MULT64_THR_PERIOD);
850 }
851
852 /**
853 * @brief This function returns the current reference time expressed in system time units.
854 * The returned value can be used as absolute time parameter where needed in the other
855 * HAL_RADIO_TIMER* APIs
856 * @return absolute current time expressed in system time units.
857 */
HAL_RADIO_TIMER_GetCurrentSysTime(void)858 uint64_t HAL_RADIO_TIMER_GetCurrentSysTime(void)
859 {
860 uint32_t current_machine_time;
861 return _get_system_time_and_machine(&RADIO_TIMER_Context, ¤t_machine_time);
862 }
863
864 /**
865 * @brief This function returns the sum of an absolute time and a signed relative time.
866 * @param sysTime: Absolute time expressed in internal time units.
867 * @param msTime: Signed relative time expressed in ms.
868 * @return 64bit resulting absolute time expressed in internal time units.
869 */
HAL_RADIO_TIMER_AddSysTimeMs(uint64_t sysTime,int32_t msTime)870 uint64_t HAL_RADIO_TIMER_AddSysTimeMs(uint64_t sysTime, int32_t msTime)
871 {
872 int32_t sysTick = (msTime * TIMER_SYSTICK_PER_10MS) / 10;
873 return (sysTime + sysTick);
874 }
875
876 /**
877 * @brief Returns the difference between two absolute times: sysTime1-sysTime2.
878 * The resulting value is expressed in ms.
879 * @param sysTime2: Absolute time expressed in internal time units.
880 * @param sysTime1: Absolute time expressed in internal time units.
881 * @return resulting signed relative time expressed in ms.
882 */
HAL_RADIO_TIMER_DiffSysTimeMs(uint64_t sysTime1,uint64_t sysTime2)883 int64_t HAL_RADIO_TIMER_DiffSysTimeMs(uint64_t sysTime1, uint64_t sysTime2)
884 {
885 return ((sysTime1 - sysTime2) * 10) >> 12;
886 }
887
888 /* -------------------------- Virtual timer APIs ---------------------------- */
889
890 /**
891 * @brief Starts a one-shot virtual timer for the given relative timeout value expressed in ms
892 * @param timerHandle: The virtual timer
893 * @param msRelTimeout: The relative time, from current time, expressed in ms
894 * @retval 0 if the timerHandle is valid.
895 * @retval 1 if the timerHandle is not valid. It is already started.
896 */
HAL_RADIO_TIMER_StartVirtualTimer(VTIMER_HandleType * timerHandle,uint32_t msRelTimeout)897 uint32_t HAL_RADIO_TIMER_StartVirtualTimer(VTIMER_HandleType *timerHandle, uint32_t msRelTimeout)
898 {
899 uint64_t temp = msRelTimeout;
900 uint8_t retVal;
901 retVal = _start_timer(timerHandle, HAL_RADIO_TIMER_GetCurrentSysTime() + (temp * TIMER_SYSTICK_PER_10MS) / 10);
902 _virtualTimeBaseEnable(ENABLE);
903
904 return retVal;
905 }
906
907 /**
908 * @brief Starts a one-shot virtual timer for the given absolute timeout value
909 * expressed in internal system time units.
910 * @param timerHandle: The virtual timer
911 * @param time: Absolute time expressed in STU.
912 * @retval 0 if the timerHandle is valid.
913 * @retval 1 if the timerHandle is not valid. It is already started.
914 */
HAL_RADIO_TIMER_StartVirtualTimerSysTime(VTIMER_HandleType * timerHandle,uint64_t time)915 uint32_t HAL_RADIO_TIMER_StartVirtualTimerSysTime(VTIMER_HandleType *timerHandle, uint64_t time)
916 {
917 uint8_t retVal;
918 retVal = _start_timer(timerHandle, time);
919 _virtualTimeBaseEnable(ENABLE);
920
921 return retVal;
922 }
923
924 /**
925 * @brief Stops the one-shot virtual timer specified if found
926 * @param timerHandle: The virtual timer
927 * @retval None
928 */
HAL_RADIO_TIMER_StopVirtualTimer(VTIMER_HandleType * timerHandle)929 void HAL_RADIO_TIMER_StopVirtualTimer(VTIMER_HandleType *timerHandle)
930 {
931 VTIMER_HandleType *rootNode = _remove_timer_in_queue(RADIO_TIMER_Context.rootNode, timerHandle);
932 uint8_t expired = 0;
933 timerHandle->active = FALSE;
934 if (RADIO_TIMER_Context.rootNode != rootNode)
935 {
936 RADIO_TIMER_Context.rootNode = _update_user_timeout(rootNode, &expired);
937 if (expired)
938 {
939 /* A new root timer is already expired, mimic timer expire */
940 INCREMENT_EXPIRE_COUNT;
941 }
942 }
943 else
944 {
945 RADIO_TIMER_Context.rootNode = rootNode;
946 }
947 }
948
949 /**
950 * @brief Returns the absolute expiry time of a running virtual timer expressed in internal system time units.
951 * @param timerHandle: The virtual timer
952 * @retval sysTime: Absolute time expressed in internal system time units.
953 */
HAL_RADIO_TIMER_ExpiryTime(VTIMER_HandleType * timerHandle)954 uint64_t HAL_RADIO_TIMER_ExpiryTime(VTIMER_HandleType *timerHandle)
955 {
956 return timerHandle->expiryTime;
957 }
958
959 #if defined (STM32WB06) || defined (STM32WB07)
960 /**
961 * @brief If the wakeup timer triggers for a host wakeup, a pending radio activity is programmed.
962 * If the wakeup timer triggers for a radio activity, a pending virtual timer callback is executed.
963 * @retval None
964 */
HAL_RADIO_TIMER_WakeUpCallback(void)965 void HAL_RADIO_TIMER_WakeUpCallback(void)
966 {
967 volatile uint32_t status = 0;
968 uint8_t expired;
969 UNUSED(status);
970 _check_radio_activity(&RADIO_TIMER_Context.radioTimer, &expired);
971 if (RADIO_TIMER_Context.hostIsRadioPending)
972 {
973 RADIO_TIMER_Context.hostIsRadioPending = 0;
974 HAL_RADIO_TIMER_TimeoutCallback();
975 }
976
977 LL_RADIO_TIMER_ClearFlag_BLEWakeup(WAKEUP);
978 status = LL_RADIO_TIMER_IsActiveFlag_BLEWakeup(WAKEUP);
979 }
980 #endif
981
982 /**
983 * @brief Virtual timer Timeout Callback. It signals that a host timeout occurred.
984 * @retval None
985 */
HAL_RADIO_TIMER_TimeoutCallback(void)986 void HAL_RADIO_TIMER_TimeoutCallback(void)
987 {
988 volatile uint32_t status = 0;
989 UNUSED(status);
990 #if defined (STM32WB06) || defined (STM32WB07)
991 RADIO_TIMER_Context.hostIsRadioPending = 0;
992 #endif
993
994 /* Disable host timer */
995 LL_RADIO_TIMER_DisableCPUWakeupTimer(WAKEUP);
996 INCREMENT_EXPIRE_COUNT_ISR;
997 /* Clear the interrupt */
998 LL_RADIO_TIMER_ClearFlag_CPUWakeup(WAKEUP);
999 status = LL_RADIO_TIMER_IsActiveFlag_CPUWakeup(WAKEUP);
1000 }
1001
1002 /**
1003 * @brief Returns the number of timers in the queue.
1004 * @return number of timers in the queue.
1005 */
HAL_RADIO_TIMER_GetPendingTimers(void)1006 uint32_t HAL_RADIO_TIMER_GetPendingTimers(void)
1007 {
1008 VTIMER_HandleType *curr = RADIO_TIMER_Context.rootNode;
1009 uint32_t counter = 0;
1010 while (curr != NULL)
1011 {
1012 counter++;
1013 curr = curr->next;
1014 }
1015 return counter;
1016 }
1017
1018 /**
1019 * @brief Returns the 64-bit system time, referred to the 32-bit system time parameter.
1020 * The returned system time refers to a time between last calibration and last
1021 * calibration + 10485 seconds.
1022 * @param sys_time: system time
1023 * @warning The system time cannot be more then 10485 seconds (174 min) after the last calibration time.
1024 * @return STU value
1025 */
HAL_RADIO_TIMER_GetSysTime64(uint32_t sys_time)1026 uint64_t HAL_RADIO_TIMER_GetSysTime64(uint32_t sys_time)
1027 {
1028 uint64_t time;
1029
1030 time = RADIO_TIMER_Context.calibrationData.last_calibration_time + (uint32_t)(sys_time - (uint32_t)RADIO_TIMER_Context.calibrationData.last_calibration_time);
1031
1032 return time;
1033 }
1034
1035 /**
1036 * @brief Returns the next 64-bit system time in the future, referred to the 32-bit system time parameter.
1037 * Compared to HAL_RADIO_TIMER_GetSysTime64() this function makes sure that the returned
1038 * time is always in the future, but execution time of the function is longer.
1039 * @param sys_time: system time in the future (no more than 10485 s = 174 min in the future)
1040 * @return STU value
1041 */
HAL_RADIO_TIMER_GetFutureSysTime64(uint32_t sys_time)1042 uint64_t HAL_RADIO_TIMER_GetFutureSysTime64(uint32_t sys_time)
1043 {
1044 uint64_t current_time;
1045 uint32_t sysTime_ms32b;
1046
1047 current_time = HAL_RADIO_TIMER_GetCurrentSysTime();
1048 sysTime_ms32b = current_time >> 32; /* Most significant 32 bits of sysTime64 */
1049
1050 if (sys_time < (uint32_t)current_time)
1051 {
1052 /* Need to get most signicant 32 bits of current time increased by one */
1053 sysTime_ms32b++;
1054 }
1055
1056 return sys_time | (((uint64_t)sysTime_ms32b) << 32);
1057 }
1058
1059 /**
1060 * @}
1061 */
1062
1063 /* Private functions ---------------------------------------------------------*/
1064 /** @defgroup RADIO_TIMER_Private_Functions RADIO TIMER Private Functions
1065 * @{
1066 */
1067
_calibrationProcedure(void)1068 static void _calibrationProcedure(void)
1069 {
1070 /* Make sure any pending calibration is over */
1071 while (LL_RADIO_TIMER_IsActiveFlag_LSICalibrationEnded(RADIO_CTRL) == 0);
1072
1073 /* Set SLOW_COUNT to 23, that is calibrate over 24 clock periods, this number
1074 cannot be changed without changing all the integer maths function in the
1075 file */
1076 LL_RADIO_TIMER_SetLSIWindowCalibrationLength(RADIO_CTRL, 23);
1077
1078 /* Start a calibration and take the correct freq */
1079 _timer_calibrate(&RADIO_TIMER_Context.calibrationData);
1080 /* For first time set last to current */
1081 RADIO_TIMER_Context.calibrationData.last_period1 = RADIO_TIMER_Context.calibrationData.period1;
1082
1083 }
1084
_timer_start_calibration(void)1085 static void _timer_start_calibration(void)
1086 {
1087 /* Clear any pending interrupt */
1088 LL_RADIO_TIMER_ClearFlag_LSICalibrationEnded(RADIO_CTRL);
1089 /* Start calibration */
1090 LL_RADIO_TIMER_StartLSICalibration(RADIO_CTRL);
1091 }
1092
_timer_calibrate(CalibrationDataTypeDef * calibrationData)1093 static void _timer_calibrate(CalibrationDataTypeDef *calibrationData)
1094 {
1095 _timer_start_calibration();
1096 while (LL_RADIO_TIMER_IsActiveFlag_LSICalibrationEnded(RADIO_CTRL) == 0);
1097 _get_calibration_data(calibrationData);
1098 }
1099
_get_calibration_data(CalibrationDataTypeDef * calibrationData)1100 static void _get_calibration_data(CalibrationDataTypeDef *calibrationData)
1101 {
1102 int32_t period;
1103 int32_t freq;
1104 int32_t mul1;
1105 int32_t b1;
1106 int32_t b2;
1107 int32_t mult;
1108 int32_t a1;
1109 int32_t a2;
1110
1111 period = LL_RADIO_TIMER_GetLSIPeriod(RADIO_CTRL);
1112 while (period != LL_RADIO_TIMER_GetLSIPeriod(RADIO_CTRL) || period == 0)
1113 {
1114 period = LL_RADIO_TIMER_GetLSIPeriod(RADIO_CTRL);
1115 }
1116
1117 mul1 = 0x8BCF6 ;
1118 b1 = period >> 8 ;
1119 b2 = period & 0xff ;
1120 calibrationData->period1 = ((mul1 * b1) + ((b2 * mul1) >> 8) + 16) >> 5;
1121 calibrationData->period = period;
1122
1123 mult = 0x753 ;
1124 freq = LL_RADIO_TIMER_GetLSIFrequency(RADIO_CTRL);
1125
1126 while (freq != LL_RADIO_TIMER_GetLSIFrequency(RADIO_CTRL) || freq == 0)
1127 {
1128 freq = LL_RADIO_TIMER_GetLSIFrequency(RADIO_CTRL);
1129 }
1130 a1 = freq >> 6 ;
1131 a2 = a1 * mult ;
1132 calibrationData->freq1 = (a2 + 128) >> 8 ;
1133 calibrationData->freq = freq;
1134 }
1135
_configureTxRxDelay(RADIO_TIMER_ContextTypeDef * context,uint8_t calculate_st)1136 static void _configureTxRxDelay(RADIO_TIMER_ContextTypeDef *context, uint8_t calculate_st)
1137 {
1138 uint8_t tx_delay_start;
1139
1140 tx_delay_start = (BLUEGLOB->TXDELAYSTART * 125 / 1000) + 1;
1141
1142 BLUEGLOB->WAKEUPINITDELAY = blue_unit_conversion(WAKEUP_INIT_DELAY, context->calibrationData.freq1, MULT64_THR_FREQ);
1143 context->TxRxDelay.tim12_delay_mt = _us_to_machinetime(BLUEGLOB->TIMER12INITDELAYCAL);
1144 context->TxRxDelay.tx_cal_delay = _us_to_machinetime(BLUEGLOB->TRANSMITCALDELAYCHK + tx_delay_start);
1145 context->TxRxDelay.tx_no_cal_delay = _us_to_machinetime(BLUEGLOB->TRANSMITNOCALDELAYCHK + tx_delay_start);
1146 context->TxRxDelay.rx_cal_delay = _us_to_machinetime(BLUEGLOB->RECEIVECALDELAYCHK);
1147 context->TxRxDelay.rx_no_cal_delay = _us_to_machinetime(BLUEGLOB->RECEIVENOCALDELAYCHK);
1148
1149 if (calculate_st)
1150 {
1151 context->TxRxDelay.tx_cal_delay_st = _us_to_systime(BLUEGLOB->TRANSMITCALDELAYCHK + tx_delay_start) + WAKEUP_INIT_DELAY;
1152 }
1153
1154 }
1155
_us_to_systime(uint32_t time)1156 static uint32_t _us_to_systime(uint32_t time)
1157 {
1158 uint32_t t1, t2;
1159 t1 = time * 0x68;
1160 t2 = time * 0xDB;
1161 return (t1 >> 8) + (t2 >> 16);
1162 }
1163
_us_to_machinetime(uint32_t time)1164 static uint32_t _us_to_machinetime(uint32_t time)
1165 {
1166 uint64_t tmp = (uint64_t)RADIO_TIMER_Context.calibrationData.freq * (uint64_t)time * (uint64_t)3U;
1167 uint32_t time_mt = ((tmp + (1 << 26)) >> 27) & TIMER_MAX_VALUE;
1168
1169 return time_mt;
1170 }
1171
_update_xtal_startup_time(uint16_t hs_startup_time,int32_t freq1)1172 static void _update_xtal_startup_time(uint16_t hs_startup_time, int32_t freq1)
1173 {
1174 int32_t time1;
1175
1176 time1 = blue_unit_conversion(hs_startup_time, freq1, MULT64_THR_FREQ);
1177 if (time1 >= 4096)
1178 {
1179 time1 = 4095;
1180 }
1181 if (time1 < 16)
1182 {
1183 time1 = 16;
1184 }
1185 LL_RADIO_TIMER_SetWakeupOffset(WAKEUP, (time1 >> 4));
1186 }
1187
_calibration_callback(void * handle)1188 static void _calibration_callback(void *handle)
1189 {
1190 if (RADIO_TIMER_Context.calibrationSettings.periodicCalibration)
1191 {
1192 _timer_start_calibration();
1193 }
1194 RADIO_TIMER_Context.calibrationSettings.calibration_in_progress = TRUE;
1195 }
1196
_start_timer(VTIMER_HandleType * timerHandle,uint64_t time)1197 static int32_t _start_timer(VTIMER_HandleType *timerHandle, uint64_t time)
1198 {
1199 uint8_t expired = 0;
1200
1201 /* The timer is already started*/
1202 if (timerHandle->active)
1203 {
1204 return 1;
1205 }
1206 timerHandle->expiryTime = time;
1207 timerHandle->active = TRUE;
1208 if (_insert_timer_in_queue(RADIO_TIMER_Context.rootNode, timerHandle) == timerHandle)
1209 {
1210 RADIO_TIMER_Context.rootNode = _update_user_timeout(timerHandle, &expired);
1211 if (expired)
1212 {
1213 /* A new root timer is already expired, mimic timer expire that is normally signaled
1214 through the interrupt handler that increase the number of expired timers*/
1215 INCREMENT_EXPIRE_COUNT;
1216 }
1217 }
1218 return expired;
1219 }
1220
_get_system_time_and_machine(RADIO_TIMER_ContextTypeDef * context,uint32_t * current_machine_time)1221 static uint64_t _get_system_time_and_machine(RADIO_TIMER_ContextTypeDef *context, uint32_t *current_machine_time)
1222 {
1223 uint32_t difftime;
1224 uint64_t new_time;
1225
1226 ATOMIC_SECTION_BEGIN();
1227 new_time = context->cumulative_time;
1228 *current_machine_time = LL_RADIO_TIMER_GetAbsoluteTime(WAKEUP);
1229 difftime = TIME_ABSDIFF(*current_machine_time, context->last_machine_time);
1230 new_time += blue_unit_conversion(difftime, context->calibrationData.period1, MULT64_THR_PERIOD);
1231 if (new_time < context->last_system_time)
1232 {
1233 new_time += blue_unit_conversion(TIMER_MAX_VALUE, context->calibrationData.period1, MULT64_THR_PERIOD);
1234 }
1235 context->last_system_time = new_time;
1236 ATOMIC_SECTION_END();
1237
1238 return new_time;
1239 }
1240
1241 /* Set timeout and skip non active timers */
_update_user_timeout(VTIMER_HandleType * rootNode,uint8_t * expired)1242 static VTIMER_HandleType *_update_user_timeout(VTIMER_HandleType *rootNode, uint8_t *expired)
1243 {
1244 VTIMER_HandleType *curr = rootNode;
1245 VTIMER_HandleType *rootOrig = rootNode;
1246 int64_t delay;
1247 *expired = 0;
1248 while (curr != NULL)
1249 {
1250 if (curr->active)
1251 {
1252 ATOMIC_SECTION_BEGIN();
1253 #if defined (STM32WB06) || defined (STM32WB07)
1254 uint8_t dummy;
1255 bool share = FALSE;
1256 _check_radio_activity(&RADIO_TIMER_Context.radioTimer, &dummy);
1257 #endif
1258 delay = curr->expiryTime - HAL_RADIO_TIMER_GetCurrentSysTime();
1259 if (delay > 0)
1260 {
1261 /* Protection against interrupt must be used to avoid that the called function will be interrupted
1262 and so the timer programming will happen after the target time is already passed
1263 leading to a timer expiring after timer wraps, instead of the expected delay */
1264 #if defined (STM32WB06) || defined (STM32WB07)
1265 /* Is the active radio operation before or too close the host timeout? */
1266 if (((RADIO_TIMER_Context.radioTimer.expiryTime) < (curr->expiryTime + RADIO_TIMER_Context.hostMargin))
1267 && RADIO_TIMER_Context.radioTimer.active)
1268 {
1269 if ((RADIO_TIMER_Context.radioTimer.expiryTime >= curr->expiryTime) && RADIO_TIMER_Context.radioTimer.active)
1270 {
1271 RADIO_TIMER_Context.hostIsRadioPending = 1;
1272 }
1273 }
1274 else
1275 {
1276 /* It's fine to program the wakeup timer for an host wakeup */
1277 share = TRUE;
1278 }
1279 TIMER_SetRadioHostWakeupTime(delay, &share);
1280 if (share == TRUE)
1281 {
1282 RADIO_TIMER_Context.radioTimer.pending |= RADIO_TIMER_Context.radioTimer.active;
1283 RADIO_TIMER_Context.radioTimer.active = FALSE;
1284 }
1285 #else
1286 VTIMER_SetWakeupTime(delay, TRUE);
1287 #endif
1288 }
1289 else
1290 {
1291 *expired = 1;
1292 }
1293 ATOMIC_SECTION_END();
1294 break;
1295 }
1296 curr = curr->next;
1297 }
1298 if (*expired)
1299 {
1300 return rootOrig;
1301 }
1302
1303 return curr;
1304 }
1305
1306 #if defined (STM32WB05) || defined (STM32WB09)
VTIMER_SetWakeupTime(uint32_t delay,bool allow_sleep)1307 static uint32_t VTIMER_SetWakeupTime(uint32_t delay, bool allow_sleep)
1308 {
1309 uint32_t current_time;
1310 delay = blue_unit_conversion(delay, RADIO_TIMER_Context.calibrationData.freq1, MULT64_THR_FREQ) ;
1311 /* If the delay is too small round to minimum 2 tick */
1312 delay = MAX(32, delay);
1313 current_time = LL_RADIO_TIMER_GetAbsoluteTime(WAKEUP);
1314 /* 4 least significant bits are not taken into account. Then let's round the value */
1315 LL_RADIO_TIMER_SetCPUWakeupTime(WAKEUP, ((current_time + (delay + 8)) & TIMER_MAX_VALUE));
1316 LL_RADIO_TIMER_EnableWakeupTimerLowPowerMode(WAKEUP);
1317 LL_RADIO_TIMER_EnableCPUWakeupTimer(WAKEUP);
1318
1319 return current_time;
1320 }
1321 #endif
1322
_insert_timer_in_queue(VTIMER_HandleType * rootNode,VTIMER_HandleType * handle)1323 static VTIMER_HandleType *_insert_timer_in_queue(VTIMER_HandleType *rootNode, VTIMER_HandleType *handle)
1324 {
1325 VTIMER_HandleType *current = rootNode;
1326 VTIMER_HandleType *prev = NULL;
1327 VTIMER_HandleType *returnValue = rootNode;
1328
1329 while ((current != NULL) && (current->expiryTime < handle->expiryTime))
1330 {
1331 prev = current;
1332 current = current->next;
1333 }
1334
1335 handle->next = current;
1336
1337 if (prev == NULL)
1338 {
1339 /* We are the new root */
1340 returnValue = handle;
1341 }
1342 else
1343 {
1344 prev->next = handle;
1345 }
1346
1347 return returnValue;
1348 }
1349
_virtualTimeBaseEnable(FunctionalState state)1350 static void _virtualTimeBaseEnable(FunctionalState state)
1351 {
1352 if (state != DISABLE)
1353 {
1354 if (RADIO_TIMER_Context.enableTimeBase == FALSE)
1355 {
1356 _calibration_callback(&RADIO_TIMER_Context.calibrationTimer);
1357 RADIO_TIMER_Context.enableTimeBase = TRUE;
1358 }
1359 }
1360 else
1361 {
1362 HAL_RADIO_TIMER_StopVirtualTimer(&RADIO_TIMER_Context.calibrationTimer);
1363 RADIO_TIMER_Context.enableTimeBase = FALSE;
1364 }
1365 }
1366
_remove_timer_in_queue(VTIMER_HandleType * rootNode,VTIMER_HandleType * handle)1367 static VTIMER_HandleType *_remove_timer_in_queue(VTIMER_HandleType *rootNode, VTIMER_HandleType *handle)
1368 {
1369 VTIMER_HandleType *current = rootNode;
1370 VTIMER_HandleType *prev = NULL;
1371 VTIMER_HandleType *returnValue = rootNode;
1372
1373 while ((current != NULL) && (current != handle))
1374 {
1375 prev = current;
1376 current = current->next;
1377 }
1378
1379 if (current == NULL)
1380 {
1381 /* Not found */
1382 }
1383 else if (current == rootNode)
1384 {
1385 /* New root node */
1386 returnValue = current->next;
1387 }
1388 else
1389 {
1390 prev->next = current->next;
1391 }
1392
1393 return returnValue;
1394 }
1395
1396 /* Check the number of expired timer from rootNode (ordered list of timers) and return the list of expired timers */
_check_callbacks(VTIMER_HandleType * rootNode,VTIMER_HandleType ** expiredList)1397 static VTIMER_HandleType *_check_callbacks(VTIMER_HandleType *rootNode, VTIMER_HandleType **expiredList)
1398 {
1399
1400 VTIMER_HandleType *curr = rootNode;
1401 VTIMER_HandleType *prev = NULL;
1402 VTIMER_HandleType *returnValue = rootNode;
1403 *expiredList = rootNode;
1404
1405 int64_t delay;
1406 uint32_t expiredCount = 0;
1407
1408 while (curr != NULL)
1409 {
1410
1411 if (curr->active)
1412 {
1413 delay = curr->expiryTime - HAL_RADIO_TIMER_GetCurrentSysTime();
1414
1415 if (delay > 5) /*TBR*/
1416 {
1417 /* End of expired timers list*/
1418 break;
1419 }
1420 }
1421
1422 prev = curr;
1423 curr = curr->next;
1424 expiredCount++;
1425 }
1426
1427 if (expiredCount)
1428 {
1429 /* Some timers expired */
1430 prev->next = NULL;
1431 returnValue = curr;
1432 }
1433 else
1434 {
1435 /* No timer expired */
1436 *expiredList = NULL;
1437 }
1438
1439 return returnValue;
1440 }
1441
_updateCalibrationData(void)1442 static void _updateCalibrationData(void)
1443 {
1444 if (RADIO_TIMER_Context.calibrationSettings.periodicCalibration)
1445 {
1446 _get_calibration_data(&RADIO_TIMER_Context.calibrationData);
1447 _update_xtal_startup_time(RADIO_TIMER_Context.hs_startup_time, RADIO_TIMER_Context.calibrationData.freq1);
1448 _configureTxRxDelay(&RADIO_TIMER_Context, FALSE);
1449 RADIO_TIMER_Context.calibrationData.calibration_data_available = 1;
1450 }
1451 ATOMIC_SECTION_BEGIN();
1452 _update_system_time(&RADIO_TIMER_Context);
1453 ATOMIC_SECTION_END();
1454 }
1455
1456 /* This function update the system time after a calibration.
1457 * If the user calls too often this function, you could have rounding issues in the integer maths.
1458 */
_update_system_time(RADIO_TIMER_ContextTypeDef * context)1459 static void _update_system_time(RADIO_TIMER_ContextTypeDef *context)
1460 {
1461 uint32_t current_machine_time;
1462 uint32_t period;
1463
1464 current_machine_time = LL_RADIO_TIMER_GetAbsoluteTime(WAKEUP);
1465 period = context->calibrationData.last_period1;
1466 context->cumulative_time = context->calibrationData.last_calibration_time + \
1467 blue_unit_conversion(TIME_ABSDIFF(current_machine_time,
1468 context->last_machine_time),
1469 period, MULT64_THR_PERIOD);
1470
1471 if ((context->calibrationSettings.periodicCalibration == 0)
1472 && (TIME_ABSDIFF(current_machine_time,
1473 context->last_machine_time) < context->calibrationData.calibration_machine_interval))
1474 {
1475 context->cumulative_time += blue_unit_conversion(TIMER_MAX_VALUE, period, MULT64_THR_PERIOD);
1476 }
1477 context->last_machine_time = current_machine_time;
1478 context->calibrationData.last_calibration_time = context->cumulative_time;
1479 context->calibrationData.last_period1 = context->calibrationData.period1;
1480 }
1481
1482 /* Check if it is time to program the pending radio timer (large timeouts).
1483 1) The radio event is before the next calibration event. Then the timer must be programmed.
1484 2) The calibration timer expired but the calibration didn't start and the values not updated as expected.
1485 Then the next calibration event is misleading. The radio timer must be programmed.
1486 3) The radio event is after the next calibration event. Then the timer will be programmed with the latest values.
1487 The check on the next calibration event is made even though the calibration is disabled (max cal. interval)
1488 in order to avoid counter wrapping with timeouts far in the future.
1489 */
_check_radio_activity(RADIO_TIMER_RadioHandleTypeDef * timerHandle,uint8_t * expired)1490 static void _check_radio_activity(RADIO_TIMER_RadioHandleTypeDef *timerHandle, uint8_t *expired)
1491 {
1492 uint64_t nextCalibrationEvent, currentTime;
1493
1494 *expired = 0;
1495
1496 if (timerHandle->pending)
1497 {
1498 nextCalibrationEvent = RADIO_TIMER_Context.calibrationData.last_calibration_time + \
1499 RADIO_TIMER_Context.calibrationSettings.periodicCalibrationInterval;
1500
1501 ATOMIC_SECTION_BEGIN();
1502 currentTime = HAL_RADIO_TIMER_GetCurrentSysTime();
1503 if ((timerHandle->expiryTime < (nextCalibrationEvent + RADIO_ACTIVITY_MARGIN)) || \
1504 (currentTime > (nextCalibrationEvent + CALIB_SAFE_THR)))
1505 {
1506 if (timerHandle->expiryTime - TIMER1_INIT_DELAY > (currentTime + TIMER1_MARGIN))
1507 {
1508 *expired = TIMER_SetRadioTimerValue(timerHandle->expiryTime, timerHandle->event_type, timerHandle->cal_req);
1509 timerHandle->pending = FALSE; /* timer has been served. No more pending */
1510 timerHandle->active = TRUE; /* timer has been programmed and it becomes ACTIVE */
1511 timerHandle->intTxRx_to_be_served = TRUE;
1512 }
1513 else
1514 {
1515 RADIO_TIMER_Context.radioTimer.pending = FALSE;
1516 *expired = 1;
1517 }
1518 }
1519 else
1520 {
1521 #if defined (STM32WB06) || defined (STM32WB07)
1522 RADIO_TIMER_Context.waitCal = 1;
1523 #endif
1524 }
1525 ATOMIC_SECTION_END();
1526 }
1527 }
1528
1529 #if defined (STM32WB06) || defined (STM32WB07)
1530 /**
1531 * @brief Set the wakeup time to the specified delay. The delay is converted in machine time and only 28 most significant bits
1532 * are taken into account. The XTAL startup time is not taken into account for the wakeup, i.e. the system does not wait for
1533 * the XTAL startup time parameter to trigger the interrupt. If is it possible the wakeup timer is programmed too.
1534 * The delay is translated into machine time unit (MTU) and it is assigned to the wakeup register.
1535 * @param delay: Delay from the current time expressed in system time unit (STU). Range is 0 to maximum value of STU.
1536 * The maximum value STU is dependent on the speed of the low speed oscillator.
1537 * A value too small will force the timer to wrap, so it is recommended to use at least 5-10 STU.
1538 * @param share: flag that indicates if the wakeup timer has to be programmed too.
1539 * if other code commands the system to go to deep sleep.
1540 * @warning The API must be called with interrupts disabled to avoid programming the timer with a value in the past.
1541 * @return Current time in MTU.
1542 */
TIMER_SetRadioHostWakeupTime(uint32_t delay,bool * share)1543 static uint32_t TIMER_SetRadioHostWakeupTime(uint32_t delay, bool *share)
1544 {
1545 uint32_t current_time;
1546
1547 delay = blue_unit_conversion(delay, RADIO_TIMER_Context.calibrationData.freq1, MULT64_THR_FREQ) ;
1548 /* If the delay is too small round to minimum 2 tick */
1549 delay = MAX(32, delay);
1550 current_time = LL_RADIO_TIMER_GetAbsoluteTime(WAKEUP);
1551 /* 4 least significant bits are not taken into account. Then let's round the value */
1552 LL_RADIO_TIMER_SetCPUWakeupTime(WAKEUP, ((current_time + (delay + 8)) & TIMER_MAX_VALUE));
1553 LL_RADIO_TIMER_EnableWakeupTimerLowPowerMode(WAKEUP);
1554 LL_RADIO_TIMER_EnableCPUWakeupTimer(WAKEUP);
1555 if ((LL_RADIO_TIMER_IsEnabledTimer1(BLUE) || LL_RADIO_TIMER_IsEnabledTimer2(BLUE) || (*share != TRUE)))
1556 {
1557 *share = FALSE;
1558 return current_time;
1559 }
1560 _set_controller_as_host();
1561 /* 4 least significant bits are not taken into account. Then let's round the value */
1562 LL_RADIO_TIMER_SetBLEWakeupTime(WAKEUP, ((current_time + delay + 8) & 0xFFFFFFF0));
1563 LL_RADIO_TIMER_SetSleepRequestMode(WAKEUP, 0);
1564 LL_RADIO_TIMER_EnableBLEWakeupTimer(WAKEUP);
1565 LL_RADIO_TIMER_EnableWakeupTimerLowPowerMode(WAKEUP);
1566 return current_time;
1567 }
1568
_set_controller_as_host(void)1569 static void _set_controller_as_host(void)
1570 {
1571 BLUEGLOB->BYTE4 &= ~(1 << 7);
1572 BLUEGLOB->BYTE22 = 0x0;
1573 BLUEGLOB->BYTE23 = 0x0;
1574 }
1575
_check_host_activity(void)1576 static void _check_host_activity(void)
1577 {
1578 uint8_t expired;
1579 RADIO_TIMER_Context.rootNode = _update_user_timeout(RADIO_TIMER_Context.rootNode, &expired);
1580 if (expired == 1)
1581 {
1582 /* A new root timer is already expired, mimic timer expire */
1583 INCREMENT_EXPIRE_COUNT_ISR;
1584 }
1585 }
1586 #endif
1587
1588 /**
1589 * @brief Programs either the Wakeup timer or Timer1. Both timers are able to trigger the radio sequencer.
1590 * Then, they are able to start a transmission or a reception according to the configured radio ram tables.
1591 * Only the wakeup timer is able to let the device out from sleep.
1592 * The wakeup timer is programmed if the anolag part has enough time to settle after the wakeup or not.
1593 * The timeout passed as parameter represents the moment where the first bit must be transmitted or the
1594 * receive window must be opened.
1595 * Since the radio needs some time to setup, the final timeout programmed is compensated by certain
1596 * time intervals according to the kind of operation (Tx or Rx), calibration request and programmed timer.
1597 * The wakeup offset is compensated automatically by the hardware.
1598 * The CPU wakes up at timeout minus wakeup_offset. if the wakeup timer is programmed, the BLE event triggers
1599 * when the absolute time mathches the 28 MSB of the timeout. Otherwise the trigger event occurs considering all 32 bits.
1600 * @param timeout: absolute starting time of the desired operation expressed in STU.
1601 * If this absolute time is less then the current time, the timer will wrap.
1602 * The maximum value STU is dependent on the speed of the low speed oscillator.
1603 * @param event_type: 1 Tx event.
1604 0 Rx event
1605 * @param cal_req: 1 pll calibration is requested.
1606 0 pll calibration is not requested.
1607 * @warning The API must be called with interrupts disabled to avoid programming the timer with a value in the past
1608 * @retval 0 if a correct timeout has been programmed in the timeout register
1609 * @retval 1 if a correct timeout cannot be programmed
1610 */
TIMER_SetRadioTimerValue(uint32_t timeout,bool event_type,bool cal_req)1611 static uint8_t TIMER_SetRadioTimerValue(uint32_t timeout, bool event_type, bool cal_req)
1612 {
1613 uint32_t current_time, delay, radio_init_delay, device_delay, rel_timeout, rel_timeout_mt;
1614 uint8_t ret_val;
1615
1616 /*choose the 2nd init duration. Check the event_type and cal. request*/
1617 if (event_type == TX)
1618 {
1619 if (cal_req)
1620 {
1621 radio_init_delay = RADIO_TIMER_Context.TxRxDelay.tx_cal_delay;
1622 device_delay = RADIO_TIMER_Context.TxRxDelay.tx_cal_delay_st;
1623 }
1624 else
1625 {
1626 radio_init_delay = RADIO_TIMER_Context.TxRxDelay.tx_no_cal_delay;
1627 device_delay = RADIO_TIMER_Context.TxRxDelay.tx_cal_delay_st;
1628 }
1629 }
1630 else
1631 {
1632 if (cal_req)
1633 {
1634 radio_init_delay = RADIO_TIMER_Context.TxRxDelay.rx_cal_delay;
1635 device_delay = RADIO_TIMER_Context.TxRxDelay.tx_cal_delay_st;
1636 }
1637 else
1638 {
1639 radio_init_delay = RADIO_TIMER_Context.TxRxDelay.rx_no_cal_delay;
1640 device_delay = RADIO_TIMER_Context.TxRxDelay.tx_cal_delay_st;
1641 }
1642 }
1643
1644 /* At this point, it is care of the upper layers to guarantee that the timeout represents an absolute time in the future */
1645 rel_timeout = timeout - (uint32_t)_get_system_time_and_machine(&RADIO_TIMER_Context, ¤t_time);
1646
1647 rel_timeout_mt = blue_unit_conversion(rel_timeout, RADIO_TIMER_Context.calibrationData.freq1, MULT64_THR_FREQ);
1648
1649 /*Check if the timeout is beyond the wakeup time offset. Then program either the WakeUp timer or the Timer1*/
1650 if (rel_timeout > (device_delay + RADIO_TIMER_Context.hs_startup_time + MARGIN_EXT))
1651 {
1652 /*The timeout is after the wakeup_time_offset, So it is ok to program the wakeup timer*/
1653 delay = rel_timeout_mt - BLUEGLOB->WAKEUPINITDELAY - radio_init_delay;
1654 LL_RADIO_TIMER_SetBLEWakeupTime(WAKEUP, ((current_time + delay) & TIMER_MAX_VALUE));
1655 LL_RADIO_TIMER_SetSleepRequestMode(WAKEUP, 0);
1656 LL_RADIO_TIMER_DisableTimer1(BLUE);
1657 LL_RADIO_TIMER_DisableTimer2(BLUE);
1658 LL_RADIO_TIMER_EnableBLEWakeupTimer(WAKEUP);
1659 LL_RADIO_TIMER_EnableWakeupTimerLowPowerMode(WAKEUP);
1660 radio_init_delay += BLUEGLOB->WAKEUPINITDELAY;
1661 }
1662 else
1663 {
1664 delay = rel_timeout_mt - RADIO_TIMER_Context.TxRxDelay.tim12_delay_mt - radio_init_delay;
1665 LL_RADIO_TIMER_SetTimeout(BLUE, ((current_time + delay) & TIMER_MAX_VALUE));
1666 LL_RADIO_TIMER_DisableBLEWakeupTimer(WAKEUP);
1667 LL_RADIO_TIMER_EnableTimer1(BLUE);
1668 radio_init_delay += RADIO_TIMER_Context.TxRxDelay.tim12_delay_mt;
1669 }
1670
1671 RADIO_TIMER_Context.last_anchor_mt = (current_time + rel_timeout_mt) & TIMER_MAX_VALUE;
1672
1673 #if defined (STM32WB06) || defined (STM32WB07)
1674 BLUEGLOB->BYTE4 |= 1 << 7;
1675 BLUEGLOB->BYTE22 = 0xF0;
1676 BLUEGLOB->BYTE23 = 0xFF;
1677 #endif
1678
1679 /* Basic low level check with an extra margin of machine units */
1680 if ((delay + radio_init_delay) < (radio_init_delay + 5))
1681 {
1682 LL_RADIO_TIMER_DisableTimer1(BLUE);
1683 LL_RADIO_TIMER_DisableTimer2(BLUE);
1684 LL_RADIO_TIMER_DisableBLEWakeupTimer(WAKEUP);
1685 ret_val = 1;
1686 }
1687 else
1688 {
1689 RADIO_TIMER_Context.last_setup_time = blue_unit_conversion(radio_init_delay, RADIO_TIMER_Context.calibrationData.period1, MULT64_THR_PERIOD);
1690 ret_val = 0;
1691 }
1692
1693 return ret_val;
1694 }
1695
1696 /**
1697 * @brief Return the system time referred to the absolute machine time passed as parameter and the current system time.
1698 * @param time: Absolute machine time in the past
1699 * @param current_system_time: Current System time
1700 * @warning User should guarantee that call to this function are performed in a non-interruptible context.
1701 * @return STU value
1702 */
TIMER_GetPastSysTime(uint32_t time,uint64_t * current_system_time)1703 static uint64_t TIMER_GetPastSysTime(uint32_t time, uint64_t *current_system_time)
1704 {
1705 uint32_t delta_systime, current_machine_time;
1706
1707 *current_system_time = _get_system_time_and_machine(&RADIO_TIMER_Context, ¤t_machine_time);
1708 delta_systime = blue_unit_conversion(TIME_DIFF(current_machine_time, time), RADIO_TIMER_Context.calibrationData.period1, MULT64_THR_PERIOD);
1709
1710 return (*current_system_time - delta_systime);
1711 }
1712
1713 /**
1714 * @brief Return the consensus of the Virtual timer management to go in sleep.
1715 * @retval TRUE if all vtimers have been served and the calibration procedure has already finished.
1716 * @retval FALSE if the vtimer Tick is still busy.
1717 */
TIMER_SleepCheck(void)1718 static bool TIMER_SleepCheck(void)
1719 {
1720 return ((RADIO_TIMER_Context.expired_count == RADIO_TIMER_Context.served_count) && (RADIO_TIMER_Context.calibrationSettings.calibration_in_progress == FALSE));
1721 }
1722
1723 /**
1724 * @brief Return the status of the Radio timers and the last value programmed in the register.
1725 * @note When Timer2 is on schedule, the time is expressed in microseconds, otherwise in absolute machine time units.
1726 * @param time: pointer to value which is going to have time value.
1727 * @retval 0 if no timer has been programmed.
1728 * @retval 1 if Timer1 has been programmed.
1729 * @retval 2 if Timer2 has been programmed.
1730 * @retval 3 if Wakeup Timer has been programmed.
1731 */
TIMER_GetRadioTimerValue(uint32_t * time)1732 static uint8_t TIMER_GetRadioTimerValue(uint32_t *time)
1733 {
1734 if (LL_RADIO_TIMER_IsEnabledBLEWakeupTimer(WAKEUP))
1735 {
1736 *time = LL_RADIO_TIMER_GetBLEWakeupTime(WAKEUP);
1737 return WAKEUP_RADIO_TIMER_BUSY;
1738 }
1739 else if (LL_RADIO_TIMER_IsEnabledTimer1(BLUE))
1740 {
1741 *time = LL_RADIO_TIMER_GetTimeout(BLUE);
1742 return RADIO_TIMER1_BUSY;
1743 }
1744 else if (LL_RADIO_TIMER_IsEnabledTimer2(BLUE))
1745 {
1746 *time = LL_RADIO_TIMER_GetTimeout(BLUE);
1747 return RADIO_TIMER2_BUSY;
1748 }
1749 else
1750 {
1751 return 0;
1752 }
1753 }
1754
HAL_RADIO_TIMER_CpuWakeUpCallback(void)1755 __weak void HAL_RADIO_TIMER_CpuWakeUpCallback(void)
1756 {
1757 }
1758
1759
HAL_RADIO_TIMER_TxRxWakeUpCallback(void)1760 __weak void HAL_RADIO_TIMER_TxRxWakeUpCallback(void)
1761 {
1762 }
1763
HAL_RADIO_TIMER_CPU_WKUP_IRQHandler(void)1764 void HAL_RADIO_TIMER_CPU_WKUP_IRQHandler(void)
1765 {
1766 HAL_RADIO_TIMER_TimeoutCallback();
1767
1768 HAL_RADIO_TIMER_CpuWakeUpCallback();
1769 }
1770
HAL_RADIO_TIMER_TXRX_WKUP_IRQHandler(void)1771 void HAL_RADIO_TIMER_TXRX_WKUP_IRQHandler(void)
1772 {
1773 HAL_RADIO_TIMER_TxRxWakeUpCallback();
1774 #if defined (STM32WB06) || defined (STM32WB07)
1775 HAL_RADIO_TIMER_WakeUpCallback();
1776 #endif
1777 }
1778
HAL_RADIO_TIMER_ERROR_IRQHandler(void)1779 void HAL_RADIO_TIMER_ERROR_IRQHandler(void)
1780 {
1781 volatile uint32_t debug_cmd = 0;
1782 UNUSED(debug_cmd);
1783 BLUE->DEBUGCMDREG |= 1;
1784
1785 /* If the device is configured with
1786 System clock = 64 MHz and BLE clock = 16 MHz
1787 a register read is necessary to end fine
1788 the clear interrupt register operation,
1789 due the AHB down converter latency */
1790 debug_cmd = BLUE->DEBUGCMDREG;
1791 }
1792
1793 /**
1794 * @}
1795 */
1796
1797 /**
1798 * @}
1799 */
1800
1801 /**
1802 * @}
1803 */
1804