@@ -42,23 +42,14 @@ 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
- /* S/W interrupt enable/disable
45
+ /* Timer interrupt enable/disable
46
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.
47
+ * Because Timer interrupt enable/disable (TIMER_EnableInt/TIMER_DisableInt) needs wait for lp_ticker,
48
+ * we call NVIC_DisableIRQ/NVIC_EnableIRQ instead.
51
49
*/
52
50
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
51
/* Track ticker status */
61
- static volatile uint16_t ticker_stat = NU_TICKER_UNINIT ;
52
+ static volatile uint16_t ticker_inited = 0 ;
62
53
63
54
#define TMR_CMP_MIN 2
64
55
#define TMR_CMP_MAX 0xFFFFFFu
@@ -68,14 +59,15 @@ static volatile uint16_t ticker_stat = NU_TICKER_UNINIT;
68
59
69
60
void lp_ticker_init (void )
70
61
{
71
- if (ticker_stat ) {
62
+ if (ticker_inited ) {
72
63
/* By HAL spec, ticker_init allows the ticker to keep counting and disables the
73
64
* ticker interrupt. */
74
65
lp_ticker_disable_interrupt ();
75
66
lp_ticker_clear_interrupt ();
67
+ NVIC_ClearPendingIRQ (TIMER_MODINIT .irq_n );
76
68
return ;
77
69
}
78
- ticker_stat = NU_TICKER_INIT_INTR_DIS ;
70
+ ticker_inited = 1 ;
79
71
80
72
// Reset module
81
73
SYS_ResetModule (TIMER_MODINIT .rsetidx );
@@ -106,7 +98,7 @@ void lp_ticker_init(void)
106
98
// Set vector
107
99
NVIC_SetVector (TIMER_MODINIT .irq_n , (uint32_t ) TIMER_MODINIT .var );
108
100
109
- NVIC_EnableIRQ (TIMER_MODINIT .irq_n );
101
+ NVIC_DisableIRQ (TIMER_MODINIT .irq_n );
110
102
111
103
TIMER_EnableInt (timer_base );
112
104
wait_us ((NU_US_PER_SEC / NU_TMRCLK_PER_SEC ) * 3 );
@@ -145,12 +137,12 @@ void lp_ticker_free(void)
145
137
/* Disable IP clock */
146
138
CLK_DisableModuleClock (TIMER_MODINIT .clkidx );
147
139
148
- ticker_stat = NU_TICKER_UNINIT ;
140
+ ticker_inited = 0 ;
149
141
}
150
142
151
143
timestamp_t lp_ticker_read ()
152
144
{
153
- if (ticker_stat == NU_TICKER_UNINIT ) {
145
+ if (! ticker_inited ) {
154
146
lp_ticker_init ();
155
147
}
156
148
@@ -161,9 +153,6 @@ timestamp_t lp_ticker_read()
161
153
162
154
void lp_ticker_set_interrupt (timestamp_t timestamp )
163
155
{
164
- /* We can call ticker_irq_handler now. */
165
- ticker_stat = NU_TICKER_INIT_INTR_EN ;
166
-
167
156
/* In continuous mode, counter will be reset to zero with the following sequence:
168
157
* 1. Stop counting
169
158
* 2. Configure new CMP value
@@ -181,12 +170,15 @@ void lp_ticker_set_interrupt(timestamp_t timestamp)
181
170
182
171
/* NOTE: Rely on LPTICKER_DELAY_TICKS to be non-blocking. */
183
172
timer_base -> CMP = cmp_timer ;
173
+
174
+ /* We can call ticker_irq_handler now. */
175
+ NVIC_EnableIRQ (TIMER_MODINIT .irq_n );
184
176
}
185
177
186
178
void lp_ticker_disable_interrupt (void )
187
179
{
188
180
/* We cannot call ticker_irq_handler now. */
189
- ticker_stat = NU_TICKER_INIT_INTR_DIS ;
181
+ NVIC_DisableIRQ ( TIMER_MODINIT . irq_n ) ;
190
182
}
191
183
192
184
void lp_ticker_clear_interrupt (void )
@@ -203,12 +195,12 @@ void lp_ticker_clear_interrupt(void)
203
195
204
196
void lp_ticker_fire_interrupt (void )
205
197
{
206
- /* We can call ticker_irq_handler now. */
207
- ticker_stat = NU_TICKER_INIT_INTR_EN ;
208
-
209
198
// NOTE: This event was in the past. Set the interrupt as pending, but don't process it here.
210
199
// This prevents a recursive loop under heavy load which can lead to a stack overflow.
211
200
NVIC_SetPendingIRQ (TIMER_MODINIT .irq_n );
201
+
202
+ /* We can call ticker_irq_handler now. */
203
+ NVIC_EnableIRQ (TIMER_MODINIT .irq_n );
212
204
}
213
205
214
206
const ticker_info_t * lp_ticker_get_info ()
@@ -222,15 +214,28 @@ const ticker_info_t* lp_ticker_get_info()
222
214
223
215
static void tmr1_vec (void )
224
216
{
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. */
217
+ /* NOTE: Avoid blocking in ISR due to wait for "clear interrupt flag"
218
+ *
219
+ * "clear interrupt flag" needs wait to take effect which isn't added here to avoid
220
+ * blocking in ISR.
221
+ *
222
+ * Continuing above, we will get stuck in ISR due to dummy interrupt until
223
+ * "clear interrupt flag" takes effect. To avoid it, we disable interrupt here and enable
224
+ * interrupt in lp_ticker_fire_interrupt/lp_ticker_set_interrupt. There is another risk
225
+ * that we may get stuck in a loop of ISR and lp_ticker_fire_interrupt/
226
+ * lp_ticker_set_interrupt (called by lp_ticker_irq_handler), but actually we don't:
227
+ * 1. When lp_ticker_fire_interrupt gets called, it means there is a past event and so this
228
+ * interrupt isn't dummy.
229
+ * 2. With LPTICKER_DELAY_TICKS enabled, it is lp_ticker_set_interrupt_wrapper rather than
230
+ * lp_ticker_set_interrupt that gets straight called. lp_ticker_set_interrupt_wrapper
231
+ * guarantees that lp_ticker_set_interrupt won't get re-called in LPTICKER_DELAY_TICKS ticks
232
+ * which is just enough for "clear interrupt flag" to take effect.
233
+ */
228
234
lp_ticker_clear_interrupt ();
235
+ lp_ticker_disable_interrupt ();
229
236
230
237
// NOTE: lp_ticker_set_interrupt() may get called in lp_ticker_irq_handler();
231
- if (ticker_stat == NU_TICKER_INIT_INTR_EN ) {
232
- lp_ticker_irq_handler ();
233
- }
238
+ lp_ticker_irq_handler ();
234
239
}
235
240
236
241
#endif
0 commit comments