Bladeren bron

Rewrite IntervalTimer

main
PaulStoffregen 8 jaren geleden
bovenliggende
commit
f9813d201b
3 gewijzigde bestanden met toevoegingen van 220 en 270 verwijderingen
  1. +110
    -175
      teensy3/IntervalTimer.cpp
  2. +103
    -95
      teensy3/IntervalTimer.h
  3. +7
    -0
      teensy3/kinetis.h

+ 110
- 175
teensy3/IntervalTimer.cpp Bestand weergeven

@@ -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)
{
}


+ 103
- 95
teensy3/IntervalTimer.h Bestand weergeven

@@ -1,22 +1,32 @@
/* 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.
*/

#ifndef __INTERVALTIMER_H__
#define __INTERVALTIMER_H__
@@ -28,82 +38,80 @@ extern "C" {
#endif

class IntervalTimer {
private:
typedef void (*ISR)();
typedef volatile uint32_t* reg;
enum {TIMER_OFF, TIMER_PIT};
#if defined(KINETISK)
static const uint8_t NUM_PIT = 4;
#elif defined(KINETISL)
static const uint8_t NUM_PIT = 2;
#endif
static const uint32_t MAX_PERIOD = UINT32_MAX / (F_BUS / 1000000.0);
static void enable_PIT();
static void disable_PIT();
static bool PIT_enabled;
static bool PIT_used[NUM_PIT];
static ISR PIT_ISR[NUM_PIT];
bool allocate_PIT(uint32_t newValue);
void start_PIT(uint32_t newValue);
void stop_PIT();
bool status;
uint8_t PIT_id;
reg PIT_LDVAL;
reg PIT_TCTRL;
uint8_t IRQ_PIT_CH;
uint8_t nvic_priority;
ISR myISR;
bool beginCycles(ISR newISR, uint32_t cycles);
public:
IntervalTimer() { status = TIMER_OFF; nvic_priority = 128; }
~IntervalTimer() { end(); }
bool begin(ISR newISR, unsigned int newPeriod) {
if (newPeriod == 0 || newPeriod > MAX_PERIOD) return false;
uint32_t newValue = (F_BUS / 1000000) * newPeriod - 1;
return beginCycles(newISR, newValue);
}
bool begin(ISR newISR, int newPeriod) {
if (newPeriod < 0) return false;
return begin(newISR, (unsigned int)newPeriod);
}
bool begin(ISR newISR, unsigned long newPeriod) {
return begin(newISR, (unsigned int)newPeriod);
}
bool begin(ISR newISR, long newPeriod) {
return begin(newISR, (int)newPeriod);
}
bool begin(ISR newISR, float newPeriod) {
if (newPeriod <= 0 || newPeriod > MAX_PERIOD) return false;
uint32_t newValue = (float)(F_BUS / 1000000) * newPeriod - 0.5;
if (newValue < 40) return false;
return beginCycles(newISR, newValue);
}
bool begin(ISR newISR, double newPeriod) {
return begin(newISR, (float)newPeriod);
}
void end();
void priority(uint8_t n) {
nvic_priority = n;
if (PIT_enabled) NVIC_SET_PRIORITY(IRQ_PIT_CH, n);
}
operator IRQ_NUMBER_t() {
if (PIT_enabled) {
#if defined(KINETISK)
return (IRQ_NUMBER_t)(IRQ_PIT_CH + PIT_id);
#elif defined(KINETISL)
return IRQ_PIT;
#endif
}
return (IRQ_NUMBER_t)NVIC_NUM_INTERRUPTS;
}
#if defined(KINETISK)
friend void pit0_isr();
friend void pit1_isr();
friend void pit2_isr();
friend void pit3_isr();
#elif defined(KINETISL)
friend void pit_isr();
#endif
private:
static const uint32_t MAX_PERIOD = UINT32_MAX / (F_BUS / 1000000.0);
public:
IntervalTimer() {
channel = NULL;
nvic_priority = 128;
}
~IntervalTimer() {
end();
}
bool begin(void (*funct)(), unsigned int microseconds) {
if (microseconds == 0 || microseconds > MAX_PERIOD) return false;
uint32_t cycles = (F_BUS / 1000000) * microseconds - 1;
if (cycles < 36) return false;
return beginCycles(funct, cycles);
}
bool begin(void (*funct)(), int microseconds) {
if (microseconds < 0) return false;
return begin(funct, (unsigned int)microseconds);
}
bool begin(void (*funct)(), unsigned long microseconds) {
return begin(funct, (unsigned int)microseconds);
}
bool begin(void (*funct)(), long microseconds) {
return begin(funct, (int)microseconds);
}
bool begin(void (*funct)(), float microseconds) {
if (microseconds <= 0 || microseconds > MAX_PERIOD) return false;
uint32_t cycles = (float)(F_BUS / 1000000) * microseconds - 0.5;
if (cycles < 36) return false;
return beginCycles(funct, cycles);
}
bool begin(void (*funct)(), double microseconds) {
return begin(funct, (float)microseconds);
}
void end();
void priority(uint8_t n) {
nvic_priority = n;
#if defined(KINETISK)
if (channel) {
int index = channel - KINETISK_PIT_CHANNELS;
NVIC_SET_PRIORITY(IRQ_PIT_CH0 + index, nvic_priority);
}
#elif defined(KINETISL)
if (channel) {
int index = channel - KINETISK_PIT_CHANNELS;
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]);
}
}
#endif
}
operator IRQ_NUMBER_t() {
if (channel) {
#if defined(KINETISK)
int index = channel - KINETISK_PIT_CHANNELS;
return (IRQ_NUMBER_t)(IRQ_PIT_CH0 + index);
#elif defined(KINETISL)
return IRQ_PIT;
#endif
}
return (IRQ_NUMBER_t)NVIC_NUM_INTERRUPTS;
}
private:
KINETISK_PIT_CHANNEL_t *channel;
uint8_t nvic_priority;
#if defined(KINETISL)
static uint8_t nvic_priorites[2];
#endif
bool beginCycles(void (*funct)(), uint32_t cycles);

};



+ 7
- 0
teensy3/kinetis.h Bestand weergeven

@@ -3457,6 +3457,13 @@ typedef struct {
#define PIT_LTMR64H (*(volatile uint32_t *)0x400370E0) // PIT Upper Lifetime Timer Register
#define PIT_LTMR64L (*(volatile uint32_t *)0x400370E4) // PIT Lower Lifetime Timer Register
#endif // defined(KINETISL)
typedef struct {
volatile uint32_t LDVAL;
volatile uint32_t CVAL;
volatile uint32_t TCTRL;
volatile uint32_t TFLG;
} KINETISK_PIT_CHANNEL_t;
#define KINETISK_PIT_CHANNELS (KINETISK_PIT_CHANNEL_t *)(0x40037100)
#define PIT_LDVAL0 (*(volatile uint32_t *)0x40037100) // Timer Load Value Register
#define PIT_CVAL0 (*(volatile uint32_t *)0x40037104) // Current Timer Value Register
#define PIT_TCTRL0 (*(volatile uint32_t *)0x40037108) // Timer Control Register

Laden…
Annuleren
Opslaan