From c7e7e33cf2684147f18609fd8dc82317bc771661 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 1 Jan 2023 09:24:50 +0100 Subject: [PATCH 01/29] initial functioning version of ESP32 HardwarePWM. Tested to run on the ESP32C3 with up to 5kHz pwm frequency. --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 230 ++++++++++++++++++++++++++ Sming/Core/HardwarePWM.h | 16 ++ 2 files changed, 246 insertions(+) create mode 100644 Sming/Arch/Esp32/Core/HardwarePWM.cpp diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp new file mode 100644 index 0000000000..ce0bff5979 --- /dev/null +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -0,0 +1,230 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * HardwarePWM.cpp + * + * Original Author: https://github.com/hrsavla + * + * This HardwarePWM library enables Sming framework user to use ESP SDK PWM API + * Period of PWM is fixed to 1000us / Frequency = 1khz + * Duty at 100% = 22222. Duty at 0% = 0 + * You can use function setPeriod() to change frequency/period. + * Calculate the max duty as per the formulae give in ESP8266 SDK + * Max Duty = (Period * 1000) / 45 + * + * PWM can be generated on up to 8 pins (ie All pins except pin 16) + * Created on August 17, 2015, 2:27 PM + * + * See also ESP8266 Technical Reference, Chapter 12: + * http://espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf + * + */ + +// orig #include +// orig #include "ESP8266EX.h" +#include +//#include +#include "driver/ledc.h" +#include "esp_err.h" +#include + + +// orig #include + +// #define SOC_LEDC_TIMER_BIT_WIDE_NUM 9 +// #define SOC_LEDC_CHANNEL_NUM 6 + + + // default period in µs -> 5kHz + + +// define default resolution + +/*#if static_castSOC_LEDC_TIMER_BIT_WIDE_NUM >= DESIRED_DEFAULT_RESOLUTION + #define DEFAULT_RESOLUTION DESIRED_DEFAULT_RESOLUTION +#else + #define DEFAULT_RESOLUTION static_castSOC_LEDC_TIMER_BIT_WIDE_NUM // if 10 bit is not available, set to max +#endif +*/ +#define DEFAULT_RESOLUTION static_cast(10) +#define DEFAULT_PERIOD 200 + +HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_of_pins) +{ + ledc_timer_config_t ledc_timer; + ledc_channel_config_t ledc_channel; + debugf("starting HardwarePWM init"); + periph_module_enable(PERIPH_LEDC_MODULE); + if((no_of_pins > 0) && (no_of_pins < SOC_LEDC_CHANNEL_NUM)) + { + uint32_t io_info[SOC_LEDC_CHANNEL_NUM][3]; // pin information + uint32_t pwm_duty_init[SOC_LEDC_CHANNEL_NUM]; // pwm duty + for(uint8_t i = 0; i < no_of_pins; i++) { + /* todo: update the digitalPins map + io_info[i][0] = EspDigitalPins[pins[i]].mux; + io_info[i][1] = EspDigitalPins[pins[i]].gpioFunc; + io_info[i][2] = EspDigitalPins[pins[i]].id; + */ + pwm_duty_init[i] = 0; // Start with zero output + channels[i] = pins[i]; + + /* + / Prepare and then apply the LEDC PWM timer configuration + / this may cofigure the same timer more than once (in fact up to 8 times) + / which should not be an issue, though, since the values should be the same for all timers + */ + ledc_timer.speed_mode = pinToGroup(i); // the two groups (if available) are operating in different speed modes, hence speed mode is an alias for group or vice versa + ledc_timer.timer_num = pinToTimer(i); + ledc_timer.duty_resolution = LEDC_TIMER_10_BIT; // todo: make configurable later + ledc_timer.freq_hz = periodToFrequency(DEFAULT_PERIOD); // todo: make configurable later + ledc_timer.clk_cfg = LEDC_AUTO_CLK; + + debugf("ledc_timer.\n\tspeed_mode: %i\n\ttimer_num: %i\n\tduty_resolution: %i\n\tfreq: %i\n\tclk_cfg: %i\n\n", + (uint32_t)ledc_timer.speed_mode, (uint32_t)ledc_timer.timer_num, + (uint32_t)ledc_timer.duty_resolution, (uint32_t)ledc_timer.freq_hz, (uint32_t)ledc_timer.clk_cfg); + ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); + + /* + / Prepare and then apply the LEDC PWM channel configuration + */ + ledc_channel.speed_mode = pinToGroup(i); + ledc_channel.channel = pinToChannel(i); + ledc_channel.timer_sel = pinToTimer(i); + ledc_channel.intr_type = LEDC_INTR_DISABLE; + ledc_channel.gpio_num = pins[i]; + ledc_channel.duty = 0; // Set duty to 0% + ledc_channel.hpoint = 0; + debugf("ledc_channel\n\tspeed_mode: %i\n\tchannel: %i\n\ttimer_sel %i\n\tinr_type: %i\n\tgpio_num: " + "%i\n\tduty: %i\n\thpoint: %i\n\n", + pinToGroup(i) , pinToChannel(i), pinToTimer(i), LEDC_INTR_DISABLE, pins[i], 0, 0); + ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); + ledc_bind_channel_timer(pinToGroup(i), pinToChannel(i), pinToTimer(i)); + } + maxduty=maxDuty(DEFAULT_RESOLUTION); + const int initial_period=DEFAULT_PERIOD; + } +} + +HardwarePWM::~HardwarePWM() +{ + // There is no function in the SDK to stop PWM output, yet. +} + +/* Function Name: getChannel + * Description: This function is used to get channel number for given pin + * Parameters: pin - Esp8266 pin number + */ +uint8_t HardwarePWM::getChannel(uint8_t pin) +{ + for(uint8_t i = 0; i < channel_count; i++) { + if(channels[i] == pin) { + //debugf("getChannel %d is %d", pin, i); + return i; + } + } + return -1; +} + +/* Function Name: getDutyChan + * Description: This function is used to get the duty cycle number for a given channel + * Parameters: chan -Esp8266 channel number + */ +uint32_t HardwarePWM::getDutyChan(uint8_t chan) +{ + if(chan == PWM_BAD_CHANNEL) { + return 0; + } else { + return ledc_get_duty(pinToGroup(chan),pinToChannel(chan)); + } + // esp32 defines the frequency / period per timer, +} + +/* Function Name: setDutyChan + * Description: This function is used to set the pwm duty cycle for a given channel. If parameter 'update' is false + * then you have to call update() later to update duties. + * Parameters: chan - channel number + * duty - duty cycle value + * update - update PWM output + */ +bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) +{ + if(chan == PWM_BAD_CHANNEL) { + return false; + } else if(duty <= maxduty) { + ESP_ERROR_CHECK(ledc_set_duty(pinToGroup(chan), pinToChannel(chan), duty)); + if(update) { + ESP_ERROR_CHECK(ledc_update_duty(pinToGroup(chan), pinToChannel(chan))); + //update(); + } + return true; + } else { + debugf("Duty cycle value too high for current period."); + return false; + } +} + +/* Function Name: getPeriod + * Description: This function is used to get Period of PWM. + * Period / frequency will remain same for all pins. + * + */ +uint32_t HardwarePWM::getPeriod() +{ + // sming does not know how to handle different frequencies for channels, this will require an extended interface + // for now, just report the period for group 0 channel 0 + return frequencyToPeriod(ledc_get_freq(static_cast(0),static_cast(0))); +} + +/* Function Name: setPeriod + * Description: This function is used to set Period of PWM. + * Period / frequency will remain same for all pins. + */ +void HardwarePWM::setPeriod(uint32_t period) +{ + // setting the frequency globally, will add per timer functions later + // also, this can be done smarter + for(uint8_t i = 0; i < channel_count; i++) { + ESP_ERROR_CHECK(ledc_set_freq(pinToGroup(i), pinToTimer(i), periodToFrequency(period))); + } + //sledc_update_duty(); + update(); +} + +/* Function Name: update + * Description: This function is used to actually update the PWM. + */ +void HardwarePWM::update() +{ + //ledc_update_duty(); +} + +ledc_channel_t HardwarePWM::pinToChannel(uint8_t pin){ + return (ledc_channel_t)(pin % 8); +} + +ledc_mode_t HardwarePWM::pinToGroup(uint8_t pin){ + return (ledc_mode_t) (pin / 8); +} + +ledc_timer_t HardwarePWM::pinToTimer(uint8_t pin){ + return (ledc_timer_t) ((pin /2) % 4); +} + +uint32_t HardwarePWM::periodToFrequency(uint32_t period){ + return (1000000 / period); +} + +uint32_t HardwarePWM::frequencyToPeriod(uint32_t freq){ + return (1000000 / freq); +} + +uint32_t HardwarePWM::maxDuty(ledc_timer_bit_t bits){ + return (1<<(uint32_t)bits) - 1; +} + +uint32_t HardwarePWM::getFrequency(uint8_t pin){ + return ledc_get_freq(pinToGroup(pin), pinToTimer(pin)); +} \ No newline at end of file diff --git a/Sming/Core/HardwarePWM.h b/Sming/Core/HardwarePWM.h index 78b27476fb..99fed4041b 100644 --- a/Sming/Core/HardwarePWM.h +++ b/Sming/Core/HardwarePWM.h @@ -29,6 +29,9 @@ #include #include +#if SMING_SOC == Esp32 + #include "hal/ledc_types.h" +#endif #define PWM_BAD_CHANNEL 0xff ///< Invalid PWM channel @@ -121,8 +124,21 @@ class HardwarePWM /** @brief This function is used to actually update the PWM. */ void update(); + + #if SMING_ARCH == Esp32 + uint32_t getFrequency(uint8_t pin); + #endif private: +#if SMING_ARCH == Esp32 + ledc_mode_t pinToGroup(uint8_t pin); + ledc_channel_t pinToChannel(uint8_t pin); + ledc_timer_t pinToTimer(uint8_t pin); + uint32_t periodToFrequency(uint32_t period); + uint32_t frequencyToPeriod(uint32_t freq); + uint32_t maxDuty(ledc_timer_bit_t bits); +#endif + uint8_t channel_count; uint8_t channels[PWM_CHANNEL_NUM_MAX]; uint32_t maxduty; From 806b9de1ee53a86d476bbc4396fcee4e8f8c997e Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 1 Jan 2023 10:25:36 +0100 Subject: [PATCH 02/29] some documentation updates --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 47 +++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index ce0bff5979..e00d3d9516 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -7,10 +7,51 @@ * HardwarePWM.cpp * * Original Author: https://github.com/hrsavla + * Esp32 version: https://github.com/pljakobs * - * This HardwarePWM library enables Sming framework user to use ESP SDK PWM API - * Period of PWM is fixed to 1000us / Frequency = 1khz - * Duty at 100% = 22222. Duty at 0% = 0 + * This HardwarePWM library enables Sming framework uses to use the ESP32 ledc PWM api + * + * the ESP32 PWM Hardware is much more powerful than the ESP8266, allowing wider PWM timers (up to 20 bit) + * as well as much higher PWM frequencies (up to 40MHz for a 1 Bit wide PWM) + * + * Timer width for PWM: + * ==================== + * esp32 SOC_LEDC_TIMER_BIT_WIDE_NUM (20) + * esp32c3 SOC_LEDC_TIMER_BIT_WIDE_NUM (14) + * esp32s2 SOC_LEDC_TIMER_BIT_WIDE_NUM (14) + * esp32s3 SOC_LEDC_TIMER_BIT_WIDE_NUM (14) + * + * Number of Channels: + * =================== + * esp32 SOC_LEDC_CHANNEL_NUM (8) + * esp32c3 SOC_LEDC_CHANNEL_NUM (6) + * esp32s2 SOC_LEDC_CHANNEL_NUM (8) + * esp32s3 SOC_LEDC_CHANNEL_NUM 8 + * + * Some Architectures support a mode called HIGHSPEED_MODE which is essentially another full block of PWM hardware + * that adds SOC_LEDC_CHANNEL_NUM channels. + * Those Architectures have SOC_LEDC_SUPPORT_HS_MODE defined as 1. + * In esp-idf-4.3 that's currently only the esp32 SOC + * + * Supports highspeed mode: + * ======================== + * esp32 SOC_LEDC_SUPPORT_HS_MODE (1) + * + * ToDo: implement awareness of hs mode availablility + * + * hardware technical reference: + * ============================= + * https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#ledpwm + * + * Overview of the whole ledc-system here: + * https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html + * + * the ledc interface also exposes some advanced functions such as fades that are then done in hardware. + * ToDo: implement a Sming interface for fades + * + + + * You can use function setPeriod() to change frequency/period. * Calculate the max duty as per the formulae give in ESP8266 SDK * Max Duty = (Period * 1000) / 45 From 34ca1461403d0ca7a6f262a852f7bd159149c8d3 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 1 Jan 2023 11:30:37 +0100 Subject: [PATCH 03/29] more documentation --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 76 +++++++++++++++++++++++++-- 1 file changed, 73 insertions(+), 3 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index e00d3d9516..e40c5c6be3 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -14,8 +14,60 @@ * the ESP32 PWM Hardware is much more powerful than the ESP8266, allowing wider PWM timers (up to 20 bit) * as well as much higher PWM frequencies (up to 40MHz for a 1 Bit wide PWM) * - * Timer width for PWM: - * ==================== + * Overview: + * +------------------------------------------------------------------------------------------------+ + * | LED_PWM | + * | +-------------------------------------------+ +-------------------------------------------+ | + * | | High_Speed_Channels¹ | | Low_Speed_Channels | | + * | | +-----+ +--------+ | | +-----+ +--------+ | | + * | | | | --> | h_ch 0 | | | | | --> | l_ch 0 | | | + * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | + * | | | h_timer 0 | --> | | | | | l_timer 0 | --> | | | | + * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | + * | | | | --> | h_ch 1 | | | | | --> | l_ch 1 | | | + * | | | | +--------+ | | | | +--------+ | | + * | | | | | | | | | | + * | | | | +--------+ | | | | +--------+ | | + * | | | | --> | h_ch 2 | | | | | --> | l_ch 2 | | | + * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | + * | | | h_timer 1 | --> | | | | | l_timer 1 | --> | | | | + * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | + * | | | | --> | h_ch 3 | | | | | --> | l_ch 3 | | | + * | | | MUX | | | | MUX | | | + * | | | | +--------+ | | | | +--------+ | | + * | | | | --> | h_ch 4 | | | | | --> | l_ch 4 | | | + * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | + * | | | h_timer 2 | --> | | | | | l_timer 2 | --> | | | | + * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | + * | | | | --> | h_ch 5 | | | | | --> | l_ch 5 | | | + * | | | | +--------+ | | | | +--------+ | | + * | | | | | | | | | | + * | | | | +--------+ | | | | +--------+ | | + * | | | | --> | h_ch 6 | | | | | --> | l_ch 6²| | | + * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | + * | | | h_timer 3 | --> | | | | | l_timer 3 | --> | | | | + * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | + * | | | | --> | h_ch 7 | | | | | --> | l_ch 7²| | | + * | | | | +--------+ | | | | +--------+ | | + * | | +-----+ | | +-----+ | | + * | +-------------------------------------------+ +-------------------------------------------+ | + * +------------------------------------------------------------------------------------------------+ + * ¹ High speed channels are only available when SOC_LEDC_SUPPORT_HS_MODE is defined as 1 + * ² The ESP32C3 does only support six channels, so 6 and 7 are not available on that SoC + * + * The nomenclature of timers in the high speed / low speed blocks is a bit misleading as the idf api + * speaks of "speed mode", which, to me, implies that this would be a mode configurable in a specific timer + * while in reality, it does select a block of timers. I am considering re-naming that to "speed mode block" + * in my interface impmenentation. + * + * As an example, I would use + * setTimerFrequency(speedModeBlock, timer, frequency); + * + * ToDo: see, how this can be implemented to provide maximum overlap with the RP2040 pwm hardware so code does + * not become overly SoC specific. + * + * Maximum Timer width for PWM: + * ============================ * esp32 SOC_LEDC_TIMER_BIT_WIDE_NUM (20) * esp32c3 SOC_LEDC_TIMER_BIT_WIDE_NUM (14) * esp32s2 SOC_LEDC_TIMER_BIT_WIDE_NUM (14) @@ -28,7 +80,7 @@ * esp32s2 SOC_LEDC_CHANNEL_NUM (8) * esp32s3 SOC_LEDC_CHANNEL_NUM 8 * - * Some Architectures support a mode called HIGHSPEED_MODE which is essentially another full block of PWM hardware + * Some SoSs support a mode called HIGHSPEED_MODE which is essentially another full block of PWM hardware * that adds SOC_LEDC_CHANNEL_NUM channels. * Those Architectures have SOC_LEDC_SUPPORT_HS_MODE defined as 1. * In esp-idf-4.3 that's currently only the esp32 SOC @@ -38,6 +90,24 @@ * esp32 SOC_LEDC_SUPPORT_HS_MODE (1) * * ToDo: implement awareness of hs mode availablility + * ================================================== + * currently, the code just uses a % 8 operation on the pin index to calculate whether to assign a pin to either + * high speed or low speed pwm blocks. This doesn't make a whole lot of sense since it makes it impossible + * for Sming devs to actually use the functionality behind it. + * Also, it currently does not reflect the fact that different SOCs have a different number of channels per block + * (specifically, the esp32c3 only has six channels and no highspeed mode). + * I will continue in two ways: + * - implement the "vanilla" Sming HardwarePWM interface that will hide the underlying architecture but allow up to 16 + * channels on an ESP32 + * - implement overloads for the relevant functions that allow selecting hs mode where applicable. + * + * ToDo: implement PWM bit width control + * ===================================== + * the current HardwarePWM implementation does not care about the PWM timer bit width. To leverage the functionality + * of the ESP32 hardware, it's necessary to make this configurable. As the width is per timer and all the Sming defined + * functions are basically per pin, this needs to be an extension of the overal model, exposing at least timers. + * This, too, will require a compatible "basic" interface and an advanced interface that allows assiging pins (channels) + * to timers and the configuration of the timers themselves. * * hardware technical reference: * ============================= From 246744e479b923fe9ef3cae2e3af75cf4321dff5 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 1 Jan 2023 12:13:20 +0100 Subject: [PATCH 04/29] more documentation --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index e40c5c6be3..c09fd67fb9 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -33,6 +33,7 @@ * | | | h_timer 1 | --> | | | | | l_timer 1 | --> | | | | * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | * | | | | --> | h_ch 3 | | | | | --> | l_ch 3 | | | + * | | | | +--------+ | | | | +--------+ | | * | | | MUX | | | | MUX | | | * | | | | +--------+ | | | | +--------+ | | * | | | | --> | h_ch 4 | | | | | --> | l_ch 4 | | | @@ -108,6 +109,9 @@ * functions are basically per pin, this needs to be an extension of the overal model, exposing at least timers. * This, too, will require a compatible "basic" interface and an advanced interface that allows assiging pins (channels) * to timers and the configuration of the timers themselves. + * The esp_idf does not provide a way to read the bit width configured for a channel, but I think it'll be useful to be able + * to read back this value, not least to find the value for getMaxDuty() for a channel. It will either have to be stored in the + * module or maybe read from the hardware directly (LEDC_[HL]STIMERx_CONF_REG & 0x1f) * * hardware technical reference: * ============================= From 30b5f1a291c01d562f96922a4096c50356b71ebd Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 12 Feb 2023 11:24:17 +0100 Subject: [PATCH 05/29] implemented requested changes from initial PR --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index c09fd67fb9..d92c82b2cb 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -79,8 +79,8 @@ * esp32 SOC_LEDC_CHANNEL_NUM (8) * esp32c3 SOC_LEDC_CHANNEL_NUM (6) * esp32s2 SOC_LEDC_CHANNEL_NUM (8) - * esp32s3 SOC_LEDC_CHANNEL_NUM 8 - * + * esp32s3 SOC_LEDC_CHANNEL_NUM (8) + * * Some SoSs support a mode called HIGHSPEED_MODE which is essentially another full block of PWM hardware * that adds SOC_LEDC_CHANNEL_NUM channels. * Those Architectures have SOC_LEDC_SUPPORT_HS_MODE defined as 1. @@ -98,7 +98,8 @@ * Also, it currently does not reflect the fact that different SOCs have a different number of channels per block * (specifically, the esp32c3 only has six channels and no highspeed mode). * I will continue in two ways: - * - implement the "vanilla" Sming HardwarePWM interface that will hide the underlying architecture but allow up to 16 + * - implement the "vanilla" Sming HardwarePWM i + nterface that will hide the underlying architecture but allow up to 16 * channels on an ESP32 * - implement overloads for the relevant functions that allow selecting hs mode where applicable. * From f67fb1a1b508818d94f929cb6b8e42ff6073f76c Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 12 Feb 2023 11:32:40 +0100 Subject: [PATCH 06/29] implemented requested changes from initial PR (now *with* changes. Sigh) --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 236 ++++++++------------------ Sming/Core/HardwarePWM.h | 17 +- 2 files changed, 70 insertions(+), 183 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index d92c82b2cb..84b0cdb632 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -7,126 +7,10 @@ * HardwarePWM.cpp * * Original Author: https://github.com/hrsavla - * Esp32 version: https://github.com/pljakobs * - * This HardwarePWM library enables Sming framework uses to use the ESP32 ledc PWM api - * - * the ESP32 PWM Hardware is much more powerful than the ESP8266, allowing wider PWM timers (up to 20 bit) - * as well as much higher PWM frequencies (up to 40MHz for a 1 Bit wide PWM) - * - * Overview: - * +------------------------------------------------------------------------------------------------+ - * | LED_PWM | - * | +-------------------------------------------+ +-------------------------------------------+ | - * | | High_Speed_Channels¹ | | Low_Speed_Channels | | - * | | +-----+ +--------+ | | +-----+ +--------+ | | - * | | | | --> | h_ch 0 | | | | | --> | l_ch 0 | | | - * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | - * | | | h_timer 0 | --> | | | | | l_timer 0 | --> | | | | - * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | - * | | | | --> | h_ch 1 | | | | | --> | l_ch 1 | | | - * | | | | +--------+ | | | | +--------+ | | - * | | | | | | | | | | - * | | | | +--------+ | | | | +--------+ | | - * | | | | --> | h_ch 2 | | | | | --> | l_ch 2 | | | - * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | - * | | | h_timer 1 | --> | | | | | l_timer 1 | --> | | | | - * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | - * | | | | --> | h_ch 3 | | | | | --> | l_ch 3 | | | - * | | | | +--------+ | | | | +--------+ | | - * | | | MUX | | | | MUX | | | - * | | | | +--------+ | | | | +--------+ | | - * | | | | --> | h_ch 4 | | | | | --> | l_ch 4 | | | - * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | - * | | | h_timer 2 | --> | | | | | l_timer 2 | --> | | | | - * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | - * | | | | --> | h_ch 5 | | | | | --> | l_ch 5 | | | - * | | | | +--------+ | | | | +--------+ | | - * | | | | | | | | | | - * | | | | +--------+ | | | | +--------+ | | - * | | | | --> | h_ch 6 | | | | | --> | l_ch 6²| | | - * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | - * | | | h_timer 3 | --> | | | | | l_timer 3 | --> | | | | - * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | - * | | | | --> | h_ch 7 | | | | | --> | l_ch 7²| | | - * | | | | +--------+ | | | | +--------+ | | - * | | +-----+ | | +-----+ | | - * | +-------------------------------------------+ +-------------------------------------------+ | - * +------------------------------------------------------------------------------------------------+ - * ¹ High speed channels are only available when SOC_LEDC_SUPPORT_HS_MODE is defined as 1 - * ² The ESP32C3 does only support six channels, so 6 and 7 are not available on that SoC - * - * The nomenclature of timers in the high speed / low speed blocks is a bit misleading as the idf api - * speaks of "speed mode", which, to me, implies that this would be a mode configurable in a specific timer - * while in reality, it does select a block of timers. I am considering re-naming that to "speed mode block" - * in my interface impmenentation. - * - * As an example, I would use - * setTimerFrequency(speedModeBlock, timer, frequency); - * - * ToDo: see, how this can be implemented to provide maximum overlap with the RP2040 pwm hardware so code does - * not become overly SoC specific. - * - * Maximum Timer width for PWM: - * ============================ - * esp32 SOC_LEDC_TIMER_BIT_WIDE_NUM (20) - * esp32c3 SOC_LEDC_TIMER_BIT_WIDE_NUM (14) - * esp32s2 SOC_LEDC_TIMER_BIT_WIDE_NUM (14) - * esp32s3 SOC_LEDC_TIMER_BIT_WIDE_NUM (14) - * - * Number of Channels: - * =================== - * esp32 SOC_LEDC_CHANNEL_NUM (8) - * esp32c3 SOC_LEDC_CHANNEL_NUM (6) - * esp32s2 SOC_LEDC_CHANNEL_NUM (8) - * esp32s3 SOC_LEDC_CHANNEL_NUM (8) - * - * Some SoSs support a mode called HIGHSPEED_MODE which is essentially another full block of PWM hardware - * that adds SOC_LEDC_CHANNEL_NUM channels. - * Those Architectures have SOC_LEDC_SUPPORT_HS_MODE defined as 1. - * In esp-idf-4.3 that's currently only the esp32 SOC - * - * Supports highspeed mode: - * ======================== - * esp32 SOC_LEDC_SUPPORT_HS_MODE (1) - * - * ToDo: implement awareness of hs mode availablility - * ================================================== - * currently, the code just uses a % 8 operation on the pin index to calculate whether to assign a pin to either - * high speed or low speed pwm blocks. This doesn't make a whole lot of sense since it makes it impossible - * for Sming devs to actually use the functionality behind it. - * Also, it currently does not reflect the fact that different SOCs have a different number of channels per block - * (specifically, the esp32c3 only has six channels and no highspeed mode). - * I will continue in two ways: - * - implement the "vanilla" Sming HardwarePWM i - nterface that will hide the underlying architecture but allow up to 16 - * channels on an ESP32 - * - implement overloads for the relevant functions that allow selecting hs mode where applicable. - * - * ToDo: implement PWM bit width control - * ===================================== - * the current HardwarePWM implementation does not care about the PWM timer bit width. To leverage the functionality - * of the ESP32 hardware, it's necessary to make this configurable. As the width is per timer and all the Sming defined - * functions are basically per pin, this needs to be an extension of the overal model, exposing at least timers. - * This, too, will require a compatible "basic" interface and an advanced interface that allows assiging pins (channels) - * to timers and the configuration of the timers themselves. - * The esp_idf does not provide a way to read the bit width configured for a channel, but I think it'll be useful to be able - * to read back this value, not least to find the value for getMaxDuty() for a channel. It will either have to be stored in the - * module or maybe read from the hardware directly (LEDC_[HL]STIMERx_CONF_REG & 0x1f) - * - * hardware technical reference: - * ============================= - * https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#ledpwm - * - * Overview of the whole ledc-system here: - * https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html - * - * the ledc interface also exposes some advanced functions such as fades that are then done in hardware. - * ToDo: implement a Sming interface for fades - * - - - + * This HardwarePWM library enables Sming framework user to use ESP SDK PWM API + * Period of PWM is fixed to 1000us / Frequency = 1khz + * Duty at 100% = 22222. Duty at 0% = 0 * You can use function setPeriod() to change frequency/period. * Calculate the max duty as per the formulae give in ESP8266 SDK * Max Duty = (Period * 1000) / 45 @@ -145,8 +29,17 @@ //#include #include "driver/ledc.h" #include "esp_err.h" +#include "hal/ledc_types.h" #include +namespace{ + ledc_mode_t pinToGroup(uint8_t pin); + ledc_channel_t pinToChannel(uint8_t pin); + ledc_timer_t pinToTimer(uint8_t pin); + uint32_t periodToFrequency(uint32_t period); + uint32_t frequencyToPeriod(uint32_t freq); + uint32_t maxDuty(ledc_timer_bit_t bits); +} // orig #include @@ -172,53 +65,52 @@ HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_o { ledc_timer_config_t ledc_timer; ledc_channel_config_t ledc_channel; - debugf("starting HardwarePWM init"); + debug_d("starting HardwarePWM init"); periph_module_enable(PERIPH_LEDC_MODULE); - if((no_of_pins > 0) && (no_of_pins < SOC_LEDC_CHANNEL_NUM)) + if((no_of_pins == 0) || (no_of_pins > SOC_LEDC_CHANNEL_NUM)) { - uint32_t io_info[SOC_LEDC_CHANNEL_NUM][3]; // pin information - uint32_t pwm_duty_init[SOC_LEDC_CHANNEL_NUM]; // pwm duty - for(uint8_t i = 0; i < no_of_pins; i++) { - /* todo: update the digitalPins map - io_info[i][0] = EspDigitalPins[pins[i]].mux; - io_info[i][1] = EspDigitalPins[pins[i]].gpioFunc; - io_info[i][2] = EspDigitalPins[pins[i]].id; - */ - pwm_duty_init[i] = 0; // Start with zero output - channels[i] = pins[i]; - - /* - / Prepare and then apply the LEDC PWM timer configuration - / this may cofigure the same timer more than once (in fact up to 8 times) - / which should not be an issue, though, since the values should be the same for all timers - */ - ledc_timer.speed_mode = pinToGroup(i); // the two groups (if available) are operating in different speed modes, hence speed mode is an alias for group or vice versa - ledc_timer.timer_num = pinToTimer(i); - ledc_timer.duty_resolution = LEDC_TIMER_10_BIT; // todo: make configurable later - ledc_timer.freq_hz = periodToFrequency(DEFAULT_PERIOD); // todo: make configurable later - ledc_timer.clk_cfg = LEDC_AUTO_CLK; - - debugf("ledc_timer.\n\tspeed_mode: %i\n\ttimer_num: %i\n\tduty_resolution: %i\n\tfreq: %i\n\tclk_cfg: %i\n\n", - (uint32_t)ledc_timer.speed_mode, (uint32_t)ledc_timer.timer_num, - (uint32_t)ledc_timer.duty_resolution, (uint32_t)ledc_timer.freq_hz, (uint32_t)ledc_timer.clk_cfg); - ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); + // is there a return code for this? + return ; + } + uint32_t io_info[SOC_LEDC_CHANNEL_NUM][3]; // pin information + uint32_t pwm_duty_init[SOC_LEDC_CHANNEL_NUM]; // pwm duty + for(uint8_t i = 0; i < no_of_pins; i++) { + + pwm_duty_init[i] = 0; // Start with zero output + channels[i] = pins[i]; + + /* + / Prepare and then apply the LEDC PWM timer configuration + / this may cofigure the same timer more than once (in fact up to 8 times) + / which should not be an issue, though, since the values should be the same for all timers + */ + ledc_timer.speed_mode = pinToGroup(i); // the two groups (if available) are operating in different speed modes, hence speed mode is an alias for group or vice versa + ledc_timer.timer_num = pinToTimer(i); + ledc_timer.duty_resolution = LEDC_TIMER_10_BIT; // todo: make configurable later + ledc_timer.freq_hz = periodToFrequency(DEFAULT_PERIOD); // todo: make configurable later + ledc_timer.clk_cfg = LEDC_AUTO_CLK; + + debug_d("ledc_timer.\n\tspeed_mode: %i\n\ttimer_num: %i\n\tduty_resolution: %i\n\tfreq: %i\n\tclk_cfg: %i\n\n", + (uint32_t)ledc_timer.speed_mode, (uint32_t)ledc_timer.timer_num, + (uint32_t)ledc_timer.duty_resolution, (uint32_t)ledc_timer.freq_hz, (uint32_t)ledc_timer.clk_cfg); + ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); + + /* + / Prepare and then apply the LEDC PWM channel configuration + */ + ledc_channel.speed_mode = pinToGroup(i); + ledc_channel.channel = pinToChannel(i); + ledc_channel.timer_sel = pinToTimer(i); + ledc_channel.intr_type = LEDC_INTR_DISABLE; + ledc_channel.gpio_num = pins[i]; + ledc_channel.duty = 0; // Set duty to 0% + ledc_channel.hpoint = 0; + debug_d("ledc_channel\n\tspeed_mode: %i\n\tchannel: %i\n\ttimer_sel %i\n\tinr_type: %i\n\tgpio_num: " + "%i\n\tduty: %i\n\thpoint: %i\n\n", + pinToGroup(i) , pinToChannel(i), pinToTimer(i), LEDC_INTR_DISABLE, pins[i], 0, 0); + ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); + ledc_bind_channel_timer(pinToGroup(i), pinToChannel(i), pinToTimer(i)); - /* - / Prepare and then apply the LEDC PWM channel configuration - */ - ledc_channel.speed_mode = pinToGroup(i); - ledc_channel.channel = pinToChannel(i); - ledc_channel.timer_sel = pinToTimer(i); - ledc_channel.intr_type = LEDC_INTR_DISABLE; - ledc_channel.gpio_num = pins[i]; - ledc_channel.duty = 0; // Set duty to 0% - ledc_channel.hpoint = 0; - debugf("ledc_channel\n\tspeed_mode: %i\n\tchannel: %i\n\ttimer_sel %i\n\tinr_type: %i\n\tgpio_num: " - "%i\n\tduty: %i\n\thpoint: %i\n\n", - pinToGroup(i) , pinToChannel(i), pinToTimer(i), LEDC_INTR_DISABLE, pins[i], 0, 0); - ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); - ledc_bind_channel_timer(pinToGroup(i), pinToChannel(i), pinToTimer(i)); - } maxduty=maxDuty(DEFAULT_RESOLUTION); const int initial_period=DEFAULT_PERIOD; } @@ -226,7 +118,11 @@ HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_o HardwarePWM::~HardwarePWM() { - // There is no function in the SDK to stop PWM output, yet. + for(uint8_t i = 0; i < no_of_pins; i++) { + //stop pwm for all pins and set idle level to 0. + + ledc_stop(pinToGroup(pins[i]), pinToChannel(pins[i]), uint32_t 0); + } } /* Function Name: getChannel @@ -237,7 +133,7 @@ uint8_t HardwarePWM::getChannel(uint8_t pin) { for(uint8_t i = 0; i < channel_count; i++) { if(channels[i] == pin) { - //debugf("getChannel %d is %d", pin, i); + //debug_d("getChannel %d is %d", pin, i); return i; } } @@ -277,7 +173,7 @@ bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) } return true; } else { - debugf("Duty cycle value too high for current period."); + debug_d("Duty cycle value too high for current period."); return false; } } @@ -330,11 +226,15 @@ ledc_timer_t HardwarePWM::pinToTimer(uint8_t pin){ } uint32_t HardwarePWM::periodToFrequency(uint32_t period){ - return (1000000 / period); + if(period!=0){ + return (1000000 / period); + } } uint32_t HardwarePWM::frequencyToPeriod(uint32_t freq){ - return (1000000 / freq); + if(freq!=0){ + return (1000000 / freq); + } } uint32_t HardwarePWM::maxDuty(ledc_timer_bit_t bits){ diff --git a/Sming/Core/HardwarePWM.h b/Sming/Core/HardwarePWM.h index 99fed4041b..efd637fe2a 100644 --- a/Sming/Core/HardwarePWM.h +++ b/Sming/Core/HardwarePWM.h @@ -29,9 +29,7 @@ #include #include -#if SMING_SOC == Esp32 - #include "hal/ledc_types.h" -#endif + #define PWM_BAD_CHANNEL 0xff ///< Invalid PWM channel @@ -125,20 +123,9 @@ class HardwarePWM */ void update(); - #if SMING_ARCH == Esp32 - uint32_t getFrequency(uint8_t pin); - #endif + uint32_t getFrequency(uint8_t pin); private: -#if SMING_ARCH == Esp32 - ledc_mode_t pinToGroup(uint8_t pin); - ledc_channel_t pinToChannel(uint8_t pin); - ledc_timer_t pinToTimer(uint8_t pin); - uint32_t periodToFrequency(uint32_t period); - uint32_t frequencyToPeriod(uint32_t freq); - uint32_t maxDuty(ledc_timer_bit_t bits); -#endif - uint8_t channel_count; uint8_t channels[PWM_CHANNEL_NUM_MAX]; uint32_t maxduty; From 21ecf9af032315e3fdfcb2ffe85079210bf76ab4 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 12 Feb 2023 11:40:43 +0100 Subject: [PATCH 07/29] Revert "more documentation" This reverts commit 7279c66287e6a3be238052687ad3a0e3318a9c01. --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 246 -------------------------- 1 file changed, 246 deletions(-) delete mode 100644 Sming/Arch/Esp32/Core/HardwarePWM.cpp diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp deleted file mode 100644 index 84b0cdb632..0000000000 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ /dev/null @@ -1,246 +0,0 @@ -/**** - * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. - * Created 2015 by Skurydin Alexey - * http://github.com/SmingHub/Sming - * All files of the Sming Core are provided under the LGPL v3 license. - * - * HardwarePWM.cpp - * - * Original Author: https://github.com/hrsavla - * - * This HardwarePWM library enables Sming framework user to use ESP SDK PWM API - * Period of PWM is fixed to 1000us / Frequency = 1khz - * Duty at 100% = 22222. Duty at 0% = 0 - * You can use function setPeriod() to change frequency/period. - * Calculate the max duty as per the formulae give in ESP8266 SDK - * Max Duty = (Period * 1000) / 45 - * - * PWM can be generated on up to 8 pins (ie All pins except pin 16) - * Created on August 17, 2015, 2:27 PM - * - * See also ESP8266 Technical Reference, Chapter 12: - * http://espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf - * - */ - -// orig #include -// orig #include "ESP8266EX.h" -#include -//#include -#include "driver/ledc.h" -#include "esp_err.h" -#include "hal/ledc_types.h" -#include - -namespace{ - ledc_mode_t pinToGroup(uint8_t pin); - ledc_channel_t pinToChannel(uint8_t pin); - ledc_timer_t pinToTimer(uint8_t pin); - uint32_t periodToFrequency(uint32_t period); - uint32_t frequencyToPeriod(uint32_t freq); - uint32_t maxDuty(ledc_timer_bit_t bits); -} - -// orig #include - -// #define SOC_LEDC_TIMER_BIT_WIDE_NUM 9 -// #define SOC_LEDC_CHANNEL_NUM 6 - - - // default period in µs -> 5kHz - - -// define default resolution - -/*#if static_castSOC_LEDC_TIMER_BIT_WIDE_NUM >= DESIRED_DEFAULT_RESOLUTION - #define DEFAULT_RESOLUTION DESIRED_DEFAULT_RESOLUTION -#else - #define DEFAULT_RESOLUTION static_castSOC_LEDC_TIMER_BIT_WIDE_NUM // if 10 bit is not available, set to max -#endif -*/ -#define DEFAULT_RESOLUTION static_cast(10) -#define DEFAULT_PERIOD 200 - -HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_of_pins) -{ - ledc_timer_config_t ledc_timer; - ledc_channel_config_t ledc_channel; - debug_d("starting HardwarePWM init"); - periph_module_enable(PERIPH_LEDC_MODULE); - if((no_of_pins == 0) || (no_of_pins > SOC_LEDC_CHANNEL_NUM)) - { - // is there a return code for this? - return ; - } - uint32_t io_info[SOC_LEDC_CHANNEL_NUM][3]; // pin information - uint32_t pwm_duty_init[SOC_LEDC_CHANNEL_NUM]; // pwm duty - for(uint8_t i = 0; i < no_of_pins; i++) { - - pwm_duty_init[i] = 0; // Start with zero output - channels[i] = pins[i]; - - /* - / Prepare and then apply the LEDC PWM timer configuration - / this may cofigure the same timer more than once (in fact up to 8 times) - / which should not be an issue, though, since the values should be the same for all timers - */ - ledc_timer.speed_mode = pinToGroup(i); // the two groups (if available) are operating in different speed modes, hence speed mode is an alias for group or vice versa - ledc_timer.timer_num = pinToTimer(i); - ledc_timer.duty_resolution = LEDC_TIMER_10_BIT; // todo: make configurable later - ledc_timer.freq_hz = periodToFrequency(DEFAULT_PERIOD); // todo: make configurable later - ledc_timer.clk_cfg = LEDC_AUTO_CLK; - - debug_d("ledc_timer.\n\tspeed_mode: %i\n\ttimer_num: %i\n\tduty_resolution: %i\n\tfreq: %i\n\tclk_cfg: %i\n\n", - (uint32_t)ledc_timer.speed_mode, (uint32_t)ledc_timer.timer_num, - (uint32_t)ledc_timer.duty_resolution, (uint32_t)ledc_timer.freq_hz, (uint32_t)ledc_timer.clk_cfg); - ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); - - /* - / Prepare and then apply the LEDC PWM channel configuration - */ - ledc_channel.speed_mode = pinToGroup(i); - ledc_channel.channel = pinToChannel(i); - ledc_channel.timer_sel = pinToTimer(i); - ledc_channel.intr_type = LEDC_INTR_DISABLE; - ledc_channel.gpio_num = pins[i]; - ledc_channel.duty = 0; // Set duty to 0% - ledc_channel.hpoint = 0; - debug_d("ledc_channel\n\tspeed_mode: %i\n\tchannel: %i\n\ttimer_sel %i\n\tinr_type: %i\n\tgpio_num: " - "%i\n\tduty: %i\n\thpoint: %i\n\n", - pinToGroup(i) , pinToChannel(i), pinToTimer(i), LEDC_INTR_DISABLE, pins[i], 0, 0); - ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); - ledc_bind_channel_timer(pinToGroup(i), pinToChannel(i), pinToTimer(i)); - - maxduty=maxDuty(DEFAULT_RESOLUTION); - const int initial_period=DEFAULT_PERIOD; - } -} - -HardwarePWM::~HardwarePWM() -{ - for(uint8_t i = 0; i < no_of_pins; i++) { - //stop pwm for all pins and set idle level to 0. - - ledc_stop(pinToGroup(pins[i]), pinToChannel(pins[i]), uint32_t 0); - } -} - -/* Function Name: getChannel - * Description: This function is used to get channel number for given pin - * Parameters: pin - Esp8266 pin number - */ -uint8_t HardwarePWM::getChannel(uint8_t pin) -{ - for(uint8_t i = 0; i < channel_count; i++) { - if(channels[i] == pin) { - //debug_d("getChannel %d is %d", pin, i); - return i; - } - } - return -1; -} - -/* Function Name: getDutyChan - * Description: This function is used to get the duty cycle number for a given channel - * Parameters: chan -Esp8266 channel number - */ -uint32_t HardwarePWM::getDutyChan(uint8_t chan) -{ - if(chan == PWM_BAD_CHANNEL) { - return 0; - } else { - return ledc_get_duty(pinToGroup(chan),pinToChannel(chan)); - } - // esp32 defines the frequency / period per timer, -} - -/* Function Name: setDutyChan - * Description: This function is used to set the pwm duty cycle for a given channel. If parameter 'update' is false - * then you have to call update() later to update duties. - * Parameters: chan - channel number - * duty - duty cycle value - * update - update PWM output - */ -bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) -{ - if(chan == PWM_BAD_CHANNEL) { - return false; - } else if(duty <= maxduty) { - ESP_ERROR_CHECK(ledc_set_duty(pinToGroup(chan), pinToChannel(chan), duty)); - if(update) { - ESP_ERROR_CHECK(ledc_update_duty(pinToGroup(chan), pinToChannel(chan))); - //update(); - } - return true; - } else { - debug_d("Duty cycle value too high for current period."); - return false; - } -} - -/* Function Name: getPeriod - * Description: This function is used to get Period of PWM. - * Period / frequency will remain same for all pins. - * - */ -uint32_t HardwarePWM::getPeriod() -{ - // sming does not know how to handle different frequencies for channels, this will require an extended interface - // for now, just report the period for group 0 channel 0 - return frequencyToPeriod(ledc_get_freq(static_cast(0),static_cast(0))); -} - -/* Function Name: setPeriod - * Description: This function is used to set Period of PWM. - * Period / frequency will remain same for all pins. - */ -void HardwarePWM::setPeriod(uint32_t period) -{ - // setting the frequency globally, will add per timer functions later - // also, this can be done smarter - for(uint8_t i = 0; i < channel_count; i++) { - ESP_ERROR_CHECK(ledc_set_freq(pinToGroup(i), pinToTimer(i), periodToFrequency(period))); - } - //sledc_update_duty(); - update(); -} - -/* Function Name: update - * Description: This function is used to actually update the PWM. - */ -void HardwarePWM::update() -{ - //ledc_update_duty(); -} - -ledc_channel_t HardwarePWM::pinToChannel(uint8_t pin){ - return (ledc_channel_t)(pin % 8); -} - -ledc_mode_t HardwarePWM::pinToGroup(uint8_t pin){ - return (ledc_mode_t) (pin / 8); -} - -ledc_timer_t HardwarePWM::pinToTimer(uint8_t pin){ - return (ledc_timer_t) ((pin /2) % 4); -} - -uint32_t HardwarePWM::periodToFrequency(uint32_t period){ - if(period!=0){ - return (1000000 / period); - } -} - -uint32_t HardwarePWM::frequencyToPeriod(uint32_t freq){ - if(freq!=0){ - return (1000000 / freq); - } -} - -uint32_t HardwarePWM::maxDuty(ledc_timer_bit_t bits){ - return (1<<(uint32_t)bits) - 1; -} - -uint32_t HardwarePWM::getFrequency(uint8_t pin){ - return ledc_get_freq(pinToGroup(pin), pinToTimer(pin)); -} \ No newline at end of file From be7d04aaad63c0efb705929e4be7be09b504edbc Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 12 Feb 2023 11:58:51 +0100 Subject: [PATCH 08/29] implemented requested changes from initial PR (now *with* changes. after goofing up with git Sigh) --- Sming/Core/HardwarePWM.h | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Sming/Core/HardwarePWM.h b/Sming/Core/HardwarePWM.h index efd637fe2a..99fed4041b 100644 --- a/Sming/Core/HardwarePWM.h +++ b/Sming/Core/HardwarePWM.h @@ -29,7 +29,9 @@ #include #include - +#if SMING_SOC == Esp32 + #include "hal/ledc_types.h" +#endif #define PWM_BAD_CHANNEL 0xff ///< Invalid PWM channel @@ -123,9 +125,20 @@ class HardwarePWM */ void update(); - uint32_t getFrequency(uint8_t pin); + #if SMING_ARCH == Esp32 + uint32_t getFrequency(uint8_t pin); + #endif private: +#if SMING_ARCH == Esp32 + ledc_mode_t pinToGroup(uint8_t pin); + ledc_channel_t pinToChannel(uint8_t pin); + ledc_timer_t pinToTimer(uint8_t pin); + uint32_t periodToFrequency(uint32_t period); + uint32_t frequencyToPeriod(uint32_t freq); + uint32_t maxDuty(ledc_timer_bit_t bits); +#endif + uint8_t channel_count; uint8_t channels[PWM_CHANNEL_NUM_MAX]; uint32_t maxduty; From 9e9928c380a34cd6deefca30b07515c7045f01b0 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 12 Feb 2023 12:57:53 +0100 Subject: [PATCH 09/29] fixed some things I didn't understand at first. --- Sming/Core/HardwarePWM.h | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/Sming/Core/HardwarePWM.h b/Sming/Core/HardwarePWM.h index 99fed4041b..cfea3e9e43 100644 --- a/Sming/Core/HardwarePWM.h +++ b/Sming/Core/HardwarePWM.h @@ -29,9 +29,6 @@ #include #include -#if SMING_SOC == Esp32 - #include "hal/ledc_types.h" -#endif #define PWM_BAD_CHANNEL 0xff ///< Invalid PWM channel @@ -125,20 +122,9 @@ class HardwarePWM */ void update(); - #if SMING_ARCH == Esp32 - uint32_t getFrequency(uint8_t pin); - #endif + uint32_t getFrequency(uint8_t pin); private: -#if SMING_ARCH == Esp32 - ledc_mode_t pinToGroup(uint8_t pin); - ledc_channel_t pinToChannel(uint8_t pin); - ledc_timer_t pinToTimer(uint8_t pin); - uint32_t periodToFrequency(uint32_t period); - uint32_t frequencyToPeriod(uint32_t freq); - uint32_t maxDuty(ledc_timer_bit_t bits); -#endif - uint8_t channel_count; uint8_t channels[PWM_CHANNEL_NUM_MAX]; uint32_t maxduty; From b48fcd9cfd0a76ce3e1487bb60063a6e91f9f59c Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 12 Feb 2023 13:54:10 +0100 Subject: [PATCH 10/29] HardwarePWM.h was not part of the last commit --- Sming/Core/HardwarePWM.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Sming/Core/HardwarePWM.h b/Sming/Core/HardwarePWM.h index cfea3e9e43..2f895b478d 100644 --- a/Sming/Core/HardwarePWM.h +++ b/Sming/Core/HardwarePWM.h @@ -122,6 +122,9 @@ class HardwarePWM */ void update(); + /** @brief Get PWM Frequency + * @retval uint32_t Value of Frequency + */ uint32_t getFrequency(uint8_t pin); private: From 9fe74a1ffef7af363f835f176cb13e55a44bf9b9 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 12 Feb 2023 13:57:04 +0100 Subject: [PATCH 11/29] .cpp was also missing. why? --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 351 ++++++++++++++++++++++++++ 1 file changed, 351 insertions(+) create mode 100644 Sming/Arch/Esp32/Core/HardwarePWM.cpp diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp new file mode 100644 index 0000000000..745126c4cd --- /dev/null +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -0,0 +1,351 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * HardwarePWM.cpp + * + * Original Author: https://github.com/hrsavla + * Esp32 version: https://github.com/pljakobs + * + * This HardwarePWM library enables Sming framework uses to use the ESP32 ledc PWM api + * + * the ESP32 PWM Hardware is much more powerful than the ESP8266, allowing wider PWM timers (up to 20 bit) + * as well as much higher PWM frequencies (up to 40MHz for a 1 Bit wide PWM) + * + * Overview: + * +------------------------------------------------------------------------------------------------+ + * | LED_PWM | + * | +-------------------------------------------+ +-------------------------------------------+ | + * | | High_Speed_Channels¹ | | Low_Speed_Channels | | + * | | +-----+ +--------+ | | +-----+ +--------+ | | + * | | | | --> | h_ch 0 | | | | | --> | l_ch 0 | | | + * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | + * | | | h_timer 0 | --> | | | | | l_timer 0 | --> | | | | + * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | + * | | | | --> | h_ch 1 | | | | | --> | l_ch 1 | | | + * | | | | +--------+ | | | | +--------+ | | + * | | | | | | | | | | + * | | | | +--------+ | | | | +--------+ | | + * | | | | --> | h_ch 2 | | | | | --> | l_ch 2 | | | + * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | + * | | | h_timer 1 | --> | | | | | l_timer 1 | --> | | | | + * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | + * | | | | --> | h_ch 3 | | | | | --> | l_ch 3 | | | + * | | | | +--------+ | | | | +--------+ | | + * | | | MUX | | | | MUX | | | + * | | | | +--------+ | | | | +--------+ | | + * | | | | --> | h_ch 4 | | | | | --> | l_ch 4 | | | + * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | + * | | | h_timer 2 | --> | | | | | l_timer 2 | --> | | | | + * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | + * | | | | --> | h_ch 5 | | | | | --> | l_ch 5 | | | + * | | | | +--------+ | | | | +--------+ | | + * | | | | | | | | | | + * | | | | +--------+ | | | | +--------+ | | + * | | | | --> | h_ch 6 | | | | | --> | l_ch 6²| | | + * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | + * | | | h_timer 3 | --> | | | | | l_timer 3 | --> | | | | + * | | +-----------+ | | +--------+ | | +-----------+ | | +--------+ | | + * | | | | --> | h_ch 7 | | | | | --> | l_ch 7²| | | + * | | | | +--------+ | | | | +--------+ | | + * | | +-----+ | | +-----+ | | + * | +-------------------------------------------+ +-------------------------------------------+ | + * +------------------------------------------------------------------------------------------------+ + * ¹ High speed channels are only available when SOC_LEDC_SUPPORT_HS_MODE is defined as 1 + * ² The ESP32C3 does only support six channels, so 6 and 7 are not available on that SoC + * + * The nomenclature of timers in the high speed / low speed blocks is a bit misleading as the idf api + * speaks of "speed mode", which, to me, implies that this would be a mode configurable in a specific timer + * while in reality, it does select a block of timers. I am considering re-naming that to "speed mode block" + * in my interface impmenentation. + * + * As an example, I would use + * setTimerFrequency(speedModeBlock, timer, frequency); + * + * ToDo: see, how this can be implemented to provide maximum overlap with the RP2040 pwm hardware so code does + * not become overly SoC specific. + * + * Maximum Timer width for PWM: + * ============================ + * esp32 SOC_LEDC_TIMER_BIT_WIDE_NUM (20) + * esp32c3 SOC_LEDC_TIMER_BIT_WIDE_NUM (14) + * esp32s2 SOC_LEDC_TIMER_BIT_WIDE_NUM (14) + * esp32s3 SOC_LEDC_TIMER_BIT_WIDE_NUM (14) + * + * Number of Channels: + * =================== + * esp32 SOC_LEDC_CHANNEL_NUM (8) + * esp32c3 SOC_LEDC_CHANNEL_NUM (6) + * esp32s2 SOC_LEDC_CHANNEL_NUM (8) + * esp32s3 SOC_LEDC_CHANNEL_NUM 8 + * + * Some SoSs support a mode called HIGHSPEED_MODE which is essentially another full block of PWM hardware + * that adds SOC_LEDC_CHANNEL_NUM channels. + * Those Architectures have SOC_LEDC_SUPPORT_HS_MODE defined as 1. + * In esp-idf-4.3 that's currently only the esp32 SOC + * + * Supports highspeed mode: + * ======================== + * esp32 SOC_LEDC_SUPPORT_HS_MODE (1) + * + * ToDo: implement awareness of hs mode availablility + * ================================================== + * currently, the code just uses a % 8 operation on the pin index to calculate whether to assign a pin to either + * high speed or low speed pwm blocks. This doesn't make a whole lot of sense since it makes it impossible + * for Sming devs to actually use the functionality behind it. + * Also, it currently does not reflect the fact that different SOCs have a different number of channels per block + * (specifically, the esp32c3 only has six channels and no highspeed mode). + * I will continue in two ways: + * - implement the "vanilla" Sming HardwarePWM interface that will hide the underlying architecture but allow up to 16 + * channels on an ESP32 + * - implement overloads for the relevant functions that allow selecting hs mode where applicable. + * + * ToDo: implement PWM bit width control + * ===================================== + * the current HardwarePWM implementation does not care about the PWM timer bit width. To leverage the functionality + * of the ESP32 hardware, it's necessary to make this configurable. As the width is per timer and all the Sming defined + * functions are basically per pin, this needs to be an extension of the overal model, exposing at least timers. + * This, too, will require a compatible "basic" interface and an advanced interface that allows assiging pins (channels) + * to timers and the configuration of the timers themselves. + * The esp_idf does not provide a way to read the bit width configured for a channel, but I think it'll be useful to be able + * to read back this value, not least to find the value for getMaxDuty() for a channel. It will either have to be stored in the + * module or maybe read from the hardware directly (LEDC_[HL]STIMERx_CONF_REG & 0x1f) + * + * hardware technical reference: + * ============================= + * https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#ledpwm + * + * Overview of the whole ledc-system here: + * https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html + * + * the ledc interface also exposes some advanced functions such as fades that are then done in hardware. + * ToDo: implement a Sming interface for fades + * + + + + * You can use function setPeriod() to change frequency/period. + * Calculate the max duty as per the formulae give in ESP8266 SDK + * Max Duty = (Period * 1000) / 45 + * + * PWM can be generated on up to 8 pins (ie All pins except pin 16) + * Created on August 17, 2015, 2:27 PM + * + * See also ESP8266 Technical Reference, Chapter 12: + * http://espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf + * + */ + +// orig #include +// orig #include "ESP8266EX.h" +#include +//#include +#include "driver/ledc.h" +#include "esp_err.h" +#include "hal/ledc_types.h" +#include + +namespace{ + ledc_mode_t pinToGroup(uint8_t pin); + ledc_channel_t pinToChannel(uint8_t pin); + ledc_timer_t pinToTimer(uint8_t pin); + uint32_t periodToFrequency(uint32_t period); + uint32_t frequencyToPeriod(uint32_t freq); + uint32_t maxDuty(ledc_timer_bit_t bits); +} //namespace + +#define DEFAULT_RESOLUTION static_cast(10) +#define DEFAULT_PERIOD 200 + +HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_of_pins) +{ + ledc_timer_config_t ledc_timer; + ledc_channel_config_t ledc_channel; + debug_d("starting HardwarePWM init"); + periph_module_enable(PERIPH_LEDC_MODULE); + if((no_of_pins == 0) || (no_of_pins > SOC_LEDC_CHANNEL_NUM)) + { + return; + } + + uint32_t io_info[SOC_LEDC_CHANNEL_NUM][3]; // pin information + uint32_t pwm_duty_init[SOC_LEDC_CHANNEL_NUM]; // pwm duty + for(uint8_t i = 0; i < no_of_pins; i++) { + pwm_duty_init[i] = 0; // Start with zero output + channels[i] = pins[i]; + + /* + / Prepare and then apply the LEDC PWM timer configuration + / this may cofigure the same timer more than once (in fact up to 8 times) + / which should not be an issue, though, since the values should be the same for all timers + */ + ledc_timer.speed_mode = pinToGroup(i); // the two groups (if available) are operating in different speed modes, hence speed mode is an alias for group or vice versa + ledc_timer.timer_num = pinToTimer(i); + ledc_timer.duty_resolution = LEDC_TIMER_10_BIT; // todo: make configurable later + ledc_timer.freq_hz = periodToFrequency(DEFAULT_PERIOD); // todo: make configurable later + ledc_timer.clk_cfg = LEDC_AUTO_CLK; + + debug_d("ledc_timer.\n\tspeed_mode: %i\n\ttimer_num: %i\n\tduty_resolution: %i\n\tfreq: %i\n\tclk_cfg: " + "%i\n\n", + (uint32_t)ledc_timer.speed_mode, (uint32_t)ledc_timer.timer_num, + (uint32_t)ledc_timer.duty_resolution, (uint32_t)ledc_timer.freq_hz, + (uint32_t)ledc_timer.clk_cfg); + ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); + + /* + / Prepare and then apply the LEDC PWM channel configuration + */ + ledc_channel.speed_mode = pinToGroup(i); + ledc_channel.channel = pinToChannel(i); + ledc_channel.timer_sel = pinToTimer(i); + ledc_channel.intr_type = LEDC_INTR_DISABLE; + ledc_channel.gpio_num = pins[i]; + ledc_channel.duty = 0; // Set duty to 0% + ledc_channel.hpoint = 0; + debug_d("ledc_channel\n\tspeed_mode: %i\n\tchannel: %i\n\ttimer_sel %i\n\tinr_type: %i\n\tgpio_num: " + "%i\n\tduty: %i\n\thpoint: %i\n\n", + pinToGroup(i), pinToChannel(i), pinToTimer(i), LEDC_INTR_DISABLE, pins[i], 0, 0); + ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); + ledc_bind_channel_timer(pinToGroup(i), pinToChannel(i), pinToTimer(i)); + } + maxduty = maxDuty(DEFAULT_RESOLUTION); + const int initial_period = DEFAULT_PERIOD; +} + +HardwarePWM::~HardwarePWM() +{ + for(uint8_t i = 0; i < channel_count; i++) { + //stop pwm for all pins and set idle level to 0. + ledc_stop(pinToGroup(i), pinToChannel(i), (uint32_t) 0); + } +} + +/* Function Name: getChannel + * Description: This function is used to get channel number for given pin + * Parameters: pin - Esp8266 pin number + */ +uint8_t HardwarePWM::getChannel(uint8_t pin) +{ + for(uint8_t i = 0; i < channel_count; i++) { + if(channels[i] == pin) { + //debug_d("getChannel %d is %d", pin, i); + return i; + } + } + return -1; +} + +/* Function Name: getDutyChan + * Description: This function is used to get the duty cycle number for a given channel + * Parameters: chan -Esp8266 channel number + */ +uint32_t HardwarePWM::getDutyChan(uint8_t chan) +{ + if(chan == PWM_BAD_CHANNEL) { + return 0; + } else { + return ledc_get_duty(pinToGroup(chan),pinToChannel(chan)); + } + // esp32 defines the frequency / period per timer, +} + +/* Function Name: setDutyChan + * Description: This function is used to set the pwm duty cycle for a given channel. If parameter 'update' is false + * then you have to call update() later to update duties. + * Parameters: chan - channel number + * duty - duty cycle value + * update - update PWM output + */ +bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) +{ + if(chan == PWM_BAD_CHANNEL) { + return false; + } else if(duty <= maxduty) { + ESP_ERROR_CHECK(ledc_set_duty(pinToGroup(chan), pinToChannel(chan), duty)); + if(update) { + ESP_ERROR_CHECK(ledc_update_duty(pinToGroup(chan), pinToChannel(chan))); + //update(); + } + return true; + } else { + debug_d("Duty cycle value too high for current period."); + return false; + } +} + +/* Function Name: getPeriod + * Description: This function is used to get Period of PWM. + * Period / frequency will remain same for all pins. + * + */ +uint32_t HardwarePWM::getPeriod() +{ + // sming does not know how to handle different frequencies for channels, this will require an extended interface + // for now, just report the period for group 0 channel 0 + return frequencyToPeriod(ledc_get_freq(static_cast(0),static_cast(0))); +} + +/* Function Name: setPeriod + * Description: This function is used to set Period of PWM. + * Period / frequency will remain same for all pins. + */ +void HardwarePWM::setPeriod(uint32_t period) +{ + // setting the frequency globally, will add per timer functions later + // also, this can be done smarter + for(uint8_t i = 0; i < channel_count; i++) { + ESP_ERROR_CHECK(ledc_set_freq(pinToGroup(i), pinToTimer(i), periodToFrequency(period))); + } + //sledc_update_duty(); + update(); +} + +/* Function Name: update + * Description: This function is used to actually update the PWM. + */ +void HardwarePWM::update() +{ + //ledc_update_duty(); +} + +uint32_t HardwarePWM::getFrequency(uint8_t pin) +{ + return ledc_get_freq(pinToGroup(pin), pinToTimer(pin)); +} + +namespace{ + ledc_channel_t pinToChannel(uint8_t pin){ + return (ledc_channel_t)(pin % 8); + } + + ledc_mode_t pinToGroup(uint8_t pin){ + return (ledc_mode_t) (pin / 8); + } + + ledc_timer_t pinToTimer(uint8_t pin){ + return (ledc_timer_t) ((pin /2) % 4); + } + + uint32_t periodToFrequency(uint32_t period){ + if(period == 0){ + return -1; + }else{ + return (1000000 / period); + } + } + + uint32_t frequencyToPeriod(uint32_t freq){ + if(freq == 0) { + return -1; + } else { + return (1000000 / freq); + } + } + + uint32_t maxDuty(ledc_timer_bit_t bits){ + return (1<<(uint32_t)bits) - 1; + } + +} \ No newline at end of file From f510bbaa2ae749ec07b502693c505c53fb0382b1 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 21 Feb 2023 07:43:49 +0000 Subject: [PATCH 12/29] Apply coding style --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 103 ++++++++++++++------------ Sming/Core/HardwarePWM.h | 2 +- 2 files changed, 56 insertions(+), 49 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index 745126c4cd..dd495a477e 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -147,13 +147,14 @@ #include "hal/ledc_types.h" #include -namespace{ - ledc_mode_t pinToGroup(uint8_t pin); - ledc_channel_t pinToChannel(uint8_t pin); - ledc_timer_t pinToTimer(uint8_t pin); - uint32_t periodToFrequency(uint32_t period); - uint32_t frequencyToPeriod(uint32_t freq); - uint32_t maxDuty(ledc_timer_bit_t bits); +namespace +{ +ledc_mode_t pinToGroup(uint8_t pin); +ledc_channel_t pinToChannel(uint8_t pin); +ledc_timer_t pinToTimer(uint8_t pin); +uint32_t periodToFrequency(uint32_t period); +uint32_t frequencyToPeriod(uint32_t freq); +uint32_t maxDuty(ledc_timer_bit_t bits); } //namespace #define DEFAULT_RESOLUTION static_cast(10) @@ -165,23 +166,23 @@ HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_o ledc_channel_config_t ledc_channel; debug_d("starting HardwarePWM init"); periph_module_enable(PERIPH_LEDC_MODULE); - if((no_of_pins == 0) || (no_of_pins > SOC_LEDC_CHANNEL_NUM)) - { + if((no_of_pins == 0) || (no_of_pins > SOC_LEDC_CHANNEL_NUM)) { return; } - uint32_t io_info[SOC_LEDC_CHANNEL_NUM][3]; // pin information + uint32_t io_info[SOC_LEDC_CHANNEL_NUM][3]; // pin information uint32_t pwm_duty_init[SOC_LEDC_CHANNEL_NUM]; // pwm duty for(uint8_t i = 0; i < no_of_pins; i++) { pwm_duty_init[i] = 0; // Start with zero output channels[i] = pins[i]; - /* + /* / Prepare and then apply the LEDC PWM timer configuration / this may cofigure the same timer more than once (in fact up to 8 times) / which should not be an issue, though, since the values should be the same for all timers */ - ledc_timer.speed_mode = pinToGroup(i); // the two groups (if available) are operating in different speed modes, hence speed mode is an alias for group or vice versa + ledc_timer.speed_mode = pinToGroup( + i); // the two groups (if available) are operating in different speed modes, hence speed mode is an alias for group or vice versa ledc_timer.timer_num = pinToTimer(i); ledc_timer.duty_resolution = LEDC_TIMER_10_BIT; // todo: make configurable later ledc_timer.freq_hz = periodToFrequency(DEFAULT_PERIOD); // todo: make configurable later @@ -189,9 +190,8 @@ HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_o debug_d("ledc_timer.\n\tspeed_mode: %i\n\ttimer_num: %i\n\tduty_resolution: %i\n\tfreq: %i\n\tclk_cfg: " "%i\n\n", - (uint32_t)ledc_timer.speed_mode, (uint32_t)ledc_timer.timer_num, - (uint32_t)ledc_timer.duty_resolution, (uint32_t)ledc_timer.freq_hz, - (uint32_t)ledc_timer.clk_cfg); + (uint32_t)ledc_timer.speed_mode, (uint32_t)ledc_timer.timer_num, (uint32_t)ledc_timer.duty_resolution, + (uint32_t)ledc_timer.freq_hz, (uint32_t)ledc_timer.clk_cfg); ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); /* @@ -218,7 +218,7 @@ HardwarePWM::~HardwarePWM() { for(uint8_t i = 0; i < channel_count; i++) { //stop pwm for all pins and set idle level to 0. - ledc_stop(pinToGroup(i), pinToChannel(i), (uint32_t) 0); + ledc_stop(pinToGroup(i), pinToChannel(i), (uint32_t)0); } } @@ -246,9 +246,9 @@ uint32_t HardwarePWM::getDutyChan(uint8_t chan) if(chan == PWM_BAD_CHANNEL) { return 0; } else { - return ledc_get_duty(pinToGroup(chan),pinToChannel(chan)); + return ledc_get_duty(pinToGroup(chan), pinToChannel(chan)); } - // esp32 defines the frequency / period per timer, + // esp32 defines the frequency / period per timer, } /* Function Name: setDutyChan @@ -283,8 +283,8 @@ bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) uint32_t HardwarePWM::getPeriod() { // sming does not know how to handle different frequencies for channels, this will require an extended interface - // for now, just report the period for group 0 channel 0 - return frequencyToPeriod(ledc_get_freq(static_cast(0),static_cast(0))); + // for now, just report the period for group 0 channel 0 + return frequencyToPeriod(ledc_get_freq(static_cast(0), static_cast(0))); } /* Function Name: setPeriod @@ -294,7 +294,7 @@ uint32_t HardwarePWM::getPeriod() void HardwarePWM::setPeriod(uint32_t period) { // setting the frequency globally, will add per timer functions later - // also, this can be done smarter + // also, this can be done smarter for(uint8_t i = 0; i < channel_count; i++) { ESP_ERROR_CHECK(ledc_set_freq(pinToGroup(i), pinToTimer(i), periodToFrequency(period))); } @@ -307,7 +307,7 @@ void HardwarePWM::setPeriod(uint32_t period) */ void HardwarePWM::update() { - //ledc_update_duty(); + //ledc_update_duty(); } uint32_t HardwarePWM::getFrequency(uint8_t pin) @@ -315,37 +315,44 @@ uint32_t HardwarePWM::getFrequency(uint8_t pin) return ledc_get_freq(pinToGroup(pin), pinToTimer(pin)); } -namespace{ - ledc_channel_t pinToChannel(uint8_t pin){ - return (ledc_channel_t)(pin % 8); - } +namespace +{ +ledc_channel_t pinToChannel(uint8_t pin) +{ + return (ledc_channel_t)(pin % 8); +} - ledc_mode_t pinToGroup(uint8_t pin){ - return (ledc_mode_t) (pin / 8); - } +ledc_mode_t pinToGroup(uint8_t pin) +{ + return (ledc_mode_t)(pin / 8); +} - ledc_timer_t pinToTimer(uint8_t pin){ - return (ledc_timer_t) ((pin /2) % 4); - } +ledc_timer_t pinToTimer(uint8_t pin) +{ + return (ledc_timer_t)((pin / 2) % 4); +} - uint32_t periodToFrequency(uint32_t period){ - if(period == 0){ - return -1; - }else{ - return (1000000 / period); - } +uint32_t periodToFrequency(uint32_t period) +{ + if(period == 0) { + return -1; + } else { + return (1000000 / period); } +} - uint32_t frequencyToPeriod(uint32_t freq){ - if(freq == 0) { - return -1; - } else { - return (1000000 / freq); - } +uint32_t frequencyToPeriod(uint32_t freq) +{ + if(freq == 0) { + return -1; + } else { + return (1000000 / freq); } +} - uint32_t maxDuty(ledc_timer_bit_t bits){ - return (1<<(uint32_t)bits) - 1; - } +uint32_t maxDuty(ledc_timer_bit_t bits) +{ + return (1 << (uint32_t)bits) - 1; +} -} \ No newline at end of file +} // namespace \ No newline at end of file diff --git a/Sming/Core/HardwarePWM.h b/Sming/Core/HardwarePWM.h index 2f895b478d..9c1ec86b72 100644 --- a/Sming/Core/HardwarePWM.h +++ b/Sming/Core/HardwarePWM.h @@ -121,7 +121,7 @@ class HardwarePWM /** @brief This function is used to actually update the PWM. */ void update(); - + /** @brief Get PWM Frequency * @retval uint32_t Value of Frequency */ From 84e69f10811c8692c8c7f078ad41e53da9599158 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 21 Feb 2023 07:55:42 +0000 Subject: [PATCH 13/29] Rebase, tidy --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 214 ++++++++++---------------- 1 file changed, 79 insertions(+), 135 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index dd495a477e..f0b8a8549e 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -123,42 +123,49 @@ * the ledc interface also exposes some advanced functions such as fades that are then done in hardware. * ToDo: implement a Sming interface for fades * - - - - * You can use function setPeriod() to change frequency/period. - * Calculate the max duty as per the formulae give in ESP8266 SDK - * Max Duty = (Period * 1000) / 45 - * - * PWM can be generated on up to 8 pins (ie All pins except pin 16) - * Created on August 17, 2015, 2:27 PM - * - * See also ESP8266 Technical Reference, Chapter 12: - * http://espressif.com/sites/default/files/documentation/esp8266-technical_reference_en.pdf - * */ -// orig #include -// orig #include "ESP8266EX.h" -#include -//#include -#include "driver/ledc.h" -#include "esp_err.h" -#include "hal/ledc_types.h" #include +#include +#include +#include +#include +#define DEFAULT_RESOLUTION ledc_timer_bit_t(10) +#define DEFAULT_PERIOD 200 namespace { -ledc_mode_t pinToGroup(uint8_t pin); -ledc_channel_t pinToChannel(uint8_t pin); -ledc_timer_t pinToTimer(uint8_t pin); -uint32_t periodToFrequency(uint32_t period); -uint32_t frequencyToPeriod(uint32_t freq); -uint32_t maxDuty(ledc_timer_bit_t bits); -} //namespace +ledc_channel_t pinToChannel(uint8_t pin) +{ + return ledc_channel_t(pin % 8); +} -#define DEFAULT_RESOLUTION static_cast(10) -#define DEFAULT_PERIOD 200 +ledc_mode_t pinToGroup(uint8_t pin) +{ + return ledc_mode_t(pin / 8); +} + +ledc_timer_t pinToTimer(uint8_t pin) +{ + return ledc_timer_t((pin / 2) % 4); +} + +uint32_t periodToFrequency(uint32_t period) +{ + return (period == 0) ? -1 : (1000000 / period); +} + +uint32_t frequencyToPeriod(uint32_t freq) +{ + return (freq == 0) ? -1 : (1000000 / freq); +} + +uint32_t maxDuty(ledc_timer_bit_t bits) +{ + return (1U << bits) - 1; +} + +} //namespace HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_of_pins) { @@ -177,182 +184,119 @@ HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_o channels[i] = pins[i]; /* - / Prepare and then apply the LEDC PWM timer configuration - / this may cofigure the same timer more than once (in fact up to 8 times) - / which should not be an issue, though, since the values should be the same for all timers - */ - ledc_timer.speed_mode = pinToGroup( - i); // the two groups (if available) are operating in different speed modes, hence speed mode is an alias for group or vice versa + * Prepare and then apply the LEDC PWM timer configuration. + * This may cofigure the same timer more than once (in fact up to 8 times) + * which should not be an issue, though, since the values should be the same for all timers + */ + // The two groups (if available) are operating in different speed modes, hence speed mode is an alias for group or vice versa + ledc_timer.speed_mode = pinToGroup(i); ledc_timer.timer_num = pinToTimer(i); ledc_timer.duty_resolution = LEDC_TIMER_10_BIT; // todo: make configurable later ledc_timer.freq_hz = periodToFrequency(DEFAULT_PERIOD); // todo: make configurable later ledc_timer.clk_cfg = LEDC_AUTO_CLK; - debug_d("ledc_timer.\n\tspeed_mode: %i\n\ttimer_num: %i\n\tduty_resolution: %i\n\tfreq: %i\n\tclk_cfg: " - "%i\n\n", - (uint32_t)ledc_timer.speed_mode, (uint32_t)ledc_timer.timer_num, (uint32_t)ledc_timer.duty_resolution, - (uint32_t)ledc_timer.freq_hz, (uint32_t)ledc_timer.clk_cfg); + debug_d("ledc_timer.\r\n" + "\tspeed_mode: %i\r\n" + "\ttimer_num: %i\r\n" + "\tduty_resolution: %i\r\n" + "\tfreq: %i\n\tclk_cfg: %i\r\n\n", + ledc_timer.speed_mode, ledc_timer.timer_num, ledc_timer.duty_resolution, ledc_timer.freq_hz, + ledc_timer.clk_cfg); ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); /* - / Prepare and then apply the LEDC PWM channel configuration - */ + * Prepare and then apply the LEDC PWM channel configuration + */ ledc_channel.speed_mode = pinToGroup(i); ledc_channel.channel = pinToChannel(i); ledc_channel.timer_sel = pinToTimer(i); ledc_channel.intr_type = LEDC_INTR_DISABLE; ledc_channel.gpio_num = pins[i]; - ledc_channel.duty = 0; // Set duty to 0% + ledc_channel.duty = 0; ledc_channel.hpoint = 0; - debug_d("ledc_channel\n\tspeed_mode: %i\n\tchannel: %i\n\ttimer_sel %i\n\tinr_type: %i\n\tgpio_num: " - "%i\n\tduty: %i\n\thpoint: %i\n\n", - pinToGroup(i), pinToChannel(i), pinToTimer(i), LEDC_INTR_DISABLE, pins[i], 0, 0); + debug_d("ledc_channel\n" + "\tspeed_mode: %i\r\n" + "\tchannel: %i\r\n" + "\ttimer_sel %i\r\n" + "\tintr_type: %i\r\n" + "\tgpio_num: %i\r\n" + "\tduty: %i\r\n" + "\thpoint: %i\r\n\n", + pinToGroup(i), pinToChannel(i), pinToTimer(i), ledc_channel.intr_type, pins[i], 0, 0); ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); ledc_bind_channel_timer(pinToGroup(i), pinToChannel(i), pinToTimer(i)); } maxduty = maxDuty(DEFAULT_RESOLUTION); - const int initial_period = DEFAULT_PERIOD; } HardwarePWM::~HardwarePWM() { + // Stop pwm for all pins and set idle level to 0 for(uint8_t i = 0; i < channel_count; i++) { - //stop pwm for all pins and set idle level to 0. - ledc_stop(pinToGroup(i), pinToChannel(i), (uint32_t)0); + ledc_stop(pinToGroup(i), pinToChannel(i), 0); } } -/* Function Name: getChannel - * Description: This function is used to get channel number for given pin - * Parameters: pin - Esp8266 pin number - */ uint8_t HardwarePWM::getChannel(uint8_t pin) { for(uint8_t i = 0; i < channel_count; i++) { if(channels[i] == pin) { - //debug_d("getChannel %d is %d", pin, i); + // debug_d("getChannel %d is %d", pin, i); return i; } } return -1; } -/* Function Name: getDutyChan - * Description: This function is used to get the duty cycle number for a given channel - * Parameters: chan -Esp8266 channel number - */ uint32_t HardwarePWM::getDutyChan(uint8_t chan) { - if(chan == PWM_BAD_CHANNEL) { - return 0; - } else { - return ledc_get_duty(pinToGroup(chan), pinToChannel(chan)); - } - // esp32 defines the frequency / period per timer, + // esp32 defines the frequency / period per timer + return (chan == PWM_BAD_CHANNEL) ? 0 : ledc_get_duty(pinToGroup(chan), pinToChannel(chan)); } -/* Function Name: setDutyChan - * Description: This function is used to set the pwm duty cycle for a given channel. If parameter 'update' is false - * then you have to call update() later to update duties. - * Parameters: chan - channel number - * duty - duty cycle value - * update - update PWM output - */ bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) { if(chan == PWM_BAD_CHANNEL) { return false; - } else if(duty <= maxduty) { + } + + if(duty <= maxduty) { ESP_ERROR_CHECK(ledc_set_duty(pinToGroup(chan), pinToChannel(chan), duty)); if(update) { ESP_ERROR_CHECK(ledc_update_duty(pinToGroup(chan), pinToChannel(chan))); //update(); } return true; - } else { - debug_d("Duty cycle value too high for current period."); - return false; } + + debug_d("Duty cycle value too high for current period."); + return false; } -/* Function Name: getPeriod - * Description: This function is used to get Period of PWM. - * Period / frequency will remain same for all pins. - * - */ uint32_t HardwarePWM::getPeriod() { - // sming does not know how to handle different frequencies for channels, this will require an extended interface - // for now, just report the period for group 0 channel 0 - return frequencyToPeriod(ledc_get_freq(static_cast(0), static_cast(0))); + // Sming does not know how to handle different frequencies for channels: this will require an extended interface. + // For now, just report the period for group 0 channel 0. + return frequencyToPeriod(ledc_get_freq(ledc_mode_t(0), ledc_timer_t(0))); } -/* Function Name: setPeriod - * Description: This function is used to set Period of PWM. - * Period / frequency will remain same for all pins. - */ void HardwarePWM::setPeriod(uint32_t period) { - // setting the frequency globally, will add per timer functions later - // also, this can be done smarter + // Set the frequency globally, will add per timer functions later. + // Also, this can be done smarter. for(uint8_t i = 0; i < channel_count; i++) { ESP_ERROR_CHECK(ledc_set_freq(pinToGroup(i), pinToTimer(i), periodToFrequency(period))); } - //sledc_update_duty(); + // ledc_update_duty(); update(); } -/* Function Name: update - * Description: This function is used to actually update the PWM. - */ void HardwarePWM::update() { - //ledc_update_duty(); + // ledc_update_duty(); } uint32_t HardwarePWM::getFrequency(uint8_t pin) { return ledc_get_freq(pinToGroup(pin), pinToTimer(pin)); } - -namespace -{ -ledc_channel_t pinToChannel(uint8_t pin) -{ - return (ledc_channel_t)(pin % 8); -} - -ledc_mode_t pinToGroup(uint8_t pin) -{ - return (ledc_mode_t)(pin / 8); -} - -ledc_timer_t pinToTimer(uint8_t pin) -{ - return (ledc_timer_t)((pin / 2) % 4); -} - -uint32_t periodToFrequency(uint32_t period) -{ - if(period == 0) { - return -1; - } else { - return (1000000 / period); - } -} - -uint32_t frequencyToPeriod(uint32_t freq) -{ - if(freq == 0) { - return -1; - } else { - return (1000000 / freq); - } -} - -uint32_t maxDuty(ledc_timer_bit_t bits) -{ - return (1 << (uint32_t)bits) - 1; -} - -} // namespace \ No newline at end of file From eaf77f91d6271bf1c9c2b3d707bcdb6e92442a3e Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 21 Feb 2023 08:02:57 +0000 Subject: [PATCH 14/29] Tidy Esp8266 implementation, add new 'getFrequency' method --- Sming/Arch/Esp8266/Core/HardwarePWM.cpp | 88 ++++++++++--------------- 1 file changed, 34 insertions(+), 54 deletions(-) diff --git a/Sming/Arch/Esp8266/Core/HardwarePWM.cpp b/Sming/Arch/Esp8266/Core/HardwarePWM.cpp index a1cbb10689..02638cdfcd 100644 --- a/Sming/Arch/Esp8266/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp8266/Core/HardwarePWM.cpp @@ -23,31 +23,32 @@ * */ +#include #include #include "ESP8266EX.h" #include -#include - #define PERIOD_TO_MAX_DUTY(x) (x * 25) HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_of_pins) { - if(no_of_pins > 0) { - uint32_t io_info[PWM_CHANNEL_NUM_MAX][3]; // pin information - uint32_t pwm_duty_init[PWM_CHANNEL_NUM_MAX]; // pwm duty - for(uint8_t i = 0; i < no_of_pins; i++) { - io_info[i][0] = EspDigitalPins[pins[i]].mux; - io_info[i][1] = EspDigitalPins[pins[i]].gpioFunc; - io_info[i][2] = EspDigitalPins[pins[i]].id; - pwm_duty_init[i] = 0; // Start with zero output - channels[i] = pins[i]; - } - const int initial_period = 1000; - pwm_init(initial_period, pwm_duty_init, no_of_pins, io_info); - update(); - maxduty = PERIOD_TO_MAX_DUTY(initial_period); // for period of 1000 + if(no_of_pins == 0) { + return; + } + + uint32_t io_info[PWM_CHANNEL_NUM_MAX][3]; // pin information + uint32_t pwm_duty_init[PWM_CHANNEL_NUM_MAX]; // pwm duty + for(uint8_t i = 0; i < no_of_pins; i++) { + io_info[i][0] = EspDigitalPins[pins[i]].mux; + io_info[i][1] = EspDigitalPins[pins[i]].gpioFunc; + io_info[i][2] = EspDigitalPins[pins[i]].id; + pwm_duty_init[i] = 0; // Start with zero output + channels[i] = pins[i]; } + const int initial_period = 1000; + pwm_init(initial_period, pwm_duty_init, no_of_pins, io_info); + update(); + maxduty = PERIOD_TO_MAX_DUTY(initial_period); // for period of 1000 } HardwarePWM::~HardwarePWM() @@ -55,72 +56,47 @@ HardwarePWM::~HardwarePWM() // There is no function in the SDK to stop PWM output, yet. } -/* Function Name: getChannel - * Description: This function is used to get channel number for given pin - * Parameters: pin - Esp8266 pin number - */ uint8_t HardwarePWM::getChannel(uint8_t pin) { for(uint8_t i = 0; i < channel_count; i++) { if(channels[i] == pin) { - //debugf("getChannel %d is %d", pin, i); + // debug_d("getChannel %d is %d", pin, i); return i; } } - //debugf("getChannel: can't find pin %d", pin); + + // debug_d("getChannel: can't find pin %d", pin); return PWM_BAD_CHANNEL; } -/* Function Name: getDutyChan - * Description: This function is used to get the duty cycle number for a given channel - * Parameters: chan -Esp8266 channel number - */ uint32_t HardwarePWM::getDutyChan(uint8_t chan) { - if(chan == PWM_BAD_CHANNEL) { - return 0; - } else { - return pwm_get_duty(chan); - } + return (chan == PWM_BAD_CHANNEL) ? 0 : pwm_get_duty(chan); } -/* Function Name: setDutyChan - * Description: This function is used to set the pwm duty cycle for a given channel. If parameter 'update' is false - * then you have to call update() later to update duties. - * Parameters: chan - channel number - * duty - duty cycle value - * update - update PWM output - */ bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) { if(chan == PWM_BAD_CHANNEL) { return false; - } else if(duty <= maxduty) { + } + + if(duty <= maxduty) { pwm_set_duty(duty, chan); if(update) { this->update(); } return true; - } else { - debugf("Duty cycle value too high for current period."); - return false; } + + debugf("Duty cycle value too high for current period."); + return false; } -/* Function Name: getPeriod - * Description: This function is used to get Period of PWM. - * Period / frequency will remain same for all pins. - * - */ uint32_t HardwarePWM::getPeriod() { return pwm_get_period(); } -/* Function Name: setPeriod - * Description: This function is used to set Period of PWM. - * Period / frequency will remain same for all pins. - */ void HardwarePWM::setPeriod(uint32_t period) { maxduty = PERIOD_TO_MAX_DUTY(period); @@ -128,10 +104,14 @@ void HardwarePWM::setPeriod(uint32_t period) update(); } -/* Function Name: update - * Description: This function is used to actually update the PWM. - */ void HardwarePWM::update() { pwm_start(); } + +uint32_t HardwarePWM::getFrequency(uint8_t pin) +{ + (void)pin; + auto period = pwm_get_period(); + return (period == 0) ? 0 : 1000000U / period; +} From 333db8223a65123ebe16c4cdbd7ba2fbf5212d02 Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 21 Feb 2023 08:14:45 +0000 Subject: [PATCH 15/29] Add missing include --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index f0b8a8549e..7c6e2c7e16 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -127,6 +127,7 @@ #include #include +#include #include #include #include From b90ea67bceda7db1bc6fb459932207a6e71784cd Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 21 Feb 2023 08:51:49 +0000 Subject: [PATCH 16/29] Tidy driver/pwm.h --- .../Components/driver/include/driver/pwm.h | 62 ------------------- 1 file changed, 62 deletions(-) diff --git a/Sming/Arch/Esp32/Components/driver/include/driver/pwm.h b/Sming/Arch/Esp32/Components/driver/include/driver/pwm.h index b003ec121a..36eacc6e9d 100644 --- a/Sming/Arch/Esp32/Components/driver/include/driver/pwm.h +++ b/Sming/Arch/Esp32/Components/driver/include/driver/pwm.h @@ -1,65 +1,3 @@ -/* - * ESPRESSIF MIT License - * - * Copyright (c) 2016 - * - * Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case, - * it is free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished - * to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#ifndef __PWM_H__ -#define __PWM_H__ - #pragma once -#if defined(__cplusplus) -extern "C" { -#endif - -/*pwm.h: function and macro definition of PWM API , driver level */ -/*user_light.h: user interface for light API, user level*/ -/*user_light_adj: API for color changing and lighting effects, user level*/ - -/*NOTE!! : DO NOT CHANGE THIS FILE*/ - -/*SUPPORT UP TO 8 PWM CHANNEL*/ #define PWM_CHANNEL_NUM_MAX 8 - -//struct pwm_param { -// uint32_t period; -// uint32_t freq; -// uint32_t duty[PWM_CHANNEL_NUM_MAX]; //PWM_CHANNEL<=8 -//}; -// -///* pwm_init should be called only once, for now */ -//void pwm_init(uint32_t period, uint32_t* duty, uint32_t pwm_channel_num, uint32_t (*pin_info_list)[3]); -//void pwm_start(void); -// -//void pwm_set_duty(uint32_t duty, uint8_t channel); -//uint32_t pwm_get_duty(uint8_t channel); -//void pwm_set_period(uint32_t period); -//uint32_t pwm_get_period(void); -// -//uint32_t get_pwm_version(void); -//void set_pwm_debug_en(uint8_t print_en); - -#if defined(__cplusplus) -} -#endif - -#endif From 3c9785f19001488bf64d842116bbf1dc401e4d8b Mon Sep 17 00:00:00 2001 From: mikee47 Date: Tue, 21 Feb 2023 08:52:04 +0000 Subject: [PATCH 17/29] Use static initializers --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 30 ++++++++++++++------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index 7c6e2c7e16..be2316245a 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -170,8 +170,6 @@ uint32_t maxDuty(ledc_timer_bit_t bits) HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_of_pins) { - ledc_timer_config_t ledc_timer; - ledc_channel_config_t ledc_channel; debug_d("starting HardwarePWM init"); periph_module_enable(PERIPH_LEDC_MODULE); if((no_of_pins == 0) || (no_of_pins > SOC_LEDC_CHANNEL_NUM)) { @@ -190,11 +188,13 @@ HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_o * which should not be an issue, though, since the values should be the same for all timers */ // The two groups (if available) are operating in different speed modes, hence speed mode is an alias for group or vice versa - ledc_timer.speed_mode = pinToGroup(i); - ledc_timer.timer_num = pinToTimer(i); - ledc_timer.duty_resolution = LEDC_TIMER_10_BIT; // todo: make configurable later - ledc_timer.freq_hz = periodToFrequency(DEFAULT_PERIOD); // todo: make configurable later - ledc_timer.clk_cfg = LEDC_AUTO_CLK; + ledc_timer_config_t ledc_timer{ + .speed_mode = pinToGroup(i), + .duty_resolution = LEDC_TIMER_10_BIT, // todo: make configurable later + .timer_num = pinToTimer(i), + .freq_hz = periodToFrequency(DEFAULT_PERIOD), // todo: make configurable later + .clk_cfg = LEDC_AUTO_CLK, + }; debug_d("ledc_timer.\r\n" "\tspeed_mode: %i\r\n" @@ -208,13 +208,15 @@ HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_o /* * Prepare and then apply the LEDC PWM channel configuration */ - ledc_channel.speed_mode = pinToGroup(i); - ledc_channel.channel = pinToChannel(i); - ledc_channel.timer_sel = pinToTimer(i); - ledc_channel.intr_type = LEDC_INTR_DISABLE; - ledc_channel.gpio_num = pins[i]; - ledc_channel.duty = 0; - ledc_channel.hpoint = 0; + ledc_channel_config_t ledc_channel{ + .gpio_num = pins[i], + .speed_mode = pinToGroup(i), + .channel = pinToChannel(i), + .intr_type = LEDC_INTR_DISABLE, + .timer_sel = pinToTimer(i), + .duty = 0, + .hpoint = 0, + }; debug_d("ledc_channel\n" "\tspeed_mode: %i\r\n" "\tchannel: %i\r\n" From fc2fca2180a5c6dfe6b532204165bc581c8c6f17 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sat, 18 Mar 2023 11:25:29 +0100 Subject: [PATCH 18/29] minor updates as suggested by mikee47 --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index be2316245a..e1a42c660c 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -113,6 +113,12 @@ * to read back this value, not least to find the value for getMaxDuty() for a channel. It will either have to be stored in the * module or maybe read from the hardware directly (LEDC_[HL]STIMERx_CONF_REG & 0x1f) * + * ToDo: implement an abstraction layer + * ==================================== + * as it stands now, this impelmentation does not provide a function to synchronize all the PWM channels (HardwarePWM::update()) + * It might be a good idea to provide an intermediary abstraction that handles all the low level PWM functions (such as flexible + * timer to channel assignments, hs/ls mode awareness, pwm bit width etc) and implements the update() function on that level. + * * hardware technical reference: * ============================= * https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#ledpwm @@ -265,10 +271,14 @@ bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) if(duty <= maxduty) { ESP_ERROR_CHECK(ledc_set_duty(pinToGroup(chan), pinToChannel(chan), duty)); - if(update) { - ESP_ERROR_CHECK(ledc_update_duty(pinToGroup(chan), pinToChannel(chan))); - //update(); - } + /* + * ignoring the update flag in this release, ToDo: implement a synchronized update mechanism + * if(update) { + * ESP_ERROR_CHECK(ledc_update_duty(pinToGroup(chan), pinToChannel(chan))); + * //update(); + * } + */ + ESP_ERROR_CHECK(ledc_update_duty(pinToGroup(chan), pinToChannel(chan))); return true; } @@ -302,4 +312,4 @@ void HardwarePWM::update() uint32_t HardwarePWM::getFrequency(uint8_t pin) { return ledc_get_freq(pinToGroup(pin), pinToTimer(pin)); -} +} \ No newline at end of file From d96bc2fa0bda72d67b59b92af22c890b926c43c1 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sat, 18 Mar 2023 11:12:36 +0100 Subject: [PATCH 19/29] minor updates as suggested by mikee47 --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 49 +++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index e1a42c660c..f0a8ac2161 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -267,6 +267,20 @@ bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) { if(chan == PWM_BAD_CHANNEL) { return false; + } else if(duty <= maxduty) { + ESP_ERROR_CHECK(ledc_set_duty(pinToGroup(chan), pinToChannel(chan), duty)); + /* + * ignoring the update flag in this release, ToDo: implement a synchronized update mechanism + * if(update) { + * ESP_ERROR_CHECK(ledc_update_duty(pinToGroup(chan), pinToChannel(chan))); + * //update(); + * } + */ + ESP_ERROR_CHECK(ledc_update_duty(pinToGroup(chan), pinToChannel(chan))); + return true; + } else { + debug_d("Duty cycle value too high for current period."); + return false; } if(duty <= maxduty) { @@ -312,4 +326,39 @@ void HardwarePWM::update() uint32_t HardwarePWM::getFrequency(uint8_t pin) { return ledc_get_freq(pinToGroup(pin), pinToTimer(pin)); +} + +namespace{ + ledc_channel_t pinToChannel(uint8_t pin){ + return (ledc_channel_t)(pin % 8); + } + + ledc_mode_t pinToGroup(uint8_t pin){ + return (ledc_mode_t) (pin / 8); + } + + ledc_timer_t pinToTimer(uint8_t pin){ + return (ledc_timer_t) ((pin /2) % 4); + } + + uint32_t periodToFrequency(uint32_t period){ + if(period == 0){ + return 0; + }else{ + return (1000000 / period); + } + } + + uint32_t frequencyToPeriod(uint32_t freq){ + if(freq == 0) { + return 0; + } else { + return (1000000 / freq); + } + } + + uint32_t maxDuty(ledc_timer_bit_t bits){ + return (1<<(uint32_t)bits) - 1; + } + } \ No newline at end of file From 8b82fe02af521bad3db193e7a555cec2d5d658ef Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Thu, 30 Mar 2023 22:39:05 +0200 Subject: [PATCH 20/29] removed unused variables --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index f0a8ac2161..33b073fffe 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -182,8 +182,6 @@ HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_o return; } - uint32_t io_info[SOC_LEDC_CHANNEL_NUM][3]; // pin information - uint32_t pwm_duty_init[SOC_LEDC_CHANNEL_NUM]; // pwm duty for(uint8_t i = 0; i < no_of_pins; i++) { pwm_duty_init[i] = 0; // Start with zero output channels[i] = pins[i]; From d03688b5de80a57f653e3f7035e71f20422edd97 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Thu, 30 Mar 2023 22:48:51 +0200 Subject: [PATCH 21/29] missed one occurrence and also messed up in a merge - cleaned out now --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 35 --------------------------- 1 file changed, 35 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index 33b073fffe..e75c5a1b3d 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -183,7 +183,6 @@ HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_o } for(uint8_t i = 0; i < no_of_pins; i++) { - pwm_duty_init[i] = 0; // Start with zero output channels[i] = pins[i]; /* @@ -326,37 +325,3 @@ uint32_t HardwarePWM::getFrequency(uint8_t pin) return ledc_get_freq(pinToGroup(pin), pinToTimer(pin)); } -namespace{ - ledc_channel_t pinToChannel(uint8_t pin){ - return (ledc_channel_t)(pin % 8); - } - - ledc_mode_t pinToGroup(uint8_t pin){ - return (ledc_mode_t) (pin / 8); - } - - ledc_timer_t pinToTimer(uint8_t pin){ - return (ledc_timer_t) ((pin /2) % 4); - } - - uint32_t periodToFrequency(uint32_t period){ - if(period == 0){ - return 0; - }else{ - return (1000000 / period); - } - } - - uint32_t frequencyToPeriod(uint32_t freq){ - if(freq == 0) { - return 0; - } else { - return (1000000 / freq); - } - } - - uint32_t maxDuty(ledc_timer_bit_t bits){ - return (1<<(uint32_t)bits) - 1; - } - -} \ No newline at end of file From 0e06be001668128cc5240e8166148e90639838d0 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sat, 1 Apr 2023 13:15:00 +0200 Subject: [PATCH 22/29] removed a piece of code that was doubled --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index e75c5a1b3d..f6aefe8303 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -264,20 +264,6 @@ bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) { if(chan == PWM_BAD_CHANNEL) { return false; - } else if(duty <= maxduty) { - ESP_ERROR_CHECK(ledc_set_duty(pinToGroup(chan), pinToChannel(chan), duty)); - /* - * ignoring the update flag in this release, ToDo: implement a synchronized update mechanism - * if(update) { - * ESP_ERROR_CHECK(ledc_update_duty(pinToGroup(chan), pinToChannel(chan))); - * //update(); - * } - */ - ESP_ERROR_CHECK(ledc_update_duty(pinToGroup(chan), pinToChannel(chan))); - return true; - } else { - debug_d("Duty cycle value too high for current period."); - return false; } if(duty <= maxduty) { From b3489a10fefe89b832af9bee5c81952ec297d88f Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Wed, 5 Apr 2023 22:17:12 +0200 Subject: [PATCH 23/29] fixed typos and coding style --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index f6aefe8303..59cbd3571e 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -106,8 +106,8 @@ * ===================================== * the current HardwarePWM implementation does not care about the PWM timer bit width. To leverage the functionality * of the ESP32 hardware, it's necessary to make this configurable. As the width is per timer and all the Sming defined - * functions are basically per pin, this needs to be an extension of the overal model, exposing at least timers. - * This, too, will require a compatible "basic" interface and an advanced interface that allows assiging pins (channels) + * functions are basically per pin, this needs to be an extension of the overall model, exposing at least timers. + * This, too, will require a compatible "basic" interface and an advanced interface that allows assigning pins (channels) * to timers and the configuration of the timers themselves. * The esp_idf does not provide a way to read the bit width configured for a channel, but I think it'll be useful to be able * to read back this value, not least to find the value for getMaxDuty() for a channel. It will either have to be stored in the @@ -115,7 +115,7 @@ * * ToDo: implement an abstraction layer * ==================================== - * as it stands now, this impelmentation does not provide a function to synchronize all the PWM channels (HardwarePWM::update()) + * as it stands now, this implementation does not provide a function to synchronize all the PWM channels (HardwarePWM::update()) * It might be a good idea to provide an intermediary abstraction that handles all the low level PWM functions (such as flexible * timer to channel assignments, hs/ls mode awareness, pwm bit width etc) and implements the update() function on that level. * @@ -187,7 +187,7 @@ HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_o /* * Prepare and then apply the LEDC PWM timer configuration. - * This may cofigure the same timer more than once (in fact up to 8 times) + * This may configure the same timer more than once (in fact up to 8 times) * which should not be an issue, though, since the values should be the same for all timers */ // The two groups (if available) are operating in different speed modes, hence speed mode is an alias for group or vice versa @@ -309,5 +309,4 @@ void HardwarePWM::update() uint32_t HardwarePWM::getFrequency(uint8_t pin) { return ledc_get_freq(pinToGroup(pin), pinToTimer(pin)); -} - +} \ No newline at end of file From e5c1138185d206f2b07dfc7e141fc936ab29d681 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sat, 8 Apr 2023 10:24:54 +0200 Subject: [PATCH 24/29] removed ToDos from the comments that are to be implemented in the next iteraton as they are already underway --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 40 ++------------------------- 1 file changed, 2 insertions(+), 38 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index 59cbd3571e..3894c496d5 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -58,14 +58,7 @@ * * The nomenclature of timers in the high speed / low speed blocks is a bit misleading as the idf api * speaks of "speed mode", which, to me, implies that this would be a mode configurable in a specific timer - * while in reality, it does select a block of timers. I am considering re-naming that to "speed mode block" - * in my interface impmenentation. - * - * As an example, I would use - * setTimerFrequency(speedModeBlock, timer, frequency); - * - * ToDo: see, how this can be implemented to provide maximum overlap with the RP2040 pwm hardware so code does - * not become overly SoC specific. + * while in reality, it does select a block of timers. * * Maximum Timer width for PWM: * ============================ @@ -79,7 +72,7 @@ * esp32 SOC_LEDC_CHANNEL_NUM (8) * esp32c3 SOC_LEDC_CHANNEL_NUM (6) * esp32s2 SOC_LEDC_CHANNEL_NUM (8) - * esp32s3 SOC_LEDC_CHANNEL_NUM 8 + * esp32s3 SOC_LEDC_CHANNEL_NUM (8) * * Some SoSs support a mode called HIGHSPEED_MODE which is essentially another full block of PWM hardware * that adds SOC_LEDC_CHANNEL_NUM channels. @@ -89,35 +82,6 @@ * Supports highspeed mode: * ======================== * esp32 SOC_LEDC_SUPPORT_HS_MODE (1) - * - * ToDo: implement awareness of hs mode availablility - * ================================================== - * currently, the code just uses a % 8 operation on the pin index to calculate whether to assign a pin to either - * high speed or low speed pwm blocks. This doesn't make a whole lot of sense since it makes it impossible - * for Sming devs to actually use the functionality behind it. - * Also, it currently does not reflect the fact that different SOCs have a different number of channels per block - * (specifically, the esp32c3 only has six channels and no highspeed mode). - * I will continue in two ways: - * - implement the "vanilla" Sming HardwarePWM interface that will hide the underlying architecture but allow up to 16 - * channels on an ESP32 - * - implement overloads for the relevant functions that allow selecting hs mode where applicable. - * - * ToDo: implement PWM bit width control - * ===================================== - * the current HardwarePWM implementation does not care about the PWM timer bit width. To leverage the functionality - * of the ESP32 hardware, it's necessary to make this configurable. As the width is per timer and all the Sming defined - * functions are basically per pin, this needs to be an extension of the overall model, exposing at least timers. - * This, too, will require a compatible "basic" interface and an advanced interface that allows assigning pins (channels) - * to timers and the configuration of the timers themselves. - * The esp_idf does not provide a way to read the bit width configured for a channel, but I think it'll be useful to be able - * to read back this value, not least to find the value for getMaxDuty() for a channel. It will either have to be stored in the - * module or maybe read from the hardware directly (LEDC_[HL]STIMERx_CONF_REG & 0x1f) - * - * ToDo: implement an abstraction layer - * ==================================== - * as it stands now, this implementation does not provide a function to synchronize all the PWM channels (HardwarePWM::update()) - * It might be a good idea to provide an intermediary abstraction that handles all the low level PWM functions (such as flexible - * timer to channel assignments, hs/ls mode awareness, pwm bit width etc) and implements the update() function on that level. * * hardware technical reference: * ============================= From 5457b4a2730eac72430ebc7e6ba9a027830896b7 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Wed, 19 Apr 2023 17:09:29 +0200 Subject: [PATCH 25/29] fixed return values --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index 3894c496d5..f660958b6b 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -123,12 +123,12 @@ ledc_timer_t pinToTimer(uint8_t pin) uint32_t periodToFrequency(uint32_t period) { - return (period == 0) ? -1 : (1000000 / period); + return (period == 0) ? 0 : (1000000 / period); } uint32_t frequencyToPeriod(uint32_t freq) { - return (freq == 0) ? -1 : (1000000 / freq); + return (freq == 0) ? 0 : (1000000 / freq); } uint32_t maxDuty(ledc_timer_bit_t bits) @@ -273,4 +273,4 @@ void HardwarePWM::update() uint32_t HardwarePWM::getFrequency(uint8_t pin) { return ledc_get_freq(pinToGroup(pin), pinToTimer(pin)); -} \ No newline at end of file +} From dcf2834b3f879ef9076a0cd471e5ab899916b08f Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 23 Apr 2023 13:54:17 +0200 Subject: [PATCH 26/29] changed some coding style issues in esp8266 code --- .../Components/driver/include/driver/pwm.h | 4 ++-- .../Components/driver/new-pwm/README.md | 6 +++--- Sming/Arch/Esp8266/Core/HardwarePWM.cpp | 20 +++++++++---------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Sming/Arch/Esp8266/Components/driver/include/driver/pwm.h b/Sming/Arch/Esp8266/Components/driver/include/driver/pwm.h index e47435fa3a..2424ae320a 100644 --- a/Sming/Arch/Esp8266/Components/driver/include/driver/pwm.h +++ b/Sming/Arch/Esp8266/Components/driver/include/driver/pwm.h @@ -23,13 +23,13 @@ extern "C" { * * Example: * - * uint32 io_info[][3] = { + * uint32 ioInfo[][3] = { * {PWM_0_OUT_IO_MUX, PWM_0_OUT_IO_FUNC, PWM_0_OUT_IO_NUM}, * {PWM_1_OUT_IO_MUX, PWM_1_OUT_IO_FUNC, PWM_1_OUT_IO_NUM}, * {PWM_2_OUT_IO_MUX, PWM_2_OUT_IO_FUNC, PWM_2_OUT_IO_NUM} * }; * - * pwm_init(light_param.pwm_period, light_param.pwm_duty, 3, io_info); + * pwm_init(light_param.pwm_period, light_param.pwm_duty, 3, ioInfo); * */ diff --git a/Sming/Arch/Esp8266/Components/driver/new-pwm/README.md b/Sming/Arch/Esp8266/Components/driver/new-pwm/README.md index d9391a597e..8881d1f3c0 100644 --- a/Sming/Arch/Esp8266/Components/driver/new-pwm/README.md +++ b/Sming/Arch/Esp8266/Components/driver/new-pwm/README.md @@ -37,7 +37,7 @@ Example usage: const uint32_t period = 5000; // * 200ns ^= 1 kHz // PWM setup - uint32 io_info[PWM_CHANNELS][3] = { + uint32 ioInfo[PWM_CHANNELS][3] = { // MUX, FUNC, PIN {PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12, 12}, {PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15, 15}, @@ -47,9 +47,9 @@ Example usage: }; // initial duty: all off - uint32 pwm_duty_init[PWM_CHANNELS] = {0, 0, 0, 0, 0}; + uint32 pwmDutyInit[PWM_CHANNELS] = {0, 0, 0, 0, 0}; - pwm_init(period, pwm_duty_init, PWM_CHANNELS, io_info); + pwm_init(period, pwmDutyInit, PWM_CHANNELS, ioInfo); pwm_start(); // do something like this whenever you want to change duty diff --git a/Sming/Arch/Esp8266/Core/HardwarePWM.cpp b/Sming/Arch/Esp8266/Core/HardwarePWM.cpp index 02638cdfcd..4a1b768363 100644 --- a/Sming/Arch/Esp8266/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp8266/Core/HardwarePWM.cpp @@ -30,23 +30,23 @@ #define PERIOD_TO_MAX_DUTY(x) (x * 25) -HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_of_pins) +HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t noOfPins) : channel_count(noOfPins) { - if(no_of_pins == 0) { + if(noOfPins == 0) { return; } - uint32_t io_info[PWM_CHANNEL_NUM_MAX][3]; // pin information - uint32_t pwm_duty_init[PWM_CHANNEL_NUM_MAX]; // pwm duty - for(uint8_t i = 0; i < no_of_pins; i++) { - io_info[i][0] = EspDigitalPins[pins[i]].mux; - io_info[i][1] = EspDigitalPins[pins[i]].gpioFunc; - io_info[i][2] = EspDigitalPins[pins[i]].id; - pwm_duty_init[i] = 0; // Start with zero output + uint32_t ioInfo[PWM_CHANNEL_NUM_MAX][3]; // pin information + uint32_t pwmDutyInit[PWM_CHANNEL_NUM_MAX]; // pwm duty + for(uint8_t i = 0; i < noOfPins; i++) { + ioInfo[i][0] = EspDigitalPins[pins[i]].mux; + ioInfo[i][1] = EspDigitalPins[pins[i]].gpioFunc; + ioInfo[i][2] = EspDigitalPins[pins[i]].id; + pwmDutyInit[i] = 0; // Start with zero output channels[i] = pins[i]; } const int initial_period = 1000; - pwm_init(initial_period, pwm_duty_init, no_of_pins, io_info); + pwm_init(initial_period, pwmDutyInit, noOfPins, ioInfo); update(); maxduty = PERIOD_TO_MAX_DUTY(initial_period); // for period of 1000 } From bbacc5a8df293f94c112ebfb6dc54462d4bd5701 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 23 Apr 2023 14:05:14 +0200 Subject: [PATCH 27/29] removed unnecessary and improved useful debug output --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 3 +-- Sming/Arch/Esp8266/Core/HardwarePWM.cpp | 5 ++--- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index f660958b6b..fcc387dc34 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -211,7 +211,6 @@ uint8_t HardwarePWM::getChannel(uint8_t pin) { for(uint8_t i = 0; i < channel_count; i++) { if(channels[i] == pin) { - // debug_d("getChannel %d is %d", pin, i); return i; } } @@ -243,7 +242,7 @@ bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) return true; } - debug_d("Duty cycle value too high for current period."); + debug_d("Duty cycle value too high for current period, max duty cycle is %d", maxduty); return false; } diff --git a/Sming/Arch/Esp8266/Core/HardwarePWM.cpp b/Sming/Arch/Esp8266/Core/HardwarePWM.cpp index 4a1b768363..7df5b64786 100644 --- a/Sming/Arch/Esp8266/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp8266/Core/HardwarePWM.cpp @@ -60,12 +60,11 @@ uint8_t HardwarePWM::getChannel(uint8_t pin) { for(uint8_t i = 0; i < channel_count; i++) { if(channels[i] == pin) { - // debug_d("getChannel %d is %d", pin, i); return i; } } - // debug_d("getChannel: can't find pin %d", pin); + debug_d("getChannel: can't find pin %d", pin); return PWM_BAD_CHANNEL; } @@ -88,7 +87,7 @@ bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) return true; } - debugf("Duty cycle value too high for current period."); + debug_e("Duty cycle value too high for current period. max duty is %d.",maxduty); return false; } From e1413ff0f780d77983e26688cf698bb8bd144d50 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 23 Apr 2023 14:18:22 +0200 Subject: [PATCH 28/29] corrected the PWM_CHANNEL_NUM_MAX define in pwm.h to reflect the correct SoC config --- .../Components/driver/include/driver/pwm.h | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Sming/Arch/Esp32/Components/driver/include/driver/pwm.h b/Sming/Arch/Esp32/Components/driver/include/driver/pwm.h index 36eacc6e9d..ea069f1cbf 100644 --- a/Sming/Arch/Esp32/Components/driver/include/driver/pwm.h +++ b/Sming/Arch/Esp32/Components/driver/include/driver/pwm.h @@ -1,3 +1,18 @@ +/**** + * Sming Framework Project - Open Source framework for high efficiency native ESP8266 development. + * Created 2015 by Skurydin Alexey + * http://github.com/SmingHub/Sming + * All files of the Sming Core are provided under the LGPL v3 license. + * + * pwm.h + * + ****/ + #pragma once -#define PWM_CHANNEL_NUM_MAX 8 +#ifdef SOC_LEDC_CHANNEL_NUM + #define PWM_CHANNEL_NUM_MAX SOC_LEDC_CHANNEL_NUM +#else + // this should not happen if the correct esp32 includes are used, just to be absolutely sure + #define PWM_CHANNEL_NUM_MAX 8 +#endif From f03ce143344de7c1f4abf11d690c099b16050cbb Mon Sep 17 00:00:00 2001 From: Slavey Karadzhov Date: Fri, 28 Apr 2023 17:04:59 +0200 Subject: [PATCH 29/29] Small coding style fixes. --- Sming/Arch/Esp8266/Core/HardwarePWM.cpp | 6 +++--- Sming/Core/HardwarePWM.h | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Sming/Arch/Esp8266/Core/HardwarePWM.cpp b/Sming/Arch/Esp8266/Core/HardwarePWM.cpp index 7df5b64786..45a33d560f 100644 --- a/Sming/Arch/Esp8266/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp8266/Core/HardwarePWM.cpp @@ -45,10 +45,10 @@ HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t noOfPins) : channel_count(noOfPi pwmDutyInit[i] = 0; // Start with zero output channels[i] = pins[i]; } - const int initial_period = 1000; - pwm_init(initial_period, pwmDutyInit, noOfPins, ioInfo); + const int initialPeriod = 1000; + pwm_init(initialPeriod, pwmDutyInit, noOfPins, ioInfo); update(); - maxduty = PERIOD_TO_MAX_DUTY(initial_period); // for period of 1000 + maxduty = PERIOD_TO_MAX_DUTY(initialPeriod); // for period of 1000 } HardwarePWM::~HardwarePWM() diff --git a/Sming/Core/HardwarePWM.h b/Sming/Core/HardwarePWM.h index 9c1ec86b72..101f692899 100644 --- a/Sming/Core/HardwarePWM.h +++ b/Sming/Core/HardwarePWM.h @@ -123,6 +123,7 @@ class HardwarePWM void update(); /** @brief Get PWM Frequency + * @param pin GPIO to get frequency for * @retval uint32_t Value of Frequency */ uint32_t getFrequency(uint8_t pin);