@@ -42,7 +42,23 @@ static const struct nu_modinit_s timer1_modinit = {TIMER_1, TMR1_MODULE, CLK_CLK
42
42
43
43
#define TIMER_MODINIT timer1_modinit
44
44
45
- static int ticker_inited = 0 ;
45
+ /* S/W interrupt enable/disable
46
+ *
47
+ * Because H/W interrupt enable/disable (TIMER_EnableInt/TIMER_DisableInt) needs delay for lp_ticker,
48
+ * we introduce S/W interrupt enable/disable to avoid blocking code. With S/W interrupt enable/disable,
49
+ * H/W interrupt is always enabled after ticker_init. A S/W flag is used to tell whether or not
50
+ * ticker_irq_handler is ready to call.
51
+ */
52
+
53
+ /* Ticker uninitialized */
54
+ #define NU_TICKER_UNINIT 0
55
+ /* Ticker initialized with interrupt disabled */
56
+ #define NU_TICKER_INIT_INTR_DIS 1
57
+ /* Ticker initialized with interrupt enabled */
58
+ #define NU_TICKER_INIT_INTR_EN 2
59
+
60
+ /* Track ticker status */
61
+ static volatile uint16_t ticker_stat = NU_TICKER_UNINIT ;
46
62
47
63
#define TMR_CMP_MIN 2
48
64
#define TMR_CMP_MAX 0xFFFFFFu
@@ -52,14 +68,14 @@ static int ticker_inited = 0;
52
68
53
69
void lp_ticker_init (void )
54
70
{
55
- if (ticker_inited ) {
71
+ if (ticker_stat ) {
56
72
/* By HAL spec, ticker_init allows the ticker to keep counting and disables the
57
73
* ticker interrupt. */
58
74
lp_ticker_disable_interrupt ();
59
75
lp_ticker_clear_interrupt ();
60
76
return ;
61
77
}
62
- ticker_inited = 1 ;
78
+ ticker_stat = NU_TICKER_INIT_INTR_DIS ;
63
79
64
80
// Reset module
65
81
SYS_ResetModule (TIMER_MODINIT .rsetidx );
@@ -92,7 +108,7 @@ void lp_ticker_init(void)
92
108
93
109
NVIC_EnableIRQ (TIMER_MODINIT .irq_n );
94
110
95
- TIMER_DisableInt (timer_base );
111
+ TIMER_EnableInt (timer_base );
96
112
wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
97
113
98
114
TIMER_EnableWakeup (timer_base );
@@ -129,12 +145,12 @@ void lp_ticker_free(void)
129
145
/* Disable IP clock */
130
146
CLK_DisableModuleClock (TIMER_MODINIT .clkidx );
131
147
132
- ticker_inited = 0 ;
148
+ ticker_stat = NU_TICKER_UNINIT ;
133
149
}
134
150
135
151
timestamp_t lp_ticker_read ()
136
152
{
137
- if (! ticker_inited ) {
153
+ if (ticker_stat == NU_TICKER_UNINIT ) {
138
154
lp_ticker_init ();
139
155
}
140
156
@@ -145,6 +161,9 @@ timestamp_t lp_ticker_read()
145
161
146
162
void lp_ticker_set_interrupt (timestamp_t timestamp )
147
163
{
164
+ /* We can call ticker_irq_handler now. */
165
+ ticker_stat = NU_TICKER_INIT_INTR_EN ;
166
+
148
167
/* In continuous mode, counter will be reset to zero with the following sequence:
149
168
* 1. Stop counting
150
169
* 2. Configure new CMP value
@@ -160,27 +179,33 @@ void lp_ticker_set_interrupt(timestamp_t timestamp)
160
179
uint32_t cmp_timer = timestamp * NU_TMRCLK_PER_TICK ;
161
180
cmp_timer = NU_CLAMP (cmp_timer , TMR_CMP_MIN , TMR_CMP_MAX );
162
181
182
+ /* NOTE: Rely on LPTICKER_DELAY_TICKS to be non-blocking. */
163
183
timer_base -> CMP = cmp_timer ;
164
- wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
165
-
166
- TIMER_EnableInt (timer_base );
167
- wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
168
184
}
169
185
170
186
void lp_ticker_disable_interrupt (void )
171
187
{
172
- TIMER_DisableInt (( TIMER_T * ) NU_MODBASE ( TIMER_MODINIT . modname ));
173
- wait_us (( NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 ) ;
188
+ /* We cannot call ticker_irq_handler now. */
189
+ ticker_stat = NU_TICKER_INIT_INTR_DIS ;
174
190
}
175
191
176
192
void lp_ticker_clear_interrupt (void )
177
193
{
178
- TIMER_ClearIntFlag ((TIMER_T * ) NU_MODBASE (TIMER_MODINIT .modname ));
179
- wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
194
+ /* To avoid sync issue, we clear TIF/TWKF simultaneously rather than call separate
195
+ * driver API:
196
+ *
197
+ * TIMER_ClearIntFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
198
+ * TIMER_ClearWakeupFlag((TIMER_T *) NU_MODBASE(TIMER_MODINIT.modname));
199
+ */
200
+ TIMER_T * timer_base = (TIMER_T * ) NU_MODBASE (TIMER_MODINIT .modname );
201
+ timer_base -> INTSTS = TIMER_INTSTS_TIF_Msk | TIMER_INTSTS_TWKF_Msk ;
180
202
}
181
203
182
204
void lp_ticker_fire_interrupt (void )
183
205
{
206
+ /* We can call ticker_irq_handler now. */
207
+ ticker_stat = NU_TICKER_INIT_INTR_EN ;
208
+
184
209
// NOTE: This event was in the past. Set the interrupt as pending, but don't process it here.
185
210
// This prevents a recursive loop under heavy load which can lead to a stack overflow.
186
211
NVIC_SetPendingIRQ (TIMER_MODINIT .irq_n );
@@ -197,14 +222,15 @@ const ticker_info_t* lp_ticker_get_info()
197
222
198
223
static void tmr1_vec (void )
199
224
{
200
- TIMER_ClearIntFlag ((TIMER_T * ) NU_MODBASE (TIMER_MODINIT .modname ));
201
- wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
202
-
203
- TIMER_ClearWakeupFlag ((TIMER_T * ) NU_MODBASE (TIMER_MODINIT .modname ));
204
- wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
225
+ /* NOTE: We need to clear interrupt flag earlier to reduce possibility of dummy interrupt.
226
+ * This is because "clear interrupt flag" needs delay which isn't added here to avoid
227
+ * blocking in ISR code. */
228
+ lp_ticker_clear_interrupt ();
205
229
206
230
// NOTE: lp_ticker_set_interrupt() may get called in lp_ticker_irq_handler();
207
- lp_ticker_irq_handler ();
231
+ if (ticker_stat == NU_TICKER_INIT_INTR_EN ) {
232
+ lp_ticker_irq_handler ();
233
+ }
208
234
}
209
235
210
236
#endif
0 commit comments