3737
3838#include "lp_ticker_api.h"
3939#include "mbed_error.h"
40+ #include "mbed_power_mgmt.h"
41+ #include "platform/mbed_critical.h"
42+ #include <stdbool.h>
4043
4144#if !defined(LPTICKER_DELAY_TICKS ) || (LPTICKER_DELAY_TICKS < 3 )
4245#warning "lpticker_delay_ticks value should be set to 3"
4346#endif
4447
48+ #define LP_TIMER_WRAP (val ) (val & 0xFFFF)
49+ /* Safe guard is the number of ticks between the current tick and the next
50+ * tick we want to program an interrupt for. Programing an interrupt in
51+ * between is unreliable */
52+ #define LP_TIMER_SAFE_GUARD 5
53+
54+
4555LPTIM_HandleTypeDef LptimHandle ;
4656
4757const ticker_info_t * lp_ticker_get_info ()
@@ -58,9 +68,13 @@ const ticker_info_t *lp_ticker_get_info()
5868}
5969
6070volatile uint8_t lp_Fired = 0 ;
71+ /* Flag and stored counter to handle delayed programing at low level */
72+ volatile bool lp_delayed_prog = false;
73+ volatile bool lp_cmpok = false;
74+ volatile timestamp_t lp_delayed_counter = 0 ;
75+ volatile bool sleep_manager_locked = false;
6176
6277static int LPTICKER_inited = 0 ;
63-
6478static void LPTIM1_IRQHandler (void );
6579static void (* irq_handler )(void );
6680
@@ -168,21 +182,32 @@ void lp_ticker_init(void)
168182#endif
169183
170184 __HAL_LPTIM_ENABLE_IT (& LptimHandle , LPTIM_IT_CMPM );
185+ __HAL_LPTIM_ENABLE_IT (& LptimHandle , LPTIM_IT_CMPOK );
171186 HAL_LPTIM_Counter_Start (& LptimHandle , 0xFFFF );
172187
173188 /* Need to write a compare value in order to get LPTIM_FLAG_CMPOK in set_interrupt */
174189 __HAL_LPTIM_CLEAR_FLAG (& LptimHandle , LPTIM_FLAG_CMPOK );
175190 __HAL_LPTIM_COMPARE_SET (& LptimHandle , 0 );
176191 while (__HAL_LPTIM_GET_FLAG (& LptimHandle , LPTIM_FLAG_CMPOK ) == RESET ) {
177192 }
193+ __HAL_LPTIM_CLEAR_FLAG (& LptimHandle , LPTIM_FLAG_CMPOK );
194+
195+ /* Init is called with Interrupts disabled, so the CMPOK interrupt
196+ * will not be handler. Let's mark it is now safe to write to LP counter */
197+ lp_cmpok = true;
178198}
179199
180200static void LPTIM1_IRQHandler (void )
181201{
202+ core_util_critical_section_enter ();
203+
182204 LptimHandle .Instance = LPTIM1 ;
183205
184206 if (lp_Fired ) {
185207 lp_Fired = 0 ;
208+ /* We're already in handler and interrupt might be pending,
209+ * so clear the flag, to avoid calling irq_handler twice */
210+ __HAL_LPTIM_CLEAR_FLAG (& LptimHandle , LPTIM_FLAG_CMPM );
186211 if (irq_handler ) {
187212 irq_handler ();
188213 }
@@ -193,17 +218,33 @@ static void LPTIM1_IRQHandler(void)
193218 if (__HAL_LPTIM_GET_IT_SOURCE (& LptimHandle , LPTIM_IT_CMPM ) != RESET ) {
194219 /* Clear Compare match flag */
195220 __HAL_LPTIM_CLEAR_FLAG (& LptimHandle , LPTIM_FLAG_CMPM );
196-
197221 if (irq_handler ) {
198222 irq_handler ();
199223 }
200224 }
201225 }
202226
227+ if (__HAL_LPTIM_GET_FLAG (& LptimHandle , LPTIM_FLAG_CMPOK ) != RESET ) {
228+ if (__HAL_LPTIM_GET_IT_SOURCE (& LptimHandle , LPTIM_IT_CMPOK ) != RESET ) {
229+ __HAL_LPTIM_CLEAR_FLAG (& LptimHandle , LPTIM_FLAG_CMPOK );
230+ lp_cmpok = true;
231+ if (sleep_manager_locked ) {
232+ sleep_manager_unlock_deep_sleep ();
233+ sleep_manager_locked = false;
234+ }
235+ if (lp_delayed_prog ) {
236+ lp_ticker_set_interrupt (lp_delayed_counter );
237+ lp_delayed_prog = false;
238+ }
239+ }
240+ }
241+
242+
203243#if defined (__HAL_LPTIM_WAKEUPTIMER_EXTI_CLEAR_FLAG )
204244 /* EXTI lines are not configured by default */
205245 __HAL_LPTIM_WAKEUPTIMER_EXTI_CLEAR_FLAG ();
206246#endif
247+ core_util_critical_section_exit ();
207248}
208249
209250uint32_t lp_ticker_read (void )
@@ -217,49 +258,102 @@ uint32_t lp_ticker_read(void)
217258 return lp_time ;
218259}
219260
261+ /* This function should always be called from critical section */
220262void lp_ticker_set_interrupt (timestamp_t timestamp )
221263{
222264 LptimHandle .Instance = LPTIM1 ;
223265 irq_handler = (void (* )(void ))lp_ticker_irq_handler ;
266+ core_util_critical_section_enter ();
224267
225- /* CMPOK is set by hardware to inform application that the APB bus write operation to the LPTIM_CMP register has been successfully completed */
226- /* Any successive write before the CMPOK flag be set, will lead to unpredictable results */
227- /* LPTICKER_DELAY_TICKS value prevents OS to call this set interrupt function before CMPOK */
228- MBED_ASSERT (__HAL_LPTIM_GET_FLAG (& LptimHandle , LPTIM_FLAG_CMPOK ) == SET );
268+ /* Always store the last requested timestamp */
269+ lp_delayed_counter = timestamp ;
270+ NVIC_EnableIRQ (LPTIM1_IRQn );
229271
230- if (timestamp < lp_ticker_read ()) {
231- /* Workaround, because limitation */
232- __HAL_LPTIM_CLEAR_FLAG (& LptimHandle , LPTIM_FLAG_CMPOK );
233- __HAL_LPTIM_COMPARE_SET (& LptimHandle , ~0 );
272+ /* CMPOK is set by hardware to inform application that the APB bus write operation to the
273+ * LPTIM_CMP register has been successfully completed.
274+ * Any successive write before the CMPOK flag be set, will lead to unpredictable results
275+ * We need to prevent to set a new comparator value before CMPOK flag is set by HW */
276+ if (lp_cmpok == false) {
277+ /* if this is not safe to write, then delay the programing to the
278+ * time when CMPOK interrupt will trigger */
279+ lp_delayed_prog = true;
234280 } else {
235- __HAL_LPTIM_CLEAR_FLAG (& LptimHandle , LPTIM_FLAG_CMPOK );
236- __HAL_LPTIM_COMPARE_SET (& LptimHandle , timestamp );
237- }
238-
239- lp_ticker_clear_interrupt ();
281+ timestamp_t last_read_counter = lp_ticker_read ();
282+ lp_ticker_clear_interrupt ();
283+
284+ /* HW is not able to trig a very short term interrupt, that is
285+ * not less than few ticks away (LP_TIMER_SAFE_GUARD). So let's make sure it'
286+ * s at least current tick + LP_TIMER_SAFE_GUARD */
287+ for (uint8_t i = 0 ; i < LP_TIMER_SAFE_GUARD ; i ++ ) {
288+ if (LP_TIMER_WRAP (last_read_counter + i ) == timestamp ) {
289+ timestamp = LP_TIMER_WRAP (timestamp + LP_TIMER_SAFE_GUARD );
290+ }
291+ }
292+ /* Then check if this target timestamp is not in the past, or close to wrap-around */
293+ if ((timestamp < last_read_counter ) && (last_read_counter <= (0xFFFF - LP_TIMER_SAFE_GUARD ))) {
294+ /* Workaround, because limitation */
295+ __HAL_LPTIM_COMPARE_SET (& LptimHandle , ~0 );
296+ } else {
297+ /* It is safe to write */
298+ __HAL_LPTIM_COMPARE_SET (& LptimHandle , timestamp );
299+ }
240300
241- NVIC_EnableIRQ (LPTIM1_IRQn );
301+ /* We just programed the CMP so we'll need to wait for cmpok before
302+ * next programing */
303+ lp_cmpok = false;
304+ /* Prevent from sleeping after compare register was set as we need CMPOK
305+ * interrupt to fire (in ~3x30us cycles) before we can safely enter deep sleep mode */
306+ if (!sleep_manager_locked ) {
307+ sleep_manager_lock_deep_sleep ();
308+ sleep_manager_locked = true;
309+ }
310+ }
311+ core_util_critical_section_exit ();
242312}
243313
244314void lp_ticker_fire_interrupt (void )
245315{
316+ core_util_critical_section_enter ();
246317 lp_Fired = 1 ;
318+ /* In case we fire interrupt now, then cancel pending programing */
319+ lp_delayed_prog = false;
247320 irq_handler = (void (* )(void ))lp_ticker_irq_handler ;
248321 NVIC_SetPendingIRQ (LPTIM1_IRQn );
249322 NVIC_EnableIRQ (LPTIM1_IRQn );
323+ core_util_critical_section_exit ();
250324}
251325
252326void lp_ticker_disable_interrupt (void )
253327{
328+ core_util_critical_section_enter ();
329+
330+ if (!lp_cmpok ) {
331+ while (__HAL_LPTIM_GET_FLAG (& LptimHandle , LPTIM_FLAG_CMPOK ) == RESET ) {
332+ }
333+ __HAL_LPTIM_CLEAR_FLAG (& LptimHandle , LPTIM_FLAG_CMPOK );
334+ lp_cmpok = true;
335+ }
336+ /* now that CMPOK is set, allow deep sleep again */
337+ if (sleep_manager_locked ) {
338+ sleep_manager_unlock_deep_sleep ();
339+ sleep_manager_locked = false;
340+ }
341+ irq_handler = NULL ;
342+ lp_delayed_prog = false;
254343 NVIC_DisableIRQ (LPTIM1_IRQn );
344+ NVIC_ClearPendingIRQ (LPTIM1_IRQn );
255345 LptimHandle .Instance = LPTIM1 ;
346+
347+ core_util_critical_section_exit ();
256348}
257349
258350void lp_ticker_clear_interrupt (void )
259351{
352+ core_util_critical_section_enter ();
260353 LptimHandle .Instance = LPTIM1 ;
261354 __HAL_LPTIM_CLEAR_FLAG (& LptimHandle , LPTIM_FLAG_CMPM );
262355 NVIC_ClearPendingIRQ (LPTIM1_IRQn );
356+ core_util_critical_section_exit ();
263357}
264358
265359void lp_ticker_free (void )
0 commit comments