-
Notifications
You must be signed in to change notification settings - Fork 6
/
IntervalTimer.cpp
204 lines (154 loc) · 6.54 KB
/
IntervalTimer.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
/* Copyright (c) 2013 Daniel Gilbert, loglow@gmail.com
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in the
Software without restriction, including without limitation the rights to use, copy,
modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,
and to permit persons to whom the Software is furnished to do so, subject to the
following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
#include "IntervalTimer.h"
// ------------------------------------------------------------
// static class variables need to be reiterated here before use
// ------------------------------------------------------------
bool IntervalTimer::PIT_enabled;
bool IntervalTimer::PIT_used[];
IntervalTimer::ISR IntervalTimer::PIT_ISR[];
// ------------------------------------------------------------
// these are the ISRs (Interrupt Service Routines) that get
// called by each PIT timer when it fires. they're defined here
// so that they can auto-clear themselves and so the user can
// specify a custom ISR and reassign it as needed
// ------------------------------------------------------------
#if defined(KINETISK)
void pit0_isr() { PIT_TFLG0 = 1; IntervalTimer::PIT_ISR[0](); }
void pit1_isr() { PIT_TFLG1 = 1; IntervalTimer::PIT_ISR[1](); }
void pit2_isr() { PIT_TFLG2 = 1; IntervalTimer::PIT_ISR[2](); }
void pit3_isr() { PIT_TFLG3 = 1; IntervalTimer::PIT_ISR[3](); }
#elif defined(KINETISL)
void pit_isr() {
if (PIT_TFLG0) { PIT_TFLG0 = 1; IntervalTimer::PIT_ISR[0](); }
if (!IntervalTimer::PIT_enabled) return;
if (PIT_TFLG1) { PIT_TFLG1 = 1; IntervalTimer::PIT_ISR[1](); }
}
#endif
// ------------------------------------------------------------
// this function inits and starts the timer, using the specified
// function as a callback and the period provided. must be passed
// the name of a function taking no arguments and returning void.
// make sure this function can complete within the time allowed.
// attempts to allocate a timer using available resources,
// returning true on success or false in case of failure.
// period is specified as number of bus cycles
// ------------------------------------------------------------
bool IntervalTimer::beginCycles(ISR newISR, uint32_t newValue) {
// if this interval timer is already running, stop it
if (status == TIMER_PIT) {
stop_PIT();
status = TIMER_OFF;
}
// store callback pointer
myISR = newISR;
// attempt to allocate this timer
if (allocate_PIT(newValue)) status = TIMER_PIT;
else status = TIMER_OFF;
// check for success and return
if (status != TIMER_OFF) return true;
return false;
}
// ------------------------------------------------------------
// stop the timer if it's currently running, using its status
// to determine what hardware resources the timer may be using
// ------------------------------------------------------------
void IntervalTimer::end() {
if (status == TIMER_PIT) stop_PIT();
status = TIMER_OFF;
}
// ------------------------------------------------------------
// enables the PIT clock bit, the master PIT reg, and sets flag
// ------------------------------------------------------------
void IntervalTimer::enable_PIT() {
SIM_SCGC6 |= SIM_SCGC6_PIT;
PIT_MCR = 0;
PIT_enabled = true;
}
// ------------------------------------------------------------
// disables the master PIT reg, the PIT clock bit, and unsets flag
// ------------------------------------------------------------
void IntervalTimer::disable_PIT() {
PIT_MCR = 1;
SIM_SCGC6 &= ~SIM_SCGC6_PIT;
PIT_enabled = false;
}
// ------------------------------------------------------------
// enables the PIT clock if not already enabled, then checks to
// see if any PITs are available for use. if one is available,
// it's initialized and started with the specified value, and
// the function returns true, otherwise it returns false
// ------------------------------------------------------------
bool IntervalTimer::allocate_PIT(uint32_t newValue) {
// enable clock to the PIT module if necessary
if (!PIT_enabled) enable_PIT();
// check for an available PIT, and if so, start it
for (uint8_t id = 0; id < NUM_PIT; id++) {
if (!PIT_used[id]) {
PIT_id = id;
start_PIT(newValue);
PIT_used[id] = true;
return true;
}
}
// no PIT available
return false;
}
// ------------------------------------------------------------
// configuters a PIT's registers, function pointer, and enables
// interrupts, effectively starting the timer upon completion
// ------------------------------------------------------------
void IntervalTimer::start_PIT(uint32_t newValue) {
// point to the correct registers
PIT_LDVAL = &PIT_LDVAL0 + PIT_id * 4;
PIT_TCTRL = &PIT_TCTRL0 + PIT_id * 4;
// point to the correct PIT ISR
PIT_ISR[PIT_id] = myISR;
// write value to register and enable interrupt
*PIT_TCTRL = 0;
*PIT_LDVAL = newValue;
*PIT_TCTRL = 3;
#if defined(KINETISK)
IRQ_PIT_CH = IRQ_PIT_CH0 + PIT_id;
NVIC_SET_PRIORITY(IRQ_PIT_CH, nvic_priority);
NVIC_ENABLE_IRQ(IRQ_PIT_CH);
#elif defined(KINETISL)
NVIC_SET_PRIORITY(IRQ_PIT, nvic_priority); // TODO: use the higher of both channels, shared irq
NVIC_ENABLE_IRQ(IRQ_PIT);
#endif
}
// ------------------------------------------------------------
// stops an active PIT by disabling its interrupt, writing to
// its control register, and freeing up its state for future use.
// also, if no PITs remain in use, disables the core PIT clock
// ------------------------------------------------------------
void IntervalTimer::stop_PIT() {
// disable interrupt and PIT
*PIT_TCTRL = 0;
#if defined(KINETISK)
NVIC_DISABLE_IRQ(IRQ_PIT_CH);
#elif defined(KINETISL)
NVIC_DISABLE_IRQ(IRQ_PIT);
#endif
// free PIT for future use
PIT_used[PIT_id] = false;
// check if we're still using any PIT
for (uint8_t id = 0; id < NUM_PIT; id++) {
if (PIT_used[id]) return;
}
// none used, disable PIT clock
disable_PIT();
}