@@ -25,21 +25,9 @@ TIM_HandleTypeDef TimMasterHandle;
25
25
26
26
volatile uint32_t SlaveCounter = 0 ;
27
27
volatile uint32_t oc_int_part = 0 ;
28
- volatile uint16_t oc_rem_part = 0 ;
29
- volatile uint8_t tim_it_update ; // TIM_IT_UPDATE event flag set in timer_irq_handler()
30
- volatile uint32_t tim_it_counter = 0 ; // Time stamp to be updated by timer_irq_handler()
31
28
32
29
static int us_ticker_inited = 0 ;
33
30
34
- void set_compare (uint16_t count )
35
- {
36
- TimMasterHandle .Instance = TIM_MST ;
37
- // Set new output compare value
38
- __HAL_TIM_SET_COMPARE (& TimMasterHandle , TIM_CHANNEL_1 , count );
39
- // Enable IT
40
- __HAL_TIM_ENABLE_IT (& TimMasterHandle , TIM_IT_CC1 );
41
- }
42
-
43
31
void us_ticker_init (void )
44
32
{
45
33
if (us_ticker_inited ) return ;
@@ -52,22 +40,22 @@ void us_ticker_init(void)
52
40
53
41
uint32_t us_ticker_read ()
54
42
{
55
- uint32_t counter ;
56
-
57
- TimMasterHandle .Instance = TIM_MST ;
58
-
59
43
if (!us_ticker_inited ) us_ticker_init ();
60
44
61
- #if defined(TARGET_STM32L0 )
62
45
uint16_t cntH_old , cntH , cntL ;
63
46
do {
64
- // For some reason on L0xx series we need to read and clear the
65
- // overflow flag which give extra time to propelry handle possible
66
- // hiccup after ~60s
67
- if (__HAL_TIM_GET_FLAG (& TimMasterHandle , TIM_FLAG_CC1OF ) == SET ) {
68
- __HAL_TIM_CLEAR_FLAG (& TimMasterHandle , TIM_FLAG_CC1OF );
69
- }
70
47
cntH_old = SlaveCounter ;
48
+ /* SlaveCounter needs to be checked before AND after we read the
49
+ * current counter TIM_MST->CNT, in case it wraps around.
50
+ * there are 2 possible cases of wrap around
51
+ * 1) in case this function is interrupted by timer_irq_handler and
52
+ * the SlaveCounter is updated. In that case we will loop again.
53
+ * 2) in case this function is called from interrupt context during
54
+ * wrap-around condtion. That would prevent/delay the timer_irq_handler
55
+ * from being called so we need to locally check the FLAG_UPDATE and
56
+ * update the cntH accordingly. The SlaveCounter variable itself will
57
+ * be updated in the interrupt handler just after ...
58
+ */
71
59
if (__HAL_TIM_GET_FLAG (& TimMasterHandle , TIM_FLAG_UPDATE ) == SET ) {
72
60
cntH_old += 1 ;
73
61
}
@@ -79,35 +67,121 @@ uint32_t us_ticker_read()
79
67
} while (cntH_old != cntH );
80
68
// Glue the upper and lower part together to get a 32 bit timer
81
69
return (uint32_t )(cntH << 16 | cntL );
82
- #else
83
- tim_it_update = 0 ; // Clear TIM_IT_UPDATE event flag
84
- counter = TIM_MST -> CNT + (uint32_t )(SlaveCounter << 16 ); // Calculate new time stamp
85
- if (tim_it_update == 1 ) {
86
- return tim_it_counter ; // In case of TIM_IT_UPDATE return the time stamp that was calculated in timer_irq_handler()
87
- }
88
- else {
89
- return counter ; // Otherwise return the time stamp calculated here
90
- }
91
- #endif
92
70
}
93
71
94
72
void us_ticker_set_interrupt (timestamp_t timestamp )
95
73
{
96
- int delta = (int )((uint32_t )timestamp - us_ticker_read ());
74
+ // NOTE: This function must be called with interrupts disabled to keep our
75
+ // timer interrupt setup atomic
76
+ TimMasterHandle .Instance = TIM_MST ;
77
+ // Set new output compare value
78
+ __HAL_TIM_SET_COMPARE (& TimMasterHandle , TIM_CHANNEL_1 , timestamp & 0xFFFF );
79
+ // Ensure the compare event starts clear
80
+ __HAL_TIM_CLEAR_FLAG (& TimMasterHandle , TIM_FLAG_CC1 );
81
+ // Enable IT
82
+ __HAL_TIM_ENABLE_IT (& TimMasterHandle , TIM_IT_CC1 );
97
83
98
- uint16_t cval = TIM_MST -> CNT ;
84
+ int current_time = us_ticker_read ();
85
+ int delta = (int )(timestamp - current_time );
99
86
100
87
if (delta <= 0 ) { // This event was in the past
101
- us_ticker_irq_handler ();
88
+ /* Immediately set the compare event to cause the event to be handled in
89
+ * the next interrupt context. This prevents calling interrupt handlers
90
+ * recursively as us_ticker_set_interrupt might be called again from the
91
+ * application handler
92
+ */
93
+ oc_int_part = 0 ;
94
+ HAL_TIM_GenerateEvent (& TimMasterHandle , TIM_EVENTSOURCE_CC1 );
102
95
} else {
103
- oc_int_part = (uint32_t )(delta >> 16 );
104
- oc_rem_part = (uint16_t )(delta & 0xFFFF );
105
- if (oc_rem_part <= (0xFFFF - cval )) {
106
- set_compare (cval + oc_rem_part );
107
- oc_rem_part = 0 ;
108
- } else {
109
- set_compare (0xFFFF );
110
- oc_rem_part = oc_rem_part - (0xFFFF - cval );
96
+ /* Set the number of timer wrap-around loops before the actual timestamp
97
+ * is reached. If the calculated delta time is more than halfway to the
98
+ * next compare event, check to see if a compare event has already been
99
+ * set, and if so, add one to the wrap-around count. This is done to
100
+ * ensure the correct wrap count is used in the corner cases where the
101
+ * 16 bit counter passes the compare value during the process of
102
+ * configuring this interrupt.
103
+ *
104
+ * Assumption: The time to execute this function is less than 32ms
105
+ * (otherwise incorrect behaviour could result)
106
+ *
107
+ * Consider the following corner cases:
108
+ * 1) timestamp is 1 us in the future:
109
+ * oc_int_part = 0 initially
110
+ * oc_int_part left at 0 because ((delta - 1) & 0xFFFF) < 0x8000
111
+ * Compare event should happen in 1 us and us_ticker_irq_handler()
112
+ * called
113
+ * 2) timestamp is 0x8000 us in the future:
114
+ * oc_int_part = 0 initially
115
+ * oc_int_part left at 0 because ((delta - 1) & 0xFFFF) < 0x8000
116
+ * There should be no possibility of the CC1 flag being set yet
117
+ * (see assumption above). When the compare event does occur in
118
+ * 32768 us, us_ticker_irq_handler() will be called
119
+ * 3) timestamp is 0x8001 us in the future:
120
+ * oc_int_part = 0 initially
121
+ * ((delta - 1) & 0xFFFF) >= 0x8000 but there should be no
122
+ * possibility of the CC1 flag being set yet (see assumption above),
123
+ * so oc_int_part will be left at 0, and when the compare event
124
+ * does occur in 32769 us, us_ticker_irq_handler() will be called
125
+ * 4) timestamp is 0x10000 us in the future:
126
+ * oc_int_part = 0 initially
127
+ * ((delta - 1) & 0xFFFF) >= 0x8000
128
+ * There are two subcases:
129
+ * a) The timer counter has not incremented past the compare
130
+ * value while setting up the interrupt. In this case, the
131
+ * CC1 flag will not be set, so oc_int_part will be
132
+ * left at 0, and when the compare event occurs in 65536 us,
133
+ * us_ticker_irq_handler() will be called
134
+ * b) The timer counter has JUST incremented past the compare
135
+ * value. In this case, the CC1 flag will be set, so
136
+ * oc_int_part will be incremented to 1, and the interrupt will
137
+ * occur immediately after this function returns, where
138
+ * oc_int_part will decrement to 0 without calling
139
+ * us_ticker_irq_handler(). Then about 65536 us later, the
140
+ * compare event will occur again, and us_ticker_irq_handler()
141
+ * will be called
142
+ * 5) timestamp is 0x10001 us in the future:
143
+ * oc_int_part = 1 initially
144
+ * oc_int_part left at 1 because ((delta - 1) & 0xFFFF) < 0x8000
145
+ * CC1 flag will not be set (see assumption above). In 1 us the
146
+ * compare event will cause an interrupt, where oc_int_part will be
147
+ * decremented to 0 without calling us_ticker_irq_handler(). Then
148
+ * about 65536 us later, the compare event will occur again, and
149
+ * us_ticker_irq_handler() will be called
150
+ * 6) timestamp is 0x18000 us in the future:
151
+ * oc_int_part = 1 initially
152
+ * oc_int_part left at 1 because ((delta - 1) & 0xFFFF) < 0x8000
153
+ * There should be no possibility of the CC1 flag being set yet
154
+ * (see assumption above). When the compare event does occur in
155
+ * 32768 us, oc_int_part will be decremented to 0 without calling
156
+ * us_ticker_irq_handler(). Then about 65536 us later, the
157
+ * compare event will occur again, and us_ticker_irq_handler() will
158
+ * be called
159
+ * 7) timestamp is 0x18001 us in the future:
160
+ * oc_int_part = 1 initially
161
+ * ((delta - 1) & 0xFFFF) >= 0x8000 but there should be no
162
+ * possibility of the CC1 flag being set yet (see assumption above),
163
+ * so oc_int_part will be left at 1, and when the compare event
164
+ * does occur in 32769 us, oc_int_part will be decremented to 0
165
+ * without calling us_ticker_irq_handler(). Then about 65536 us
166
+ * later, the compare event will occur again, and
167
+ * us_ticker_irq_handler() will be called
168
+ *
169
+ * delta - 1 is used because the timer compare event happens on the
170
+ * counter incrementing to match the compare value, and it won't occur
171
+ * immediately when the compare value is set to the current counter
172
+ * value.
173
+ */
174
+ oc_int_part = ((uint32_t )delta - 1 ) >> 16 ;
175
+ if ( ((delta - 1 ) & 0xFFFF ) >= 0x8000 &&
176
+ __HAL_TIM_GET_FLAG (& TimMasterHandle , TIM_FLAG_CC1 ) == SET ) {
177
+ ++ oc_int_part ;
178
+ /* NOTE: Instead of incrementing oc_int_part here, we could clear
179
+ * the CC1 flag, but then you'd have to wait to ensure the
180
+ * interrupt is knocked down before returning and reenabling
181
+ * interrupts. Since this is a rare case, it's not worth it
182
+ * to try and optimize it, and it keeps the code simpler and
183
+ * safer to just do this increment instead.
184
+ */
111
185
}
112
186
}
113
187
}
@@ -121,9 +195,7 @@ void us_ticker_disable_interrupt(void)
121
195
void us_ticker_clear_interrupt (void )
122
196
{
123
197
TimMasterHandle .Instance = TIM_MST ;
124
- if (__HAL_TIM_GET_FLAG (& TimMasterHandle , TIM_FLAG_CC1 ) == SET ) {
125
- __HAL_TIM_CLEAR_FLAG (& TimMasterHandle , TIM_FLAG_CC1 );
126
- }
198
+ __HAL_TIM_CLEAR_FLAG (& TimMasterHandle , TIM_FLAG_CC1 );
127
199
}
128
200
129
201
#endif // TIM_MST_16BIT
0 commit comments