100kHz PWM on Arduino Nano RP2040 Connect

I want to output 100kHz PWM to a pin on my arduino. Wondering if anyone has code that can do this or can point out any error in the codes I tried.

Here is my naive approach:

#define TOGGLE(x) digitalWrite(x, digitalRead(x) ? LOW : HIGH)
float DUTY_CYCLE = 0.5; // 0.0 - 1.0
float FREQUENCY = 100000; // unit: hz
byte PIN = 2;
void setup() {
  pinMode(PIN, OUTPUT);
}
void loop() {
  TOGGLE(PIN);
  delay(1.0/FREQUENCY * DUTY_CYCLE * 1000);

  TOGGLE(PIN);
  delay(1.0/FREQUENCY * (1-DUTY_CYCLE) * 1000);
}

When measured on oscilloscope, this reads at about 32.5kHz when I expect it to be 100kHz.

Next, I tried an Arduino library "RP2040_PWM", this code is taken from the examples folder on the library github page:

/****************************************************************************************************************************
  basic_pwm.ino
  For RP2040 boards
  Written by Dr. Benjamin Bird

  A basic example to get you up and running.

  Library by Khoi Hoang https://github.com/khoih-prog/RP2040_PWM
  Licensed under MIT license

  The RP2040 PWM block has 8 identical slices. Each slice can drive two PWM output signals, or measure the frequency
  or duty cycle of an input signal. This gives a total of up to 16 controllable PWM outputs. All 30 GPIO pins can be driven
  by the PWM block
*****************************************************************************************************************************/

#define _PWM_LOGLEVEL_ 3
#include "RP2040_PWM.h"

//creates pwm instance
RP2040_PWM* PWM_Instance;

float frequency;
float dutyCycle;

#define pinToUse 25

void setup()
{
  //assigns pin 25 (built in LED), with frequency of 20 KHz and a duty cycle of 0%
  PWM_Instance = new RP2040_PWM(pinToUse, 20000, 0);
}

void loop()
{
  delay(1000);
  frequency = 20000;
  dutyCycle = 90;

  PWM_Instance->setPWM(pinToUse, frequency, dutyCycle);

  delay(1000);
  dutyCycle = 10;

  PWM_Instance->setPWM(pinToUse, frequency, dutyCycle);
}

This code results in the following errors:

In file included from C:\Users\rozno\Documents\Arduino\realpwm\realpwm.ino:17:0:
C:\Users\rozno\Documents\Arduino\libraries\RP2040_PWM\src/RP2040_PWM.h: In member function 'bool RP2040_PWM::setPWM_manual(const uint8_t&, const uint16_t&, const uint8_t&, uint16_t&, bool)':
C:\Users\rozno\Documents\Arduino\libraries\RP2040_PWM\src/RP2040_PWM.h:340:26: error: invalid conversion from 'pwm_config*' to 'uint {aka unsigned int}' [-fpermissive]
     pwm_init(_slice_num, &config, true);
                          ^~~~~~~
C:\Users\rozno\Documents\Arduino\libraries\RP2040_PWM\src/RP2040_PWM.h:340:39: error: cannot convert 'bool' to 'pwm_config*' for argument '3' to 'void pwm_init(uint, uint, pwm_config*, bool)'
     pwm_init(_slice_num, &config, true);
                                       ^
C:\Users\rozno\Documents\Arduino\libraries\RP2040_PWM\src/RP2040_PWM.h: In member function 'bool RP2040_PWM::setPWMPushPull_Int(const uint8_t&, const uint8_t&, const float&, const uint32_t&)':
C:\Users\rozno\Documents\Arduino\libraries\RP2040_PWM\src/RP2040_PWM.h:478:32: error: invalid conversion from 'pwm_config*' to 'uint {aka unsigned int}' [-fpermissive]
           pwm_init(_slice_num, &config, true);
                                ^~~~~~~
C:\Users\rozno\Documents\Arduino\libraries\RP2040_PWM\src/RP2040_PWM.h:478:45: error: cannot convert 'bool' to 'pwm_config*' for argument '3' to 'void pwm_init(uint, uint, pwm_config*, bool)'
           pwm_init(_slice_num, &config, true);
                                             ^
C:\Users\rozno\Documents\Arduino\libraries\RP2040_PWM\src/RP2040_PWM.h: In member function 'bool RP2040_PWM::setPWM_Int(const uint8_t&, const float&, const uint32_t&, bool)':
C:\Users\rozno\Documents\Arduino\libraries\RP2040_PWM\src/RP2040_PWM.h:617:32: error: invalid conversion from 'pwm_config*' to 'uint {aka unsigned int}' [-fpermissive]
           pwm_init(_slice_num, &config, true);
                                ^~~~~~~
C:\Users\rozno\Documents\Arduino\libraries\RP2040_PWM\src/RP2040_PWM.h:617:45: error: cannot convert 'bool' to 'pwm_config*' for argument '3' to 'void pwm_init(uint, uint, pwm_config*, bool)'
           pwm_init(_slice_num, &config, true);
                                             ^

exit status 1

Compilation error: exit status 1

Any help is appreciated. Let me know if I should upload any more info to help with a solution.

Don't use floating point math if you expect something to run fast.

From Your naive attempt:

First: 1.0 divide by frequency give 0.000 001. Mult 0.5 gives 0.000 000 5. Mult by 1000 gives 0.000 5. 0.0005 milliseconds.... Read Arduino/reference for the delay function. It accepts integer number of milliseconds.

Read the documentation for the RP2040 for how to use it. Check it's written for the unknown controller You use.
Clearly either another library needs to be added, the lib not suitable for the controller or some other thing.
Look for example code for that library.

Railroader is right, you could use delayMicros() keeping in mind that the delay you're looking for is 5 microseconds for 100khz, or you could use the micros() function to create a timer without using the delays.

Taking what you said and @Railroader, I tried using the delayMicroseconds function and hardcoded the delay to 5 microseconds. This is better, but not quite there. On the oscilloscope I read 62 kHz.

#define TOGGLE(x) digitalWrite(x, digitalRead(x) ? LOW : HIGH)
//float DUTY_CYCLE = 0.5; // 0.0 - 1.0
//float FREQUENCY = 100000; // unit: hz
byte PIN = 2;
void setup() {
  pinMode(PIN, OUTPUT);
}
void loop() {
  TOGGLE(PIN);
  delayMicroseconds(5);

  TOGGLE(PIN);
  delayMicroseconds(5);
}

I will try out the micros function tomorrow.

Can't you use a PWM enabled pin?

There's delay caused by the execution time of the code. Lower that 5 to 4? Any difference?

1 Like

I did read up on that but couldn't get it to work. If you have any code that I can use or resources I can read to learn how I could do that, that would be great. I tried using the library "RP2040_PWM" as mentioned in the original post to no avail.

Yep, this works. I changed the two lines of code to delayMicroseconds(2); and got around 97.5kHz which is good enough for my purposes. Thanks.

It’s kind of a pity that the Arduino PWM library couldn’t be gotten to work. I regularly use PWM on an RP2040 board programmed in MicroPython and 100 kHz is a walk in the park.
It might be worth the time it takes to troubleshoot the errors you were getting while trying the Arduino library.

You need to take into account how long it takes to execute the TOGGLE(PIN):

Also the waveform might be slightly asymmetrical, as it takes extra time to go from the second delayMicroseconds(5); , and back to the top of loop.

If I need to do similar processes more often in the future I'll definitely look into it.

You could also consider using the official Raspberry Pi RP2040 C++ SDK instead of the Arduino IDE unless there is some reason you have to use the Arduino core.

To tell you the truth, I haven't used the RP2040 yet, but I think if you posted details of that, there may be some advice from other helpers...

In case you are still interested in the subject, there is some information about the problem here:

1 Like

Thanks, saved a lot of debug time!

You are welcome. I'm glad if I was able to be of assistance.

Regards,
Per

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.