Skip to content
This repository has been archived by the owner on Feb 4, 2023. It is now read-only.
/ RP2040_PWM Public archive

This library enables you to use Hardware-based PWM channels on RP2040-based boards, such as Nano_RP2040_Connect, RASPBERRY_PI_PICO, with either Arduino-mbed (mbed_nano or mbed_rp2040) or arduino-pico core to create and output PWM any GPIO pin. The most important feature is they're purely hardware-based PWM channels, supporting very high PWM freq…

License

Notifications You must be signed in to change notification settings

khoih-prog/RP2040_PWM

Repository files navigation

RP2040_PWM Library

arduino-library-badge GitHub release GitHub contributions welcome GitHub issues

Donate to my libraries using BuyMeACoffee



Table of Contents



Why do we need this RP2040_PWM library

Features

This PWM-wrapper library enables you to use Hardware-PWM blocks on RP2040-based boards to create and output PWM any GPIO pin. These purely hardware-based PWM channels can generate from very low (lowest is 7.5Hz * (F_CPU/125)) to very high PWM frequencies (in the MHz range, up to (F_CPU/2) or 62.5MHz).


The most important feature is they're purely hardware-based PWM channels. Therefore, their operations are not blocked by bad-behaving software functions / tasks.

This important feature is absolutely necessary for mission-critical tasks. These hardware PWM-channels, still work even if other software functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software timers using millis() or micros(). That's necessary if you need to control external systems (Servo, etc.) requiring better accuracy.

The PWM_Multi example will demonstrate the usage of multichannel PWM using multiple Hardware-PWM blocks (slices). The 8 independent Hardware-PWM slices are used to control 8 different PWM outputs, with totally independent frequencies and dutycycles.

Being hardware-based PWM, their executions are not blocked by bad-behaving functions / tasks, such as connecting to WiFi, Internet or Blynk services. You can also have many (up to 16) PWM output signals to use.

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

This non-being-blocked important feature is absolutely necessary for mission-critical tasks.

You'll see software-based SimpleTimer is blocked while system is connecting to WiFi / Internet / Blynk, as well as by blocking task in loop(), using delay() function as an example. The elapsed time then is very unaccurate


Why using hardware-based PWM is better

Imagine you have a system with a mission-critical function, controlling a robot or doing something much more important. You normally use a software timer to poll, or even place the function in loop(). But what if another function is blocking the loop() or setup().

So your function might not be executed, and the result would be disastrous.

You'd prefer to have your function called, no matter what happening with other functions (busy loop, bug, etc.).

The correct choice is to use a Hardware Timer with Interrupt to call your function.

These hardware-based PWM channels still work even if other software functions are blocking. Moreover, they are much more precise (certainly depending on clock frequency accuracy) than other software-based PWMs, using millis() or micros(). That's necessary if you need to measure some data requiring better accuracy.

Functions using normal software-based PWMs, relying on loop() and calling millis(), won't work if the loop() or setup() is blocked by certain operation. For example, certain function is blocking while it's connecting to WiFi or some services.


Currently supported Boards

  1. RP2040-based boards such as NANO_RP2040_CONNECT, RASPBERRY_PI_PICO, RASPBERRY_PI_PICO_W, ADAFRUIT_FEATHER_RP2040 and GENERIC_RP2040, etc. using either Arduino-mbed mbed_nano or mbed_rp2040 core or Earle Philhower's arduino-pico core.


Prerequisites

  1. Arduino IDE 1.8.19+ for Arduino. Using Arduino IDE 2.0.0+ at your own risk.
  2. ArduinoCore-mbed mbed_nano or mbed_rp2040 core 3.3.0+ for Arduino NANO_RP2040_CONNECT, RASPBERRY_PI_PICO boards. GitHub release
  3. Earle Philhower's arduino-pico core v2.6.0+ for RP2040-based boards such as RASPBERRY_PI_PICO, RASPBERRY_PI_PICO_W, ADAFRUIT_FEATHER_RP2040, ADAFRUIT_ITSYBITSY_RP2040, CYTRON_MAKER_NANO_RP2040, SPARKFUN_PROMICRO_RP2040, CHALLENGER_2040_WIFI_RP2040, ILABS_2040_RPICO32_RP2040, MELOPERO_SHAKE_RP2040, SOLDERPARTY_RP2040_STAMP, UPESY_RP2040_DEVKIT, WIZNET_5100S_EVB_PICO, GENERIC_RP2040, etc. GitHub release


Installation

Use Arduino Library Manager

The best and easiest way is to use Arduino Library Manager. Search for RP2040_PWM, then select / install the latest version. You can also use this link arduino-library-badge for more detailed instructions.

Manual Install

Another way to install is to:

  1. Navigate to RP2040_PWM page.
  2. Download the latest release RP2040_PWM-main.zip.
  3. Extract the zip file to RP2040_PWM-main directory
  4. Copy whole RP2040_PWM-main folder to Arduino libraries' directory such as ~/Arduino/libraries/.

VS Code & PlatformIO

  1. Install VS Code
  2. Install PlatformIO
  3. Install RP2040_PWM library by using Library Manager. Search for RP2040_PWM in Platform.io Author's Libraries
  4. Use included platformio.ini file from examples to ensure that all dependent libraries will installed automatically. Please visit documentation for the other options and examples at Project Configuration File


More useful Information about RP2040 PWM

PWM overview

From rp2040-datasheet.pdf, page 543

Pulse width modulation (PWM) is a scheme where a digital signal provides a smoothly varying average voltage. This is achieved with positive pulses of some controlled width, at regular intervals. The fraction of time spent high is known as the duty cycle. This may be used to approximate an analog output, or control switchmode power electronics.

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.

Each PWM slice is equipped with the following:

  • 16-bit counter
  • 8.4 fractional clock divider
  • Two independent output channels, duty cycle from 0% to 100% inclusive
  • Dual slope and trailing edge modulation
  • Edge-sensitive input mode for frequency measurement
  • Level-sensitive input mode for duty cycle measurement
  • Configurable counter wrap value
  • Wrap and level registers are double buffered and can be changed race-free while PWM is running
  • Interrupt request and DMA request on counter wrap
  • Phase can be precisely advanced or retarded while running (increments of one count)

Slices can be enabled or disabled simultaneously via a single, global control register. The slices then run in perfect lockstep, so that more complex power circuitry can be switched by the outputs of multiple slices.

Programmer’s Model

All 30 GPIO pins on RP2040 can be used for PWM:

  • The 16 PWM channels (8 2-channel slices) appear on GPIO0 to GPIO15, in the order PWM0_A, PWM0_B, PWM1_A, etc.
  • This repeats for GPIO16 to GPIO29. GPIO16 is PWM0 A, GPIO17 is PWM0 B, so on, up to PWM6 B on GPIO29
  • The same PWM output can be selected on two GPIO pins; the same signal will appear on each GPIO.
  • If a PWM B pin is used as an input, and is selected on multiple GPIO pins, then the PWM slice will see the logical OR of those two GPIO inputs


Usage

Before using any PWM slice, you have to make sure the slice has not been used by any other purpose.

1. Create PWM Instance with Pin, Frequency and dutycycle

RP2040_PWM* PWM_Instance;

PWM_Instance = new RP2040_PWM(PWM_Pins, freq, dutyCycle);

2. Initialize PWM Instance

if (PWM_Instance)
{
  PWM_Instance->setPWM();
}

3. Set or change PWM frequency or dutyCycle

To use float new_dutyCycle

PWM_Instance->setPWM(PWM_Pins, new_frequency, new_dutyCycle);

such as

dutyCycle = 10.0f;
  
Serial.print(F("Change PWM DutyCycle to ")); Serial.println(dutyCycle);
PWM_Instance->setPWM(pinToUse, frequency, dutyCycle);

To use uint32_t new_dutyCycle = real_dutyCycle * 1000

PWM_Instance->setPWM_Int(PWM_Pins, new_frequency, new_dutyCycle);

such as for real_dutyCycle = 20%

// dutyCycle = real_dutyCycle * 1000
dutyCycle = 20000;
  
Serial.print(F("Change PWM DutyCycle to ")); Serial.println((float) dutyCycle / 1000);
PWM_Instance->setPWM_Int(pinToUse, frequency, dutyCycle);

4. Set or change PWM frequency and dutyCycle manually and efficiently in waveform creation

Need to call only once for each pin

PWM_Instance->setPWM_manual(PWM_Pins, new_top, new_div, new_level);

after that, if just changing dutyCycle / level, use

PWM_Instance->setPWM_manual(PWM_Pins, new_level);

5. Important Notes

When using phaseCorrect == true
  1. the output frequency will be half of the input frequency freq
  2. the init and later usage must be the same phaseCorrect == true
RP2040_PWM* PWM_Instance;

PWM_Instance = new RP2040_PWM(PWM_Pins, freq, dutyCycle, true);

...

PWM_Instance->setPWM_Int(pinToUse, frequency, dutyCycle, true);
When using phaseCorrect == false
  1. the output frequency will be equal of the input frequency freq
  2. the init and later usage must be the same phaseCorrect == false
RP2040_PWM* PWM_Instance;

PWM_Instance = new RP2040_PWM(PWM_Pins, freq, dutyCycle);

...

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


Examples:

  1. PWM_Multi
  2. PWM_DynamicFreq
  3. PWM_DynamicDutyCycle
  4. PWM_MultiChannel
  5. PWM_Waveform
  6. PWM_Waveform_Fast
  7. PWM_DynamicDutyCycle_Int
  8. PWM_Basic. New


Example PWM_Multi

/****************************************************************************************************************************
PWM_Multi.ino
For RP2040 boards
Written by Khoi Hoang
Built 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_ 1
#if ( defined(ARDUINO_NANO_RP2040_CONNECT) || defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || \
defined(ARDUINO_GENERIC_RP2040) ) && defined(ARDUINO_ARCH_MBED)
#if(_PWM_LOGLEVEL_>3)
#warning USING_MBED_RP2040_PWM
#endif
#elif ( defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_RASPBERRY_PI_PICO) || defined(ARDUINO_ADAFRUIT_FEATHER_RP2040) || \
defined(ARDUINO_GENERIC_RP2040) ) && !defined(ARDUINO_ARCH_MBED)
#if(_PWM_LOGLEVEL_>3)
#warning USING_RP2040_PWM
#endif
#else
#error This code is intended to run on the RP2040 mbed_nano, mbed_rp2040 or arduino-pico platform! Please check your Tools->Board setting.
#endif
#include "RP2040_PWM.h"
#define LED_ON LOW
#define LED_OFF HIGH
#define pin0 25 // PWM channel 4B, BUILTIN_LED
//#define pin0 0 // PWM channel 0A
#define pin1 2 // PWM channel 1A
#define pin2 4 // PWM channel 2A
#define pin3 6 // PWM channel 3A
#define pin4 0 // PWM channel 0A
#define pin5 10 // PWM channel 5A
#define pin6 12 // PWM channel 6A
#define pin7 14 // PWM channel 7A
uint32_t PWM_Pins[] = { pin0, pin1, pin2, pin3, pin4, pin5, pin6, pin7 };
#define NUM_OF_PINS ( sizeof(PWM_Pins) / sizeof(uint32_t) )
float dutyCycle[NUM_OF_PINS] = { 10.0f, 50.0f, 30.0f, 40.0f, 50.0f, 60.0f, 70.0f, 80.0f };
float freq[] = { 20.0f, 30.0f, 40.0f, 1000.0f, 2000.0f, 3000.0f, 8000.0f, 9999.0f };
RP2040_PWM* PWM_Instance[NUM_OF_PINS];
char dashLine[] = "=============================================================";
void setup()
{
Serial.begin(115200);
while (!Serial);
delay(100);
Serial.print(F("\nStarting PWM_Multi on ")); Serial.println(BOARD_NAME);
Serial.println(RP2040_PWM_VERSION);
Serial.println(dashLine);
Serial.println("Index\tPin\tPWM_freq\tDutyCycle\tActual Freq");
Serial.println(dashLine);
for (uint8_t index = 0; index < NUM_OF_PINS; index++)
{
Serial.print(index);
Serial.print("\t"); Serial.print(PWM_Pins[index]);
Serial.print("\t"); Serial.print(freq[index]);
Serial.print("\t\t"); Serial.print(dutyCycle[index]);
PWM_Instance[index] = new RP2040_PWM(PWM_Pins[index], freq[index], dutyCycle[index]);
if (PWM_Instance[index])
{
PWM_Instance[index]->setPWM();
uint32_t div = PWM_Instance[index]->get_DIV();
uint32_t top = PWM_Instance[index]->get_TOP();
Serial.print("\t\t"); Serial.println(PWM_Instance[index]->getActualFreq());
PWM_LOGDEBUG5("TOP =", top, ", DIV =", div, ", CPU_freq =", PWM_Instance[index]->get_freq_CPU());
}
else
{
Serial.println();
}
}
Serial.println(dashLine);
}
void loop()
{
//Long delay has no effect on the operation of hardware-based PWM channels
delay(1000000);
}



Debug Terminal Output Samples

1. PWM_Multi on MBED RaspberryPi Pico

The following is the sample terminal output when running example PWM_Multi on RaspberryPi Pico, running ArduinoCore-mbed mbed_rp2040 core, to demonstrate the ability to provide high PWM frequencies and the accuracy of Hardware-based PWM, especially when system is very busy.

Starting PWM_Multi on RaspberryPi Pico
RP2040_PWM v1.4.0
=============================================================
Index	Pin	PWM_freq	DutyCycle	Actual Freq
=============================================================
0	25	20.00		10.00		20.00
1	2	30.00		50.00		30.00
2	4	40.00		30.00		40.00
3	6	1000.00		40.00		1000.00
4	0	2000.00		50.00		2000.00
5	10	3000.00		60.00		3000.00
6	12	8000.00		70.00		8000.00
7	14	9999.00		80.00		9999.00
=============================================================


2. PWM_Multi on RASPBERRY_PI_PICO

The following is the sample terminal output when running example PWM_Multi on RASPBERRY_PI_PICO, running Earle Philhower's arduino-pico core, to demonstrate the ability to provide high PWM frequencies and the accuracy of Hardware-based PWM, especially when system is very busy.

Starting PWM_Multi on RASPBERRY_PI_PICO
RP2040_PWM v1.4.0
=============================================================
Index	Pin	PWM_freq	DutyCycle	Actual Freq
=============================================================
0	25	20.00		10.00		20.00
1	2	30.00		50.00		30.00
2	4	40.00		30.00		40.00
3	6	1000.00		40.00		1000.00
4	0	2000.00		50.00		2000.00
5	10	3000.00		60.00		3000.00
6	12	8000.00		70.00		8000.00
7	14	9999.00		80.00		9999.00
=============================================================


3. PWM_DynamicFreq on Nano RP2040 Connect

The following is the sample terminal output when running example PWM_DynamicFreq on Nano RP2040 Connect, running ArduinoCore-mbed mbed_rp2040 core, to demonstrate the ability to change dynamically PWM frequencies and the accuracy of Hardware-based PWM.

Starting PWM_DynamicFreq on Nano RP2040 Connect
RP2040_PWM v1.4.0
[PWM] _PWM_config.top = 12499 , _actualFrequency = 1000.00
[PWM] PWM enabled, frequency = 1000.00
=============================================================
Change PWM Freq to 2000.00
[PWM] _PWM_config.top = 62499 , _actualFrequency = 2000.00
[PWM] Changing PWM frequency to 2000.00
[PWM] PWM enabled, frequency = 2000.00
Actual PWM Frequency = 2000.00
[PWM] TOP = 62499 , DIV = 1 , CPU_freq = 125000000
Change PWM Freq to 1000.00
[PWM] _PWM_config.top = 12499 , _actualFrequency = 1000.00
[PWM] Changing PWM frequency to 1000.00
[PWM] PWM enabled, frequency = 1000.00
Actual PWM Frequency = 1000.00
[PWM] TOP = 12499 , DIV = 10 , CPU_freq = 125000000
Change PWM Freq to 2000.00
[PWM] _PWM_config.top = 62499 , _actualFrequency = 2000.00
[PWM] Changing PWM frequency to 2000.00
[PWM] PWM enabled, frequency = 2000.00
Actual PWM Frequency = 2000.00
[PWM] TOP = 62499 , DIV = 1 , CPU_freq = 125000000
Change PWM Freq to 1000.00
[PWM] _PWM_config.top = 12499 , _actualFrequency = 1000.00
[PWM] Changing PWM frequency to 1000.00
[PWM] PWM enabled, frequency = 1000.00
Actual PWM Frequency = 1000.00
[PWM] TOP = 12499 , DIV = 10 , CPU_freq = 125000000
Change PWM Freq to 2000.00
[PWM] _PWM_config.top = 62499 , _actualFrequency = 2000.00
[PWM] Changing PWM frequency to 2000.00
[PWM] PWM enabled, frequency = 2000.00
Actual PWM Frequency = 2000.00
[PWM] TOP = 62499 , DIV = 1 , CPU_freq = 125000000

4. PWM_DynamicDutyCycle on RASPBERRY_PI_PICO

The following is the sample terminal output when running example PWM_DynamicDutyCycle on RASPBERRY_PI_PICO, running Earle Philhower's arduino-pico core, to demonstrate the ability to change dynamically PWM dutyCycle and the accuracy of Hardware-based PWM.

Starting PWM_DynamicDutyCycle on RASPBERRY_PI_PICO
RP2040_PWM v1.4.0
[PWM] _PWM_config.top = 13299 , _actualFrequency = 1000.00
[PWM] pin =  25 , PWM_CHAN = 1
[PWM] PWM enabled, slice =  4 , _frequency =  1000.00
=============================================================
Change PWM DutyCycle to 50.00
[PWM] _PWM_config.top = 6649 , _actualFrequency = 2000.00
[PWM] Changing PWM frequency to 2000.00 and dutyCycle = 50.00
[PWM] pin =  25 , PWM_CHAN = 1
[PWM] PWM enabled, slice =  4 , _frequency =  2000.00
Actual PWM Frequency = 2000.00
Change PWM DutyCycle to 10.00
[PWM] Changing PWM DutyCycle to 10.00 and keeping frequency = 2000.00
[PWM] pin =  25 , PWM_CHAN = 1
[PWM] PWM enabled, slice =  4 , _frequency =  2000.00
Actual PWM Frequency = 2000.00
Change PWM DutyCycle to 50.00
[PWM] Changing PWM DutyCycle to 50.00 and keeping frequency = 2000.00
[PWM] pin =  25 , PWM_CHAN = 1
[PWM] PWM enabled, slice =  4 , _frequency =  2000.00
Actual PWM Frequency = 2000.00
Change PWM DutyCycle to 10.00
[PWM] Changing PWM DutyCycle to 10.00 and keeping frequency = 2000.00
[PWM] pin =  25 , PWM_CHAN = 1
[PWM] PWM enabled, slice =  4 , _frequency =  2000.00
Actual PWM Frequency = 2000.00
Change PWM DutyCycle to 50.00
[PWM] Changing PWM DutyCycle to 50.00 and keeping frequency = 2000.00
[PWM] pin =  25 , PWM_CHAN = 1
[PWM] PWM enabled, slice =  4 , _frequency =  2000.00
Actual PWM Frequency = 2000.00
Change PWM DutyCycle to 10.00
[PWM] Changing PWM DutyCycle to 10.00 and keeping frequency = 2000.00
[PWM] pin =  25 , PWM_CHAN = 1
[PWM] PWM enabled, slice =  4 , _frequency =  2000.00
Actual PWM Frequency = 2000.00
Change PWM DutyCycle to 50.00
[PWM] Changing PWM DutyCycle to 50.00 and keeping frequency = 2000.00
[PWM] pin =  25 , PWM_CHAN = 1
[PWM] PWM enabled, slice =  4 , _frequency =  2000.00
Actual PWM Frequency = 2000.00
Change PWM DutyCycle to 10.00
[PWM] Changing PWM DutyCycle to 10.00 and keeping frequency = 2000.00
[PWM] pin =  25 , PWM_CHAN = 1
[PWM] PWM enabled, slice =  4 , _frequency =  2000.00
Actual PWM Frequency = 2000.00

5. PWM_MultiChannel on RASPBERRY_PI_PICO

The following is the sample terminal output when running example PWM_MultiChannel on RASPBERRY_PI_PICO, running Earle Philhower's arduino-pico core, to demonstrate the ability to output for both channels of a PWM slice

Starting PWM_MultiChannel on RASPBERRY_PI_PICO
RP2040_PWM v1.4.0
=============================================================
Index	Pin	PWM_freq	DutyCycle	Actual Freq
=============================================================
0	10	1000.00		10.00		1000.00
1	11	1000.00		50.00		1000.00
=============================================================

6. PWM_Waveform on RASPBERRY_PI_PICO

The following is the sample terminal output when running example PWM_Waveform on RASPBERRY_PI_PICO, running Earle Philhower's arduino-pico core, to demonstrate how to use new setPWM_manual() function in wafeform creation

Starting PWM_Waveform on RASPBERRY_PI_PICO
RP2040_PWM v1.4.0
[PWM] _PWM_config.top = 12499 , _actualFrequency = 1000.00
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 0
=============================================================
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 0
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 50
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 100
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 200
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 300
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 400
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 500
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 600
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 700
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 800
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 900
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 1000
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 1000
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 900
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 800
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 700
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 600
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 500
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 400
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 300
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 200
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 100
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 50
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 0
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 0
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 50
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 100
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 200
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 300
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 400
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 500
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 600
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 700
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 800
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 900
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 1000
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 1000
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 900
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 800
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 700
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 600
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 500
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 400
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 300
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 200
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 100
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 50
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 0
[PWM] pin =  10 , PWM_CHAN = 0
...

7. PWM_Waveform_Fast on RASPBERRY_PI_PICO

The following is the sample terminal output when running example PWM_Waveform_Fast on RASPBERRY_PI_PICO, running Earle Philhower's arduino-pico core, to demonstrate how to use new setPWM_manual() function in wafeform creation

Starting PWM_Waveform_Fast on RASPBERRY_PI_PICO
RP2040_PWM v1.4.0
[PWM] _PWM_config.top = 12499 , _actualFrequency = 1000.00
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 0
=============================================================
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 0
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 50
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 100
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 200
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 300
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 400
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 500
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 600
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 700
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 800
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 900
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 1000
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 1000
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 900
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 800
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 700
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 600
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 500
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 400
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 300
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 200
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 100
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 50
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 0
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 0
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 50
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 100
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 200
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 300
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 400
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 500
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 600
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 700
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 800
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 900
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 1000
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 1000
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 900
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 800
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 700
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 600
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 500
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 400
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 300
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 200
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 100
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 50
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 0
[PWM] pin =  10 , PWM_CHAN = 0
[PWM] PWM enabled, slice = 5 , top = 1000 , div = 10 , level = 0
...


Debug

Debug is enabled by default on Serial.

You can also change the debugging level _PWM_LOGLEVEL_ from 0 to 4

// Don't define _PWM_LOGLEVEL_ > 0. Only for special ISR debugging only. Can hang the system.
#define _PWM_LOGLEVEL_     0

Troubleshooting

If you get compilation errors, more often than not, you may need to install a newer version of the core for Arduino boards.

Sometimes, the library will only work if you update the board core to the latest version because I am using newly added functions.



Issues

Submit issues to: RP2040_PWM issues


TO DO

  1. Search for bug and improvement.
  2. Similar features for remaining Arduino boards

DONE

  1. Basic hardware-based multi-channel PWMs for RP2040-based boards such as Nano_RP2040_Connect, RASPBERRY_PI_PICO, etc. using either RP2040 ArduinoCore-mbed mbed_nano or mbed_rp2040 core or Earle Philhower's arduino-pico core
  2. Add Table of Contents
  3. Split changelog.md
  4. Permit PWM output for both channels of PWM slice.
  5. Use float instead of double for frequency and duty-cycle
  6. Add example PWM_MultiChannel to demonstrate how to use both channels of PWM slice.
  7. Add efficient setPWM_manual() function to use in wafeform creation using PWM
  8. Add example PWM_Waveform to demonstrate how to use new setPWM_manual() function in wafeform creation
  9. Optimize library code and examples by using reference-passing instead of value-passing and inline
  10. Add setPWM_manual(pin, level) function for efficiency in wafeform creation using PWM
  11. Add example PWM_Waveform_Fast to demonstrate how to use new setPWM_manual(pin, level) function
  12. Add setPWM_Int() function for optional uint32_t dutycycle = real_dutycycle * 1000
  13. Add example PWM_DynamicDutyCycle_Int to demonstrate how to use new setPWM_Int() function
  14. Add minimal example PWM_Basic
  15. Fix glitch when dynamically changing dutycycle. Check Changing Duty Cycle Dynamically Creates Runt PWM pulse #10
  16. Adjust MIN_PWM_FREQUENCY and MAX_PWM_FREQUENCY dynamically according to actual F_CPU


Contributions and Thanks

Many thanks for everyone for bug reporting, new feature suggesting, testing and contributing to the development of this library.

  1. Thanks to americodias to report bugs in
  1. Thanks to Austin K. Litman to report bugs in
  1. Thanks to Joerg Starkmuth to propose enhancement in
  1. Thanks to Dr. Benjamin Bird to make PR
  1. Thanks to Rocking Y Productions to request enhancement in Changing Duty Cycle Dynamically Creates Runt PWM pulse #10, leading to v1.4.0
americodias
⭐️ Américo Dias

AKLitman
⭐️ Austin K. Litman

Laserjones
⭐️ Joerg Starkmuth

Chick92
⭐️ Dr. Benjamin Bird

RockingYProductions
Rocking Y Productions


Contributing

If you want to contribute to this project:

  • Report bugs and errors
  • Ask for enhancements
  • Create issues and pull requests
  • Tell other people about this library

License

  • The library is licensed under MIT

Copyright

Copyright 2021- Khoi Hoang

About

This library enables you to use Hardware-based PWM channels on RP2040-based boards, such as Nano_RP2040_Connect, RASPBERRY_PI_PICO, with either Arduino-mbed (mbed_nano or mbed_rp2040) or arduino-pico core to create and output PWM any GPIO pin. The most important feature is they're purely hardware-based PWM channels, supporting very high PWM freq…

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published