analogWrite inside an interrupt

Hi,

I'm trying to make an RBG led blink at 1Hz (pink -> off -> pink -> off ...)

So I'd like to use analogWrite on the 3 pins to set the pink color, then 1s later alalogWrite to 0.

I tried with the Portenta_H7_TimerInterrupt library. It works if I use digitalWrite, but the portenta crashes if I use analogWrite to change my PWM value.

Is it intended? Did I do something wrong?

My code :

Portenta_H7_Timer ITimer0(TIM1);

void TimerHandler0() {
static bool state = 0;
if(state == true) {
analogWrite(LEDB_PIN, val_ledr);
analogWrite(LEDG_PIN, val_ledg);
analogWrite(LEDB_PIN, val_ledb);
} else {
analogWrite(LEDB_PIN, 0);
analogWrite(LEDG_PIN, 0);
analogWrite(LEDB_PIN, 0);
}
state = !state;
}

void setup() {
pinMode(LEDR_PIN, OUTPUT);
pinMode(LEDG_PIN, OUTPUT);
pinMode(LEDB_PIN, OUTPUT);
ITimer0.attachInterruptInterval(1000 * 1000, TimerHandler0);
}

Please post all your code. The error is probably in the bits of code you missed posting.

Here is a full sketch that make MBed crash :

#include <Portenta_H7_TimerInterrupt.h>

#define LEDR_PIN            3
#define LEDG_PIN            4
#define LEDB_PIN            5

char val_ledr;
char val_ledg;
char val_ledb;

Portenta_H7_Timer ITimer0(TIM1);

void TimerHandler0() {
  static bool state = 0;

  if(state == true) {
    analogWrite(LEDR_PIN, val_ledr);
    analogWrite(LEDG_PIN, val_ledg);
    analogWrite(LEDB_PIN, val_ledb);
  } else {
    analogWrite(LEDR_PIN, 0);
    analogWrite(LEDG_PIN, 0);
    analogWrite(LEDB_PIN, 0);
  }
  state = !state;
}

void setup() {
  pinMode(LEDR_PIN, OUTPUT);
  pinMode(LEDG_PIN, OUTPUT);
  pinMode(LEDB_PIN, OUTPUT);

  val_ledr = 255;
  val_ledg = 51;
  val_ledb = 153;

  ITimer0.attachInterruptInterval(1000 * 1000, TimerHandler0);
}

void loop() {
}

All variables use both inside and outside an ISR should be declared as volatile.

It is also possible that the timer you are using is involved with the generation of the PWM signal and you are pulling the rug from underneath your self.
Just try using one PWM pin as a test, change the number until layout find one that doesn’t crash.

I added volatile for val_led*. I kept only LEDR_PIN and tried 4 timers (1, 4, 7 and 15), none worked.

How about trying to set a flag in the ISR then in the main loop looking for the flag when you see it set stare the PWM and then clear the flag?

Do you know that the timer is firing only every 16 minutes?

You should set compiler warnings on. This should be 1000UL * 1000UL, otherwise it truncates, which is probably what @Grumpy_Mike was referring to. I make it 16.96 seconds.

This is from one of the examples in that library;

// In Portenta_H7, avoid doing something fancy in ISR, for example Serial.print
// Or you can get this run-time error / crash

You could open an incident on Github: Issues · khoih-prog/Portenta_H7_TimerInterrupt · GitHub

Currently, mbed_portenta core has some bug, making analogWrite crashing the Portenta_H7

Just try the simplest code and see

#define LEDB_PIN            25

void setup()
{
  pinMode(LEDB_PIN, OUTPUT);

  Serial.begin(115200);
  while (!Serial);

  delay(100);

  Serial.print(F("\nStarting analogWrite_Crash on ")); Serial.println(BOARD_NAME);
  analogWrite(LEDB_PIN, 150);
}


void loop()
{
}

If possible, you can open an issue on ArduinoCore-mbed issues

Indeed analogWrite seems buggy. I tried with the mbed PwmOut, and it seems to work, even inside the interrupt.

I added the UL, but timer was correctly 1s even without that.

and issue is opened here

1 Like

Oh OK. The int datatype on that platform is anyway 4 bytes.

as replied in my opened ticket 25 is not an allowed pin for analogWrite.

Si my problem remain, why doesn't it work in an interrupt?

#include <Portenta_H7_TimerInterrupt.h>

#define LEDB_PIN            3

volatile char val_ledb;

Portenta_H7_Timer ITimer0(TIM1);

void TimerHandler0() {
  static bool state = 0;

  if(state == true) {
    analogWrite(LEDB_PIN, val_ledb);
  } else {
    analogWrite(LEDB_PIN, 0);
  }
  state = !state;
}

void setup() {
  pinMode(LEDB_PIN, OUTPUT);

  val_ledb = 153;

  ITimer0.attachInterruptInterval(1000UL * 1000UL, TimerHandler0);
}

void loop() {
}

In general, analogWrite is also using Timer Interrupt, and can create crashing issue if used inside ISR and must be avoided. This issue happens so far in many platforms and very implementation-dependent. Only try-and-error can guarantee which platform / core is working OK.

You can see the crashing effect, just by using analogWrite for not-PWM pin, no compile error and no warning at all, just crash.

The correct way for coding inside ISR is just using very short code, possibly just setting a flag, then use a non-ISR timer to check and do real / lengthy / complex works.

Try using Portenta_H7_Slow_PWM library for your simple job.