decoding a 80 KHz signal with interrupts, Arduino too slow

Hello all,

I need to decode a signal of around 80 KHz with an Arduino Uno and I am using interrupts to do so. However, it seems it takes to long to step into and out of the ISR. Which is the fastest way to decode a signal like that? Here is a very simple sketch that doesn't behave as I would want to. It simply changes the value of a flag in the ISR and changes it back in the loop(). However I loose rising edges when I do so:

int counter=0;
volatile boolean flag=false;

void setup(){
    pinMode(pinInt2, INPUT);
    attachInterrupt(digitalPinToInterrupt(pinInt2), myISR_Rising, RISING);
}

loop(){
    if(flag){
        counter++;
        flag=false;
    }
}

void myISR_Rising(){
    if(!flag){
        flag=true;
    }
}

After sending a signal of 48 rises, the interruption doesn't get all of them since "counter" is around 30 once the signal has been sent. There must be something I do wrong since it doesn't make sense that the Arduino cannot process a 80 Khz signal. Can anybody help me?

(deleted)

How are you getting the answer out of that sketch? It doesn’t do any serial output. I suspect your ACTUAL sketch has additional code that might be causing the missed interrupts.

According to Nick Gammon's analysis, the interrupt latency(enter and exit) for an external interrupt is about 5us.

80Khz pulse counting with a simple ISR is certainly achievable. For significantly higher frequencies you can use one of the timers as a counter.

I agree with @johnwasser that there is something in the code you haven't shown which is creating a problem at 80K.

Yes, I have this inside the loop():

loop(){
    if(flag){
        counter++;
        flag=false;
    }

    if(micros()>10000000){
        Serial.println(counter);
    }


}

It just waits 10 seconds to print the "counter" value so that I have time to send my signal. I didn't think it was the cause of my code to be slow, and I wanted to be clear and simple with my post, that's why I didn't include it. Do you think the micros() function can be the problem? Thank you!

Is counting the pulses all you are looking to do? If so you'd be much better off doing that with Timer 1 configured as a counter, instead of incrementing a variable in a pin interrupt.

The PD5 pin has a dual purpose as T1. There is hardware in the microprocessor to count pulses on that pins entirely in hardware. It looks like this thread discusses how to do this -> T1 timer counter sketch - Programming Questions - Arduino Forum

No, what I really want to do is to get the time differences between two rising edges. If it is too slow to even count all the pulses, I guess it is not going to be fast enough to get the time differences (and call other functions depending on these differences).

To capture the time differences between the pulses, you can probably achieve that with timer1 in input capture mode.

I've never had much luck making that work - but that is the proper tool for the job.

Try changing

 if(micros()>10000000){

to

 if(micros()>10000000UL){

Mark

Oh by the way the max for an int is 32K so use an unsigned long for counter

Mark

It didn't work with if(micros()>10000000UL){ either...

It just waits 10 seconds to print the "counter" value so that I have time to send my signal.

No, what I really want to do is to get the time differences between two rising edges.

Can you please explain more about what you are trying to measure?

Do you get the expected count with this?

const int pinInt2 = 2;
volatile int counter = 0;

void setup() {
  pinMode(pinInt2, INPUT);
  attachInterrupt(digitalPinToInterrupt(pinInt2), myISR_Rising, RISING);
}

void loop() {

  if (micros() > 10000000UL) {
    Serial.println(counter);
  }
}

void myISR_Rising() {
  counter++;
}

If you get the right count you at least know all the rising edges are being detected.

My final target is to measure the time difference between 2 pulses. This will allow me to decode the data received. To make it simple, I first defined the "counter" variable to just count the pulses received to know if my code was actually working properly or if it was missing pulses (which is what is happening now it seems, because the "counter" doesn't get up to 48 which are all the pulses I receive).

johnwasser:
Do you get the expected count with this?

Yes I do. It is only when I go to the loop() that I miss some of the pulses.
(I also get all of the pulses if I don't switch the flag to true in the ISR)

mengolo:
My final target is to measure the time difference between 2 pulses. This will allow me to decode the data received. To make it simple, I first defined the "counter" variable to just count the pulses received to know if my code was actually working properly or if it was missing pulses (which is what is happening now it seems, because the "counter" doesn't get up to 48 which are all the pulses I receive).

Do you really need the time difference between every set of pulses? Would counting pulses over a time period and dividing to get pulses per second be acceptable?

How DrAzzy said to use Timer1 is truly the best way to measure the time between two pulses, but if just measuring average pulses/sec works for you then it's an easier way to go.

Yes I do.

No you are not getting the right answer. See post number #9

An 80,000 Hz pluse for 10 seconds is how many counts? Is it bigger than the max for an int?

Mark

10 seconds is just the approximate time it takes me to have my TX to transmit the signal I want. The signal contains a fixed number of pulses that I know (48), it does not transmit during all 10 seconds.

I am not sure if I understand your suggestions about using Timer1. Could you post an example code of how you would use Timer1 to measure the time difference between two rising edges? Thank you!

Does this report the expected intervals between rising edges? If there are 48 edges there should be 47 durations.

 const int pinInt2 = 2;
volatile int PulseCount = 0;
volatile unsigned long Durations[50];

void setup() {
  pinMode(pinInt2, INPUT);
  attachInterrupt(digitalPinToInterrupt(pinInt2), myISR_Rising, RISING);
}

void loop() {
  if (millis() > 10000UL) {
    Serial.print(F("PulseCount= "));
    Serial.println(PulseCount);
    for (int i = 0; i < PulseCount; i++) {
      Serial.print(F("Duration "));
      Serial.print(i + 1);
      Serial.print(F("= "));
      Serial.println(Durations[i]);
    }
    while (1);
  }
}

void myISR_Rising() {
  static unsigned long previousTime = 0;
  unsigned long currentTime = micros();
  if (previousTime != 0) {
    Durations[PulseCount++] = currentTime - previousTime;
  }
  previousTime = currentTime;
}