Lost with interrupts

Good morning
This is what I am trying to achieve without great success so far...
I have to manage 4 interruptions triggered by a change from 1 to 0 of the pin value (FALLING), this is OK.

In the 4 routine service I would like to:
1- When entering in the function executed by the interruption => Mask/Unable new interruption on the same pin while keeping interruptions allowed/possible on the 3 others pins
2- When exiting from the function executed by the interruption => Allowed again new interruption on the same pin.

It means that I want to have only one interruption at the same time on one pin but accept to be interrupted by an another interruption while executing the interruption routine.

Do you know if it is feasible? What do you recommend?

Thanks a lot for your help.
Raoul.

Yes, this is possible. You have to take care that you do not overflow the stack though.

Mask/Unable new interruption on the same pin while keeping interruptions allowed/possible on the 3 others pins

When you enter an ISR all other interrupts are ignored until you exit. You could manually enable them but that's a bad idea unless you really know what you are doing.

When exiting from the function executed by the interruption => Allowed again new interruption on the same pin.

That will happen automatically.

It means that I want to have only one interruption at the same time on one pin but accept to be interrupted by an another interruption while executing the interruption routine.

Not really possible (see above). However the other interrupts will be "remembered" and handled when you exit the ISR. It's for this reason that an ISR should not be very long.

What do you recommend?

Each ISR simply sets a flag eg

volatile boolean pin_2_high = false;

my_ISR_for_pin_2 () { // repeat for other pins
    pin_2_high = true;
}


loop () {

   if (pin_2_high) { // repeat for other pins
      // do stuff for pin 2 
      pin_2_high = false;
   }
}

Rob

Garynomad
Thanks a lot.
You mentionned that the other interrupts that may happen while executing in ISR are remembered, meaning not lost ?

I was wondering if there if any possibility to prioritize among the interruptions to ensure the highest priority is executed in case of simultaneous interrupts?

Last basic question, I am testing interrupts with the simple code below.
ISR is trigered (as expected) when the signal is FALLING but then ISR is executed in loop until the signal is again HIGH, that was not expected and not what I want to achieve. I would like to have only one execution of ISR by FALLING signal.
Is there something wrong?

void setup(){

// Initialisation port serie pour le test
Serial.begin(9600);
Serial.print("test liaison serie OK // ");

// Attache la routine readPulse0 à l'interruption 0
attachInterrupt(0,readPulse0,FALLING); // la routine est exécurée suite à un changement d'état de 1 à 0
}

void loop()
{
}

// Routine d'interruption readPulse0
void readPulse0(){
Serial.print("Execution de readPulse0() ... ");
}

Thanks again to supporting.
Raoul.

You mentionned that the other interrupts that may happen while executing in ISR are remembered, meaning not lost ?

Correct, the hardware raises a flag for each interrupt, these flags are only reset by executing the appropriate ISR. On exiting an ISR the processor will execute a single instruction from the interrupted program then service the next interrupt.

I was wondering if there if any possibility to prioritize among the interruptions to ensure the highest priority is executed in case of simultaneous interrupts?

On AVR processors the lower an interrupt's position in the interrupt vector table the higher the priority, thus INT0 is the highest priority (of the external interrupts that is) and INT7 is the lowest. Note that these numbers DO NOT map directly to the Arduino interrupt numbers (for reasons that one can only guess at)

The mapping is as follows

AVR Ard
0 2
1 3
2 4
3 5
4 0
5 1
6 na
7 na

So if my mapping is correct the priority order for Ardruino interrupts is 2,3,4,5,0,1.

The only way to have priority without external hardware is to connect your inputs as per the above.

but then ISR is executed in loop until the signal is again HIGH

I don't see how that can be happening with that code, maybe a few triggers if the input is not debounced, but not continuos.

What is driving the input?


Rob

Rob
Thank you, this is cristal clear now.

The test is done with the pin 2 (of Dualmilanove) connected to the 5V power supply (of the board)
Then I disconnect manually and reconnect manually.

I was wondering if the serial port could interfer on the interrupt behaviour.
I eliminated possible rebound on pin 2 because pin remains on air and then we should stop entering into the ISR...

Raoul.

The test is done with the pin 2 (of Dualmilanove) connected to the 5V power supply (of the board)
Then I disconnect manually and reconnect manually.

Any input pin that is left floating can oscillate or take on whatever voltage it likes. If you are holding the wire there's a good chance 50/60Hz is being coupled through to the wire and therefore to the interrupt.

You must use a pull down resistor, or reverse the arrangment and use the internal pull up resistor.

I eliminated possible rebound on pin 2 because pin remains on air and then we should stop entering into the ISR...

Not quite sure what you mean here, but see above. Even with a resistor you WILL get bounce.

I was wondering if the serial port could interfer on the interrupt behaviour.

No, I can't see how, unless the pin is floating as the two pins are next to each other (see above again :)).


Rob

Rob

Thank you, you are helping me a lot.

I am now testing the "real" software for one interrupt in the Arduino Mega 2560.

Pin 2 and pin 7 are reiciving the same signal (a set of several pulses lasting from 50 microsecondes to 400 microsecondes)
Pin 2 is used to trog ISR on interrupt 0.
Pin 7 is used to measur the lenght of the valid pulses received (NPmax pulses are read).

The ISR is triggerered in the first FALLING in pin2 and measures the duration of each valid pulse until NBmax is reached, then a flag int0 is position at true for the loop(). All durations are stored in an array.

The loop sort the array and calculate the average of the highest values ("moy" values are processed), in order to identify the emitter of the pulses according to the duration. (in fact the emitter is a digital car running in a slot car circuit, the signal sent constinuously by the car is read by a simple electronic based on a photo-transistor). Then the lag int0 is set at false.
This is working fine, I can identify the different cars.
BUT only ONCE and have to reset the soft to make it again.

I will have understand if you don't have time to read the following code, maybe based on your experience you will see immediatly what is going wrong...I am sorry for the comments in french but this is my mother tong.

Thanks for all.
Raoul

// LIBRAIRIES
// inclure la librairie de gestion de l'afficheur LCD: LiquidCrystal
//#include <LiquidCrystal.h>
// Inclure la librairie mathématiques
#include <math.h>

unsigned long duree;

// Nombre maximum de voitures à identifier
const int maxCar = 8; // EN REALITE CETTE VALEUR SEERA INITILISEE AU DEMARRAGE DE LA COURSE, 8 est le maximum théorique

// Définition de la classe Car contenant l'ensemble des informations relatives à la voiture
class Car{
public:
unsigned long pulseID; // Longueur du pulse d'identification (chaque voiture emet un signal carré constant dont la durée permet d'identifier la voiture
} race[maxCar];

unsigned long newDuree;
volatile int NP; // Compteur de pulses utilisé dans l'ISR
const int NPmax=5; // nombre maximum de pulses enregistrés lors du passage d'une voiture
const int moy=3; // nombre de pulses à prendre en compte pour calculer la moyenne
const int nbCar=4; // indique le nombre de voiture en course (sera fixé lors de la routine d'init)

int pin; // pin utilisée pour la détection

// Structure d'enregistrement de la longueur des pulses pour le détecteur 0 (correspondant à l'interruption 0)
class PulseDetecteur0 {
public:
unsigned long pulseTime0;
} pulseL0[NPmax];

// Structure d'enregistrement de la longueur des pulses pour le détecteur 1 (correspondant à l'interruption 1)
class PulseDetecteur1 {
public:
unsigned long pulseTime1;
} pulseL1[NPmax];

// Structure d'enregistrement de la longueur des pulses pour le détecteur 2 (correspondant à l'interruption 2)
class PulseDetecteur2 {
public:
unsigned long pulseTime2;
} pulseL2[NPmax];

// Structure d'enregistrement de la longueur des pulses pour le détecteur 3 (correspondant à l'interruption 3)
class PulseDetecteur3 {
public:
unsigned long pulseTime3;
} pulseL3[NPmax];

// déclaration des flags d'éxecution d'interruption
volatile boolean int0;
volatile boolean int1;
volatile boolean int2;
volatile boolean int3;

// Fixe le timeout en microsecondes de la fonction pulseIn
const unsigned long timeOut=400;

void setup(){

// Initialisation des pulseID pour identification de la voiture
race[0].pulseID=32; // la durée nominale est 64 microsecondes
race[1].pulseID=70; // la durée nominale est 128 microsecondes
race[2].pulseID=140;// la durée nominale est 192 microsecondes
race[3].pulseID=175; // la durée nominale est 256 microsecondes
race[4].pulseID=210; // la durée nominale est 320 microsecondes
race[5].pulseID=352; // la durée nominale est 384 microsecondes
race[6].pulseID=416; // la durée nominale est 448 microsecondes
race[7].pulseID=480; // la durée nominale est 512 microsecondes
race[8].pulseID=544;

// Définir ISR attachée à l'interruption 0
attachInterrupt(0,readPulse0,FALLING); // readPulse0 est activée par un passage de 1 à 0 sur la pin correspondant à l'interruption 0

// Définir la pin sur laquelle lire les pulses d'identification de la voiture
pin=7;
pinMode(pin,INPUT);

// les flags d'interruptions sont mis à la valeur false (ce qui signifie aucune interruption n'est survenue)
volatile boolean int0 = false;
volatile boolean int1 = false;
volatile boolean int2 = false;
volatile boolean int3 = false;

// Initialisation et test de la liaison série pour les besoins des tests
Serial.begin(9600);
Serial.print("test serie OK // ");

}

void loop(){
if (int0) { // cela signifie que une l'interruption 0 a été exécutée, il faut classer le tableau correspondant à l'interruption 0
// classement du tableau par ordre croissant des pulses mesurés
for (int i=1; i<NPmax; i++){
for(int j=0; j<NPmax-1;j++){
if (pulseL0[j].pulseTime0 > pulseL0[j+1].pulseTime0) { // les nombres échanges leur position dans le tableau
unsigned long inter=0;
inter=pulseL0[j].pulseTime0;
pulseL0[j].pulseTime0=pulseL0[j+1].pulseTime0;
pulseL0[j+1].pulseTime0=inter;
}
}
}
duree=0;
//calcul de la moyenne des moy plus grands pulse
for(int i=NPmax-1; i>=NPmax-moy; i--){
duree= duree+ pulseL0*.pulseTime0;*

  • }*

  • duree=duree/moy; *

  • // identification voiture qui a declanchée l'interruption*

  • for (int v = 0; v <= maxCar-1; v++) {*

  • if (duree >= race[v].pulseID && duree <= race[v+1].pulseID) {*

  • Serial.print("D=");*

  • Serial.print(duree);*

  • Serial.print(" // ");*

  • Serial.print("Car=");*

  • Serial.print(v+1);*

  • Serial.print("\n");*

  • duree=0;*

  • break;*

  • }*

  • }*

  • //AFFICHAGE POUR LES BESOINS DU TEST*

  • for( int i=0; i<NPmax; i++){*
    _ Serial.print(pulseL0*.pulseTime0);_
    _
    Serial.print(" // ");}_
    _
    //FIN AFFICHAGE POUR LES BEOSINS DU TEST*_
    * int0 = false; // reset du flag d'execution de l'interruption 0*
    * }*
    }

// ISR pour l'interruption 0 = readPulse0()
void readPulse0()
* {*
* NP=0;*
* while (NP <= NPmax){ // On veut mesurer NPmax pulses puis sortir de l'ISR*
* newDuree=pulseIn(pin,LOW,timeOut);*
* if (newDuree >= 10 && newDuree <= race[nbCar+1].pulseID){ // le pulse est valide*
* pulseL0[NP].pulseTime0=newDuree; // le pulse valide est conservé*
* NP++;*
* }*
* }*
* int0 = true; // indique l'éxecution de l'ISR*
* NP=0;*
* Serial.print("readPulse0 "); // for test*
* }*

As I see it, each car produces a pulse train with a different frequency? This pulse train is read as the car passes a sensor?

Are the cars on seperate tracks? If so why the need to have this identifiaction scheme? And you really need to average pulses to identify a car?

I can't see a reason for this to work only once.

There's a lot going on in the ISR, including blocking code like the pulseIn() and a long println at 9600bps. I gather the cars don't pass that close to each other.

One thing that doesn't seem right is an ISR that depends on exactly NPmax pulses from the real world.

Can you garrantee you will always get NPmax pulses (5?) to measure? If not the ISR may block until enough pulses arrive or you get enough timeouts.

Conversly what happens if you get say 7 pulses, you will re-enter the ISR immediately having just counted 5 pulses then only have 2 left, at which point you have the problem I mentioned in the last point.

Then to complicate matters, what happens if another car arrives at the same time? You have interrupts happening all over the place.

Sorry, more questions than answers I'm afraid.


Rob

Hi
I found a work around, but still not understand what was wrong.
Raoul.

Hi Rob
I just noticed that you answered...

Up to 6 cars can run on 2 slots that is why identification is mandatory to count laps and timing.

I suspected your point about NPmax and tried different values without sucess...

Finally I decided to measure the lenght of all pulses and exit when the highest value is reached (doing this I am sure not to be blocked in the ISR). It seems to work even if it could be less secure to rely on one measure only.

It may happen that 2 cars crossed at the same time 2 sensors (lane 1 and lane 2 or pitlane in/pitlane out), in this case the lap timing of one of the cars will be affected by few ms (but nost lost as you told me that interrupts are remembered by AVR) what is acceptable for a such application.
Of course it is not fully satisfactory in terms of design.

Thank you a lot for spending your time on my questions.
It has been a helpful.

Regards.
Raoul.

I posted a picture where you can see the sensors (photo transistor) and Arduino

Slot car.jpg

Hi
First time using interruptions with Arduino and still fighting...

Following the same project I am now abble to identify a car, i stressed the solution forcing 2 cars crossing at the same time the sensor, seems OK.

My problem now is that I am not able to use/share the data modified during the execution of the ISR. In may case it's one integer identifying the car and one unsigned long recording the time in ms (via millis()) when crossing the sensor.
I would like to use this data in the loop and get 0 as value while the data was Ok at the end of ISR execution.

I read some post advising to mask interruptions when reading shared data, tried the code proposed but it doesn't work.

Any idea?

Thanks.
Raoul.

Don't use Serial.println in an interrupt routine, even for testing. It will totally throw out what is happening, so your debugging will effectively alter what it is you are trying to debug.

Can you repost the code that doesn't work, and use the "code" tags? (select it and hit the "#" (insert code) button on the forum). I'm finding it hard to read.

Thank you Nick.
Here it is one ISR (there are all in the same model), I added the declarations in case there is something wrong here (I take care to use volatile for shared data), comments in the code are in french, sorry for that...

volatile boolean int0; 
volatile unsigned long duree;
volatile unsigned long newduree;
volatile int carID1; // identifié par le détecteur 1
// Définition de la classe Car contenant l'ensemble des informations relatives à la voiture
class Car{
  public:
  unsigned long pulseID; // Longueur du pulse d'identification (chaque voiture emet un signal carré constant dont la durée permet d'identifier la voiture
  int lapDone; // Nombre de tours effectués
  int carPos; // Position de course 
  unsigned long lapTime; // durée du tour en ms
  unsigned long oldLapTime; // durée du tour sur passage détecteur
  unsigned long startTime; // temps machine au premier passage sur le détecteur
  unsigned long pitStopTime; // durée d'arrêt dans les stands en ms
  volatile unsigned long pitStopInTime; // Temps système à l'entrée des stands en ms
  unsigned long pitStopOutTime; // Temps système à la sortie des stands en ms
  unsigned long bestLapTime; // Meilleur tour en ms
  unsigned long totalLapTime; // temps écoulé depuis le début de la course
  volatile unsigned long intTime1; // Temps de passage au détecteur 1
  volatile unsigned long intTime2; // Temps de passage au détecteur 2
  volatile unsigned long intTime3; // Temps de passage au détecteur 3
  volatile unsigned long intTime4; // Temps de passage au détecteur 4
} race[maxCar];




// ISR pour l'interruption 0  = readPulse0() >>>>> DETECTEUR 1 (sur ligne d'arrivée)
 void readPulse0()
  {
  newduree=pulseIn(7,LOW,timeOut);  // lecture du pulse sur la pin 7
  if (newduree >= 10 && newduree <= 400){
    if (newduree > duree){
      duree=newduree;
    }
    NP++;
  }
  else
  {
    NP=0;
  }
  if (duree >= 10 && NP == 0) {
     for (int v = 0; v <= maxCar-1; v++) {
      if (duree >=  race[v].pulseID && duree <= race[v+1].pulseID) {
        carID1=v;
        intTime1=millis();
        if ((intTime1 - race[v].intTime1) > 3000) { // le temps de passage est valide (sinon il s'agit du même passage sur le même détecteur)
          race[v].intTime1=intTime1;
        }
        break;
      }
    }
  } 
    int0 = true; // indique l'éxecution de l'ISR
  }

Here is an extract of the loop() processing data after ISR:

 if (int0) { // cela signifie que une l'interruption 0 a été exécutée suite à une détection sur le détecteur 1
  crossLine(carID);
  int0 = false; // reset du flag d'execution de l'interruption 0
  }

crossLine(carID) is doing lap counting, timing and other similar stuff for the car carID.

My point is not being able to use the carID generated by the ISR (here called carID1) within the loop(). I removed Serial.print from ISR but unfortunately behaviour is the same. I was suspecting entering several times in the same ISR, as the principle for identifying a car is to stop reading pulses when the highest value is reached in the first pulses received, then of course if pulses continue to arrive ISR can be triggered again and so on... So i added at the end of the ISR the following instruction to wait until the last pulse of the train but stil not working, I can not use carID in the loop (always at 0). By the way the same happens with the second data I would like to share: race[v].intTime1.

while(pulseIn(7,LOW,timeOut)){} // Attendre la fin du train de pulse

Thank you for helping.
Raoul.

I wouldn't use pulseIn in an ISR. That itself waits for things which may stretch out the routine far too long.

Basically in an ISR you should just be counting stuff or setting flags. Maybe note what the current time is in ms.

Rather than trying to debug what you have, I would be thinking about the basic design. You have 6 cars and 2 tracks, right? And as they pass a sensor you try to work out which car is which? And this is done by detecting a series of slots (holes) or something on the side of the cars? So 4 slots might be car 4?

This model CPU can handle 2 interrupts if I am correct, so you want one sensor per track, connected to the 2 interrupt pins (pins 2 and 3). My problem would be detecting the start of a car from the middle of it. Is there a way of telling this (perhaps a slight gap between cars at some height level)?

So when the interrupt fires - that is a car is somewhere near the sensor - perhaps the 2nd sensor can tell you this is the start of the car? (A digitalRead of the other sensor). If so, you clear the count of slots for this track, and remember the time. Then you let the interrupts just count up, and with luck you could use millis () to find the distance between the slots (the car might be going too fast, I would have to check that in practice).

Then at some point you detect that the car must have passed - some sort of "end of car" sensor. You know your setup better than me. At this stage the count of transitions in the ISR gives you the car number.

Thank you Nick and Rob for your help.

It seems I found a solution reducing as much as possible the job done in the ISR and using a save interrupt register/mask interrupts/read share data/restore interrupt register/allow interrupt sequence to read from loop the 2 data generated by ISR (time and carID) (http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1261124850).

unsigned long CalledFromLoopToGetPulseCounts( void )
  {
    unsigned long c;
    {
      uint8_t SaveSREG = SREG;   // save interrupt flag
      cli();   // disable interrupts
      c = PulseCounts;  // access the shared data
      SREG = SaveSREG;   // restore the interrupt flag
    }
    return( c );
  }

Nick your solution is feasible, nevertheless cars already sent a continuous train of pulses (via IR diode under the car), the duration of the pulse is giving the carID. The gap between 2 cars id 64 micro secondes, this is why I need to read how long a pulse is lasting.

After some time spent in debugging it seems to work.
Let see if the solution will resist to a real and intensive usage...

Thank you.
Raoul.

There's no need to save SREG, a simple

cli();
c = PulseCounts;
sei();

would do I think. And if that's all the function does I'd just put this code inline, it will be 10x faster.


Rob

Rob.
I will try, thank you.
Raoul.