18
18
#include "PeripheralNames.h"
19
19
#include "clk_freqs.h"
20
20
21
- static void pit_init (void );
22
- static void lptmr_init (void );
21
+ #define PIT_TIMER PIT->CHANNEL[0]
22
+ #define PIT_TIMER_IRQ PIT0_IRQn
23
+ #define PIT_TICKER PIT->CHANNEL[1]
24
+ #define PIT_TICKER_IRQ PIT1_IRQn
25
+
26
+ static void timer_init (void );
27
+ static void ticker_init (void );
23
28
24
29
25
30
static int us_ticker_inited = 0 ;
26
- static uint32_t pit_ldval = 0 ;
31
+ static uint32_t clk_mhz ;
27
32
28
33
void us_ticker_init (void ) {
29
34
if (us_ticker_inited )
30
35
return ;
31
36
us_ticker_inited = 1 ;
32
-
33
- pit_init ();
34
- lptmr_init ();
35
- }
36
-
37
- static uint32_t pit_us_ticker_counter = 0 ;
38
-
39
- void pit0_isr (void ) {
40
- pit_us_ticker_counter ++ ;
41
- PIT -> CHANNEL [0 ].LDVAL = pit_ldval ; // 1us
42
- PIT -> CHANNEL [0 ].TFLG = 1 ;
37
+
38
+ SIM -> SCGC6 |= SIM_SCGC6_PIT_MASK ; // Clock PIT
39
+ PIT -> MCR = 0 ; // Enable PIT
40
+
41
+ clk_mhz = bus_frequency () / 1000000 ;
42
+
43
+ timer_init ();
44
+ ticker_init ();
43
45
}
44
46
45
47
/******************************************************************************
46
48
* Timer for us timing.
49
+ *
50
+ * The K20D5M does not have a prescaler on its PIT timer nor the option
51
+ * to chain timers, which is why a software timer is required to get 32-bit
52
+ * word length.
47
53
******************************************************************************/
48
- static void pit_init (void ) {
49
- SIM -> SCGC6 |= SIM_SCGC6_PIT_MASK ; // Clock PIT
50
- PIT -> MCR = 0 ; // Enable PIT
54
+ static volatile uint32_t msb_counter = 0 ;
55
+ static uint32_t timer_ldval = 0 ;
56
+
57
+ static void timer_isr (void ) {
58
+ msb_counter ++ ;
59
+ PIT_TIMER .TFLG = 1 ;
60
+ }
51
61
52
- pit_ldval = bus_frequency () / 1000000 ;
62
+ static void timer_init (void ) {
63
+ //CLZ counts the leading zeros, returning number of bits not used by clk_mhz
64
+ timer_ldval = clk_mhz << __CLZ (clk_mhz );
53
65
54
- PIT -> CHANNEL [ 0 ] .LDVAL = pit_ldval ; // 1us
55
- PIT -> CHANNEL [ 0 ] .TCTRL |= PIT_TCTRL_TIE_MASK ;
56
- PIT -> CHANNEL [ 0 ] .TCTRL |= PIT_TCTRL_TEN_MASK ; // Start timer 1
66
+ PIT_TIMER .LDVAL = timer_ldval ; // 1us
67
+ PIT_TIMER .TCTRL |= PIT_TCTRL_TIE_MASK ;
68
+ PIT_TIMER .TCTRL |= PIT_TCTRL_TEN_MASK ; // Start timer 0
57
69
58
- NVIC_SetVector (PIT0_IRQn , (uint32_t )pit0_isr );
59
- NVIC_EnableIRQ (PIT0_IRQn );
70
+ NVIC_SetVector (PIT_TIMER_IRQ , (uint32_t )timer_isr );
71
+ NVIC_EnableIRQ (PIT_TIMER_IRQ );
60
72
}
61
73
62
74
uint32_t us_ticker_read () {
63
75
if (!us_ticker_inited )
64
76
us_ticker_init ();
77
+
78
+ uint32_t retval ;
79
+ __disable_irq ();
80
+ retval = (timer_ldval - PIT_TIMER .CVAL ) / clk_mhz ; //Hardware bits
81
+ retval |= msb_counter << __CLZ (clk_mhz ); //Software bits
82
+
83
+ if (PIT_TIMER .TFLG == 1 ) { //If overflow bit is set, force it to be handled
84
+ timer_isr (); //Handle IRQ, read again to make sure software/hardware bits are synced
85
+ NVIC_ClearPendingIRQ (PIT_TIMER_IRQ );
86
+ return us_ticker_read ();
87
+ }
65
88
66
- return pit_us_ticker_counter ;
89
+ __enable_irq ();
90
+ return retval ;
67
91
}
68
92
69
93
/******************************************************************************
70
94
* Timer Event
71
95
*
72
96
* It schedules interrupts at given (32bit)us interval of time.
73
- * It is implemented used the 16bit Low Power Timer that remains powered in all
74
- * power modes .
97
+ * It is implemented using PIT channel 1, since no prescaler is available,
98
+ * some bits are implemented in software .
75
99
******************************************************************************/
76
- static void lptmr_isr (void );
77
-
78
- static void lptmr_init (void ) {
79
- /* Clock the timer */
80
- SIM -> SCGC5 |= SIM_SCGC5_LPTIMER_MASK ;
81
-
82
- /* Reset */
83
- LPTMR0 -> CSR = 0 ;
100
+ static void ticker_isr (void );
84
101
102
+ static void ticker_init (void ) {
85
103
/* Set interrupt handler */
86
- NVIC_SetVector (LPTimer_IRQn , (uint32_t )lptmr_isr );
87
- NVIC_EnableIRQ (LPTimer_IRQn );
88
-
89
- /* Clock at (1)MHz -> (1)tick/us */
90
- /* Check if the external oscillator can be divided to 1MHz */
91
- uint32_t extosc = extosc_frequency ();
92
-
93
- if (extosc != 0 ) { //If external oscillator found
94
- OSC0 -> CR |= OSC_CR_ERCLKEN_MASK ;
95
- if (extosc % 1000000u == 0 ) { //If it is a multiple if 1MHz
96
- extosc /= 1000000 ;
97
- if (extosc == 1 ) { //1MHz, set timerprescaler in bypass mode
98
- LPTMR0 -> PSR = LPTMR_PSR_PCS (3 ) | LPTMR_PSR_PBYP_MASK ;
99
- return ;
100
- } else { //See if we can divide it to 1MHz
101
- uint32_t divider = 0 ;
102
- extosc >>= 1 ;
103
- while (1 ) {
104
- if (extosc == 1 ) {
105
- LPTMR0 -> PSR = LPTMR_PSR_PCS (3 ) | LPTMR_PSR_PRESCALE (divider );
106
- return ;
107
- }
108
- if (extosc % 2 != 0 ) //If we can't divide by two anymore
109
- break ;
110
- divider ++ ;
111
- extosc >>= 1 ;
112
- }
113
- }
114
- }
115
- }
116
- //No suitable external oscillator clock -> Use fast internal oscillator (4MHz)
117
- MCG -> C1 |= MCG_C1_IRCLKEN_MASK ;
118
- MCG -> C2 |= MCG_C2_IRCS_MASK ;
119
- LPTMR0 -> PSR = LPTMR_PSR_PCS (0 ) | LPTMR_PSR_PRESCALE (1 );
104
+ NVIC_SetVector (PIT_TICKER_IRQ , (uint32_t )ticker_isr );
105
+ NVIC_EnableIRQ (PIT_TICKER_IRQ );
120
106
}
121
107
122
108
void us_ticker_disable_interrupt (void ) {
123
- LPTMR0 -> CSR &= ~LPTMR_CSR_TIE_MASK ;
109
+ PIT_TICKER . TCTRL &= ~PIT_TCTRL_TIE_MASK ;
124
110
}
125
111
126
112
void us_ticker_clear_interrupt (void ) {
127
113
// we already clear interrupt in lptmr_isr
128
114
}
129
115
130
116
static uint32_t us_ticker_int_counter = 0 ;
131
- static uint16_t us_ticker_int_remainder = 0 ;
132
-
133
- static void lptmr_set (unsigned short count ) {
134
- /* Reset */
135
- LPTMR0 -> CSR = 0 ;
136
-
137
- /* Set the compare register */
138
- LPTMR0 -> CMR = count ;
139
117
140
- /* Enable interrupt */
141
- LPTMR0 -> CSR |= LPTMR_CSR_TIE_MASK ;
142
-
143
- /* Start the timer */
144
- LPTMR0 -> CSR |= LPTMR_CSR_TEN_MASK ;
118
+ inline static void ticker_set (uint32_t count ) {
119
+ PIT_TICKER .TCTRL = 0 ;
120
+ PIT_TICKER .LDVAL = count ;
121
+ PIT_TICKER .TCTRL = PIT_TCTRL_TIE_MASK | PIT_TCTRL_TEN_MASK ;
145
122
}
146
123
147
- static void lptmr_isr (void ) {
148
- // write 1 to TCF to clear the LPT timer compare flag
149
- LPTMR0 -> CSR |= LPTMR_CSR_TCF_MASK ;
124
+ static void ticker_isr (void ) {
125
+ // Clear IRQ flag
126
+ PIT_TICKER . TFLG = 1 ;
150
127
151
128
if (us_ticker_int_counter > 0 ) {
152
- lptmr_set ( 0xFFFF );
129
+ ticker_set ( 0xFFFFFFFF );
153
130
us_ticker_int_counter -- ;
154
-
155
131
} else {
156
- if (us_ticker_int_remainder > 0 ) {
157
- lptmr_set (us_ticker_int_remainder );
158
- us_ticker_int_remainder = 0 ;
159
-
160
- } else {
161
- // This function is going to disable the interrupts if there are
162
- // no other events in the queue
163
- us_ticker_irq_handler ();
164
- }
132
+ // This function is going to disable the interrupts if there are
133
+ // no other events in the queue
134
+ us_ticker_irq_handler ();
165
135
}
166
136
}
167
137
@@ -173,13 +143,17 @@ void us_ticker_set_interrupt(unsigned int timestamp) {
173
143
return ;
174
144
}
175
145
176
- us_ticker_int_counter = (uint32_t )(delta >> 16 );
177
- us_ticker_int_remainder = (uint16_t )(0xFFFF & delta );
178
- if (us_ticker_int_counter > 0 ) {
179
- lptmr_set (0xFFFF );
146
+ //Calculate how much falls outside the 32-bit after multiplying with clk_mhz
147
+ //We shift twice 16-bit to keep everything within the 32-bit variable
148
+ us_ticker_int_counter = (uint32_t )(delta >> 16 );
149
+ us_ticker_int_counter *= clk_mhz ;
150
+ us_ticker_int_counter >>= 16 ;
151
+
152
+ uint32_t us_ticker_int_remainder = (uint32_t )delta * clk_mhz ;
153
+ if (us_ticker_int_remainder == 0 ) {
154
+ ticker_set (0xFFFFFFFF );
180
155
us_ticker_int_counter -- ;
181
156
} else {
182
- lptmr_set (us_ticker_int_remainder );
183
- us_ticker_int_remainder = 0 ;
157
+ ticker_set (us_ticker_int_remainder );
184
158
}
185
159
}
0 commit comments