|
- /* 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();
-
- }
-
|