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