//
// Robert Jenkins Technology Channel
//
// https://www.youtube.com/@RJTC
//
// Real time interrupt example
// Part of my multitasking series
//
// This example has been tested on Arduino Uno R3 and Mega2560 R3 boards,
// and it will likely also work on other AVR based Arduinos
//
// Set the board type appropriately in the Tools > Board menu!
//
// See the "Multitasking_Periodic_Interrupt_ARM" version for ARM based Arduinos.
//
// Standardised variable type includes:
// Adding these allows variable type names to be used the same
// Across different compilers, rather than having to redefine
// for each different compiler.
// intxx_t for signed with xx bit size,
// uintxx_t for unnsigned with xx bit size.
//
#include <stdint.h>
#include <stdbool.h>
// Global variable definitions
// Time / timing related
int8_t t_xsec; // Fractional mS, used with interrupts >1KHz
int8_t t_msec; // Millisecond counter
int8_t t_csec; // 1/100th second counter
int8_t t_dsec; // 1/10th second counter
int8_t t_ssec; // second counter
// t_min, t_hour etc. could also be added if time of day tracking is needed,
// though preferably add a section in the main program loop that checks t_secflag
// and increments counters as appropriate each time that is set, to minimise the interrupt duration.
// struct_tm xtime; // Not used in this program; used with time.h functions for time conversions
uint32_t tick_counter; // "ticks; count of 1mS incerments since program was started.
bool i_msecflag; // flag bits used (set) by the interrupt
bool i_csecflag; // routine at the various intervals
bool i_dsecflag;
bool i_ssecflag;
bool t_msecflag; // Copies of the above, used in the main program
bool t_csecflag; // to synchronise timed actions
bool t_dsecflag;
bool t_ssecflag;
// Plus a byte store the demo LED states
uint8_t ledbits;
void setup() {
// put your setup code here, to run once:
// Configure ports and any other peripherals etc.
// Using 8 & 9 as they are close to GND for LED resistor common.
pinMode(8, OUTPUT);
pinMode(9, OUTPUT);
// Initialise and configure a timer:
cli(); // Probably not needed, but keeps things safe if this section is re-used
TIMSK0 = 0; // Clear register to disable timer interrupts, also probably not needed
// TCCR0A and TCCR0B are configuration registers for Timer 0
TCCR0A = _BV(WGM01); // Sets the counter module in counter (as opposed to PWM) mode.
// The low three bits of TCCR0B control the prescaler for the counter input,
// from the CPU I/O clock frequency. 001 = /1 (direct), 010 = /8, 011 = /64, 100 = /256, 101 = /1024.
// 000 = no input, 110 & 111 set input from an MCU pin.
// The remaining bits should be zero for a repetitive interrupt use, such as this.
TCCR0B = _BV(CS01) | _BV(CS00); // Write bit 1 and 0 to 1 = [011]. I/O clock /64 to timer.
// OCR0A is the value that the count will be compared to, to trigger an interrupt.
// The MCU clock (16MHz on a Mega2560) will be divided by the prescaler value to
// to increment the timer0 counter & an interrupt will be triggered each time the
// the count reaches the number in OCR0A
// With the prescaler bits 011, prescale /64, that timer clock will be 250 KHz.
// At that, setting OCR0A to 250 would give 1mS interrupts.
// For approx. 10mS, use TCCR0B = _BV(CS02); giving /256 and set OCR0A to 156
// Or use the valuse as below and ignore the t_csecflag fo 10mS indication...
OCR0A = 250; // 16KHz / 16 count = 1mS interrupt rate
// Now enable the interrupt from Timer 0 when there is a compare match.
// That interrupt is called TIMER0_COMPA_vect
TIMSK0 = _BV(OCIE0A);
// Finally, after all hardware initialisation etc. is complete,
// enable the overall MCU interrupts
sei();
}
void loop() {
// put your main code here, to run repeatedly:
// As the interrupts could occur at any time in the main program,
// check and copy the time flag bits here so they are consistent all
// the way through the main program loop.
// This can be done a bit more efficiently, but this is a demo
// to explain the overall methodology!
// See the Arduino Due example for the faster version
cli(); // temporarily disable interrupts to nothing changes while testing the bits!
if(i_msecflag) {
t_msecflag = true;
i_msecflag = false;
}
else {
t_msecflag = false;
}
if(i_csecflag) {
t_csecflag = true;
i_csecflag = false;
}
else {
t_csecflag = false;
}
if(i_dsecflag) {
t_dsecflag = true;
i_dsecflag = false;
}
else {
t_dsecflag = false;
}
if(i_ssecflag) {
t_ssecflag = true;
i_ssecflag = false;
}
else {
t_ssecflag = false;
}
// The t_ flag bits above will be set for one pass through the main program loop
// at their respective intervals.
// eg. If you want to do something ten times per second, check for the t_dsecflag being set
// Or test (t_flags & F_DSEC), in the fast version of the flag update routine as in the Arduino Duo example.
sei(); // Re-enable the interrupts
// The rest of the program from here on.
// This demo is just going to toggle a couple of outputs at 10Hz and 1Hz
// (so flashing cycles at 5Hz and 0.5Hz)
if(t_dsecflag)
// Toggle LED on pin 9, bit 1 of store
if(ledbits & 0x02) // LED is on
{
digitalWrite(9, LOW);
ledbits &= 0xFD; // turn off bit 1
}
else
{
digitalWrite(9, HIGH);
ledbits |= 0x02; // turn on bit 1
}
if(t_ssecflag)
// Toggle LED on pin 8, bit 0 of store
if(ledbits & 0x01) // LED is on
{
digitalWrite(8, LOW);
ledbits &= 0xFE; // turn off bit 0
}
else
{
digitalWrite(8, HIGH);
ledbits |= 0x01; // turn on bit 0
}
// Add to the program here, as you wish.
// End of main program loop
}
// This is the function that will be called each time the timer 0 periodic interrupt occurs:
ISR(TIMER0_COMPA_vect)
{
// The code in an interrupt routine must be minimal,
// to do only what is absoluetly essential - no waiting
// or delays of any type!
// With the settings above, this will be called at 1KHz, every 1mS.
// Count milliseconds through to seconds
t_msec++;
i_msecflag = true;
if(t_msec > 9) {
// 10 counts, reset and increment 1/100sec
t_msec = 0;
t_csec++;
i_csecflag = true;
if(t_csec > 9) {
// Same for each decade used
t_csec = 0;
t_dsec++;
i_dsecflag = true;
if(t_dsec > 9) {
t_dsec = 0;
t_ssec++;
i_ssecflag = true;
}
}
}
// Counters done, any other stuff such as serial
// port & buffer checks for up to 9600 baud
// could also go in here.
// My software timer library would also be called from here
}