Hi folks,
I'm new here. I read a lot of interesting posts, but it's the first time I post.
I'm currently writing a code, which goal is to run a list of instruction one by one in the background while another programm is running in the "loop".
Currently only one instruction is coded "Txxxx", where "T" stands for Timing, and "xxxx" for "minutes and seconds". It decounts every second and writes it to Serial. when it reaches 0, it stops and move to the next instruction. The aim of the timer is to check if the instruction is done (in this case the time but it could be some other function). I will add an instruction decoder and each instruction will have its callback function.
The programm is a s follow :
attach an interrupt to the Timer1
each callback check if it's finished or decounts one second
when its finished dettach the innterrupt
move to next instruction "curCmd++"
The problem is "curCmd++" increments only once:
It perfoms Instruction 0, then Instruction 1 and then 1 again... and again... and again...
#include <TimerOne.h>
#include <avr/pgmspace.h>
// this is a list of instructions to do
// T stands for "Time" then 2digits for minutes
// and 2 digits for seconds
#define MY_INSTR_LENGTH 6
prog_char cmd_0[] PROGMEM = "T0002";
prog_char cmd_1[] PROGMEM = "T0004";
prog_char cmd_2[] PROGMEM = "T0006";
prog_char cmd_3[] PROGMEM = "T0008";
prog_char cmd_4[] PROGMEM = "T0010";
prog_char cmd_5[] PROGMEM = "T0100";
char cmd[] = {
0,0,0,0,0}; // This is the instruction buffer
int curCmd = 0; // this is the current instruction to read
// Then set up a table to refer to your strings.
PROGMEM const char *cmd_table[] = // change "string_table" name to suit
{
cmd_0,
cmd_1,
cmd_2,
cmd_3,
cmd_4,
cmd_5 };
void setup(){
Timer1.initialize(1000000); // initialize timer1 to 1 sec
Serial.begin(9600); // init Serial
nextStep(); // read next instruction (currently 0)
curCmd++; // increment to next instruction
}
void loop(){
}
int remTime = 0; // remainig time to decount
void remTimeDecount(){
Serial.print(remTime/60); //display minutes
Serial.print(":");
Serial.println(remTime%60); //display seconds
if(remTime-- <= 0){ //if 0 or less the timer has finished
Serial.println(remTime); //display remaining time (-1 means finished)
Timer1.detachInterrupt(); // no more need for the interrupt. stop the decounter
nextStep(); // read next instruction (this has to be incremented now)
curCmd++; // increment to next instruction
}
}
void nextStep(){
if(curCmd < MY_INSTR_LENGTH){ // check if there are some any more instructions to perform
Serial.print("Command ");
Serial.println(curCmd);
strcpy_P(cmd, (char*)pgm_read_word(&(cmd_table[curCmd]))); // get the instruction in the Progmem
if(cmd[0] == 'T'){ // This "if" section will be replaced by a instruction decoder
remTime = (cmd[1]-48)*600+(cmd[2]-48)*60+(cmd[3]-48)*10+(cmd[4]-48); // set the decounter
Timer1.attachInterrupt(remTimeDecount); // attach interrupt to start the decounter
}
}
else
{
Serial.println("The end !!");
}
}
When you have a string in C++, it adds an invisible 0 to the end, so that it can easily determine where the string ends. So what happens is that when you copy the string into cmd, it copies 6 bytes, instead of just 5. Because cmd is 5 long, and curCmd is declared right after cmd, curCmd happens to end up in the next memory space. So when you copy 6 bytes, it copies a zero into the memory that is allocated to curCmd!
Fortunately the fix is easy, delcare your Cmd array with one extra byte:
char cmd[] = {
0,0,0,0,0,0}; // This is the instruction buffer
Why do you keep detaching and reattaching the interrupt handlers?
I just coded one function wich is countdown for xxminutes. But there will be others, for exemple to wait until a condition is met and update some variable.
The purpose is being able to run the main program in the loop (In may case it will be a PID) and the updates in the interrupt (for example to change the setpoint or to program a pause).
In this case detaching and reattaching the interruption handler is useless but in the end I will dettach the handler of the decounter for exemple and then attach the handler of "go to that setpoint and wait till it's reach" and so on. Like a little OS ... in order to be able to change the heating profiles without recoding the whole program, just the lines "cmd_0 to cmd_x".
Certainly not the best method but it seemed logical to me but maybe it isn't ;D
Why are you doing slow Serial.print()s in what is supposed to be a very fast interrupt handler?
I really think you need a restructure. I don't like the way the interrupt routine calls another function which starts the timer again. But anyway.
I've restructured a bit so you only add 1 to curCmd in one place. This now keeps incrementing:
#include <TimerOne.h>
#include <avr/pgmspace.h>
// this is a list of instructions to do
// T stands for "Time" then 2digits for minutes
// and 2 digits for seconds
#define MY_INSTR_LENGTH 6
prog_char cmd_0[] PROGMEM = "T0002";
prog_char cmd_1[] PROGMEM = "T0004";
prog_char cmd_2[] PROGMEM = "T0006";
prog_char cmd_3[] PROGMEM = "T0008";
prog_char cmd_4[] PROGMEM = "T0010";
prog_char cmd_5[] PROGMEM = "T0100";
char cmd[MY_INSTR_LENGTH]; // This is the instruction buffer
volatile int curCmd = 0; // this is the current instruction to read
// Then set up a table to refer to your strings.
PROGMEM const char *cmd_table[] = // change "string_table" name to suit
{
cmd_0,
cmd_1,
cmd_2,
cmd_3,
cmd_4,
cmd_5 };
void setup(){
Timer1.initialize(1000000); // initialize timer1 to 1 sec
Serial.begin(9600); // init Serial
nextStep(); // read next instruction (currently 0)
}
void loop(){
}
int remTime = 0; // remainig time to decount
void remTimeDecount(){
Serial.print(remTime/60); //display minutes
Serial.print(":");
Serial.println(remTime%60); //display seconds
if(remTime-- <= 0){ //if 0 or less the timer has finished
Serial.println(remTime); //display remaining time (-1 means finished)
Timer1.detachInterrupt(); // no more need for the interrupt. stop the decounter
nextStep(); // read next instruction (this has to be incremented now)
}
}
void nextStep(){
if(curCmd < MY_INSTR_LENGTH){ // check if there are some any more instructions to perform
Serial.print("Command ");
Serial.println(curCmd);
strcpy_P(cmd, (char*)pgm_read_word(&(cmd_table[curCmd]))); // get the instruction in the Progmem
Serial.print("cmd: ");
Serial.println (cmd);
delay (100);
if(cmd[0] == 'T'){ // This "if" section will be replaced by a instruction decoder
remTime = (cmd[1]-48)*600+(cmd[2]-48)*60+(cmd[3]-48)*10+(cmd[4]-48); // set the decounter
Timer1.attachInterrupt(remTimeDecount); // attach interrupt to start the decounter
}
curCmd++;
}
else
{
Serial.println("The end !!");
}
}