UNDERSTANDING THE CODE FOR CONTROLLER

I am using an arduino mega2560 to make an engine controller in which i am taking 3 signals coming from an engine as input, i want this code to do this: as soon as the states of master and proximity becomes high, the board should generate a pulse at a particular time specified, which should be on for some time and then go off, i am measuring the time by counting the no. of highs occurring on the incremental signal, which should start counting only just after the states of master and proximity match as high.
But the code is not doing the same, its actually generating the pulse much before the point it should, Can someone please explain me where it is going wrong or explain me the functioning of this code so that i may myself figure out what's going wrong ?

const int master = 21;                                                // interrupt 2    
const int proximity = 20;                                             // interrupt 3
const int incremental = 3;                                            // interrupt 1
const int ledPin = 12;                                                // injector pin
volatile int counter = 0;                                             // to count 360 degrees 
int master_state = 0;
int proximity_state = 0;
volatile float s_o_i = 300;                                             // start of injection
volatile float e_o_i = 360;                                             // end of injection  
volatile int n_o_s = 0;                                               // no of strokes  


void setup()
{
  Serial.begin(9600);                                                  // initialize serial communication
  pinMode(master, INPUT);
  pinMode(proximity, INPUT);
  pinMode(incremental, INPUT);
  pinMode(ledPin, OUTPUT); 
  digitalWrite(ledPin,LOW);                              // so that at start it should not start injecting
  
   attachInterrupt (1, count, RISING);                   // attach interrupt handler of incremental
 }

void count()                                                          // ISR
{
   if(counter == s_o_i)
        {
          digitalWrite(ledPin,HIGH);                                  // start injection
        }
  if(counter == e_o_i)
        {
          digitalWrite(ledPin,LOW);                                   // stop injection
          counter = 0;
         }
  counter++;
}

void loop()
{
  noInterrupts();                                                      // turning off the interrupts
  master_state = digitalRead(master);
  proximity_state = digitalRead(proximity);
  
  if (master_state == HIGH && proximity_state == HIGH)
  {
    interrupts();                                                      // turning on the interrupts           
  }
}

Perhaps detecting proximity too soon?

Codewise I would replace

  noInterrupts();                                                      // turning off the interrupts
  master_state = digitalRead(master);
  proximity_state = digitalRead(proximity);
  
  if (master_state == HIGH && proximity_state == HIGH)
  {
    interrupts();                                                      // turning on the interrupts           
  }

with

  if ( digitalRead(master)) // true if HIGH, false if LOW
  {
    if ( digitalRead(proximity))
    {
      interrupts();                                                      // turning on the interrupts
    } 
     else
    {
       noInterrupts();                                                 // turning off the interrupts
    }
  }
  else
  {          
     noInterrupts();                                                   // turning off the interrupts
  }

And put noInterrupts() is setup and lose the 2 int variables and assignments and &&.

But that does not explain early triggering.

Why not just put a test in the ISR so it does nothing if a variable is false

Pseudo code

startInjecting = false;
if (conditionsAreRight) {
     startInjecting = true;
}

void count() {
  if (startInjecting == false) {
    return;
  }
  // other stuff
}

...R

, i am measuring the time by counting the no. of highs occurring on the incremental signal

you define const int incremental = 3;
Yet you attach your interrupt to pin 1 rising. Why?

pin no 3 is the interrupt no 1, refer to this link:

pranesh:
pin no 3 is the interrupt no 1, refer to this link:

attachInterrupt() - Arduino Reference

Ooops. You're right. In that case I'll checkout your code more carefully. :slight_smile:

OK This is what I think happens.

On the first occassion that master_state and proximity_state are high, the interrupts are enabled.

After 300 pulses of your incremental signal LEDpin is sent high
60 pulses later LEDpin is sent low again and counter is reset

For these events to occur, master_state and proximity_state must remain high
(otherwise the loop function will disable interrupts and the count stops happening)

SO
once the counter gets set back to zero, master_state and proximity_state must still be high
the loop function therefore enables interrupts
and counting continues.
(even though it's not the next revolution yet)
if master_state or proximity_state go low before the increment has reached 300, you won't be aware this has happened.

When master_state and proximity_state are both high again the counting will start again
BUT not from zero this time. So the led will blink early

So building on my prior assumption. I've modified the loop routine that decides when the interrupts are enabled and disabled.

See how this goes.

const int master = 21;                                                // interrupt 2    
const int proximity = 20;                                             // interrupt 3
const int incremental = 3;                                            // interrupt 1
const int ledPin = 12;                                                // injector pin
volatile int counter = 0;                                             // to count 360 degrees 
int master_state = 0;
int proximity_state = 0;
volatile float s_o_i = 300;                                             // start of injection
volatile float e_o_i = 360;                                             // end of injection  
volatile int n_o_s = 0;                                               // no of strokes  


volatile bool fired=0;  //SET BY ISR to signal injection has finished

void setup()
{
  Serial.begin(9600);                                                  // initialize serial communication
  pinMode(master, INPUT);
  pinMode(proximity, INPUT);
  pinMode(incremental, INPUT);
  pinMode(ledPin, OUTPUT); 
  digitalWrite(ledPin,LOW);                              // so that at start it should not start injecting
  attachInterrupt (1, count, RISING);                   // attach interrupt handler of incremental
  noInterrupts();                                                      // turning off the interrupts
 }


void count()                                                          // ISR
{
  if(counter == s_o_i)
        {
          digitalWrite(ledPin,HIGH);                                  // start injection
        }
  if(counter == e_o_i)
        {
          digitalWrite(ledPin,LOW);                                   // stop injection
          counter = 0;
          fired=1;
         }
  counter++;
}

bool MPLastState=0;    //keeps track of master/proximity

void loop()
{
bool MPThisState=digitalRead(master)&digitalRead(proximity);//high if they're both high
 
  if(fired)
  {noInterrupts();// turning off the interrupts until next revolution
   fired=0;
   counter=0;//belt and braces;   
  }
  
if (MPThisState > MPLastState)//Signal has just become positive
  {interrupts();
  } 
MPLastState=MPThisState;
}

KenF:
So building on my prior assumption. I've modified the loop routine that decides when the interrupts are enabled and disabled.

Why not leave them enabled all the time and use a variable to indicate when to take notice of them.

...R

Robin2:

KenF:
So building on my prior assumption. I've modified the loop routine that decides when the interrupts are enabled and disabled.

Why not leave them enabled all the time and use a variable to indicate when to take notice of them.

I was just following the OPs lead. But it comes out simpler your way.

const int master = 21;                                                // interrupt 2    
const int proximity = 20;                                             // interrupt 3
const int incremental = 3;                                            // interrupt 1
const int ledPin = 12;                                                // injector pin
volatile int counter = 0;                                             // to count 360 degrees 
int master_state = 0;
int proximity_state = 0;
volatile float s_o_i = 300;                                             // start of injection
volatile float e_o_i = 360;                                             // end of injection  
volatile int n_o_s = 0;                                               // no of strokes  
volatile bool fired=0;  //SET BY ISR to signal injection has finished
volatile bool inTheZone=0; //High during countdown to injection.



void setup()
{
  Serial.begin(9600);                                                  // initialize serial communication
  pinMode(master, INPUT);
  pinMode(proximity, INPUT);
  pinMode(incremental, INPUT);
  pinMode(ledPin, OUTPUT); 
  digitalWrite(ledPin,LOW);                              // so that at start it should not start injecting
  attachInterrupt (1, count, RISING);                   // attach interrupt handler of incremental
 }


void count()                                                          // ISR
{
  if(counter == s_o_i)
        {
          digitalWrite(ledPin,HIGH);                                  // start injection
        }
  if(counter == e_o_i)
        {
          digitalWrite(ledPin,LOW);                                   // stop injection
          counter = 0;
          inTheZone=false;
         }
if(inTheZone)
  counter++;
}

bool MPLastState=0;    //keeps track of master/proximity
void loop()
{

bool MPThisState=digitalRead(master)&digitalRead(proximity);
//high if they're both high

if (MPThisState > MPLastState)//Signal has just become positive
  {inTheZone=true;
  } 
MPLastState=MPThisState;
}
const int master = 21;                                                // interrupt 2    
const int proximity = 20;                                             // interrupt 3
const int incremental = 3;                                            // interrupt 1
const int ledPin = 12;                                                // injector pin
volatile int counter = 0;                                             // to count 360 degrees 
int master_state = 0;
int proximity_state = 0;
volatile float s_o_i = 300;                                             // start of injection
volatile float e_o_i = 360;                                             // end of injection  
volatile int n_o_s = 0;                                               // no of strokes  


void setup()
{
  Serial.begin(9600);                                                  // initialize serial communication
  pinMode(master, INPUT);
  pinMode(proximity, INPUT);
  pinMode(incremental, INPUT);
  pinMode(ledPin, OUTPUT); 
  digitalWrite(ledPin,LOW);                              // so that at start it should not start injecting
  
   attachInterrupt (1, count, RISING);                   // attach interrupt handler of incremental
 }

void count()                                                          // ISR
{
   if(counter == s_o_i)
        {
          digitalWrite(ledPin,HIGH);                                  // start injection
        }
  if(counter == e_o_i)
        {
          digitalWrite(ledPin,LOW);                                   // stop injection
          counter = 0;
         }
  counter++;
}

void loop()
{
  noInterrupts();                                                      // turning off the interrupts
  master_state = digitalRead(master);
  proximity_state = digitalRead(proximity);
  
  if (master_state == HIGH && proximity_state == HIGH)
  {
    interrupts();                                                      // turning on the interrupts           
  }
}

I see that you zero counter at e_o_i.
But the states may still be HIGH, correct?
In shich case the count continues to increment until one or both go low and count is not zero so next time it begins with a head start and early triggers every time.

GoForSmoke:
I see that you zero counter at e_o_i.
But the states may still be HIGH, correct?
In shich case the count continues to increment until one or both go low and count is not zero so next time it begins with a head start and early triggers every time.

NO The count continues until the count function tells it to stop. Counting will not start again until the digitalRead(master)&digitalRead(proximity) TURNS high. Simply still being high will not restart counting. They have to TURN from LOW to HIGH to restart the counter.

KenF:

GoForSmoke:
I see that you zero counter at e_o_i.
But the states may still be HIGH, correct?
In shich case the count continues to increment until one or both go low and count is not zero so next time it begins with a head start and early triggers every time.

NO The count continues until the count function tells it to stop. Counting will not start again until the digitalRead(master)&digitalRead(proximity) TURNS high. Simply still being high will not restart counting. They have to TURN from LOW to HIGH to restart the counter.

In Pranesh's code, the interrupts are enabled when master and proximity are high. Count is incremented when interrupts are on and pin 3 goes LOW to HIGH. Master and proximity simply need to be HIGH to turn interrupts on, no transition is required.

He has a short time with interrupts disabled from the start of every loop() to the evaluation of master and proximity enabling interrupts, but look at the time slice interrupts stay on. It is maybe less than half the loop() cycle which is why I made the first suggestion in code, leave interrupts on during the whole time proximity is detected to catch all the triggers.

Do we know what drives proximity? I don't recall seeing a circuit or diagram. Maybe it is a Hall sensor detecting a magnet or a gear tooth counter or light detector or direct wire feed. Do you know?

I think I would not bother resetting the count. I would define it as unsigned long and use it the way one uses millis() for timing.

if (curCount - prevCount >= nnn) {
   prevCount = prevCount + nnn;

   // other stuff
   curCount ++;
}

...R

i would zero it whenever proximity or master is LOW and avoid the subtract, compare and addition.

That would get rid of the need to turn interrupts on and off too though the clocks would run then.

GoForSmoke:
i would zero it whenever proximity or master is LOW and avoid the subtract, compare and addition.

That would get rid of the need to turn interrupts on and off too though the clocks would run then.

The example I gave in answer #9 doesn't turn interrupts on or off. It simply implements a global variable inTheZone to decide whether we're counting or not.

Can anyone explain why s_o_i and e_o_i are declared as volatile floats rather than constant int's?

And where the heck is n_o_s used or referenced?

Whatever passes between the interrupt routine and outer code must be volatile or it may be corrupted or lost.

There's actually more to do in some cases, one reason why I avoid interrupts when they're not actually needed.

Here's more than I've memorized, explained well.

I tried KenF's code but it is not even generating a single pulse now, i think the board's processing speed is slow for this application since the master signal remains on for only 380 microseconds whereas the proximity is on for 27.9 ms (the engine is running at 2 Hz i.e a master signal comes every 0.5 second), so the whole comparison and stuff has to happen in a very small piece of time.
Is it the way i think or something else?

pranesh:
I tried KenF's code but it is not even generating a single pulse now, i think the board's processing speed is slow for this application since the master signal remains on for only 380 microseconds whereas the proximity is on for 27.9 ms

You may need to use an interrupt to detect the master signal.

But I am now unclear about the logic of the system. You are counting pulses when both master and proximity are HIGH. Does that mean you only want to count pulses for the 380 usec duration of the master signal. That may mean using a CHANGE interrupt for the master signal.

I may well be wrong, but I get the feeling that it would do no harm to write out the requirement again from scratch taking account of all that has been said in the various posts so far.

...R