2
2
* @file us_ticker.c
3
3
*******************************************************************************
4
4
* @section License
5
- * <b>(C) Copyright 2015 Silicon Labs, http://www.silabs.com</b>
5
+ * <b>(C) Copyright 2016 Silicon Labs, http://www.silabs.com</b>
6
6
*******************************************************************************
7
7
*
8
8
* SPDX-License-Identifier: Apache-2.0
42
42
43
43
static uint8_t us_ticker_inited = 0 ; // Is ticker initialized yet
44
44
45
- static volatile uint32_t ticker_cnt = 0x2ff00 ; //Internal overflow count, used to extend internal 16-bit counter to (MHz * 32-bit)
46
- static volatile uint16_t ticker_int_rem = 0 ; //Timer match value for user interrupt
45
+ static volatile uint32_t ticker_cnt = 0 ; //Internal overflow count, used to extend internal 16-bit counter to (MHz * 32-bit)
47
46
static volatile uint32_t ticker_int_cnt = 0 ; //Amount of overflows until user interrupt
48
47
static volatile uint8_t ticker_freq_mhz = 0 ; //Frequency of timer in MHz
48
+ static volatile uint32_t ticker_top_us = 0 ; //Amount of us corresponding to the top value of the timer
49
49
50
50
void us_ticker_irq_handler_internal (void )
51
51
{
52
+ /* Handle timer overflow */
53
+ if (TIMER_IntGet (US_TICKER_TIMER ) & TIMER_IF_OF ) {
54
+ ticker_cnt ++ ;
55
+ if (ticker_cnt >= ((uint32_t )ticker_freq_mhz << 16 )) ticker_cnt = 0 ;
56
+ TIMER_IntClear (US_TICKER_TIMER , TIMER_IF_OF );
57
+ }
58
+
52
59
/* Check for user interrupt expiration */
53
60
if (TIMER_IntGet (US_TICKER_TIMER ) & TIMER_IF_CC0 ) {
54
- if (ticker_int_rem > 0 ) {
55
- TIMER_CompareSet (US_TICKER_TIMER , 0 , ticker_int_rem );
56
- ticker_int_rem = 0 ;
57
- TIMER_IntClear (US_TICKER_TIMER , TIMER_IF_CC0 );
58
- } else if (ticker_int_cnt > 0 ) {
61
+ if (ticker_int_cnt > 0 ) {
59
62
ticker_int_cnt -- ;
60
63
TIMER_IntClear (US_TICKER_TIMER , TIMER_IF_CC0 );
61
64
} else {
62
65
us_ticker_irq_handler ();
63
66
}
64
67
}
65
-
66
- /* Handle timer overflow */
67
- if (TIMER_IntGet (US_TICKER_TIMER ) & TIMER_IF_OF ) {
68
- ticker_cnt ++ ;
69
- if (ticker_cnt >= (((uint32_t )ticker_freq_mhz ) << 16 )) ticker_cnt = 0 ;
70
- TIMER_IntClear (US_TICKER_TIMER , TIMER_IF_OF );
71
- }
72
68
}
73
69
74
70
void us_ticker_init (void )
@@ -104,6 +100,9 @@ void us_ticker_init(void)
104
100
/* Set prescaler */
105
101
US_TICKER_TIMER -> CTRL = (US_TICKER_TIMER -> CTRL & ~_TIMER_CTRL_PRESC_MASK ) | (prescaler << _TIMER_CTRL_PRESC_SHIFT );
106
102
103
+ /* calculate top value */
104
+ ticker_top_us = (uint32_t ) 0x10000 / ticker_freq_mhz ;
105
+
107
106
/* Select Compare Channel parameters */
108
107
TIMER_InitCC_TypeDef timerCCInit = TIMER_INITCC_DEFAULT ;
109
108
timerCCInit .mode = timerCCModeCompare ;
@@ -117,15 +116,16 @@ void us_ticker_init(void)
117
116
NVIC_EnableIRQ (US_TICKER_TIMER_IRQ );
118
117
119
118
/* Set top value */
120
- TIMER_TopSet (US_TICKER_TIMER , 0xFFFF );
119
+ TIMER_TopSet (US_TICKER_TIMER , ( ticker_top_us * ticker_freq_mhz ) - 1 );
121
120
122
121
/* Start TIMER */
123
122
TIMER_Enable (US_TICKER_TIMER , true);
124
123
}
125
124
126
125
uint32_t us_ticker_read ()
127
126
{
128
- uint32_t volatile countH_old , countH , countL ;
127
+ uint32_t countH_old , countH ;
128
+ uint16_t countL ;
129
129
130
130
if (!us_ticker_inited ) {
131
131
us_ticker_init ();
@@ -134,65 +134,82 @@ uint32_t us_ticker_read()
134
134
/* Avoid jumping in time by reading high bits twice */
135
135
do {
136
136
countH_old = ticker_cnt ;
137
- /* If the counter overflowed while in the IRQ handler for the CC0 interrupt,
138
- * it hasn't had time to update ticker_cnt yet. Take this into account here. */
139
137
if (TIMER_IntGet (US_TICKER_TIMER ) & TIMER_IF_OF ) {
140
- countH_old += 1 ;
138
+ countH_old ++ ;
141
139
}
142
140
countL = US_TICKER_TIMER -> CNT ;
143
141
countH = ticker_cnt ;
144
- /* If the counter overflowed while in the IRQ handler for the CC0 interrupt,
145
- * it hasn't had time to update ticker_cnt yet. Take this into account here. */
146
142
if (TIMER_IntGet (US_TICKER_TIMER ) & TIMER_IF_OF ) {
147
- countH += 1 ;
143
+ countH ++ ;
148
144
}
149
145
} while (countH_old != countH );
150
146
151
- /* Merge upper (mhz * 16-bit) and lower 16-bit into 64bit */
152
- uint64_t count = (( uint64_t ) countH << 16 ) | ( uint64_t ) countL ;
153
- /* Divide by ticker_freq_mhz to get 32-bit 1MHz timestamp */
154
- return (count / ticker_freq_mhz );
147
+ /* Timer count value needs to be div'ed by the frequency to get to 1MHz ticks.
148
+ * For the software-extended part, the amount of us in one overflow is constant.
149
+ */
150
+ return (countL / ticker_freq_mhz ) + ( countH * ticker_top_us );
155
151
}
156
152
157
153
void us_ticker_set_interrupt (timestamp_t timestamp )
158
154
{
159
- int32_t delta = 0 , ts = timestamp , time = us_ticker_read ();
155
+ uint64_t goal = timestamp ;
156
+ uint32_t trigger ;
160
157
161
158
if ((US_TICKER_TIMER -> IEN & TIMER_IEN_CC0 ) == 0 ) {
162
159
//Timer was disabled, but is going to be enabled. Set sleep mode.
163
160
blockSleepMode (TIMER_LEAST_ACTIVE_SLEEPMODE );
164
161
}
165
162
TIMER_IntDisable (US_TICKER_TIMER , TIMER_IEN_CC0 );
166
163
167
- delta = ts - time ;
168
- if (delta <= ticker_freq_mhz ) {
169
- delta = ticker_freq_mhz ;
170
- timestamp = us_ticker_read () + 0x100 ;
171
- }
164
+ /* convert us delta value back to timer ticks */
165
+ goal -= us_ticker_read ();
166
+ trigger = US_TICKER_TIMER -> CNT ;
172
167
173
- /* Multiply by ticker_freq_mhz to get clock ticks */
174
- delta *= ticker_freq_mhz ;
175
- /* Overflowing this doesn't matter, since we only need the lower 16 bits */
176
- ts *= ticker_freq_mhz ;
168
+ /* Catch "Going back in time" */
169
+ if (goal < (50 / (REFERENCE_FREQUENCY / 1000000 )) ||
170
+ goal >= 0xFFFFFF00UL ) {
171
+ TIMER_IntClear (US_TICKER_TIMER , TIMER_IFC_CC0 );
172
+ TIMER_CompareSet (US_TICKER_TIMER , 0 , (US_TICKER_TIMER -> CNT + 3 > US_TICKER_TIMER -> TOP ? 3 : US_TICKER_TIMER -> CNT + 3 ));
173
+ TIMER_IntEnable (US_TICKER_TIMER , TIMER_IEN_CC0 );
174
+ return ;
175
+ }
177
176
178
- /* Split delta between timers */
179
- ticker_int_cnt = (((uint64_t )delta ) >> 16 ) & 0xFFFFFFFF ;
180
- ticker_int_rem = ts & 0xFFFF ;
177
+ /* Cap at 32 bit */
178
+ goal &= 0xFFFFFFFFUL ;
179
+ /* Convert to ticker timebase */
180
+ goal *= ticker_freq_mhz ;
181
181
182
- /* Set compare channel 0 to (current position + lower 16 bits of delta).
183
- * If lower 16 bits is a small number, we a do one compare of (current + lower 16 + 0x8000)
184
- * and then one of (current + lower 16). Else, we simply use (current + lower 16).
182
+ /* Note: we should actually populate the following fields by the division and remainder
183
+ * of goal / ticks_per_overflow, but since we're keeping the frequency as low
184
+ * as possible, and ticks_per_overflow as close to FFFF as possible, we can
185
+ * get away with ditching the division here and saving cycles.
185
186
*
186
- * When time from lower 16 bits have elapsed, run complete cycles with ticker_int_rem as
187
- * reference ticker_int_cnt times. */
188
- if ((delta & 0xFFFF ) < 0x8000 && ticker_int_cnt > 0 ) {
189
- TIMER_CompareSet (US_TICKER_TIMER , 0 , ticker_int_rem + 0x8000 );
190
- ticker_int_cnt -- ;
187
+ * "exact" implementation:
188
+ * ticker_int_cnt = goal / TIMER_TopGet(US_TICKER_TIMER);
189
+ * ticker_int_rem = goal % TIMER_TopGet(US_TICKER_TIMER);
190
+ */
191
+ ticker_int_cnt = (goal >> 16 ) & 0xFFFFFFFF ;
192
+
193
+ /* Set compare channel 0 to (current position + lower 16 bits of target).
194
+ * When lower 16 bits match, run complete cycles with ticker_int_rem as trigger value
195
+ * for ticker_int_cnt times. */
196
+ TIMER_IntClear (US_TICKER_TIMER , TIMER_IFC_CC0 );
197
+
198
+ /* Take top of timer into account so that we don't end up missing a cycle */
199
+ /* Set trigger point by adding delta to current time */
200
+ if ((goal & 0xFFFF ) >= TIMER_TopGet (US_TICKER_TIMER )) {
201
+ trigger += (goal & 0xFFFF ) - TIMER_TopGet (US_TICKER_TIMER );
202
+ ticker_int_cnt ++ ;
191
203
} else {
192
- TIMER_CompareSet (US_TICKER_TIMER , 0 , ticker_int_rem );
193
- ticker_int_rem = 0 ;
204
+ trigger += (goal & 0xFFFF );
194
205
}
195
- TIMER_IntClear (US_TICKER_TIMER , TIMER_IFC_CC0 );
206
+
207
+ if (trigger >= TIMER_TopGet (US_TICKER_TIMER )) {
208
+ trigger -= TIMER_TopGet (US_TICKER_TIMER );
209
+ }
210
+
211
+ TIMER_CompareSet (US_TICKER_TIMER , 0 , trigger );
212
+
196
213
TIMER_IntEnable (US_TICKER_TIMER , TIMER_IEN_CC0 );
197
214
}
198
215
0 commit comments