38
38
* the upper 16 bits are implemented in software.
39
39
*/
40
40
41
- static uint8_t us_ticker_inited = 0 ; // Is ticker initialized yet
41
+ static uint8_t us_ticker_inited = 0 ; // Is ticker initialized yet
42
42
43
- static volatile uint32_t ticker_cnt = 0 ; //Internal overflow count, used to extend internal 16-bit counter to (MHz * 32-bit)
44
- static volatile uint32_t ticker_int_cnt = 0 ; //Amount of overflows until user interrupt
45
- static volatile uint8_t ticker_freq_mhz = 0 ; //Frequency of timer in MHz
46
- static volatile uint32_t ticker_top_us = 0 ; //Amount of us corresponding to the top value of the timer
43
+ static volatile uint32_t ticker_cnt = 0 ; // Internal overflow count, used to extend internal 16-bit counter to (MHz * 32-bit)
44
+ static volatile uint32_t ticker_int_cnt = 0 ; // Amount of overflows until user interrupt
45
+ static volatile uint32_t ticker_freq_khz = 0 ; // Frequency of timer in MHz
46
+ static volatile uint32_t ticker_top_ms = 0 ; // Amount of ms corresponding to the top value of the timer
47
+ static volatile uint32_t soft_timer_top = 0 ; // When to wrap the software counter
47
48
48
49
void us_ticker_irq_handler_internal (void )
49
50
{
50
- /* Handle timer overflow */
51
- if (TIMER_IntGet (US_TICKER_TIMER ) & TIMER_IF_OF ) {
52
- ticker_cnt ++ ;
53
- if (ticker_cnt >= ((uint32_t )ticker_freq_mhz << 16 )) ticker_cnt = 0 ;
54
- TIMER_IntClear (US_TICKER_TIMER , TIMER_IF_OF );
55
- }
51
+ /* Handle timer overflow */
52
+ if (TIMER_IntGet (US_TICKER_TIMER ) & TIMER_IF_OF ) {
53
+ ticker_cnt ++ ;
54
+
55
+ /* Wrap ticker_cnt when we've gone over 32-bit us value */
56
+ if (ticker_cnt >= soft_timer_top ) {
57
+ ticker_cnt = 0 ;
58
+ }
59
+
60
+ TIMER_IntClear (US_TICKER_TIMER , TIMER_IF_OF );
61
+ }
56
62
57
63
/* Check for user interrupt expiration */
58
64
if (TIMER_IntGet (US_TICKER_TIMER ) & TIMER_IF_CC0 ) {
@@ -78,28 +84,28 @@ void us_ticker_init(void)
78
84
/* Clear TIMER counter value */
79
85
TIMER_CounterSet (US_TICKER_TIMER , 0 );
80
86
81
- /* Get frequency of clock in MHz for scaling ticks to microseconds */
82
- ticker_freq_mhz = (REFERENCE_FREQUENCY / 1000000 );
83
- MBED_ASSERT (ticker_freq_mhz > 0 );
87
+ /* Get frequency of clock in kHz for scaling ticks to microseconds */
88
+ ticker_freq_khz = (REFERENCE_FREQUENCY / 1000 );
89
+ MBED_ASSERT (ticker_freq_khz > 0 );
84
90
85
91
/*
86
- * Calculate maximum prescaler that gives at least 1 MHz frequency, while keeping clock as an integer multiple of 1 MHz.
87
- * Example: 14 MHz => prescaler = 1 (i.e. DIV2), ticker_freq_mhz = 7;
88
- * 24 MHz => prescaler = 3 (i.e. DIV8), ticker_freq_mhz = 3;
89
- * 48 MHz => prescaler = 4 (i.e. DIV16), ticker_freq_mhz = 3;
92
+ * Calculate maximum prescaler that gives at least 1 MHz frequency, giving us 1us resolution.
90
93
* Limit prescaling to maximum prescaler value, which is 10 (DIV1024).
91
94
*/
92
95
uint32_t prescaler = 0 ;
93
- while ((ticker_freq_mhz & 1 ) == 0 && prescaler <= 10 ) {
94
- ticker_freq_mhz = ticker_freq_mhz >> 1 ;
96
+ while ((ticker_freq_khz >= 2000 ) && prescaler <= 10 ) {
97
+ ticker_freq_khz = ticker_freq_khz >> 1 ;
95
98
prescaler ++ ;
96
99
}
97
100
98
101
/* Set prescaler */
99
102
US_TICKER_TIMER -> CTRL = (US_TICKER_TIMER -> CTRL & ~_TIMER_CTRL_PRESC_MASK ) | (prescaler << _TIMER_CTRL_PRESC_SHIFT );
100
103
101
- /* calculate top value */
102
- ticker_top_us = (uint32_t ) 0x10000 / ticker_freq_mhz ;
104
+ /* calculate top value.*/
105
+ ticker_top_ms = (uint32_t ) 0x10000 / ticker_freq_khz ;
106
+
107
+ /* calculate software timer overflow */
108
+ soft_timer_top = ((0xFFFFFFFFUL / 1000UL ) / ticker_top_ms ) + 1 ;
103
109
104
110
/* Select Compare Channel parameters */
105
111
TIMER_InitCC_TypeDef timerCCInit = TIMER_INITCC_DEFAULT ;
@@ -114,7 +120,7 @@ void us_ticker_init(void)
114
120
NVIC_EnableIRQ (US_TICKER_TIMER_IRQ );
115
121
116
122
/* Set top value */
117
- TIMER_TopSet (US_TICKER_TIMER , (ticker_top_us * ticker_freq_mhz ) - 1 );
123
+ TIMER_TopSet (US_TICKER_TIMER , (ticker_top_ms * ticker_freq_khz ) - 1 );
118
124
119
125
/* Start TIMER */
120
126
TIMER_Enable (US_TICKER_TIMER , true);
@@ -123,7 +129,7 @@ void us_ticker_init(void)
123
129
uint32_t us_ticker_read ()
124
130
{
125
131
uint32_t countH_old , countH ;
126
- uint16_t countL ;
132
+ uint32_t countL ;
127
133
128
134
if (!us_ticker_inited ) {
129
135
us_ticker_init ();
@@ -145,12 +151,12 @@ uint32_t us_ticker_read()
145
151
/* Timer count value needs to be div'ed by the frequency to get to 1MHz ticks.
146
152
* For the software-extended part, the amount of us in one overflow is constant.
147
153
*/
148
- return (countL / ticker_freq_mhz ) + (countH * ticker_top_us );
154
+ return (( countL * 1000UL ) / ticker_freq_khz ) + (countH * ticker_top_ms * 1000 );
149
155
}
150
156
151
157
void us_ticker_set_interrupt (timestamp_t timestamp )
152
158
{
153
- uint64_t goal = timestamp ;
159
+ uint32_t goal = timestamp ;
154
160
uint32_t trigger ;
155
161
156
162
if ((US_TICKER_TIMER -> IEN & TIMER_IEN_CC0 ) == 0 ) {
@@ -160,52 +166,42 @@ void us_ticker_set_interrupt(timestamp_t timestamp)
160
166
TIMER_IntDisable (US_TICKER_TIMER , TIMER_IEN_CC0 );
161
167
162
168
/* convert us delta value back to timer ticks */
163
- goal -= us_ticker_read ();
169
+ trigger = us_ticker_read ();
170
+ if (trigger < goal ) {
171
+ goal -= trigger ;
172
+ } else {
173
+ goal = (0xFFFFFFFFUL - (trigger - goal ));
174
+ }
164
175
trigger = US_TICKER_TIMER -> CNT ;
165
176
166
177
/* Catch "Going back in time" */
167
- if (goal < ( 50 / ( REFERENCE_FREQUENCY / 1000000 )) ||
178
+ if (goal < 10 ||
168
179
goal >= 0xFFFFFF00UL ) {
169
180
TIMER_IntClear (US_TICKER_TIMER , TIMER_IFC_CC0 );
170
- TIMER_CompareSet (US_TICKER_TIMER , 0 , (US_TICKER_TIMER -> CNT + 3 > US_TICKER_TIMER -> TOP ? 3 : US_TICKER_TIMER -> CNT + 3 ));
181
+ TIMER_CompareSet (US_TICKER_TIMER , 0 , (US_TICKER_TIMER -> CNT + 3 >= US_TICKER_TIMER -> TOP ? 3 : US_TICKER_TIMER -> CNT + 3 ));
171
182
TIMER_IntEnable (US_TICKER_TIMER , TIMER_IEN_CC0 );
172
183
return ;
173
184
}
174
185
175
- /* Cap at 32 bit */
176
- goal &= 0xFFFFFFFFUL ;
177
- /* Convert to ticker timebase */
178
- goal *= ticker_freq_mhz ;
179
-
180
- /* Note: we should actually populate the following fields by the division and remainder
181
- * of goal / ticks_per_overflow, but since we're keeping the frequency as low
182
- * as possible, and ticks_per_overflow as close to FFFF as possible, we can
183
- * get away with ditching the division here and saving cycles.
184
- *
185
- * "exact" implementation:
186
- * ticker_int_cnt = goal / TIMER_TopGet(US_TICKER_TIMER);
187
- * ticker_int_rem = goal % TIMER_TopGet(US_TICKER_TIMER);
188
- */
189
- ticker_int_cnt = (goal >> 16 ) & 0xFFFFFFFF ;
186
+ uint32_t timer_top = TIMER_TopGet (US_TICKER_TIMER );
187
+ uint32_t top_us = 1000 * ticker_top_ms ;
190
188
191
- /* Set compare channel 0 to (current position + lower 16 bits of target).
192
- * When lower 16 bits match, run complete cycles with ticker_int_rem as trigger value
193
- * for ticker_int_cnt times. */
194
- TIMER_IntClear (US_TICKER_TIMER , TIMER_IFC_CC0 );
189
+ /* Amount of times we expect to overflow: us offset / us period of timer */
190
+ ticker_int_cnt = goal / top_us ;
195
191
196
- /* Take top of timer into account so that we don't end up missing a cycle */
197
- /* Set trigger point by adding delta to current time */
198
- if ((goal & 0xFFFF ) >= TIMER_TopGet (US_TICKER_TIMER )) {
199
- trigger += (goal & 0xFFFF ) - TIMER_TopGet (US_TICKER_TIMER );
200
- ticker_int_cnt ++ ;
201
- } else {
202
- trigger += (goal & 0xFFFF );
203
- }
192
+ /* Leftover microseconds need to be converted to timer timebase */
193
+ trigger += (((goal % top_us ) * ticker_freq_khz ) / 1000 );
204
194
205
- if (trigger >= TIMER_TopGet (US_TICKER_TIMER )) {
206
- trigger -= TIMER_TopGet (US_TICKER_TIMER );
195
+ /* Cap compare value to timer top */
196
+ if (trigger >= timer_top ) {
197
+ trigger -= timer_top ;
207
198
}
208
199
200
+ /* Set compare channel 0 to (current position + lower 16 bits of target).
201
+ * When lower 16 bits match, run complete cycles with ticker_int_rem as trigger value
202
+ * for ticker_int_cnt times. */
203
+ TIMER_IntClear (US_TICKER_TIMER , TIMER_IEN_CC0 );
204
+
209
205
TIMER_CompareSet (US_TICKER_TIMER , 0 , trigger );
210
206
211
207
TIMER_IntEnable (US_TICKER_TIMER , TIMER_IEN_CC0 );
0 commit comments