[solved] Proceed on interrupt condition

Hi,

I am having a problem with the use of interrupts.
Over the course of about a minute I want to output values (in demo-code analogWrite values to a Pin) every 50 msfor example. The heartbeat is generated with mstimer2. These y values are generated by linear interpolation of time-value data which is stored in the arrays xvals and yvals.

My program should now generate a value (run one step through the for loops) every time the interrupt variable is set by mstimer2, output the variable and return control to the rest of the program (for collection of data, outuputting to LCD/serial/whatever else is necessary, so I can’t use delay for instance)

Here is my example code:

//more includes for lcd...
#include <MsTimer2.h>

#define PWMPin 11

float xvals[] = {   0,    5,  15,    35,   40,   50,  60};//Time in Seconds (integer)
float yvals[] = { 0.0,  1.0, 0.2,   0.8,  0.5,  1.0, 0.0};//y-value

float delta;
float y=0;
int nsteps=0;
float floatnsteps=0.0;
int i,j;
int nlegs;
int xmax;
int intdtm;


//Tick Counter and interrupt Variable for MsTimer2
float deltaTms =50; //Tick step size
volatile int Tick=0;
volatile boolean interrupt_var=false;

//***** OUTPUT VARIABLES
float MappedOutValue;


void setup(){
  Serial.begin(9600);
  Serial.println("\nStarting...");

  pinMode(PWMPin,OUTPUT);
  analogWrite(PWMPin,0);
  MsTimer2::set(deltaTms,inctime); //increase time every deltaTms ms
}



void loop(){
  //Housekeeping
  nlegs=sizeof(xvals)/4-1; //if erratic results check if 
                           //nlegs=sizeof(xvals)/sizeof(int)-1;
                           //works. Arduino gives wrong results, thus manual setting 
  delta=deltaTms/1000;
  intdtm=(int)deltaTms;
  xmax=xvals[nlegs];
  MsTimer2::start();
  //Starting loop
  
  //***** These for loops should only proceed one step at a time whenever the interrupt variable is called
  for (i=0;i<nlegs;i++){
    nsteps=(int)(((xvals[i+1]-xvals[i])/delta));
    floatnsteps=(float)(nsteps);
    for(j = 0; j<nsteps;j++){
//      if(interrupt_var){                  //UNCOMMENT
//        interrupt_var=false;              //UNCOMMENT
        y=lininterpol(yvals[i], yvals[i+1], j/floatnsteps);
        MappedOutValue=1024*y;
        analogWrite(PWMPin,(int)MappedOutValue);
        Serial.println("Tick ");
        Serial.println(Tick);
        Serial.print("MappedOutValue ");
        Serial.println((int)MappedOutValue);
//      }                                   //UNCOMMENT
    }
  }
  MsTimer2::stop();
}

float lininterpol(float a, float b, float t){
  return (1-t)*a + t*b;
}

void inctime(){
  Tick=Tick++;
  interrupt_var=true;
}

If the //UNCOMMENT lines are in line, the program just outputs “Starting” to serial.
This method wouldn’t work anyways as it’s just running through the code anyways.

//The alternative would be to pre-generate a huge array with the y values and do step through and any maths/conversion/IO on LCD on the fly. But as one minute has 1200 ticks (deltaTms=50 ms), that would be 4.8 kbyte (y values in float or int), far exceeding the RAM of any Arduino I have at hand…

then it would be(pseudocode)

loop{
  do measurement
  output measurement to serial
  if interruptvar{      //would be called once every time mstimer2 sets interruptvar to 1
    interruptvar=false
    analogwrite{Hugearray(tickvalue)
  }
  write to lcd
  userinteraction
}
[code]

Thank you very much for any hints.

[/code]

  Tick=Tick++;

http://www.gammon.com.au/forum/?id=12153#trap8

Thank you very much for catching that. I changed it to just

Tick++

and in another code where I use the timer, too. But unfortunately I am still stumped with my proceed on interrupt.

  MsTimer2::start();
  //Starting loop
  
  //***** These for loops should only proceed one step at a time whenever the interrupt variable is called
  for (i=0;i<nlegs;i++){
    nsteps=(int)(((xvals[i+1]-xvals[i])/delta));
    floatnsteps=(float)(nsteps);
    for(j = 0; j<nsteps;j++){
//      if(interrupt_var){                  //UNCOMMENT
//        interrupt_var=false;              //UNCOMMENT
        y=lininterpol(yvals[i], yvals[i+1], j/floatnsteps);
        MappedOutValue=1024*y;
        analogWrite(PWMPin,(int)MappedOutValue);
        Serial.println("Tick ");
        Serial.println(Tick);
        Serial.print("MappedOutValue ");
        Serial.println((int)MappedOutValue);
//      }                                   //UNCOMMENT
    }
  }
  MsTimer2::stop();

You’ve lost me here. Can you explain, in your own words, what this is doing?

Hi,

thank you very much for looking at it!
I want to output a predefined set of values in a range (normalized to 0…1 in the header, afterward multiplied by e.g. 1023 for analogWrite).
The values are determined by linear interpolation of a set of points (time vs value, e.g):

time value
  0    0 
 10    1.0 
 20    0.2 
 30    0.834 
 40     0.0

on a 50 ms grid.(so 1 second in this case would be 0.1, 2 s=0.2, 3s=0.3… 11 s 0.9x… and all the values in between)
As the arrays can be of different lengt (well time and values need the same number of points) I have to determine the number of legs (5 values → 4 legs, c counts 0…3)
Then for each of the legs I have to do the linear interpolation at each point t [start, start+t, start+2t, … start+nt-1, start +nt] where t the 50 ms.
the interpolated value is then multiplied by 1023 for generating the PWM cycle.

The profile looks as follows:

This c-code compiles with gcc and generates it, but I’d like to have that done on the fly in my microcontroller:

#include <stdio.h>
#include <math.h>

float xvals[] = {   0,  10,  20,    30,  40};//Time in Seconds (integer)
float yvals[] = { 0.0, 1.0, 0.2, 0.834, 0.0};//scaled y values
float deltaTms =50; //Tick step size

int PWMmax = 1023;

float delta;
float x=0;
float y=0;
int nsteps=0;
float floatnsteps=0.0;
int tick=0;
int yPWM=0;
int i,j;

int nlegs;
int xmax;
int intdtm;

float lininterpol(float a, float b, float t){
  return (1-t)*a + t*b;
}

int main(){
  nlegs=sizeof(xvals)/sizeof(int)-1;
  delta=deltaTms/1000;
  intdtm=(int)deltaTms;
  xmax=xvals[nlegs];
  /*  printf("delta %f\n",delta);
  printf("intdtm %i\n",intdtm);
  printf("nlegs %i\n",nlegs);
  printf("xmax %i\n",xmax);*/
  printf("%i\n",intdtm);
  for (i=0;i<nlegs;i++){
    nsteps=(int)(((xvals[i+1]-xvals[i])/delta));
    floatnsteps=(float)(nsteps);
    for(j = 0; j<nsteps;j++){
      x=lininterpol(xvals[i], xvals[i+1], j/floatnsteps);
      y=lininterpol(yvals[i], yvals[i+1], j/floatnsteps);
      yPWM=(int)(PWMmax*y);
      //      printf("%i %f %f\n",tick,x,y);
      //      printf("%i ",tick);
      printf("%i\n",yPWM);
      tick++;
    }
  }
  yPWM=(int)(PWMmax*yvals[nlegs]);
  /*
    print(tick);
    print(" ");
    print(xvals[nlegs]);
    print(" ");
    print(yvals[nlegs]);
    print(" ");
  */
  //printf("%i ",tick);
  printf("%i\n",yPWM);
}

Here is the arduino code commented a bit more:

  MsTimer2::start(); 
         // This starts the timer which sets the interrupt flag and increases Tick
         // (Tick not explicitely needed, but for so I know what is going on
  //Starting loop
  
  //***** These for loops should only proceed one step at a time whenever the interrupt variable is called
  for (i=0;i<nlegs;i++){
        //nlegs is the number of intervals in the arrays 0 10 20 30 seconds would be 3 legs for example
    nsteps=(int)(((xvals[i+1]-xvals[i])/delta));
        //this determines how many steps are in the interval. 10 sec/50 ms=200 steps for example
    floatnsteps=(float)(nsteps);
        //casting to float needed for linear interpolation
    for(j = 0; j<nsteps;j++){
        //for every step of a leg 
      if(interrupt_var){                  //UNCOMMENT  well this is where I don't know how to go about it
        interrupt_var=false;              //UNCOMMENT
        y=lininterpol(yvals[i], yvals[i+1], j/floatnsteps);
        //  gives me the y value for linear equation (ax+b) at position a
        MappedOutValue=1024*y;
          //as input range [0..1]
        analogWrite(PWMPin,(int)MappedOutValue);
        Serial.println("Tick ");
        Serial.println(Tick);
        Serial.print("MappedOutValue ");
        Serial.println((int)MappedOutValue);
      }                                   //UNCOMMENT
    }
  }
  MsTimer2::stop();  //finished, timer interrupt not needed 
}

I hope this cleared up some questions. If not, please don’t hesitate to ask. (English is not my first language, so I hope there’s no unnecessary obfuscation owing to this fact).

The maximum value you can put into an analogWrite is 255 not 1023. Otherwise it wraps round just like you are finding.

Hi,

thank you very much. (In the real code I use an output to a DAC, but for demonstration purposes, PWM has to make do… Sorry I didn’t read up on that one, assumed it was 10 bit like analogRead )
Now it’s limited to 255:

Starting...
Tick MappedOutValue
0 0
0 6
0 12
0 19
0 25
0 31
0 38
0 44
0 51
0 57
0 63
0 70
0 76
0 82
0 89
0 95
0 102
0 108
0 114
0 121
0 127
0 133
0 140
0 146
0 153
0 159
0 165
0 172
0 178
0 184
0 191
0 197
0 204
0 210
0 216
0 223
0 229
0 235
0 242
0 248
0 255
0 249
0 244
0 239
0 234
0 229
0 224
0 219
0 214
0 209
0 204
0 198
0 193
0 188
0 183
0 178
0 173
0 168
0 163
0 158
0 153
0 147
0 142
0 137
0 132
0 127
0 122
0 117
0 112
0 107
0 102
0 96
0 91
0 86
0 81
0 76
0 71
0 66
0 61
0 56
0 51
0 54
0 58
0 62
0 66
0 70
0 73
0 77
0 81
0 85
0 89
0 93
0 96
0 100
0 104
0 108
0 112
0 116
0 119
0 123
0 127
0 131
0 135
0 138
0 142
0 146
0 150
0 154
0 158
0 161
0 165
0 169
0 173
0 177
0 181
0 184
0 188
0 192
0 196
0 200
0 204
0 202
0 200
0 198
1 196
1 194
1 192
1 190
1 188
1 186
1 184
1 182
1 181
1 179
1 177
1 175
1 173
1 171
1 169
1 167
1 165
1 163
1 161
1 160
1 158
1 156
1 154
1 152
1 150
1 148
1 146
1 144
1 142
1 140
1 138
1 137
1 135
1 133
1 131
1 129
1 127
1 130
1 133
1 137
1 140
1 143
1 146
1 149
1 153
1 156
1 159
1 162
1 165
1 168
1 172
1 175
1 178
1 181
1 184
1 188
1 191
1 194
1 197
1 200
1 204
1 207
1 210
1 213
1 216
1 219
1 223
1 226
1 229
1 232
1 235
1 239
1 242
1 245
1 248
1 251
1 255
1 254
1 254
1 254
1 253
1 253
1 253
1 253
1 252
1 252
1 252
1 252
1 251
1 251
1 251
1 251
1 250
1 250
1 250
1 250
1 249
1 249
1 249
1 249
1 248
1 248
1 248
1 248
1 247
1 247
1 247
1 247
1 246
1 246
1 246
1 246
1 245
1 245
1 245
1 245
1 244
1 244
2 244
2 244
2 243
2 243
2 243
2 243
2 242
2 242
2 242
2 241
2 241
2 241
2 241
2 240
2 240
2 240
2 240
2 239
2 239
2 239
2 239
2 238
2 238
2 238
2 238
2 237
2 237
2 237
2 237
2 236
2 236
2 236
2 236
2 235
2 235
2 235
2 235
2 234
2 234
2 234
2 234
2 233
2 233
2 233
2 233
2 232
2 232
2 232
2 232
2 231
2 231
2 231
2 231
2 230
2 230
2 230
2 230
2 229
2 229
2 229
2 228
2 228
2 228
2 228
2 227
2 227
2 227
2 227
2 226
2 226
2 226
2 226
2 225
2 225
2 225
2 225
2 224
2 224
2 224
2 224
2 223
2 223
2 223
2 223
2 222
2 222
2 222
2 222
2 221
2 221
2 221
2 221
2 220
2 220
2 220
2 220
2 219
2 219
2 219
2 219
2 218
2 218
//more includes for lcd...
#include <MsTimer2.h>

#define PWMPin 13

float xvals[] = {   0,    2,   4,     6,    8,   10,  60};//Time in Seconds (integer)
float yvals[] = { 0.0,  1.0, 0.2,   0.8,  0.5,  1.0, 0.0};//y-value

float delta;
float y=0;
int nsteps=0;
float floatnsteps=0.0;
int i,j;
int nlegs;
int xmax;
int intdtm;


//Tick Counter and interrupt Variable for MsTimer2
float deltaTms =50; //Tick step size
volatile int Tick=0;
volatile boolean interrupt_var=false;

//***** OUTPUT VARIABLES
float MappedOutValue;


void setup(){
  Serial.begin(115200);
//**** Only for Leonardo
  while (!Serial) {
    ; 
  }
  delay(10);
//*****
  Serial.println("\nStarting...");

  pinMode(PWMPin,OUTPUT);
  analogWrite(PWMPin,0);
  MsTimer2::set(deltaTms,inctime); //increase time every deltaTms ms
        Serial.println("Tick MappedOutValue");

}




void loop(){
  //Housekeeping
  nlegs=sizeof(xvals)/4-1; //if erratic results check if 
                           //nlegs=sizeof(xvals)/sizeof(int)-1;
                           //works. Arduino gives wrong results, thus manual setting 
  delta=deltaTms/1000;
  intdtm=(int)deltaTms;
  xmax=xvals[nlegs];
  MsTimer2::start();
  //Starting loop
  
  //***** These for loops should only proceed one step at a time whenever the interrupt variable is called
  for (i=0;i<nlegs;i++){
    nsteps=(int)(((xvals[i+1]-xvals[i])/delta));
    floatnsteps=(float)(nsteps);
    for(j = 0; j<nsteps;j++){
    //  if(interrupt_var){                  //UNCOMMENT
    //    interrupt_var=false;              //UNCOMMENT
        y=lininterpol(yvals[i], yvals[i+1], j/floatnsteps);
        MappedOutValue=255*y;
        analogWrite(PWMPin,(int)MappedOutValue);
        Serial.print(Tick);
        Serial.print(" ");
        Serial.println((int)MappedOutValue);
 //     }                                   //UNCOMMENT
    }
  }
  MsTimer2::stop();
}

float lininterpol(float a, float b, float t){
  return (1-t)*a + t*b;
}

void inctime(){
  Tick++;
  interrupt_var=true;
}

so now it doesn’t map wrongly. But unfortunately I still don’t know where to start looking for stepping through every 50 ms directed by interrupt.

I still don’t know where to start looking for stepping through every 50 ms directed by interrupt.

Try this:-

 for (i=0;i<nlegs;i++){
    nsteps=(int)(((xvals[i+1]-xvals[i])/delta));
    floatnsteps=(float)(nsteps);
    for(j = 0; j<nsteps;j++){
    while(interrupt_var == false){    }      // hold until the interrupt occurs
    interrupt_var=false;              // reset it to false now we have seen it
        y=lininterpol(yvals[i], yvals[i+1], j/floatnsteps);
   ...........................................

Oh thank you very very much, that works! I didn't think of actively waiting for something to occur!