diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 951330a..f1afbb3 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -15,7 +15,7 @@ If you don't find anything, please [open a new issue](https://github.com/khoih-p Please ensure to specify the following: * Arduino IDE version (e.g. 1.8.19) or Platform.io version -* `RP2040` Core Version (e.g. Arduino-mbed RP2040 v2.7.2 or arduino-pico core v1.13.0) +* `RP2040` Core Version (e.g. Arduino-mbed RP2040 v3.0.1 or arduino-pico core v1.13.1) * `RP2040` Board type (e.g. NANO_RP2040_CONNECT, RASPBERRY_PI_PICO, ADAFRUIT_FEATHER_RP2040, GENERIC_RP2040, etc.) * Contextual information (e.g. what you were trying to achieve) * Simplest possible steps to reproduce @@ -28,10 +28,10 @@ Please ensure to specify the following: ``` Arduino IDE version: 1.8.19 -Arduino-mbed mbed_nano v2.7.2 +Arduino-mbed mbed_nano v3.0.1 NANO_RP2040_CONNECT Module OS: Ubuntu 21.04 LTS -Linux kh-Inspiron-3593 5.13.0-30-generic #33-Ubuntu SMP Fri Feb 4 17:03:31 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux +Linux kh-Inspiron-3593 5.13.0-39-generic #44~20.04.1-Ubuntu SMP Thu Mar 24 16:43:35 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux Context: I encountered a crash while using TimerInterrupt. diff --git a/README.md b/README.md index 2e1c286..3459a9b 100644 --- a/README.md +++ b/README.md @@ -31,11 +31,13 @@ * [1. Create PWM Instance with Pin, Frequency and dutycycle](#1-Create-PWM-Instance-with-Pin-Frequency-and-dutycycle) * [2. Initialize PWM Instance](#2-Initialize-PWM-Instance) * [3. Set or change PWM frequency or dutyCycle](#3-set-or-change-PWM-frequency-or-dutyCycle) + * [4. Set or change PWM frequency and dutyCycle manually and efficiently in waveform creation](#4-Set-or-change-PWM-frequency-and-dutyCycle-manually-and-efficiently-in-waveform-creation) * [Examples](#examples) * [ 1. PWM_Multi](examples/PWM_Multi) * [ 2. PWM_DynamicFreq](examples/PWM_DynamicFreq) * [ 3. PWM_DynamicDutyCycle](examples/PWM_DynamicDutyCycle) - * [ 4. PWM_MultiChannel](examples/PWM_MultiChannel) **New** + * [ 4. PWM_MultiChannel](examples/PWM_MultiChannel) + * [ 5. PWM_Waveform](examples/PWM_Waveform) **New** * [Example PWM_Multi](#example-PWM_Multi) * [Debug Terminal Output Samples](#debug-terminal-output-samples) * [1. PWM_Multi on MBED RaspberryPi Pico](#1-PWM_Multi-on-MBED-RaspberryPi-Pico) @@ -109,8 +111,8 @@ Functions using normal software-based PWMs, relying on loop() and calling millis ## Prerequisites 1. [`Arduino IDE 1.8.19+` for Arduino](https://www.arduino.cc/en/Main/Software) - 2. [`ArduinoCore-mbed mbed_nano or mbed_rp2040 core 2.7.2`](https://github.com/arduino/ArduinoCore-mbed) for Arduino **NANO_RP2040_CONNECT, RASPBERRY_PI_PICO** boards. [![GitHub release](https://img.shields.io/github/release/arduino/ArduinoCore-mbed.svg)](https://github.com/arduino/ArduinoCore-mbed/releases/latest) - 3. [`Earle Philhower's arduino-pico core v1.13.0+`](https://github.com/earlephilhower/arduino-pico) for RP2040-based boards such as **RASPBERRY_PI_PICO, 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](https://img.shields.io/github/release/earlephilhower/arduino-pico.svg)](https://github.com/earlephilhower/arduino-pico/releases/latest) + 2. [`ArduinoCore-mbed mbed_nano or mbed_rp2040 core 3.0.1+`](https://github.com/arduino/ArduinoCore-mbed) for Arduino **NANO_RP2040_CONNECT, RASPBERRY_PI_PICO** boards. [![GitHub release](https://img.shields.io/github/release/arduino/ArduinoCore-mbed.svg)](https://github.com/arduino/ArduinoCore-mbed/releases/latest) + 3. [`Earle Philhower's arduino-pico core v1.13.1+`](https://github.com/earlephilhower/arduino-pico) for RP2040-based boards such as **RASPBERRY_PI_PICO, 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](https://img.shields.io/github/release/earlephilhower/arduino-pico.svg)](https://github.com/earlephilhower/arduino-pico/releases/latest) --- @@ -219,6 +221,13 @@ if (PWM_Instance) PWM_Instance->setPWM(PWM_Pins, new_frequency, new_dutyCycle, true); ``` +#### 4. Set or change PWM frequency and dutyCycle manually and efficiently in waveform creation + +``` +PWM_Instance->setPWM_manual(PWM_Pins, new_top, new_div, new_level, true) +``` + + --- --- @@ -226,8 +235,10 @@ PWM_Instance->setPWM(PWM_Pins, new_frequency, new_dutyCycle, true); 1. [PWM_Multi](examples/PWM_Multi) 2. [PWM_DynamicFreq](examples/PWM_DynamicFreq) - 3. [PWM_DynamicDutyCycle](examples/PWM_DynamicDutyCycle). - 4. [PWM_MultiChannel](examples/PWM_MultiChannel). **New** + 3. [PWM_DynamicDutyCycle](examples/PWM_DynamicDutyCycle) + 4. [PWM_MultiChannel](examples/PWM_MultiChannel) + 5. [PWM_Waveform](examples/PWM_Waveform). **New** + --- --- @@ -249,7 +260,7 @@ The following is the sample terminal output when running example [PWM_Multi](exa ``` Starting PWM_Multi on RaspberryPi Pico -RP2040_PWM v1.1.1 +RP2040_PWM v1.2.0 ============================================================= Index Pin PWM_freq DutyCycle Actual Freq ============================================================= @@ -272,7 +283,7 @@ The following is the sample terminal output when running example [**PWM_Multi**] ``` Starting PWM_Multi on RASPBERRY_PI_PICO -RP2040_PWM v1.1.1 +RP2040_PWM v1.2.0 ============================================================= Index Pin PWM_freq DutyCycle Actual Freq ============================================================= @@ -295,7 +306,7 @@ The following is the sample terminal output when running example [**PWM_DynamicF ``` Starting PWM_DynamicFreq on Nano RP2040 Connect -RP2040_PWM v1.1.1 +RP2040_PWM v1.2.0 [PWM] _PWM_config.top = 12499 , _actualFrequency = 1000.00 [PWM] PWM enabled, frequency = 1000.00 ============================================================= @@ -340,7 +351,7 @@ The following is the sample terminal output when running example [**PWM_DynamicD ``` Starting PWM_DynamicDutyCycle on RASPBERRY_PI_PICO -RP2040_PWM v1.1.1 +RP2040_PWM v1.2.0 [PWM] _PWM_config.top = 12499 , _actualFrequency = 1000.00 [PWM] PWM enabled, frequency = 1000.00 ============================================================= @@ -377,7 +388,7 @@ The following is the sample terminal output when running example [**PWM_MultiCha ``` Starting PWM_MultiChannel on RASPBERRY_PI_PICO -RP2040_PWM v1.1.1 +RP2040_PWM v1.2.0 ============================================================= Index Pin PWM_freq DutyCycle Actual Freq ============================================================= @@ -386,6 +397,55 @@ Index Pin PWM_freq DutyCycle Actual Freq ============================================================= ``` +--- + +### 6. PWM_Waveform on RASPBERRY_PI_PICO + +The following is the sample terminal output when running example [**PWM_Waveform**](examples/PWM_Waveform) on **RASPBERRY_PI_PICO**, running [`Earle Philhower's arduino-pico core`](https://github.com/earlephilhower/arduino-pico), to demonstrate how to use new `setPWM_manual()` function in wafeform creation + + +``` +Starting PWM_Waveform on RASPBERRY_PI_PICO +RP2040_PWM v1.2.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 = 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 +... +``` + --- --- @@ -427,13 +487,15 @@ Submit issues to: [RP2040_PWM issues](https://github.com/khoih-prog/RP2040_PWM/i ## 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](https://github.com/arduino/ArduinoCore-mbed) or [**Earle Philhower's arduino-pico core**](https://github.com/earlephilhower/arduino-pico) -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](https://github.com/khoih-prog/RP2040_PWM/tree/main/examples/PWM_MultiChannel) to demonstrate how to use both channels of PWM slice. - + 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](https://github.com/arduino/ArduinoCore-mbed) or [**Earle Philhower's arduino-pico core**](https://github.com/earlephilhower/arduino-pico) + 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](https://github.com/khoih-prog/RP2040_PWM/tree/main/examples/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](https://github.com/khoih-prog/RP2040_PWM/tree/main/examples/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**. --- --- @@ -445,17 +507,17 @@ Many thanks for everyone for bug reporting, new feature suggesting, testing and 1. Thanks to [americodias](https://github.com/americodias) to report bugs in - [Wrong frequency #1](https://github.com/khoih-prog/RP2040_PWM/issues/1) leading to v1.0.1 - [Change the PWM frequency #2](https://github.com/khoih-prog/RP2040_PWM/issues/2) leading to v1.0.2 - 2. Thanks to [Austin K. Litman](https://github.com/AKLitman) to report bugs in - [Attempting to Alter the Duty Cycle w/o changing any other values #3](https://github.com/khoih-prog/RP2040_PWM/issues/3) leading to v1.0.5 - [Request for Clarification on PWM Slices and A/B sides #5](https://github.com/khoih-prog/RP2040_PWM/issues/5) leading to v1.1.0 - - +3. Thanks to [Joerg Starkmuth](https://github.com/Laserjones) to propose enhancement in +- [Duty cycle as integer rather than float #6](https://github.com/khoih-prog/RP2040_PWM/issues/6) leading to v1.2.0 +
americodias
⭐️ Américo Dias

AKLitman
⭐️ Austin K. Litman

Laserjones
Joerg Starkmuth

diff --git a/changelog.md b/changelog.md index 225706a..dcda2ca 100644 --- a/changelog.md +++ b/changelog.md @@ -12,6 +12,7 @@ ## Table of Contents * [Changelog](#changelog) + * [Releases v1.2.0](#Releases-v120) * [Releases v1.1.1](#Releases-v111) * [Releases v1.1.0](#Releases-v110) * [Releases v1.0.5](#Releases-v105) @@ -26,6 +27,12 @@ ## Changelog +### Releases v1.2.0 + +1. Add efficient `setPWM_manual()` function to use in wafeform creation using PWM. Check [Duty cycle as integer rather than float #6](https://github.com/khoih-prog/RP2040_PWM/issues/6) +2. Add example [PWM_Waveform](https://github.com/khoih-prog/RP2040_PWM/tree/main/examples/PWM_Waveform) to demonstrate how to use new `setPWM_manual()` function in wafeform creation +3. Optimize library code and examples by using **reference-passing instead of value-passing**. + ### Releases v1.1.1 1. Fix compiler warnings. diff --git a/examples/PWM_Waveform/PWM_Waveform.ino b/examples/PWM_Waveform/PWM_Waveform.ino new file mode 100644 index 0000000..39e6386 --- /dev/null +++ b/examples/PWM_Waveform/PWM_Waveform.ino @@ -0,0 +1,130 @@ +/**************************************************************************************************************************** + PWM_Waveform.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 +*****************************************************************************************************************************/ + +// This example to demo the new function setPWM_manual(uint8_t pin, uint16_t top, uint8_t div, uint16_t level, bool phaseCorrect = false) +// used to generate a waveform. Check https://github.com/khoih-prog/RP2040_PWM/issues/6 + +#define _PWM_LOGLEVEL_ 2 + +#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 pinLed 25 // GP25, On-board BUILTIN_LED +#define pin0 16 // GP16, PWM channel 4B (D2) +#define pin10 10 // PWM channel 5A +#define pin11 11 // PWM channel 5B + +#define pinToUse pin10 + +RP2040_PWM* PWM_Instance; + +typedef struct +{ + uint16_t top; + uint8_t div; + uint16_t level; +} PWD_Data; + +#define NUM_PWM_POINTS 12 + +// PWM Freq = 12487 Hz +PWD_Data PWM_data[NUM_PWM_POINTS] = +{ + { 1000, 10, 0 }, + { 1000, 10, 50 }, + { 1000, 10, 100 }, + { 1000, 10, 200 }, + { 1000, 10, 300 }, + { 1000, 10, 400 }, + { 1000, 10, 500 }, + { 1000, 10, 600 }, + { 1000, 10, 700 }, + { 1000, 10, 800 }, + { 1000, 10, 900 }, + { 1000, 10, 1000 }, +}; + +float frequency; + +// You can select any value +PWD_Data PWM_data_idle = PWM_data[0]; + +char dashLine[] = "============================================================="; + +void setup() +{ + Serial.begin(115200); + while (!Serial && millis() < 5000); + + delay(100); + + Serial.print(F("\nStarting PWM_Waveform on ")); Serial.println(BOARD_NAME); + Serial.println(RP2040_PWM_VERSION); + + frequency = 1000; + + // Create a dummy instance + PWM_Instance = new RP2040_PWM(pinToUse, frequency, 0); + + if (PWM_Instance) + { + // setPWM_manual(uint8_t pin, uint16_t top, uint8_t div, uint16_t level, bool phaseCorrect = false) + PWM_Instance->setPWM_manual(pinToUse, PWM_data_idle.top, PWM_data_idle.div, PWM_data_idle.level, true); + } + + Serial.println(dashLine); +} + +void printPWMInfo(RP2040_PWM* PWM_Instance) +{ + uint32_t div = PWM_Instance->get_DIV(); + uint32_t top = PWM_Instance->get_TOP(); + + // PWM_Freq = ( F_CPU ) / [ ( TOP + 1 ) * ( DIV + DIV_FRAC/16) ] + PWM_LOGINFO1("Actual PWM Frequency = ", PWM_Instance->get_freq_CPU() / ( (PWM_Instance->get_TOP() + 1) * (PWM_Instance->get_DIV() ) ) ); + + PWM_LOGDEBUG5("TOP =", top, ", DIV =", div, ", CPU_freq =", PWM_Instance->get_freq_CPU()); +} + +void loop() +{ + for (int index = 0; index < NUM_PWM_POINTS; index++) + { + PWM_Instance->setPWM_manual(pinToUse, PWM_data[index].top, PWM_data[index].div, PWM_data[index].level, true); + + // Use at low freq to check + //printPWMInfo(PWM_Instance); + + // delay something here between data + delay(100); + } +} diff --git a/keywords.txt b/keywords.txt index 6772d07..9e38c3b 100644 --- a/keywords.txt +++ b/keywords.txt @@ -14,6 +14,7 @@ PWM_slice KEYWORD1 ################################### setPWM KEYWORD2 +setPWM_manual KEYWORD2 setPWM_Period KEYWORD2 enablePWM KEYWORD2 disablePWM KEYWORD2 diff --git a/library.json b/library.json index 3fcaa4c..8478579 100644 --- a/library.json +++ b/library.json @@ -1,8 +1,8 @@ { "name": "RP2040_PWM", - "version": "1.1.1", + "version": "1.2.0", "keywords": "timing, device, control, timer, pwm, pwm-slice, hardware-based-pwm, high-frequency-pwm, hardware-pwm, mission-critical, accuracy, non-blocking, mbed, mbed-nano, mbed-rp2040, rpi-pico, rp2040, nano-rp2040-connect, duty-cycle, hardware", - "description": "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 frequencies. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware-based PWMs, 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 PWM using ISR, millis() or micros(). That's necessary if you need to control devices requiring high precision", + "description": "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 frequencies. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware-based PWMs, 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 PWM using ISR, millis() or micros(). That's necessary if you need to control devices requiring high precision. New efficient setPWM_manual function to facilitate waveform creation using PWM", "authors": { "name": "Khoi Hoang", diff --git a/library.properties b/library.properties index 84d1085..6b99c68 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,9 @@ name=RP2040_PWM -version=1.1.1 +version=1.2.0 author=Khoi Hoang maintainer=Khoi Hoang sentence=his 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 to any GPIO pin. -paragraph=TThe most important feature is they're purely hardware-based PWM channels, supporting very high PWM frequencies. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware-based PWMs, 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 PWM using ISR, millis() or micros(). That's necessary if you need to control devices requiring high precision +paragraph=The most important feature is they're purely hardware-based PWM channels, supporting very high PWM frequencies. Therefore, their executions are not blocked by bad-behaving functions or tasks. This important feature is absolutely necessary for mission-critical tasks. These hardware-based PWMs, 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 PWM using ISR, millis() or micros(). That's necessary if you need to control devices requiring high precision. New efficient setPWM_manual function to facilitate waveform creation using PWM category=Device Control url=https://github.com/khoih-prog/RP2040_PWM architectures=mbed,mbed_nano,ArduinoCore-mbed,rp2040,mbed_rp2040 diff --git a/src/PWM_Generic_Debug.h b/src/PWM_Generic_Debug.h index 4534f78..d72e070 100644 --- a/src/PWM_Generic_Debug.h +++ b/src/PWM_Generic_Debug.h @@ -12,7 +12,7 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.1.1 + Version: 1.2.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -24,6 +24,7 @@ 1.0.5 K Hoang 06/01/2022 Permit changing dutyCycle and keep same frequency on-the-fly 1.1.0 K Hoang 24/02/2022 Permit PWM output for both channels of PWM slice. Use float instead of double 1.1.1 K Hoang 06/03/2022 Fix compiler warnings. Display informational warning when debug level > 3 + 1.2.0 K Hoang 16/04/2022 Add manual setPWM function to use in wafeform creation *****************************************************************************************************************************/ #pragma once @@ -71,7 +72,7 @@ const char PWM_SP[] = " "; #define PWM_LOGERROR2(x,y,z) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINTLN(z); } #define PWM_LOGERROR3(x,y,z,w) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINTLN(w); } #define PWM_LOGERROR5(x,y,z,w,xx,yy) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINTLN(yy); } -#define PWM_LOGERROR7(x,y,z,w,xx,yy,zz,ww) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINT(yy); PWM_PRINT_SP; PWM_PRINT(zz); PWM_PRINTLN(ww); } +#define PWM_LOGERROR7(x,y,z,w,xx,yy,zz,ww) if(_PWM_LOGLEVEL_>0) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINT(yy); PWM_PRINT_SP; PWM_PRINT(zz); PWM_PRINT_SP; PWM_PRINTLN(ww); } ////////////////////////////////////////// @@ -93,7 +94,7 @@ const char PWM_SP[] = " "; #define PWM_LOGINFO2(x,y,z) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINTLN(z); } #define PWM_LOGINFO3(x,y,z,w) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINTLN(w); } #define PWM_LOGINFO5(x,y,z,w,xx,yy) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINTLN(yy); } -#define PWM_LOGINFO7(x,y,z,w,xx,yy,zz,ww) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINT(yy); PWM_PRINT_SP; PWM_PRINT(zz); PWM_PRINTLN(ww); } +#define PWM_LOGINFO7(x,y,z,w,xx,yy,zz,ww) if(_PWM_LOGLEVEL_>2) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINT(yy); PWM_PRINT_SP; PWM_PRINT(zz); PWM_PRINT_SP; PWM_PRINTLN(ww); } ////////////////////////////////////////// @@ -104,7 +105,7 @@ const char PWM_SP[] = " "; #define PWM_LOGDEBUG2(x,y,z) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINTLN(z); } #define PWM_LOGDEBUG3(x,y,z,w) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINTLN(w); } #define PWM_LOGDEBUG5(x,y,z,w,xx,yy) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINTLN(yy); } -#define PWM_LOGDEBUG7(x,y,z,w,xx,yy,zz,ww) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINT(yy); PWM_PRINT_SP; PWM_PRINT(zz); PWM_PRINTLN(ww); } +#define PWM_LOGDEBUG7(x,y,z,w,xx,yy,zz,ww) if(_PWM_LOGLEVEL_>3) { PWM_PRINT_MARK; PWM_PRINT(x); PWM_PRINT_SP; PWM_PRINT(y); PWM_PRINT_SP; PWM_PRINT(z); PWM_PRINT_SP; PWM_PRINT(w); PWM_PRINT_SP; PWM_PRINT(xx); PWM_PRINT_SP; PWM_PRINT(yy); PWM_PRINT_SP; PWM_PRINT(zz); PWM_PRINT_SP; PWM_PRINTLN(ww); } ////////////////////////////////////////// diff --git a/src/RP2040_PWM.h b/src/RP2040_PWM.h index 9c00787..2adb776 100644 --- a/src/RP2040_PWM.h +++ b/src/RP2040_PWM.h @@ -12,7 +12,7 @@ Therefore, their executions are not blocked by bad-behaving functions / tasks. This important feature is absolutely necessary for mission-critical tasks. - Version: 1.1.1 + Version: 1.2.0 Version Modified By Date Comments ------- ----------- ---------- ----------- @@ -24,6 +24,7 @@ 1.0.5 K Hoang 06/01/2022 Permit changing dutyCycle and keep same frequency on-the-fly 1.1.0 K Hoang 24/02/2022 Permit PWM output for both channels of PWM slice. Use float instead of double 1.1.1 K Hoang 06/03/2022 Fix compiler warnings. Display informational warning when debug level > 3 + 1.2.0 K Hoang 16/04/2022 Add manual setPWM function to use in wafeform creation *****************************************************************************************************************************/ #pragma once @@ -63,13 +64,13 @@ #endif #ifndef RP2040_PWM_VERSION - #define RP2040_PWM_VERSION "RP2040_PWM v1.1.1" + #define RP2040_PWM_VERSION "RP2040_PWM v1.2.0" #define RP2040_PWM_VERSION_MAJOR 1 - #define RP2040_PWM_VERSION_MINOR 1 - #define RP2040_PWM_VERSION_PATCH 1 + #define RP2040_PWM_VERSION_MINOR 2 + #define RP2040_PWM_VERSION_PATCH 0 - #define RP2040_PWM_VERSION_INT 1001001 + #define RP2040_PWM_VERSION_INT 1002000 #endif #include @@ -96,7 +97,7 @@ typedef struct bool channelA_Active; bool channelB_Active; } PWM_slice; - + static PWM_slice PWM_slice_data[NUM_PWM_SLICES] = { { 0, 0, 0, false, false }, @@ -108,6 +109,28 @@ static PWM_slice PWM_slice_data[NUM_PWM_SLICES] = { 0, 0, 0, false, false }, { 0, 0, 0, false, false } }; + +// Not using float for waveform creating +typedef struct +{ + uint16_t channelA_div; + uint16_t channelB_div; + bool channelA_Active; + bool channelB_Active; +} PWM_slice_manual; + +// Default to 0% PWM +static PWM_slice_manual PWM_slice_manual_data[NUM_PWM_SLICES] = +{ + { 0, 0, false, false }, + { 0, 0, false, false }, + { 0, 0, false, false }, + { 0, 0, false, false }, + { 0, 0, false, false }, + { 0, 0, false, false }, + { 0, 0, false, false }, + { 0, 0, false, false } +}; /////////////////////// /////////////////////////////////////////////////////////////////// @@ -116,7 +139,7 @@ class RP2040_PWM { public: - RP2040_PWM(uint8_t pin, float frequency, float dutycycle, bool phaseCorrect = false) + RP2040_PWM(const uint8_t& pin, const float& frequency, const float& dutycycle, bool phaseCorrect = false) { #if defined(F_CPU) freq_CPU = F_CPU; @@ -129,16 +152,18 @@ class RP2040_PWM _dutycycle = dutycycle; _phaseCorrect = phaseCorrect; - + if (!calc_TOP_and_DIV(frequency)) { _frequency = 0; } + else + { + _frequency = frequency; + } - _pin = pin; - _frequency = frequency; - _dutycycle = dutycycle; - + _pin = pin; + _dutycycle = dutycycle; _phaseCorrect = phaseCorrect; _enabled = false; @@ -157,7 +182,85 @@ class RP2040_PWM /////////////////////////////////////////// - bool setPWM(uint8_t pin, float frequency, float dutycycle, bool phaseCorrect = false) + bool setPWM_manual(const uint8_t& pin, const uint16_t& top, const uint8_t& div, + uint16_t& level, bool phaseCorrect = false) + { + _pin = pin; + + _PWM_config.top = top; + _PWM_config.div = div; + + // Limit level <= top + if (level > top) + level = top; + + gpio_set_function(_pin, GPIO_FUNC_PWM); + + _slice_num = pwm_gpio_to_slice_num(_pin); + + pwm_config config = pwm_get_default_config(); + + // Set phaseCorrect + pwm_set_phase_correct(_slice_num, phaseCorrect); + + pwm_config_set_clkdiv_int(&config, _PWM_config.div); + pwm_config_set_wrap(&config, _PWM_config.top); + + // auto start running once configured + pwm_init(_slice_num, &config, true); + pwm_set_gpio_level(_pin, level); + + // From v1.1.0 + //////////////////////////////// + // Update PWM_slice_manual_data[] + //PWM_slice_manual_data[_slice_num].freq = _frequency; + + if ( (pwm_gpio_to_channel(_pin)) == PWM_CHAN_A) + { + PWM_slice_manual_data[_slice_num].channelA_div = level; + PWM_slice_manual_data[_slice_num].channelA_Active = true; + + // If B is active, set the data now + if (PWM_slice_manual_data[_slice_num].channelB_Active) + { + pwm_set_chan_level(_slice_num, PWM_CHAN_B, PWM_slice_manual_data[_slice_num].channelB_div); + } + } + else if ( (pwm_gpio_to_channel(_pin)) == PWM_CHAN_B) + { + PWM_slice_manual_data[_slice_num].channelB_div = level; + PWM_slice_manual_data[_slice_num].channelB_Active = true; + + // If A is active, set the data now + if (PWM_slice_manual_data[_slice_num].channelA_Active) + { + pwm_set_chan_level(_slice_num, PWM_CHAN_A, PWM_slice_manual_data[_slice_num].channelA_div); + } + } + else + { + PWM_LOGERROR1("Error, not correct PWM pin = ", _pin); + + return false; + } + + pwm_set_enabled(_slice_num, true); + + PWM_LOGINFO3("pin = ", _pin, ", PWM_CHAN =", pwm_gpio_to_channel(_pin)); + + //////////////////////////////// + + _enabled = true; + + PWM_LOGINFO7("PWM enabled, slice =", _slice_num, ", top =", _PWM_config.top, + ", div =", _PWM_config.div, ", level =", level); + + return true; + } + + /////////////////////////////////////////// + + bool setPWM(const uint8_t& pin, const float& frequency, const float& dutycycle, bool phaseCorrect = false) { bool newFreq = false; bool newDutyCycle = false; @@ -270,7 +373,7 @@ class RP2040_PWM /////////////////////////////////////////// - bool setPWM_Period(uint8_t pin, float period_us, float dutycycle, bool phaseCorrect = false) + bool setPWM_Period(const uint8_t& pin, const float& period_us, const float& dutycycle, bool phaseCorrect = false) { return setPWM(pin, 1000000.0f / period_us, dutycycle, phaseCorrect); } @@ -293,28 +396,28 @@ class RP2040_PWM /////////////////////////////////////////// - uint32_t get_TOP() + inline uint32_t get_TOP() { return _PWM_config.top; } /////////////////////////////////////////// - uint32_t get_DIV() + inline uint32_t get_DIV() { return _PWM_config.div; } /////////////////////////////////////////// - float getActualFreq() + inline float getActualFreq() { return _actualFrequency; } /////////////////////////////////////////// - uint32_t get_freq_CPU() + inline uint32_t get_freq_CPU() { return freq_CPU; } @@ -384,6 +487,5 @@ class RP2040_PWM /////////////////////////////////////////// - #endif // RP2040_PWM_H