Help with interrupts

I am trying to run this code on a Mega but get a 0 return from the newCycle serialPrint test and it doesnt read the PWM signal. I cant see what's wrong?

const byte fuelin = 44;
const byte fuelout = A6;
volatile unsigned long riseTime = 0;
volatile unsigned long lowTime = 0;
volatile unsigned long highTime = 0;
volatile bool newCycle = false;

void risingEdgeISR();
void fallingEdgeISR();

void setup() {
  Serial.begin(9600);
  
  
 pinMode(fuelin, INPUT);
  pinMode(fuelout, OUTPUT);
  attachInterrupt(digitalPinToInterrupt(fuelin), risingEdgeISR, RISING);
  attachInterrupt(digitalPinToInterrupt(fuelin), fallingEdgeISR, FALLING);
}

void loop(){//change to fuel later
            
    
    Serial.print(newCycle); 
  if (newCycle) {
    noInterrupts(); // Prevent variable changes during read
    unsigned long hTime = highTime;
    unsigned long lTime = lowTime;
    newCycle = false;
    interrupts();

    // Calculate duty cycle
    float dutyCycle = 0.0;
    if (hTime + lTime > 0) {
      dutyCycle = (float)hTime / (hTime + lTime);
    }

    // Map duty cycle to PWM output
    int outputValue = (int)(dutyCycle * 255.0);
    analogWrite(fuelout, outputValue);

    // Debug output
    Serial.print("Duty Cycle: ");
    Serial.print(dutyCycle * 100.0);
    Serial.print("%  |  PWM Output Value: ");
    Serial.println(outputValue);
  
}
  }

// ISR for rising edge
void risingEdgeISR() {
  unsigned long now = micros();
  lowTime = now - (riseTime + highTime); // Time between last high end and new rise
  riseTime = now;
}

// ISR for falling edge
void fallingEdgeISR() {
  unsigned long now = micros();
  highTime = now - riseTime;
  newCycle = true; // Mark that we have a complete cycle
}

Did you intend both interrupts to callback on fuelin or did you intend on one interrupt for fuelin and one for fuelout?

Pin 44 is the main problem: on the Mega, only pins 2, 3, 18, 19, 20 and 21 have external interrupts. The Mega variant file in the Arduino AVR core maps every other pin to NOT_AN_INTERRUPT, so both attachInterrupt calls silently do nothing and newCycle stays 0 forever. There is a second issue: two attachInterrupt calls on the same pin do not run side by side - the core stores exactly one callback per interrupt (intFunc[] in WInterrupts.c), so the second call replaces the first. Move the signal to one of the six interrupt pins and use a single ISR on CHANGE that reads the pin state to tell rising from falling edges.

Right!


Figure-1:

I'm not sure this will work. If I remember correctly you can have one, and only one, of RISING, FALLING or CHANGE on an interrupt pin. (I think there is LOW and HIGH also). You can't have two of them on the same pin.

So I think you need to use CHANGE, and you will need to check the pin level in the interrupt code to decide what action to take.

Another option is to setup a handlers for both rising and falling interrupts, but starting with enabling only one of it and switch to another one at the end of the handler itself.

And it definitely doesn't make sense to call the same ISR.

In short i would

  • Attach one interrupt on an external interrupt pin triggered by CHANGE
  • Test what the status of the pin is on entry of the ISR and execute relevant code

You are probably still pushing the limit of the processor a bit

Depending on how high the PWM frequency is. (How fast is it and where does it come from ??)

Based on Mega pin number (44) I suppose that it is an Arduino analogWrite() output with pwm freq below 1Khz. So the measurement the pwm freq and duty is definitely a possible and a not a very complex task.

Let us try to prove/disprove your above proposition:

1. Let us build the following circuit (Fig-1) where the 1 Hz PWM (~50% duty cycle) signal will interrupt the UNO on both rising and falling edges. The intention is to see if the MCU can distinctly detect both Rising Edge and Falling Edge interrupts on the single INT0 pin.


Figure-1:

2. Let us upload the following sketch:

#define OC1A 9
volatile bool flagR = false;
volatile bool flagF = false;

void setup() {
  Serial.begin(9600);
  pinMode(OC1A, OUTPUT);  //Ch-A

  TCCR1A = 0x00;  //reset
  TCCR1B = 0x00;  //TC1 reset and OFF
  //fOC1A/B = clckSys/(N*(1+ICR1); Mode-14 FPWM; OCR1A controls duty cycle
  // 1 Hz = 16000000/(256*(1+ICR1) N=1,8,64,256,1024==> ICR1 = 62500
  TCCR1A |= (1 << WGM11);                   //Mode-14 Fast PWM
  TCCR1B |= (1 << WGM13) | (1 << WGM12);    //Mode-14 Fast PWM
  TCCR1A |= (1 << COM1A1) | (0 << COM1A0);  //Non-invert: HIGH-LOW
  ICR1 = 62499;                             // TOP for 1Hz frequnecy
  OCR1A = 31250;                            //= 50% duty cycle
  TCNT1 = 0;
  TCCR1B |= (1 << CS12);  //TC1 statrt with N = 256;

  attachInterrupt(digitalPinToInterrupt(2), isrRising, RISING);
}

void loop() {
  if (flagR == true) {
    Serial.println("Rising edge interrupt happened.");
    flagR = false;
    attachInterrupt(digitalPinToInterrupt(2), isrFalling, FALLING);
  }

  if (flagF == true) {
    Serial.println("Falling edge interrupt happened.");
    flagF = false;
    attachInterrupt(digitalPinToInterrupt(2), isrRising, RISING);
  }
}

void isrRising() {
  flagR = true;
}

void isrFalling() {
  flagF = true;
}

3. Output

14:48:12.911 -> Rising edge interrupt happened.
14:48:13.910 -> Falling edge interrupt happened.
14:48:14.895 -> Rising edge interrupt happened.
14:48:15.918 -> Falling edge interrupt happened.
14:48:16.930 -> Rising edge interrupt happened.
14:48:17.927 -> Falling edge interrupt happened

4. Conclusion
The Serial Monitor shows that the MCU is interrupted on both edges of the interrupting signal. But the timestamp shows a time gap of 1 sec between rising and falling interrupts, which should be 500 ms. So, the experiment is ambiguous.

This conclusion does not contradict what @PaulRB wrote; you simply misunderstood him.
The limitation he described is more accurately stated as follows: at any given time, each interrupt source can have only one type of handler attached to it. Now, look at your code—that is exactly what you have. You first set a RISING handler, and then, once the interrupt fires, you set a falling-edge handler. In doing so, the newly assigned handler REPLACES the previous one.

In this respect, your code differs fundamentally from the OP's code: she(he) attempts to attach two handlers to a single interrupt simultaneously—this will not work.

1. I have the following array with name buf[].

I have not misunderstood @PaulRB at all. I ran the experiment because he mentioned the words "If I remember correctly ..."!

My experiment is giving ambiguous results -- Serial Monitor shows that the MCU is apparently interrupted on both edges, but the timestamp does not support it.

Your method of using interrupts is perfectly correct. The inaccuracy in the results is most likely due to a typo in the code. I haven’t examined it closely—for instance, I don’t recall the AVR timer settings without rereading the docs, so I can’t verify your constants.
If you’d like, I can share my own code for measuring PWM on an Uno; it works without any errors.

That 1 Hz PWM with ~50% duty cycle is correct and can be monitored using onboard led L. The constants have come from the frequency equation, which is also given in the setup() function.

I am unable to undestand why does the Serial Monitor show the message that the MCU is interrupted by both edges. I have measured the time difference using millis() which shows the same 1 sec time difference.

I have expected a time difference of 500 ms -- the time difference between the rising and falling edges!

True, but would that be any more efficient than using CHANGE and reading the pin state in the interrupt code? Probably about the same, I would imagine.

It means that the issue is in the your timer settings and not in interrupt firing. The Monitor shows that the MCU is interrupted by both edges because that’s how it actually is.

The CHANGE interrupt requires tracking the direction of the changes.
When I first tackled this problem, it seemed simpler to use Rising and Failing. And then I just proceeded in the way I had already tried :)

I loaded GolamMostafa's code into an Arduino UNO R3.

I checked the frequency at pin 9 using an oscilloscope, and can confirm that it was a 1Hz, 50% duty cycle signal.

I then connected pin 9 to pin 2 and got the following results on the Serial Monitor:

The timestamps indicate that the code is behaving as expected.

I can't explain why you do not see the same, GolamMostafa.

I downloaded the code from post #10, and did not edit it in any way.

If you need accurate timestamps, use millis() at the Arduino side. The serial monitor relies on the PC telling the serial monitor that there is data which might have a delay.

Diferent operating system?

The following setup (Fig-1) works well using two separate interrupt pins, proving that @PaulRB is right in saying that a single interrupt pin can handle only one triggering mode of interrupt signal.


Figure-1:

Sketch:

#define OC1A 9
volatile bool flagR = false;
volatile bool flagF = false;

void setup() {
  Serial.begin(9600);
  pinMode(OC1A, OUTPUT);  //Ch-A

  TCCR1A = 0x00;  //reset
  TCCR1B = 0x00;  //TC1 reset and OFF
  //fOC1A/B = clckSys/(N*(1+ICR1); Mode-14 FPWM; OCR1A controls duty cycle
  // 1 Hz = 16000000/(256*(1+ICR1) N=1,8,64,256,1024==> ICR1 = 62500
  TCCR1A |= (1 << WGM11);                   //Mode-14 Fast PWM
  TCCR1B |= (1 << WGM13) | (1 << WGM12);    //Mode-14 Fast PWM
  TCCR1A |= (1 << COM1A1) | (0 << COM1A0);  //Non-invert: HIGH-LOW
  ICR1 = 62499;                             // TOP for 1Hz frequnecy
  OCR1A = 31250;                            //= 50% duty cycle
  TCNT1 = 0;
  TCCR1B |= (1 << CS12);  //TC1 statrt with N = 256;

  attachInterrupt(digitalPinToInterrupt(2), isrRising, RISING);
  attachInterrupt(digitalPinToInterrupt(3), isrFalling, FALLING);
}

void loop() {
  if (flagR == true) {
    Serial.println("Rising edge interrupt happened.");
    flagR = false;
  }

  if (flagF == true) {
    Serial.println("Falling edge interrupt happened.");
    flagF = false;
  }
}

void isrRising() {
  flagR = true;
}

void isrFalling() {
  flagF = true;
}

Output:

16:02:27.296 -> Rising edge interrupt happened.
16:02:27.754 -> Falling edge interrupt happened.

I would change your code this way (leave all above the loop() untouched):

Test it on your original setup, with a single interrupt pin.