While I am still busy with writing code and project generating scripts, I want to share one of these scripts here.
Beginners are taught to use the millis() function to handle software timed events. And it is not the most efficient way. You need to declare 4 bytes of memory for every timer. You need to perform a function call and fetch a 4 byte arguement, subtract these 4 bytes from 4 other bytes and compare the result to another 4 bytes And lastly you need to copy the 4 bytes of the current value in the previous value. And for longer running arduino's, you are a few weeks removed from a possible buggy condition when millis overflows.
I have developed and worked with more fancier SW timer code with which I could simply do:
start(&foo, someInterval); // repetitive
trigger(&bar, someOtherInterval); // one shot timer
// and
stop(foo);
usage was easy
But this also came with too much overhead and inefficiency. Therefor I wanted something more efficient and more simplistic.
The usage of a single SW timer in my code can look as follows:
#include "timers.h"
#define someInterval 50
void loop() {
if(someTimer == 0) { // or "if(!someTimer) {"
someTimer = someInterval;
// .. code to run ever 50 ms/ds/hs/s
}
If you want to use one SW timer, you can do so as shown in the example. You wait for a timer to reach 0, than set the timer at a desired interval and perform your code. I use this method alot in combination with state machines. It has proven to be usefull for simple time-outs of states, in-state delays and inter state delays.
My new software timers have the following properties:
-
All timers are decremented in interrupt untill the timers reach 0
-
All timers are of an 8-bit type
-
both timer .ccp and .h files are generated for you by a python script
-
Timers can have different interval bases of 1ms, 10ms, 100ms or 1000ms, which can let you run tasks up to every 255 seconds. This ofcourse can easily be expanded to more or other intervals
-
If you want to add, modify or remove a timer, you modify the file timers.tab and run the script
-
There is no need for some 'update' function in the main loop
-
There won't be any overflows
The works.
To make/modify/remove timers, you must make and edit "timers.tab" (just a plain txt file) and run the attached python script.
task1T 100
task2T 1000
task3T 10
task4T 1
helloT 10
worldT 100
The capital T is just a syntax thing of mine, for me this indicates that the used variable is a SW timer. Feel free to use whatever name you want.
The format in timers.tab must be maintained. "name", "1 tab", "interval base (1, 10, 100 or 100)" and a \n character
The purpose of this script is to reduce the workload for you. If you would manually add a SW timer, it would have to be done in 3 separate places. Now it is just one place.
The generated cpp and h files with some example timers look as follows:
#include <Arduino.h>
#include "timers.h"
extern void initTimers() {
TCCR2B = 0;// same for TCCR2B
TCNT2 = 0;//initialize counter value to 0
// set compare match register for 8khz increments
OCR2A = 249;// = (16*10^6) / (1000*64) - 1 (must be <256)
// turn on CTC mode
TCCR2A |= (1 << WGM21);
// Set CS21and and CS20 bit for 64 prescaler
TCCR2B |= (1 << CS20);
TCCR2B |= (1 << CS21);
TCCR2B &= ~(1 << CS22);
// enable timer compare interrupt
TIMSK2 |= (1 << OCIE2A); }
volatile unsigned char task1T;
volatile unsigned char task2T;
volatile unsigned char task3T;
volatile unsigned char task4T;
volatile unsigned char helloT;
volatile unsigned char worldT;
// Don't complain about the indentations. This code is generated for you and you shouldn't be looking at this part.
ISR(TIMER2_OVF_vect) {
static unsigned char _1ms, _10ms, _100ms;
// 1ms timers
_1ms += 1;
if(task4T) task4T--;
// 10ms timers
if(!(_1ms % 10)) { _1ms = 0; _10ms += 1;
if(task3T) task3T--;
if(helloT) helloT--;
// 100ms timers
if(!(_10ms % 10)) { _10ms = 0; _100ms += 1;
if(task1T) task1T--;
if(worldT) worldT--;
//1000ms timers
if(!(_100ms % 10)) { _100ms = 0;
if(task2T) task2T--;
}
}
}
}
You can see that the indentation is not standard. For this particular use-case I found this to be more logical. If you do not agree, I really do not want to know Besides, you don't have to modify these files yourself.
And the .h
extern void initTimers();
extern volatile unsigned char task1T;
extern volatile unsigned char task2T;
extern volatile unsigned char task3T;
extern volatile unsigned char task4T;
extern volatile unsigned char helloT;
extern volatile unsigned char worldT;
Perhaps I do slightly more in an ISR than most programmers do prefer, but still I don't do sooo much that other parts of my programs are affected in a negative way. And I don't have a roundRobin time consuming update function. Only the timers you are currently working with, are polled.
I deliberately did not use any for loops in the ISR to make the ISR as fast as possible. I came to believe that this SW comes with minimal overhead and it is easy to use (afteral it differs little with the millis() method)
Let me know what you think about this method.
Bas.
P.S. forum doesn't allow me to upload a .py so I made it a .txt instead.
Also, pythonese is not my native programming language and I have not much experience in python so I am sorry if you find the script is poorly written.
updateTimers.txt (2.49 KB)