37
37
38
38
#include "lp_ticker_api.h"
39
39
#include "mbed_error.h"
40
+ #include "mbed_power_mgmt.h"
41
+ #include "platform/mbed_critical.h"
42
+ #include <stdbool.h>
40
43
41
44
#if !defined(LPTICKER_DELAY_TICKS ) || (LPTICKER_DELAY_TICKS < 3 )
42
45
#warning "lpticker_delay_ticks value should be set to 3"
43
46
#endif
44
47
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
+
45
55
LPTIM_HandleTypeDef LptimHandle ;
46
56
47
57
const ticker_info_t * lp_ticker_get_info ()
@@ -58,9 +68,13 @@ const ticker_info_t *lp_ticker_get_info()
58
68
}
59
69
60
70
volatile 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;
61
76
62
77
static int LPTICKER_inited = 0 ;
63
-
64
78
static void LPTIM1_IRQHandler (void );
65
79
static void (* irq_handler )(void );
66
80
@@ -168,21 +182,32 @@ void lp_ticker_init(void)
168
182
#endif
169
183
170
184
__HAL_LPTIM_ENABLE_IT (& LptimHandle , LPTIM_IT_CMPM );
185
+ __HAL_LPTIM_ENABLE_IT (& LptimHandle , LPTIM_IT_CMPOK );
171
186
HAL_LPTIM_Counter_Start (& LptimHandle , 0xFFFF );
172
187
173
188
/* Need to write a compare value in order to get LPTIM_FLAG_CMPOK in set_interrupt */
174
189
__HAL_LPTIM_CLEAR_FLAG (& LptimHandle , LPTIM_FLAG_CMPOK );
175
190
__HAL_LPTIM_COMPARE_SET (& LptimHandle , 0 );
176
191
while (__HAL_LPTIM_GET_FLAG (& LptimHandle , LPTIM_FLAG_CMPOK ) == RESET ) {
177
192
}
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;
178
198
}
179
199
180
200
static void LPTIM1_IRQHandler (void )
181
201
{
202
+ core_util_critical_section_enter ();
203
+
182
204
LptimHandle .Instance = LPTIM1 ;
183
205
184
206
if (lp_Fired ) {
185
207
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 );
186
211
if (irq_handler ) {
187
212
irq_handler ();
188
213
}
@@ -193,17 +218,33 @@ static void LPTIM1_IRQHandler(void)
193
218
if (__HAL_LPTIM_GET_IT_SOURCE (& LptimHandle , LPTIM_IT_CMPM ) != RESET ) {
194
219
/* Clear Compare match flag */
195
220
__HAL_LPTIM_CLEAR_FLAG (& LptimHandle , LPTIM_FLAG_CMPM );
196
-
197
221
if (irq_handler ) {
198
222
irq_handler ();
199
223
}
200
224
}
201
225
}
202
226
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
+
203
243
#if defined (__HAL_LPTIM_WAKEUPTIMER_EXTI_CLEAR_FLAG )
204
244
/* EXTI lines are not configured by default */
205
245
__HAL_LPTIM_WAKEUPTIMER_EXTI_CLEAR_FLAG ();
206
246
#endif
247
+ core_util_critical_section_exit ();
207
248
}
208
249
209
250
uint32_t lp_ticker_read (void )
@@ -217,49 +258,102 @@ uint32_t lp_ticker_read(void)
217
258
return lp_time ;
218
259
}
219
260
261
+ /* This function should always be called from critical section */
220
262
void lp_ticker_set_interrupt (timestamp_t timestamp )
221
263
{
222
264
LptimHandle .Instance = LPTIM1 ;
223
265
irq_handler = (void (* )(void ))lp_ticker_irq_handler ;
266
+ core_util_critical_section_enter ();
224
267
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 );
229
271
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;
234
280
} 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
+ }
240
300
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 ();
242
312
}
243
313
244
314
void lp_ticker_fire_interrupt (void )
245
315
{
316
+ core_util_critical_section_enter ();
246
317
lp_Fired = 1 ;
318
+ /* In case we fire interrupt now, then cancel pending programing */
319
+ lp_delayed_prog = false;
247
320
irq_handler = (void (* )(void ))lp_ticker_irq_handler ;
248
321
NVIC_SetPendingIRQ (LPTIM1_IRQn );
249
322
NVIC_EnableIRQ (LPTIM1_IRQn );
323
+ core_util_critical_section_exit ();
250
324
}
251
325
252
326
void lp_ticker_disable_interrupt (void )
253
327
{
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;
254
343
NVIC_DisableIRQ (LPTIM1_IRQn );
344
+ NVIC_ClearPendingIRQ (LPTIM1_IRQn );
255
345
LptimHandle .Instance = LPTIM1 ;
346
+
347
+ core_util_critical_section_exit ();
256
348
}
257
349
258
350
void lp_ticker_clear_interrupt (void )
259
351
{
352
+ core_util_critical_section_enter ();
260
353
LptimHandle .Instance = LPTIM1 ;
261
354
__HAL_LPTIM_CLEAR_FLAG (& LptimHandle , LPTIM_FLAG_CMPM );
262
355
NVIC_ClearPendingIRQ (LPTIM1_IRQn );
356
+ core_util_critical_section_exit ();
263
357
}
264
358
265
359
void lp_ticker_free (void )
0 commit comments