/*
This sketch exercises four instances of IntervalTimer to generate
squarewaves with different duty cycles, such as for confirming hardware
operation with a logic analyzer or oscilloscope. It drives pins two through
five.
*/
// *** Start user configurable declarations ***
/*
The waveform period microseconds
*/
static const uint32_t PERIOD = 50000;
/*
Microseconds between duty cycle mutations
*/
static const uint32_t USEC_BETWEEN_MUTATIONS = 10000;
/*
Next dutycycle adjustment time.
*/
static uint32_t report_micros;
/*
The amount to change the timing of the two "halves" of the waveform
*/
static const uint32_t INCR = 50;
/*
Name and version. Change the number when you change the code
*/
#define NAME_AND_VERSION "IntervalTimer exerciser 1.00"
// *** End user configurable declarations ***
#define NUM_TIMERS (4)
#include <IntervalTimer.h>
/*
The type of a generic function with no return value and no parameters
*/
typedef void (*voidFuncPtr)();
/*
The timer objects
*/
IntervalTimer itimer0, itimer1, itimer2, itimer3;
IntervalTimer timer_object[NUM_TIMERS] = {
itimer0, itimer1, itimer2, itimer3
};
/*
Square wave output pins
*/
static const uint32_t pin[NUM_TIMERS] = {
2, 3, 4, 5
};
/*
The (variable) microseconds for each of the two parts of an output waveform for
each timer.
*/
static uint32_t interval[2][NUM_TIMERS];
/*
The starting microseconds for each of the two parts of an output waveform for
each timer
*/
static const uint32_t interval_initial[2][NUM_TIMERS] = {
PERIOD-(INCR*10), PERIOD-(INCR*8), PERIOD-(INCR*6), PERIOD-(INCR*5),
INCR*10, INCR*8, INCR*6, INCR*5
};
/*
The microseconds to use as lower (first timing state) and upper
(second timing state) limits
*/
static const uint32_t interval_limit[2][NUM_TIMERS] = {
INCR*10, INCR*8, INCR*6, INCR*5,
PERIOD-(INCR*10), PERIOD-(INCR*8), PERIOD-(INCR*6), PERIOD-(INCR*5)
};
/*
The state of each timer: 0 means "first portion of the waveform" and
"output off". A value of 1 means "second portion of the waveform" and
"output on".
*/
static uint32_t state[NUM_TIMERS] = {
0, 0, 0, 0
};
/*
Common interrupt handler. Flip the state and output pin and update the next
time interval.
*/
void tickCommon(uint32_t timer) {
// Flip to the next part of the waveform
if (state[timer]) {
state[timer] = 0;
} else {
state[timer] = 1;
}
// Have the pin reflect the state
digitalWrite(pin[timer], state[timer]);
// Update the timer with the new interval
timer_object[timer].update(interval[state[timer]][timer]);
};
void tick0() {
tickCommon(0);
}
void tick1() {
tickCommon(1);
}
void tick2() {
tickCommon(2);
}
void tick3() {
tickCommon(3);
}
/*
Interrupt handlers
*/
voidFuncPtr handler[NUM_TIMERS] = {
tick0, tick1, tick2, tick3
};
/*
Like common interrupt handler, but using begin to get the timers started
*/
void setupTimer(uint32_t timer) {
// Have the pin reflect the state
digitalWrite(pin[timer], state[timer]);
// Start the timer with the new interval
if (! timer_object[timer].begin(handler[timer],
interval[state[timer]][timer])) {
Serial.print("timer: ");
Serial.print(timer);
Serial.print(" interval: ");
Serial.print(interval[state[timer]][timer]);
Serial.println("usec IntervalTimer::begin returned false: HANGING");
while(true);
}
};
// Set up I/O, announce name and version and start the timers.
void setup() {
// Serial monitor window output speed
Serial.begin(115200);
// Have to do this with for Teensy boards
while(!Serial) {
}
Serial.println(NAME_AND_VERSION);
for (uint32_t timer = 0; timer < NUM_TIMERS; timer++) {
interval[0][timer] = interval_initial[0][timer];
interval[1][timer] = interval_initial[1][timer];
pinMode(pin[timer], OUTPUT);
setupTimer(timer);
}
} // setup
/*
Every USEC_BETWEEN_MUTATIONS change the waveform timing of all four
channels. With a logic analyzer or oscilloscope a set of squarewaves will
be seen that shrink their low periods and grow their high periods until
limits are reached, then the changes begin again.
*/
void loop() {
if (micros() > report_micros) {
report_micros = micros() + USEC_BETWEEN_MUTATIONS;
for (uint32_t timer = 0; timer < NUM_TIMERS; timer++) {
/*
Disabling interrupts might not be necessary, but this prevents us
from the possible pain of discovering why it is, and this application
isn't sensitive to the slight glitch caused by the calls. The compiler
cannot "hide" updates here and the data manipulation matches the
word size of the machine, so probalby don't need volatile decls either.
*/
noInterrupts();
interval[0][timer] = interval[0][timer] - INCR;
if (interval[0][timer] < interval_limit[0][timer]) {
interval[0][timer] = interval_initial[0][timer];
}
interval[1][timer] = interval[1][timer] + INCR;
if (interval[1][timer] > interval_limit[1][timer]) {
interval[1][timer] = interval_initial[1][timer];;
}
interrupts();
}
}
} // loop