1
1
/*
2
- wiring.c - Partial implementation of the Wiring API for the ATmega8.
2
+ wiring.c
3
3
Part of Arduino - http://www.arduino.cc/
4
4
5
5
Copyright (c) 2005-2006 David A. Mellis
18
18
Public License along with this library; if not, write to the
19
19
Free Software Foundation, Inc., 59 Temple Place, Suite 330,
20
20
Boston, MA 02111-1307 USA
21
+
22
+ Substantially rewritten by Egil Kvaleberg, 23 Sep 2019, to
23
+ use a dedicated 16 bit TSB timer for all timing purposes. This
24
+ cleans up a lot of things, and makes the code much simpler and
25
+ faster. The TCB timers have limitations making it impossible to
26
+ use the same timer for both PWM and timing, so this is no loss.
27
+
21
28
*/
22
29
23
30
#include "wiring_private.h"
24
31
25
- // the prescaler is set so that timer ticks every 64 clock cycles, and the
26
- // the overflow handler is called every 256 ticks.
27
- volatile uint16_t microseconds_per_timer_overflow ;
28
- volatile uint16_t microseconds_per_timer_tick ;
29
-
30
32
uint32_t F_CPU_CORRECTED = F_CPU ;
31
33
32
- // the whole number of milliseconds per timer overflow
33
- uint16_t millis_inc ;
34
-
35
- // the fractional number of milliseconds per timer overflow
36
- uint16_t fract_inc ;
37
- #define FRACT_MAX (1000)
38
-
39
- // whole number of microseconds per timer tick
40
-
41
- volatile uint32_t timer_overflow_count = 0 ;
42
34
volatile uint32_t timer_millis = 0 ;
43
- static uint16_t timer_fract = 0 ;
44
-
45
- inline uint16_t clockCyclesPerMicrosecondComp (uint32_t clk ){
46
- return ( (clk ) / 1000000L );
47
- }
48
-
49
- inline uint16_t clockCyclesPerMicrosecond (){
50
- return clockCyclesPerMicrosecondComp (F_CPU_CORRECTED );
51
- }
52
-
53
- inline unsigned long clockCyclesToMicroseconds (unsigned long cycles ){
54
- return ( cycles / clockCyclesPerMicrosecond () );
55
- }
56
-
57
- inline unsigned long microsecondsToClockCycles (unsigned long microseconds ){
58
- return ( microseconds * clockCyclesPerMicrosecond () );
59
- }
60
35
61
36
static volatile TCB_t * _timer =
62
37
#if defined(MILLIS_USE_TIMERB0 )
@@ -65,10 +40,8 @@ static volatile TCB_t* _timer =
65
40
& TCB1 ;
66
41
#elif defined(MILLIS_USE_TIMERB2 )
67
42
& TCB2 ;
68
- #elif defined(MILLIS_USE_TIMERB3 )
69
- & TCB3 ;
70
- #else
71
- & TCB0 ; //TCB0 fallback
43
+ #else // fallback or defined(MILLIS_USE_TIMERB3)
44
+ & TCB3 ; //TCB3 fallback
72
45
#endif
73
46
74
47
#if defined(MILLIS_USE_TIMERB0 )
@@ -77,27 +50,11 @@ static volatile TCB_t* _timer =
77
50
ISR (TCB1_INT_vect )
78
51
#elif defined(MILLIS_USE_TIMERB2 )
79
52
ISR (TCB2_INT_vect )
80
- #elif defined(MILLIS_USE_TIMERB3 )
53
+ #else // fallback or defined(MILLIS_USE_TIMERB3)
81
54
ISR (TCB3_INT_vect )
82
- #else //TCB0 fallback
83
- ISR (TCB0_INT_vect )
84
55
#endif
85
56
{
86
- // copy these to local variables so they can be stored in registers
87
- // (volatile variables must be read from memory on every access)
88
- uint32_t m = timer_millis ;
89
- uint16_t f = timer_fract ;
90
-
91
- m += millis_inc ;
92
- f += fract_inc ;
93
- if (f >= FRACT_MAX ) {
94
- f -= FRACT_MAX ;
95
- m ++ ;
96
- }
97
-
98
- timer_fract = f ;
99
- timer_millis = m ;
100
- timer_overflow_count ++ ;
57
+ timer_millis ++ ;
101
58
102
59
/* Clear flag */
103
60
_timer -> INTFLAGS = TCB_CAPT_bm ;
@@ -108,9 +65,10 @@ unsigned long millis()
108
65
unsigned long m ;
109
66
110
67
// disable interrupts while we read timer0_millis or we might get an
111
- // inconsistent value (e.g. in the middle of a write to timer0_millis )
68
+ // inconsistent value (e.g. in the middle of a write to timer_millis )
112
69
uint8_t status = SREG ;
113
70
cli ();
71
+
114
72
m = timer_millis ;
115
73
116
74
SREG = status ;
@@ -120,44 +78,27 @@ unsigned long millis()
120
78
121
79
unsigned long micros () {
122
80
uint32_t m ;
123
- uint8_t t ;
81
+ uint16_t t ;
124
82
125
83
/* Save current state and disable interrupts */
126
84
uint8_t status = SREG ;
127
85
cli ();
128
86
129
- /* Get current number of overflows and timer count */
130
- m = timer_overflow_count ;
131
- t = _timer -> CNTL ;
87
+ /* Get current number of millis (i.e. overflows) and timer count */
88
+ m = timer_millis ;
89
+ t = _timer -> CNT ;
132
90
133
91
/* If the timer overflow flag is raised, we just missed it,
134
92
increment to account for it, & read new ticks */
135
93
if (_timer -> INTFLAGS & TCB_CAPT_bm ){
136
94
m ++ ;
137
- t = _timer -> CNTL ;
95
+ t = _timer -> CNT ;
138
96
}
139
97
140
98
// Restore SREG
141
99
SREG = status ;
142
100
143
- #if F_CPU >= 24000000L && F_CPU < 32000000L
144
- // m needs to be multiplied by 682.67
145
- // and t by 2.67
146
- m = (m << 8 ) + t ;
147
- return (m << 1 ) + (m >> 1 ) + (m >> 3 ) + (m >> 4 ); // Multiply by 2.6875
148
- #elif F_CPU == 20000000L
149
- // m needs to be multiplied by 819.2
150
- // t needs to be multiplied by 3.2
151
- m = (m << 8 ) + t ;
152
- return m + (m << 1 ) + (m >> 2 ) - (m >> 4 ); // Multiply by 3.1875
153
- #elif F_CPU == 12000000L
154
- // m needs to be multiplied by 1365.33
155
- // and t by 5.33
156
- m = (m << 8 ) + t ;
157
- return m + (m << 2 ) + (m >> 2 ) + (m >> 3 ) - (m >> 4 ) + (m >> 5 ); // Multiply by 5.3437
158
- #else // 16 MHz, 8 MHz, 4 MHz, 2 MHz, 1 MHz
159
- return ((m << 8 ) + t ) * (64 / clockCyclesPerMicrosecond ());
160
- #endif
101
+ return (m * 1000L ) + (t / (TIME_TRACKING_TIMER_COUNT / 1000 ));
161
102
}
162
103
163
104
void delay (unsigned long ms )
@@ -178,6 +119,7 @@ void delay(unsigned long ms)
178
119
}
179
120
180
121
/* Delay for the given number of microseconds. Assumes a 1, 8, 12, 16, 20 or 24 MHz clock. */
122
+ // BUG: should really be implemented using _timer instead!!!!!!!!!!
181
123
void delayMicroseconds (unsigned int us )
182
124
{
183
125
// call = 4 cycles + 2 to 4 cycles to init us(2 for constant delay, 4 for variable)
@@ -387,22 +329,19 @@ void init()
387
329
388
330
/********************* TCB for system time tracking **************************/
389
331
390
- /* Calculate relevant time tracking values */
391
- microseconds_per_timer_overflow = clockCyclesToMicroseconds (TIME_TRACKING_CYCLES_PER_OVF );
392
- microseconds_per_timer_tick = microseconds_per_timer_overflow /TIME_TRACKING_TIMER_PERIOD ;
332
+ // BUG: we can compensate for F_CPU_CORRECTED by fine tuning value of TIME_TRACKING_TIMER_COUNT
393
333
394
- millis_inc = microseconds_per_timer_overflow / 1000 ;
395
- fract_inc = (( microseconds_per_timer_overflow % 1000 )) ;
334
+ /* Select vanilla 16 bit periodic interrupt mode */
335
+ _timer -> CTRLB = TCB_CNTMODE_INT_gc ;
396
336
397
- /* Default Periodic Interrupt Mode */
398
- /* TOP value for overflow every 1024 clock cycles */
399
- _timer -> CCMP = TIME_TRACKING_TIMER_PERIOD ;
337
+ /* TOP value for overflow every N clock cycles */
338
+ _timer -> CCMP = TIME_TRACKING_TIMER_COUNT - 1 ;
400
339
401
340
/* Enable TCB interrupt */
402
341
_timer -> INTCTRL |= TCB_CAPT_bm ;
403
342
404
- /* Clock selection -> same as TCA (F_CPU/64 -- 250kHz) */
405
- _timer -> CTRLA = TCB_CLKSEL_CLKTCA_gc ;
343
+ /* Clock selection is F_CPU/2 -- ~8 MHz -- which is independent of TCA */
344
+ _timer -> CTRLA = TCB_CLKSEL_CLKDIV2_gc ;
406
345
407
346
/* Enable & start */
408
347
_timer -> CTRLA |= TCB_ENABLE_bm ; /* Keep this last before enabling interrupts to ensure tracking as accurate as possible */
@@ -412,4 +351,4 @@ void init()
412
351
sei ();
413
352
}
414
353
415
- void setup_timers (void ) __attribute__((weak ));
354
+ void setup_timers (void ) __attribute__((weak ));
0 commit comments