Arduino Nano 33 BLE mbed os crashes when PWM on more than 3 digital pins

Arduino Nano 33 BLE mbed os crashes when PWM on more than 3 digital pins.

We are using ARDUINO NANO 33 BLE, Model NINA-8306

This is a relatively long post.

We are evaluating Nano 33 BLE for a project. The project requires 8 digital PWM outputs.

According to the documentation, pins D2 to D13 are PWM capable. We chose pins D2 to D9.

First we discovered that PWM on more than 4 digital pins crashed the mbed os (4 long, 4 short yellow blinks). Going over the nordic documentation we discovered that more than 4 pwm at a time seems like a HW limitation. Having a modern silicon with such a limitation is something I’d rather not comment on. Fine. I went over the github cpp code, RE’d it and found wiring_analog.cpp. Inside we found someone already discovered the problem and made a change to it. Being rather lazy I made a similar change directly in my wiring_analog.cpp and recompiled. All digital pins can now be used one at a time provided we destroy them after use. Next we tried to run 4 pins simultaneously and this is where we hit a wall. We could not run more than 3 at a time, adding the fourth again crashes the mbed os.

Has anyone encountered a similar problem and if you did and solved it, what is the solution?

Below I am attaching my test code. Inside I also added the cpp mod in a comment if you want to reproduce the first problem.

I know this is whining but having such a good silicon with the rest of the hw on the pcb, BT, acc, etc, and not being able to use pwm on more than 4 pins at a time as a hw limit and more than 3 pins simultenously is a ridiculous deal-breaker. I’d be very happy someone to point out that I am wrong and full of sh**. Even the old atmega nano could beat that (though it has 6 pwm 8 bit pins only).

We are using the latest IDE and libraries as of time of this post.

Home someone could chime in.

Cheers.

#ifdef ARDUINO_SAMD_VARIANT_COMPLIANCE
#define SERIAL SerialUSB
#define SYS_VOL   3.3
#else
#define SERIAL Serial
#define SYS_VOL   5
#endif

// Duty cycle PWM pin assignment
int FL_RPWM_Output = D2; // Arduino PWM duty output
int FL_LPWM_Output = D3; // Arduino PWM duty output

int FR_RPWM_Output = D4; // Arduino PWM duty output
int FR_LPWM_Output = D5; // Arduino PWM duty output

int RL_RPWM_Output = D6; // Arduino PWM duty output
int RL_LPWM_Output = D7; // Arduino PWM duty output

int RR_RPWM_Output = D8; // Arduino PWM duty output
int RR_LPWM_Output = D9; // Arduino PWM duty output

void setup()
{
}

void loop()
{
    // Seems that Nano 33 BLE cannot simultaneously PWM on more than 4 digital pins which is a severe design deficiency. Who the ****  made this decision. Is it baked into the silicon? Or mbed os?
    // In order to workaround this sh***** design the wiring_analog.cpp was updated to destroy any digital pin which value was set to <= 0 (in the case below I set it to -1).
    // Any time you need to move the pwm from pin to pin, you must analogWrite(DIGITAL_PIN, -1); What a load of manure. I am lucky I need to use only 4 at a time.
    /* Below is the change I made.
  float duty = (float)val/(float)(1 << write_resolution);
  mbed::PwmOut* pwm = digitalPinToPwm(pin);
  if (pwm == NULL)
  {
    pwm = new mbed::PwmOut(digitalPinToPinName(pin));
    digitalPinToPwm(pin) = pwm;
    pwm->period_ms(2);
  }
  if (duty <= 0)
  {
    delete pwm;
    digitalPinToPwm(pin) = NULL;
  } 
  else
  {
    pwm->write(duty);
  }
}
     */

    // TEST ALL 8 INDIVIDUALY

    // D2 FRONT LEFT FORWARD
    pinMode(FL_RPWM_Output, OUTPUT);
    analogWrite(FL_RPWM_Output, 255);
    delay(1000);
    analogWrite(FL_RPWM_Output, 0);
    delay(1); // For physical stability
    analogWrite(FL_RPWM_Output, -1);
    delay(1000);

    // D3 FRONT LEFT BACKWARD
    pinMode(FL_LPWM_Output, OUTPUT);
    analogWrite(FL_LPWM_Output, 255);
    delay(1000);
    analogWrite(FL_LPWM_Output, 0);
    delay(1); // For physical stability
    analogWrite(FL_LPWM_Output, -1);
    delay(1000);

    // D4 FRONT RIGHT FORWARD
    pinMode(FR_RPWM_Output, OUTPUT);
    analogWrite(FR_RPWM_Output, 255);
    delay(1000);
    analogWrite(FR_RPWM_Output, -1);
    delay(1); // For physical stability
    analogWrite(FR_RPWM_Output, -1);
    delay(1000);

    // D5 FRONT RIGHT BACKWARD
    pinMode(FR_LPWM_Output, OUTPUT);
    analogWrite(FR_LPWM_Output, 255);
    delay(1000);
    analogWrite(FR_LPWM_Output, 0);
    delay(1); // For physical stability
    analogWrite(FR_LPWM_Output, -1);
    delay(1000);

    // D6 REAR LEFT FORWARD
    pinMode(RL_RPWM_Output, OUTPUT);
    analogWrite(RL_RPWM_Output, 255);
    delay(1000);
    analogWrite(RL_RPWM_Output, 0);
    delay(1); // For physical stability
    analogWrite(RL_RPWM_Output, -1);
    delay(1000);

    // D7 REAR LEFT BACKWARD
    pinMode(RL_LPWM_Output, OUTPUT);
    analogWrite(RL_LPWM_Output, 255);
    delay(1000);
    analogWrite(RL_LPWM_Output, 0);
    delay(1); // For physical stability
    analogWrite(RL_LPWM_Output, -1);
    delay(1000);

    // D8 REAR RIGHT FORWARD
    pinMode(RR_RPWM_Output, OUTPUT);
    analogWrite(RR_RPWM_Output, 255);
    delay(1000);
    analogWrite(RR_RPWM_Output, 0);
    delay(1); // For physical stability
    analogWrite(RR_RPWM_Output, -1);
    delay(1000);

    // D9 REAR RIGHT BACKWARD
    pinMode(RR_LPWM_Output, OUTPUT);
    analogWrite(RR_LPWM_Output, 255);
    delay(1000);
    analogWrite(RR_LPWM_Output, 0);
    delay(1); // For physical stability
    analogWrite(RR_LPWM_Output, -1);
    delay(1000);


//////////////////////////////////
// 4 SIMULTENEOUS

    // D2 FRONT LEFT FORWARD
    pinMode(FL_RPWM_Output, OUTPUT);
    analogWrite(FL_RPWM_Output, 255);

    // D4 FRONT RIGHT FORWARD
    pinMode(FR_RPWM_Output, OUTPUT);
    analogWrite(FR_RPWM_Output, 255);

    // D6 REAR LEFT FORWARD
    pinMode(RL_RPWM_Output, OUTPUT);
    analogWrite(RL_RPWM_Output, 255);

    // D8 REAR RIGHT FORWARD
    // this crashes mbed os
    pinMode(RR_RPWM_Output, OUTPUT);
    analogWrite(RR_RPWM_Output, 255);

    delay(1000);
    analogWrite(FL_RPWM_Output, -1);
    analogWrite(FR_RPWM_Output, -1);
    analogWrite(RL_RPWM_Output, -1);
    analogWrite(RR_RPWM_Output, -1);
    delay(1000);

//////////////////////////////////
}

nano33blepwmcrashtest.ino (4.4 KB)

elvielabs:
Has anyone encountered a similar problem

Yeah, I think I did have the same problem. Here's a minimal demonstration sketch that will cause the crash:

void setup() {
  analogWrite(2, 1);
  analogWrite(2, -1);
  analogWrite(3, 1);
  analogWrite(3, -1);
  analogWrite(4, 1);
  analogWrite(4, -1);
  analogWrite(5, 1);
  analogWrite(6, 1);
}
void loop() {}

You can see that I'm setting each pin to a negative value to delete the PWM object. As you mentioned, this is a capability that was made recently to the Arduino Mbed OS boards platform:

and is not yet available in the release version you install via Boards Manager.

From reading the commit message of the fix, I would assume I can do analogWrite() on as many pins as I like, so long as I am careful to always keep the number of pwm objects below 5. But as you can see from the sketch, this does not work. Here I should only have two PWM objects, and thus be within the limit of 4, but I still get the crash. So I guess I don't understand it correctly.

The fix I found was to just delete the previous object every time before creating the new one:

void setup() {
  analogWrite(2, 1);
  analogWrite(2, -1);
  analogWrite(3, 1);
  analogWrite(3, -1);
  analogWrite(4, 1);
  analogWrite(4, -1);
  analogWrite(5, 1);
  analogWrite(5, -1);
  analogWrite(6, 1);
}
void loop() {}

For my application, that actually works just fine, but it's just not what I'm accustomed to from my years of working with the non-Mbed OS Arduino boards.

Thank you. Have you solved the 3+ PWM simultaneuos bug? At that point I'd call it a bug, since according to the nordic documentation provided by arduino (which by the way does a very poor job in specifying the timers and frequency mapping to physical pins) we should be able to run at least 4 pins simultaneously.

I could probably read the real silicon implementation documentation but not sure of my return on the time investment there.

At that point we are gravitating towards something more potent, like Teensy 4.0. They are also very explicit about the timers to pin mapping. I'd expect when designing a new product arduino to be a lot more transparent. Incorporating nano 33 ble even in a semi-serious POC project is a no-go for us at that point.n OK, getting off my soapbox.

Cheers.

elvielabs:
Have you solved the 3+ PWM simultaneuos bug?

No. I only found the workaround I described of only having one at a time.

elvielabs:
At that point I'd call it a bug

You're welcome to report it in the issue tracker:

Have a look at the new example FakeAnalogWrite of NRF52_MBED_TimerInterrupt Library

The example has just been created for fun, to demonstrate how to use fakeAnalogWrite() command to output PWM to many more pins without crash.

Certainly this is not as accurate as original PWM analogWrite() and to be used only whenever it's absolutely necessary to overcome the crash.

Starting FakeAnalogWrite on Nano 33 BLE
Version : v1.0.2
NRF52_MBED_TimerInterrupt: Timer = NRF_TIMER3
NRF52_MBED_TimerInterrupt: _fre = 1000000.00, _count = 100
Starting  ITimer OK, millis() = 1811
Add index 0, pin = 2, input PWM_Value=0, mapped PWM_Value= 0
Add index 1, pin = 3, input PWM_Value=0, mapped PWM_Value= 0
Add index 2, pin = 4, input PWM_Value=0, mapped PWM_Value= 0
Add index 3, pin = 5, input PWM_Value=0, mapped PWM_Value= 0
Add index 4, pin = 6, input PWM_Value=0, mapped PWM_Value= 0
Add index 5, pin = 7, input PWM_Value=0, mapped PWM_Value= 0
Add index 6, pin = 8, input PWM_Value=0, mapped PWM_Value= 0
Add index 7, pin = 9, input PWM_Value=0, mapped PWM_Value= 0
Test PWM_Value = 0, max = 255
Update index 0, pin = 2, input PWM_Value=5, mapped PWM_Value= 14
Update index 1, pin = 3, input PWM_Value=5, mapped PWM_Value= 14
Update index 2, pin = 4, input PWM_Value=5, mapped PWM_Value= 14
Update index 3, pin = 5, input PWM_Value=5, mapped PWM_Value= 14
Update index 4, pin = 6, input PWM_Value=5, mapped PWM_Value= 14
Update index 5, pin = 7, input PWM_Value=5, mapped PWM_Value= 14
Update index 6, pin = 8, input PWM_Value=5, mapped PWM_Value= 14
Update index 7, pin = 9, input PWM_Value=5, mapped PWM_Value= 14
Test PWM_Value = 5, max = 255
Update index 0, pin = 2, input PWM_Value=10, mapped PWM_Value= 27
Update index 1, pin = 3, input PWM_Value=10, mapped PWM_Value= 27
Update index 2, pin = 4, input PWM_Value=10, mapped PWM_Value= 27
Update index 3, pin = 5, input PWM_Value=10, mapped PWM_Value= 27
Update index 4, pin = 6, input PWM_Value=10, mapped PWM_Value= 27
Update index 5, pin = 7, input PWM_Value=10, mapped PWM_Value= 27
Update index 6, pin = 8, input PWM_Value=10, mapped PWM_Value= 27
Update index 7, pin = 9, input PWM_Value=10, mapped PWM_Value= 27
Test PWM_Value = 10, max = 255
Update index 0, pin = 2, input PWM_Value=15, mapped PWM_Value= 39
Update index 1, pin = 3, input PWM_Value=15, mapped PWM_Value= 39
Update index 2, pin = 4, input PWM_Value=15, mapped PWM_Value= 39
Update index 3, pin = 5, input PWM_Value=15, mapped PWM_Value= 39
Update index 4, pin = 6, input PWM_Value=15, mapped PWM_Value= 39
Update index 5, pin = 7, input PWM_Value=15, mapped PWM_Value= 39
Update index 6, pin = 8, input PWM_Value=15, mapped PWM_Value= 39
Update index 7, pin = 9, input PWM_Value=15, mapped PWM_Value= 39
Test PWM_Value = 15, max = 255
Update index 0, pin = 2, input PWM_Value=20, mapped PWM_Value= 49
Update index 1, pin = 3, input PWM_Value=20, mapped PWM_Value= 49
Update index 2, pin = 4, input PWM_Value=20, mapped PWM_Value= 49
Update index 3, pin = 5, input PWM_Value=20, mapped PWM_Value= 49
Update index 4, pin = 6, input PWM_Value=20, mapped PWM_Value= 49
Update index 5, pin = 7, input PWM_Value=20, mapped PWM_Value= 49
Update index 6, pin = 8, input PWM_Value=20, mapped PWM_Value= 49
Update index 7, pin = 9, input PWM_Value=20, mapped PWM_Value= 49
Test PWM_Value = 20, max = 255
...
Update index 0, pin = 2, input PWM_Value=100, mapped PWM_Value= 118
Update index 1, pin = 3, input PWM_Value=100, mapped PWM_Value= 118
Update index 2, pin = 4, input PWM_Value=100, mapped PWM_Value= 118
Update index 3, pin = 5, input PWM_Value=100, mapped PWM_Value= 118
Update index 4, pin = 6, input PWM_Value=100, mapped PWM_Value= 118
Update index 5, pin = 7, input PWM_Value=100, mapped PWM_Value= 118
Update index 6, pin = 8, input PWM_Value=100, mapped PWM_Value= 118
Update index 7, pin = 9, input PWM_Value=100, mapped PWM_Value= 118
Test PWM_Value = 100, max = 255
Update index 0, pin = 2, input PWM_Value=105, mapped PWM_Value= 120
Update index 1, pin = 3, input PWM_Value=105, mapped PWM_Value= 120
Update index 2, pin = 4, input PWM_Value=105, mapped PWM_Value= 120
Update index 3, pin = 5, input PWM_Value=105, mapped PWM_Value= 120
Update index 4, pin = 6, input PWM_Value=105, mapped PWM_Value= 120
Update index 5, pin = 7, input PWM_Value=105, mapped PWM_Value= 120
Update index 6, pin = 8, input PWM_Value=105, mapped PWM_Value= 120
Update index 7, pin = 9, input PWM_Value=105, mapped PWM_Value= 120
Test PWM_Value = 105, max = 255
Update index 0, pin = 2, input PWM_Value=110, mapped PWM_Value= 121
Update index 1, pin = 3, input PWM_Value=110, mapped PWM_Value= 121
Update index 2, pin = 4, input PWM_Value=110, mapped PWM_Value= 121
Update index 3, pin = 5, input PWM_Value=110, mapped PWM_Value= 121
Update index 4, pin = 6, input PWM_Value=110, mapped PWM_Value= 121
Update index 5, pin = 7, input PWM_Value=110, mapped PWM_Value= 121
Update index 6, pin = 8, input PWM_Value=110, mapped PWM_Value= 121
Update index 7, pin = 9, input PWM_Value=110, mapped PWM_Value= 121
Test PWM_Value = 110, max = 255
Update index 0, pin = 2, input PWM_Value=115, mapped PWM_Value= 123
Update index 1, pin = 3, input PWM_Value=115, mapped PWM_Value= 123
Update index 2, pin = 4, input PWM_Value=115, mapped PWM_Value= 123
Update index 3, pin = 5, input PWM_Value=115, mapped PWM_Value= 123
Update index 4, pin = 6, input PWM_Value=115, mapped PWM_Value= 123
Update index 5, pin = 7, input PWM_Value=115, mapped PWM_Value= 123
Update index 6, pin = 8, input PWM_Value=115, mapped PWM_Value= 123
Update index 7, pin = 9, input PWM_Value=115, mapped PWM_Value= 123
Test PWM_Value = 115, max = 255
Update index 0, pin = 2, input PWM_Value=120, mapped PWM_Value= 124
Update index 1, pin = 3, input PWM_Value=120, mapped PWM_Value= 124
Update index 2, pin = 4, input PWM_Value=120, mapped PWM_Value= 124
Update index 3, pin = 5, input PWM_Value=120, mapped PWM_Value= 124
Update index 4, pin = 6, input PWM_Value=120, mapped PWM_Value= 124
Update index 5, pin = 7, input PWM_Value=120, mapped PWM_Value= 124
Update index 6, pin = 8, input PWM_Value=120, mapped PWM_Value= 124
Update index 7, pin = 9, input PWM_Value=120, mapped PWM_Value= 124
Test PWM_Value = 120, max = 255

@elvielabs' bug report:

The minimal code you posted there makes things more clear to me, but also less clear at the same time. I tried using this sketch to reproduce it:

void setup() {
  // D2
  pinMode(D2, OUTPUT);
  analogWrite(D2, 255);

  // D4
  pinMode(D4, OUTPUT);
  analogWrite(D4, 255);

  // D6
  pinMode(D6, OUTPUT);
  analogWrite(D6, 255);

  // D8
  // this crashes mbed os
  pinMode(D8, OUTPUT);
  analogWrite(D8, 255);
}
void loop() {}

but alas, this does not result in a crash for me.

Does that sketch cause the crash for you @elvielabs?

I'm using the Arduino Mbed OS Boards platform at the tip of the master branch (727c018)
With ArduinoCore-API also at the tip of its master branch (4bd75df).

I also can't reproduce the crash using the 1.3.0 release version of Arduino Mbed OS Boards platform from Boards Manager.


khoih-prog:
The example has just been created for fun, to demonstrate how to use fakeAnalogWrite() command to output PWM to many more pins without crash.

Thanks for sharing that!

As a comment on the limitations of those parts - microcontrollers with integrated wireless are often not really made for generating fancy PWM waveforms. higher clock speeds, integrated wireless, more flash/ram and lower operating voltage are on one side of the spectrum, and good low level GPIO capability and 5v Vcc are on the other.

@pert, you are correct, your code did not, however below I distilled a piece that does based on my much larger codebase.

Thank you for taking your time and pointing out that the problem is a bit different than what I originally thought.

I will head over to github to modify the issue report and post the entire code later, got to do my morning exercise, the covid situation is not helping with my productivity.

We are using IDE 1.8.13 with Mbed OS 1.3.0 (whatever the board manager latest downloaded) with a manually edited wiring analog cpp adding the object destruction. My Nano 33 BLE is also stock.

void setup() {
  // D2
  pinMode(D2, OUTPUT);
  analogWrite(D2, 255);

  // D4
  pinMode(D4, OUTPUT);
  analogWrite(D4, 255);

  // D6
  pinMode(D6, OUTPUT);
  analogWrite(D6, 255);

  // D8
  pinMode(D8, OUTPUT);
  analogWrite(D8, 255);

  // Destroy D2, D4, D6, D8
  analogWrite(D2, -1);
  analogWrite(D4, -1);
  analogWrite(D6, -1);
  analogWrite(D8, -1);

  // At that point we have again 4 pins available since the previous 4 were presumably destroyed.
  // D3
  pinMode(D3, OUTPUT);
  analogWrite(D3, 255);

  // D5 or any other pwm not part of the above pin set
  pinMode(D5, OUTPUT);  
  // This next line will crash mbed
  analogWrite(D5, 255);

}

void loop() {}

khoih-prog:
Have a look at the new example FakeAnalogWrite of NRF52_MBED_TimerInterrupt Library

The example has just been created for fun, to demonstrate how to use fakeAnalogWrite() command to output PWM to many more pins without crash.

Thank you. In my case the code is so complex I cannot afford more cycles to do this. I may do this in other situations where we can afford it.

elvielabs:
however below I distilled a piece that does based on my much larger codebase.

OK, great. That sketch does cause the crash for me with the latest development version of the Arduino Mbed OS boards platform, and I think it's the same issue as the one I ran into. When I saw your previous code, I started thinking maybe we were actually talking about two completely different bugs.

Having that nice minimal demonstration sketch will give the developers a big head start on investigating the issue.

For the record, this is the updated issue, if anyone else in the future hits it, to check the status.