Nice use of onboard RGB led, digitalWrite after analogWrite Fails

I made two nice ways of playing with the onboard RGB led. One with sharp and one with smooth changing colors.
In the program below they are shown alternating in 16s perionds.

const int ledPin1 = 22;
const int ledPin2 = 23;
const int ledPin3 = 24;

void setup() {
pinMode(ledPin1, OUTPUT);
pinMode(ledPin2, OUTPUT);
pinMode(ledPin3, OUTPUT);
}
float GoldenSection= ((sqrt(5)-1)/2);
void loop() {
   if (bitRead(millis(),14)==1)   // approx 16s duration
   { analogWrite(ledPin1, 127.5*(1.0+sin(float(millis())/(1000.0*GoldenSection))));
     analogWrite(ledPin2, 127.5*(1.0+sin( float(millis())/1000.0  )));
     analogWrite(ledPin3, 127.5*(1.0+sin(float(millis())*GoldenSection/1000.0  )));
   } else 
   // { digitalWrite(ledPin1, bitRead(millis(),8));
  //   digitalWrite(ledPin2, bitRead(millis(),9));
  //   digitalWrite(ledPin3, bitRead(millis(),10)); } 
   { analogWrite(ledPin1, 255*bitRead(millis(),8));
     analogWrite(ledPin2, 255*bitRead(millis(),9));
     analogWrite(ledPin3, 255*bitRead(millis(),10)); } 
}

The sharp method was originally made with digitalWrite's (now commented out). The problem was that it worked only the first time. To see the problem: comment out the last three analogWrite's and un-comment the digitalWrites
I tried it on other pins of the BLE Sense with the same result. On a Uno the problem does not occur.

I just wondered is it a bug or a feature. :confused: :o :frowning: >:( :slight_smile:
I can imagine that it may lead to unexpected problems.

There is a known issue with digitalWrite on the Arduino Nano 33 BLE. Its comes from the way mbedOS handles pins which is slightly incompatible to Arduinos way. This issue becomes more noticeable/problematic when you write to the pin very often per second. There are more information about this in the following post.

I reply #4 you can find a solution by using the mbedOS pin functionality directly.

Thanks for finding this.
Unfortunately the solution does not work, neither does it seem to be related to the fast repeated writing to the same pin.

Program below: The first 10 fast blinks (digital) work normal. The next slow 5 1/2 blink are with increasing strength.
From that moment on the digitalWrites stop working and the led keeps its last value set with analogWrite.

void setup() { pinMode(LED_BUILTIN, OUTPUT); }
boolean bling=false;
void loop()
{ int i; 
  for (i=0;i<20;i++) 
  { digitalWrite(LED_BUILTIN, bling = !bling);
    delay(200);}
  for (i=0;i<11;i++) 
  { analogWrite(LED_BUILTIN, i*25*(bling = !bling)); 
    delay(400);}  
}

It looks like a new problem to me… digitalWrite works fine until you make a call to analogWrite to the same pin.

Where would be the place to report findings like this.(Github I guess?)

OT. just found out that the power led can be controlled just like any other output. The predefined constant is LED_PWR. You can do lots of creative things with it. :slight_smile:

This is still a mbedOS vs Arduino framework issue. When you use analogWrite a new mbed::PwmOut object is created that will continue to interfere with the pin when you use digitalWrite.

I build your example based on mbedOS. When the mbed::PwmOut object is created inside loop it gets freed properly when the loop() function finishes and allows to create a new mbed::DigitalInOut object when the loop() starts again.

Note: When you duplicate the digital blinking code after the PWM blinking code you will see the same pattern the second time just like your example. But every time loop() starts the digital blinking works as expected.

I used the PWR LED to indicate when the digital blinking code runs.

#include "mbed.h"

mbed::DigitalInOut ledPwrPin( digitalPinToPinName( LED_PWR ) );

void setup()
{
  ledPwrPin.output();
}

bool bling = false;

void loop()
{
  ledPwrPin = HIGH;
  mbed::DigitalInOut ledDigitalPin( digitalPinToPinName( LED_BUILTIN ) );
  ledDigitalPin.output();
  for ( int i = 0; i < 20; i++ )
  {
    ledDigitalPin = !ledDigitalPin;
    delay( 200 );
  }

  ledPwrPin = LOW;
  mbed::PwmOut ledPwmPin( digitalPinToPinName( LED_BUILTIN ) );
  ledPwmPin.period(0.002);
  for ( int i = 0; i <= 10; i++ )
  {
    ledPwmPin.write( i * 0.1 * ( bling = !bling ));
    delay( 400 );
  }
}

I reported an issue.

A digitalWrite preceded by a call to analogWrite does not work on any pin
A call to analogWrite to the LED_PWR hangs-up the program.

I’ll be needing some time to digest your code. :slight_smile: It looks like it’s working.
Would this also enable an analog output on the power led?

I tried to solve the two problems using conditional defines by replacing
analogWrite for digitalWrite on pin LED_PWR and
digitalWrite for analogWrite on all other pins.

It’s probably inefficient, but it requires no rewriting of existing programs, just an include.

//#ifdef  __MBED_CONFIG_DATA__
#ifdef __PINS_ARDUINO__
   #define digitalWrite digitalWrite2
   #define analogWrite analogWrite2
#endif

void setup() { 
  pinMode(LED_BUILTIN, OUTPUT);
  pinMode(LED_PWR, OUTPUT);
}

boolean bling=false;
void loop()
{ int i; 
  for (i=0;i<20;i++) 
  { digitalWrite(LED_BUILTIN, bling = !bling);
    digitalWrite(LED_PWR,!bling );
    delay(200);}
  for (i=0;i<11;i++) 
  { analogWrite(LED_BUILTIN, i*25*(bling = !bling)); 
    analogWrite(LED_PWR, i*25*(bling)); 
    delay(400);}  
}

// workaround for Nano 33 BLE types bugs, 
//analogWrite on LED_PWR crashes the program
//On any other pin digitalWrite stops working after a call to analogWrite 
void digitalWrite2(int pinnr , boolean pinhigh){
#undef digitalWrite
  if (pinnr==LED_PWR) digitalWrite(pinnr, pinhigh);
  else analogWrite(pinnr, 255*pinhigh);
#ifdef __PINS_ARDUINO__
   #define digitalWrite digitalWrite2
#endif  
}
void analogWrite2(int pinnr ,uint8_t value){
#undef analogWrite
if (pinnr==LED_PWR) digitalWrite2(pinnr,value>127);
else analogWrite(pinnr,value);
#ifdef __PINS_ARDUINO__
   #define analogWrite analogWrite2
#endif  
}

femmeverbeek:
Would this also enable an analog output on the power led?

Yes, I just exchange the two LEDs in my code and my example also works the other way around.

Running a little experiment I found that you can have 4 PwmOut pins (if you try to use more you get a mbedOS kernel crash LED blink pattern). Looking at the datasheet of the nRF52840 I found there is a PWM module with 4 channels. Each channel can be assigned to any of 32 pins of port 0 or port 1 (so any pin since the device has 48 GP I/O).

What I understood so far is that a call to analogWrite assigns 1 out of 4 PWM's to a pin. Trying to assign a fifth hangs up the program. :frowning:

Assigning a PWM disables the use of digital write to that pin. :o

Is there a way to Un-assign the PWM and/or to assign it to an other pin?

femmeverbeek:
Is there a way to Un-assign the PWM and/or to assign it to an other pin?

Here are two examples of how you can do that. Both examples do the same thing. They assign PWM to pin 2 to 11 for one second each and then repeat. This only uses one PwmOut object. You should be able to create up to 4 at the same time. You need to be careful. If you create more than 4 your sketch will crash.

In this example the PwmOut object gets deleted automatically every time when loop is finished.

#include "mbed.h"

void setup(){}

void loop()
{
  static int pinNumber = 2;

  pinNumber = ( ( pinNumber + 1 ) % 10 ) + 2;
  mbed::PwmOut pin( digitalPinToPinName( pinNumber ) );
  pin.period( 0.002 );
  pin.write( 0.5 );

  delay( 1000 );
}

In this example the PwmOut object gets deleted inside the loop function.

#include "mbed.h"

void setup(){}

void loop()
{
  mbed::PwmOut *ptr;
  int pinNumber = 2;

  while ( 1 )
  {
    pinNumber = ( ( pinNumber + 1 ) % 10 ) + 2;

    ptr = new mbed::PwmOut( digitalPinToPinName( pinNumber ) );
    ptr->period( 0.002 );
    ptr->write( 0.5 );

    delay( 1000 );

    delete ptr;
  }
}