|
|
@@ -1,204 +1,139 @@ |
|
|
|
/* 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. */ |
|
|
|
|
|
|
|
/* Teensyduino Core Library |
|
|
|
* http://www.pjrc.com/teensy/ |
|
|
|
* Copyright (c) 2016 PJRC.COM, LLC. |
|
|
|
* |
|
|
|
* 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: |
|
|
|
* |
|
|
|
* 1. The above copyright notice and this permission notice shall be |
|
|
|
* included in all copies or substantial portions of the Software. |
|
|
|
* |
|
|
|
* 2. If the Software is incorporated into a build system that allows |
|
|
|
* selection among a list of target devices, then similar target |
|
|
|
* devices manufactured by PJRC.COM must be included in the list of |
|
|
|
* target devices and selectable in the same manner. |
|
|
|
* |
|
|
|
* 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 void dummy_funct(void); |
|
|
|
|
|
|
|
// ------------------------------------------------------------ |
|
|
|
// 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](); } |
|
|
|
#define NUM_CHANNELS 4 |
|
|
|
static void (*funct_table[4])(void) = {dummy_funct, dummy_funct, dummy_funct, dummy_funct}; |
|
|
|
|
|
|
|
#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](); } |
|
|
|
} |
|
|
|
#define NUM_CHANNELS 2 |
|
|
|
static void (*funct_table[2])(void) = {dummy_funct, dummy_funct}; |
|
|
|
uint8_t IntervalTimer::nvic_priorites[2] = {255, 255}; |
|
|
|
#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; |
|
|
|
|
|
|
|
bool IntervalTimer::beginCycles(void (*funct)(), uint32_t cycles) |
|
|
|
{ |
|
|
|
if (channel) { |
|
|
|
channel->TCTRL = 0; |
|
|
|
channel->TFLG = 1; |
|
|
|
} else { |
|
|
|
SIM_SCGC6 |= SIM_SCGC6_PIT; |
|
|
|
PIT_MCR = 1; |
|
|
|
channel = KINETISK_PIT_CHANNELS; |
|
|
|
while (1) { |
|
|
|
if (channel->TCTRL == 0) break; |
|
|
|
if (++channel >= KINETISK_PIT_CHANNELS + NUM_CHANNELS) { |
|
|
|
channel = NULL; |
|
|
|
return false; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
int index = channel - KINETISK_PIT_CHANNELS; |
|
|
|
funct_table[index] = funct; |
|
|
|
channel->LDVAL = cycles; |
|
|
|
channel->TCTRL = 3; |
|
|
|
#if defined(KINETISK) |
|
|
|
NVIC_SET_PRIORITY(IRQ_PIT_CH0 + index, nvic_priority); |
|
|
|
NVIC_ENABLE_IRQ(IRQ_PIT_CH0 + index); |
|
|
|
#elif defined(KINETISL) |
|
|
|
nvic_priorites[index] = nvic_priority; |
|
|
|
if (nvic_priorites[0] <= nvic_priorites[1]) { |
|
|
|
NVIC_SET_PRIORITY(IRQ_PIT, nvic_priorites[0]); |
|
|
|
} else { |
|
|
|
NVIC_SET_PRIORITY(IRQ_PIT, nvic_priorites[1]); |
|
|
|
} |
|
|
|
NVIC_ENABLE_IRQ(IRQ_PIT); |
|
|
|
#endif |
|
|
|
return true; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------ |
|
|
|
// 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; |
|
|
|
if (channel) { |
|
|
|
int index = channel - KINETISK_PIT_CHANNELS; |
|
|
|
funct_table[index] = dummy_funct; |
|
|
|
channel->TCTRL = 0; |
|
|
|
#if defined(KINETISL) |
|
|
|
nvic_priorites[index] = 255; |
|
|
|
if (nvic_priorites[0] <= nvic_priorites[1]) { |
|
|
|
NVIC_SET_PRIORITY(IRQ_PIT, nvic_priorites[0]); |
|
|
|
} else { |
|
|
|
NVIC_SET_PRIORITY(IRQ_PIT, nvic_priorites[1]); |
|
|
|
} |
|
|
|
#endif |
|
|
|
channel = 0; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------ |
|
|
|
// 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; |
|
|
|
#if defined(KINETISK) |
|
|
|
void pit0_isr() |
|
|
|
{ |
|
|
|
PIT_TFLG0 = 1; |
|
|
|
funct_table[0](); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------ |
|
|
|
// 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; |
|
|
|
void pit1_isr() { |
|
|
|
PIT_TFLG1 = 1; |
|
|
|
funct_table[1](); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------ |
|
|
|
// 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; |
|
|
|
|
|
|
|
void pit2_isr() { |
|
|
|
PIT_TFLG2 = 1; |
|
|
|
funct_table[2](); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------ |
|
|
|
// 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 |
|
|
|
|
|
|
|
void pit3_isr() { |
|
|
|
PIT_TFLG3 = 1; |
|
|
|
funct_table[3](); |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ------------------------------------------------------------ |
|
|
|
// 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(); |
|
|
|
|
|
|
|
void pit_isr() { |
|
|
|
if (PIT_TFLG0) { |
|
|
|
PIT_TFLG0 = 1; |
|
|
|
funct_table[0](); |
|
|
|
} |
|
|
|
if (PIT_TFLG1) { |
|
|
|
PIT_TFLG1 = 1; |
|
|
|
funct_table[1](); |
|
|
|
} |
|
|
|
} |
|
|
|
#endif |
|
|
|
|
|
|
|
static void dummy_funct(void) |
|
|
|
{ |
|
|
|
} |
|
|
|
|