From 50a5efdbf97b18b3a5b4c3a5253c4a6cef8afd7d Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 1 Jan 2023 09:24:50 +0100 Subject: [PATCH 01/28] 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 b9b6170affccb6661b6c3c1c5cef3e9e1a1d4805 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 1 Jan 2023 10:25:36 +0100 Subject: [PATCH 02/28] 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 e44e1a98c5c989a0111323465ef0e1bc0461fb57 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 1 Jan 2023 11:30:37 +0100 Subject: [PATCH 03/28] 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 7279c66287e6a3be238052687ad3a0e3318a9c01 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 1 Jan 2023 12:13:20 +0100 Subject: [PATCH 04/28] 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 61d41dafaf407f47913a0c5ced0852151358a218 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 12 Feb 2023 11:24:17 +0100 Subject: [PATCH 05/28] 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 a2f962891e138a68003ea50f713cbf7a29fe2f91 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 12 Feb 2023 11:32:40 +0100 Subject: [PATCH 06/28] 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 a9b597d794da34c02cc48ce5fa1086b96e3264a8 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 12 Feb 2023 11:40:43 +0100 Subject: [PATCH 07/28] 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 292f5898b0cf56e112f1f89f6d905439d3844d1c Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 12 Feb 2023 11:58:51 +0100 Subject: [PATCH 08/28] 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 61f3feb30f33ccad841331a7f29593c05fd52598 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 12 Feb 2023 12:57:53 +0100 Subject: [PATCH 09/28] 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 e71532f0d880a08949ff5c8dfc6271ebe6b821bf Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 12 Feb 2023 13:54:10 +0100 Subject: [PATCH 10/28] 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 b6c34844be2c925823f8ecfa7d6d132c9ae394b3 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 12 Feb 2023 13:57:04 +0100 Subject: [PATCH 11/28] .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 6550205fba68d87f9094eb4ce8dfde6329e9be92 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sat, 18 Mar 2023 11:12:36 +0100 Subject: [PATCH 12/28] minor updates as suggested by mikee47 --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index 745126c4cd..b74288a3d9 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 @@ -264,10 +270,14 @@ bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) 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(); - } + /* + * 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."); @@ -330,7 +340,7 @@ namespace{ uint32_t periodToFrequency(uint32_t period){ if(period == 0){ - return -1; + return 0; }else{ return (1000000 / period); } @@ -338,7 +348,7 @@ namespace{ uint32_t frequencyToPeriod(uint32_t freq){ if(freq == 0) { - return -1; + return 0; } else { return (1000000 / freq); } From f2d63333a3a305831b01152c1f47907b0c2feed7 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 19 Mar 2023 17:18:48 +0100 Subject: [PATCH 13/28] initial work to create a more flexible pwm implementation --- Sming/Arch/Esp32/Core/pwmGroups.cpp | 0 Sming/Arch/Esp32/Core/pwmGroups.h | 0 Sming/Arch/Esp32/Core/singelton.cpp | 65 ++++++++ Sming/Arch/Esp32/Core/singleton.h | 237 ++++++++++++++++++++++++++++ 4 files changed, 302 insertions(+) create mode 100644 Sming/Arch/Esp32/Core/pwmGroups.cpp create mode 100644 Sming/Arch/Esp32/Core/pwmGroups.h create mode 100644 Sming/Arch/Esp32/Core/singelton.cpp create mode 100644 Sming/Arch/Esp32/Core/singleton.h diff --git a/Sming/Arch/Esp32/Core/pwmGroups.cpp b/Sming/Arch/Esp32/Core/pwmGroups.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Arch/Esp32/Core/pwmGroups.h b/Sming/Arch/Esp32/Core/pwmGroups.h new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Sming/Arch/Esp32/Core/singelton.cpp b/Sming/Arch/Esp32/Core/singelton.cpp new file mode 100644 index 0000000000..67749a2e0e --- /dev/null +++ b/Sming/Arch/Esp32/Core/singelton.cpp @@ -0,0 +1,65 @@ +#include "./singleton.h" +/** + * @namespace HardwarePWM - stuffing away my HardwarePWM specific functions in a separate Namespace + * +*/ +namespace HardwarePWM +{ +/** + * @brief get a pwm timer + * + * @return timer number + * + */ + +ledc_timer_t Timer::getTimer(ledc_mode_t mode) +{ + for(uint8_t i = 0; i < (uint8_t)LEDC_TIMER_MAX; i++) { + if(!isUsed[(uint8_t)mode][i]) { + isUsed[(uint8_t)mode][i] = true; + return (ledc_timer_t)i; + } + } + return (ledc_timer_t)LEDC_TIMER_MAX; +} + +void Timer::freeTimer(ledc_mode_t mode, ledc_timer_t timer) +{ + isUsed[(uint8_t)mode][(uint8_t)timer] = false; +} + +Timer::Timer() +{ + for(uint8_t i = 0; i < (uint8_t)LEDC_SPEED_MODE_MAX; i++) { + for(uint8_t j = 0; j < (uint8_t)LEDC_TIMER_MAX; j++) { + isUsed[i][j] = false; + } + } +} + +ledc_channel_t Channel::getChannel(ledc_mode_t mode) +{ + for(uint8_t i = 0; i < (uint8_t)LEDC_CHANNEL_MAX; i++) { + if(!isUsed[(uint8_t)mode][i]) { + isUsed[(uint8_t)mode][i] = true; + return (ledc_channel_t)i; + } + } + return (ledc_channel_t)LEDC_CHANNEL_MAX; +} + +void Channel::freeChannel(ledc_mode_t mode, ledc_channel_t channel) +{ + isUsed[(uint8_t)mode][(uint8_t)channel] = false; +} + +Channel::Channel() +{ + for(int i = 0; i < LEDC_CHANNEL_MAX; i++) { + isUsed[0][i] = false; +#ifdef SOC_LEDC_SUPPORT_HS_MODE + isUsed[1][i] = false; +#endif + } +} +} // end namespace HardwarePWM \ No newline at end of file diff --git a/Sming/Arch/Esp32/Core/singleton.h b/Sming/Arch/Esp32/Core/singleton.h new file mode 100644 index 0000000000..2f20c908f7 --- /dev/null +++ b/Sming/Arch/Esp32/Core/singleton.h @@ -0,0 +1,237 @@ +#include +#ifndef LEDC_TYPES_H_ +#include +#endif +#define PWM_MAX_TIMER 8 + +namespace HardwarePWM +{ +template class Singleton +{ +public: + static C* instance() + { + if(!_instance) + _instance = new C(); + return _instance; + } + virtual ~Singleton() + { + _instance = 0; + } + +private: + static C* _instance; + +protected: + Singleton() + { + } +}; + +template C* Singleton::_instance = 0; + +class Timer : public Singleton +{ + friend class Singleton; + +public: + ~Timer(){}; + + /** + * @brief get an unspecified timer + * @param none + * @note as the only timer available eveywhere are low speed, this is low speed + * @retval ledc_timer_t , + * LEDC_TIMER_MAX if no more timers are available + */ + ledc_timer_t getTimer() + { + return getLSTimer(); + }; + + /** + * @brief get a low speed timer + * @param none + * @retval ledc_timer_t + * LEDC_TIMER_MAX if no more timers are available + */ + ledc_timer_t getLSTimer() + { + return getTimer(LEDC_LOW_SPEED_MODE); + }; + + /** + * @brief get a high speed timer + * @param none + * @note you are not guaranteed that the SoC has high speed timers + * @retval ledc_timer_t + * LEDC_TIMER_MAX if no more timers are available + */ + ledc_timer_t getHSTimer() + { + return getTimer(LEDC_HIGH_SPEED_MODE); + }; + + /** + * @brief get a timer of specified speed mode + * @param ledc_mode_t (LEDC_LOW_SPEED_MODE or LEDC_HIGH_SPEED_MODE) + * @note you are not guaranteed that the SoC has high speed timers + * also, you are required to keep track of the speed mode of the timers yourself. + * @retval ledc_timer_t + * LEDC_TIMER_MAX if no more timers with the specified mode are available + */ + ledc_timer_t getTimer(ledc_mode_t mode); + + /** + * @brief free an unspecified timer + * @param ledc_timer_t + * @note as the only timer available eveywhere are low speed, this is low speed timers + * using this to free a high speed mode timer will lead to unexpected effects + * because the seemingly freed timer might be re-used and thus re-configured later on + * @retval none + */ + void freeTimer(ledc_timer_t timer) + { + freeLSTimer(timer); + }; + + /** + * @brief free a low speed timer + * @param ledc_timer_t + * @note using this to free a high speed mode timer will lead to unexpected effects + * because the seemingly freed timer might be re-used and thus re-configured later on + * @retval none + */ + void freeLSTimer(ledc_timer_t timer) + { + freeTimer(LEDC_LOW_SPEED_MODE, timer); + }; + + /** + * @brief free a high speed timer + * @param ledc_timer_t + * @note using this to free a low speed mode timer will lead to unexpected effects + * because the seemingly freed timer might be re-used and thus re-configured later on + * @retval none + */ + void freeHSTimer(ledc_timer_t timer) + { + freeTimer(LEDC_HIGH_SPEED_MODE, timer); + }; + + /** + * @brief free a timer + * @param ledc_timer_t + * @note if you are mixing low speed and high speed timers, this is the safest call to use + * @retval none + */ + void freeTimer(ledc_mode_t mode, ledc_timer_t timer); + +protected: + Timer(); + bool isUsed[LEDC_SPEED_MODE_MAX][LEDC_TIMER_MAX]; +}; + +class Channel : public Singleton +{ + friend class Singleton; + +public: + ~Channel(){}; + + /** + * @brief get an unspecified channel + * @param none + * @note as the only channels available eveywhere are low speed, this is low speed + * @retval ledc_channel_t , + * LEDC_CHANNEL_MAX if no more CHANNELS are available + */ + ledc_channel_t getChannel() + { + return getLSChannel(); + }; + + /** + * @brief get a low speed channel + * @param none + * @retval ledc_channel_t , + * LEDC_CHANNEL_MAX if no more low speed CHANNELS are available + */ + ledc_channel_t getLSChannel() + { + return getChannel(LEDC_LOW_SPEED_MODE); + }; + + /** + * @brief get a high speed channel + * @param none + * @note you are not guaranteed that the SoC has high speed channels + * @retval ledc_channel_t , + * LEDC_CHANNEL_MAX if no more high speed CHANNELS are available + */ + ledc_channel_t getHSChannel() + { + return getChannel(LEDC_HIGH_SPEED_MODE); + }; + + /** + * @brief get a channel with a defined speed mode + * @param ledc_speedmode_t mode + * @note you are not guaranteed that the SoC has high speed channels + * also, you are required to keep track of the speed mode of the channels yourself. + * @retval ledc_channel_t , + * LEDC_CHANNEL_MAX if no more channels with the specified mode are available + */ + ledc_channel_t getChannel(ledc_mode_t mode); + + /** + * @brief free an unspecified channel + * @param ledc_channel_t + * @note as the only channels available eveywhere are low speed, this is low speed channels + * using this to free a high speed channel will lead to unexpected effects + * because the seemingly freed channel might be re-used and thus re-configured later on + * @retval none + */ + void freeChannel(ledc_channel_t channel) + { + freeLSChannel(channel); + }; + + /** + * @brief free a low speed channel + * @param ledc_channel_t + * @note using this to free a high speed channel will lead to unexpected effects + * because the seemingly freed channel might be re-used and thus re-configured later on + * @retval none + */ + void freeLSChannel(ledc_channel_t channel) + { + freeChannel(LEDC_LOW_SPEED_MODE, channel); + }; + + /** + * @brief free a high speed channel + * @param ledc_channel_t + * @note using this to free a low speed channel will lead to unexpected effects + * because the seemingly freed channel might be re-used and thus re-configured later on + * @retval none + */ + void freeHSChannel(ledc_channel_t channel) + { + freeChannel(LEDC_HIGH_SPEED_MODE, channel); + }; + + /** + * @brief free a channel + * @param ledc_channel_t + * @note if you are mixing low speed and high speed channels, this is the safest call to use + * @retval none + */ + void freeChannel(ledc_mode_t mode, ledc_channel_t channel); + +protected: + Channel(); + bool isUsed[LEDC_SPEED_MODE_MAX][LEDC_CHANNEL_MAX]; +}; +} //end namespace HardwarePWM \ No newline at end of file From f06150180886f3f0de1acf81637ca08beedd0f14 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 19 Mar 2023 17:19:44 +0100 Subject: [PATCH 14/28] initial work to create a more flexible pwm implementation --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 13 ++++++++----- Sming/Arch/Esp32/Core/pwmGroups.cpp | 0 Sming/Arch/Esp32/Core/pwmGroups.h | 0 3 files changed, 8 insertions(+), 5 deletions(-) delete mode 100644 Sming/Arch/Esp32/Core/pwmGroups.cpp delete mode 100644 Sming/Arch/Esp32/Core/pwmGroups.h diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index b74288a3d9..94ad6f2a8f 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -76,7 +76,7 @@ * * Number of Channels: * =================== - * esp32 SOC_LEDC_CHANNEL_NUM (8) + * esp32 SOC_LEDC_CHANNEL_NUM 16 (8?) * esp32c3 SOC_LEDC_CHANNEL_NUM (6) * esp32s2 SOC_LEDC_CHANNEL_NUM (8) * esp32s3 SOC_LEDC_CHANNEL_NUM 8 @@ -121,7 +121,8 @@ * * hardware technical reference: * ============================= - * https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#ledpwm + * ESP32: https://www.espressif.com/sites/default/files/documentation/esp32_technical_reference_manual_en.pdf#ledpwm + * ESP32c3: https://www.espressif.com/sites/default/files/documentation/esp32-c3_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 @@ -150,8 +151,10 @@ //#include #include "driver/ledc.h" #include "esp_err.h" -#include "hal/ledc_types.h" +//#include "hal/ledc_types.h" +#include #include +#include "./singleton.h" namespace{ ledc_mode_t pinToGroup(uint8_t pin); @@ -177,9 +180,9 @@ HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_o } uint32_t io_info[SOC_LEDC_CHANNEL_NUM][3]; // pin information - uint32_t pwm_duty_init[SOC_LEDC_CHANNEL_NUM]; // pwm duty + //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 + //pwm_duty_init[i] = 0; // Start with zero output channels[i] = pins[i]; /* diff --git a/Sming/Arch/Esp32/Core/pwmGroups.cpp b/Sming/Arch/Esp32/Core/pwmGroups.cpp deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/Sming/Arch/Esp32/Core/pwmGroups.h b/Sming/Arch/Esp32/Core/pwmGroups.h deleted file mode 100644 index e69de29bb2..0000000000 From 458234ddc3ffab77f42507280fc5f136f0df0b1c Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sun, 19 Mar 2023 17:52:19 +0100 Subject: [PATCH 15/28] added const overloads for timer and channel calls --- Sming/Arch/Esp32/Core/singleton.h | 172 +++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 2 deletions(-) diff --git a/Sming/Arch/Esp32/Core/singleton.h b/Sming/Arch/Esp32/Core/singleton.h index 2f20c908f7..f924e4f402 100644 --- a/Sming/Arch/Esp32/Core/singleton.h +++ b/Sming/Arch/Esp32/Core/singleton.h @@ -47,7 +47,7 @@ class Timer : public Singleton */ ledc_timer_t getTimer() { - return getLSTimer(); + return getLSTimer(LEDC_LOW_SPEED_MODE); }; /** @@ -83,6 +83,18 @@ class Timer : public Singleton */ ledc_timer_t getTimer(ledc_mode_t mode); + /** + * @brief get a timer of specified speed mode + * @param const int (LEDC_LOW_SPEED_MODE or LEDC_HIGH_SPEED_MODE) + * @note you are not guaranteed that the SoC has high speed timers + * also, you are required to keep track of the speed mode of the timers yourself. + * @retval ledc_timer_t + * LEDC_TIMER_MAX if no more timers with the specified mode are available + */ + ledc_timer_t getTimer(const int mode) { + return getTimer((ledc_mode_t)mode); + }; + /** * @brief free an unspecified timer * @param ledc_timer_t @@ -96,6 +108,19 @@ class Timer : public Singleton freeLSTimer(timer); }; + /** + * @brief free an unspecified timer + * @param const int + * @note as the only timer available eveywhere are low speed, this is low speed timers + * using this to free a high speed mode timer will lead to unexpected effects + * because the seemingly freed timer might be re-used and thus re-configured later on + * @retval none + */ + void freeTimer(const int timer) + { + freeLSTimer((ledc_timer_t) timer); + }; + /** * @brief free a low speed timer * @param ledc_timer_t @@ -108,6 +133,18 @@ class Timer : public Singleton freeTimer(LEDC_LOW_SPEED_MODE, timer); }; + /** + * @brief free a low speed timer + * @param const int + * @note using this to free a high speed mode timer will lead to unexpected effects + * because the seemingly freed timer might be re-used and thus re-configured later on + * @retval none + */ + void freeLSTimer(const int timer) + { + freeTimer(LEDC_LOW_SPEED_MODE, (ledc_timer_t) timer); + }; + /** * @brief free a high speed timer * @param ledc_timer_t @@ -120,14 +157,60 @@ class Timer : public Singleton freeTimer(LEDC_HIGH_SPEED_MODE, timer); }; + /** + * @brief free a high speed timer + * @param const int + * @note using this to free a low speed mode timer will lead to unexpected effects + * because the seemingly freed timer might be re-used and thus re-configured later on + * @retval none + */ + void freeHSTimer(const int timer) + { + freeTimer(LEDC_HIGH_SPEED_MODE, timer); + }; + /** * @brief free a timer * @param ledc_timer_t + * @param ledc_mode_t * @note if you are mixing low speed and high speed timers, this is the safest call to use * @retval none */ void freeTimer(ledc_mode_t mode, ledc_timer_t timer); + /** + * @brief free a timer + * @param ledc_timer_t + * @param const int + * @note if you are mixing low speed and high speed timers, this is the safest call to use + * @retval none + */ + void freeTimer(const int mode, ledc_timer_t timer) { + freeTimer((ledc_mode_t)mode, timer); + }; + + /** + * @brief free a timer + * @param const int + * @param ledc_mode_t + * @note if you are mixing low speed and high speed timers, this is the safest call to use + * @retval none + */ + void freeTimer(ledc_mode_t mode, const int timer){ + freeTimer(mode,(ledc_timer_t)timer); + }; + + /** + * @brief free a timer + * @param const int + * @param const int + * @note if you are mixing low speed and high speed timers, this is the safest call to use + * @retval none + */ + void freeTimer(const int mode, const int timer){ + void freeTimer((ledc_mode_t) mode, (ledc_timer_t) timer); + }; + protected: Timer(); bool isUsed[LEDC_SPEED_MODE_MAX][LEDC_TIMER_MAX]; @@ -149,7 +232,7 @@ class Channel : public Singleton */ ledc_channel_t getChannel() { - return getLSChannel(); + return getLSChannel(LEDC_LOW_SPEED_MODE); }; /** @@ -185,6 +268,18 @@ class Channel : public Singleton */ ledc_channel_t getChannel(ledc_mode_t mode); + /** + * @brief get a channel with a defined speed mode + * @param const int mode + * @note you are not guaranteed that the SoC has high speed channels + * also, you are required to keep track of the speed mode of the channels yourself. + * @retval ledc_channel_t , + * LEDC_CHANNEL_MAX if no more channels with the specified mode are available + */ + ledc_channel_t getChannel(const int mode){ + getChannel((ledc_mode_t) mode); + }; + /** * @brief free an unspecified channel * @param ledc_channel_t @@ -198,6 +293,19 @@ class Channel : public Singleton freeLSChannel(channel); }; + /** + * @brief free an unspecified channel + * @param const int + * @note as the only channels available eveywhere are low speed, this is low speed channels + * using this to free a high speed channel will lead to unexpected effects + * because the seemingly freed channel might be re-used and thus re-configured later on + * @retval none + */ + void freeChannel(const int channel) + { + freeLSChannel((ledc_channel_t) channel); + }; + /** * @brief free a low speed channel * @param ledc_channel_t @@ -210,6 +318,18 @@ class Channel : public Singleton freeChannel(LEDC_LOW_SPEED_MODE, channel); }; + /** + * @brief free a low speed channel + * @param const int + * @note using this to free a high speed channel will lead to unexpected effects + * because the seemingly freed channel might be re-used and thus re-configured later on + * @retval none + */ + void freeLSChannel(const int channel) + { + freeChannel(LEDC_LOW_SPEED_MODE, (ledc_channel_t) channel); + }; + /** * @brief free a high speed channel * @param ledc_channel_t @@ -222,14 +342,62 @@ class Channel : public Singleton freeChannel(LEDC_HIGH_SPEED_MODE, channel); }; + /** + * @brief free a high speed channel + * @param const int + * @note using this to free a low speed channel will lead to unexpected effects + * because the seemingly freed channel might be re-used and thus re-configured later on + * @retval none + */ + void freeHSChannel(const int channel) + { + freeChannel(LEDC_HIGH_SPEED_MODE, (ledc_channel_t) channel); + }; + /** * @brief free a channel + * @param ledc_mode_t * @param ledc_channel_t * @note if you are mixing low speed and high speed channels, this is the safest call to use * @retval none */ void freeChannel(ledc_mode_t mode, ledc_channel_t channel); + /** + * @brief free a channel + * @param const int + * @param ledc_channel_t + * @note if you are mixing low speed and high speed channels, this is the safest call to use + * @retval none + */ + void freeChannel(const int mode, ledc_channel_t channel){ + freeChannel((ledc_mode_t ) mode, channel); + }; + + /** + * @brief free a channel + * @param ledc_mode_t int + * @param const int + * @note if you are mixing low speed and high speed channels, this is the safest call to use + * @retval none + */ + void freeChannel(ledc_mode_t mode, const int channel) + { + freeChannel( mode, (ledc_chanel_t)channel); + }; + + /** + * @brief free a channel + * @param const int + * @param const int + * @note if you are mixing low speed and high speed channels, this is the safest call to use + * @retval none + */ + void freeChannel(const int mode, const int channel) + { + freeChannel((ledc_mode_t)mode, (ledc_channel_t) channel); + }; + protected: Channel(); bool isUsed[LEDC_SPEED_MODE_MAX][LEDC_CHANNEL_MAX]; From d53ae8c30edb6aeceec0096e0972bacaeaae8fb9 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Thu, 30 Mar 2023 22:21:20 +0200 Subject: [PATCH 16/28] missing singleton.h --- Sming/Arch/Esp32/Core/singleton.h | 60 +++++++++++++++++-------------- 1 file changed, 33 insertions(+), 27 deletions(-) diff --git a/Sming/Arch/Esp32/Core/singleton.h b/Sming/Arch/Esp32/Core/singleton.h index f924e4f402..3f55dadaf6 100644 --- a/Sming/Arch/Esp32/Core/singleton.h +++ b/Sming/Arch/Esp32/Core/singleton.h @@ -47,7 +47,7 @@ class Timer : public Singleton */ ledc_timer_t getTimer() { - return getLSTimer(LEDC_LOW_SPEED_MODE); + return getLSTimer(); }; /** @@ -91,9 +91,10 @@ class Timer : public Singleton * @retval ledc_timer_t * LEDC_TIMER_MAX if no more timers with the specified mode are available */ - ledc_timer_t getTimer(const int mode) { - return getTimer((ledc_mode_t)mode); - }; + ledc_timer_t getTimer(const int mode) + { + return getTimer((ledc_mode_t)mode); + }; /** * @brief free an unspecified timer @@ -118,7 +119,7 @@ class Timer : public Singleton */ void freeTimer(const int timer) { - freeLSTimer((ledc_timer_t) timer); + freeLSTimer((ledc_timer_t)timer); }; /** @@ -142,7 +143,7 @@ class Timer : public Singleton */ void freeLSTimer(const int timer) { - freeTimer(LEDC_LOW_SPEED_MODE, (ledc_timer_t) timer); + freeTimer(LEDC_LOW_SPEED_MODE, (ledc_timer_t)timer); }; /** @@ -185,9 +186,10 @@ class Timer : public Singleton * @note if you are mixing low speed and high speed timers, this is the safest call to use * @retval none */ - void freeTimer(const int mode, ledc_timer_t timer) { - freeTimer((ledc_mode_t)mode, timer); - }; + void freeTimer(const int mode, ledc_timer_t timer) + { + freeTimer((ledc_mode_t)mode, timer); + }; /** * @brief free a timer @@ -196,9 +198,10 @@ class Timer : public Singleton * @note if you are mixing low speed and high speed timers, this is the safest call to use * @retval none */ - void freeTimer(ledc_mode_t mode, const int timer){ - freeTimer(mode,(ledc_timer_t)timer); - }; + void freeTimer(ledc_mode_t mode, const int timer) + { + freeTimer(mode, (ledc_timer_t)timer); + }; /** * @brief free a timer @@ -207,9 +210,10 @@ class Timer : public Singleton * @note if you are mixing low speed and high speed timers, this is the safest call to use * @retval none */ - void freeTimer(const int mode, const int timer){ - void freeTimer((ledc_mode_t) mode, (ledc_timer_t) timer); - }; + void freeTimer(const int mode, const int timer) + { + freeTimer((ledc_mode_t)mode, (ledc_timer_t)timer); + }; protected: Timer(); @@ -232,7 +236,7 @@ class Channel : public Singleton */ ledc_channel_t getChannel() { - return getLSChannel(LEDC_LOW_SPEED_MODE); + return getLSChannel(); }; /** @@ -276,9 +280,10 @@ class Channel : public Singleton * @retval ledc_channel_t , * LEDC_CHANNEL_MAX if no more channels with the specified mode are available */ - ledc_channel_t getChannel(const int mode){ - getChannel((ledc_mode_t) mode); - }; + ledc_channel_t getChannel(const int mode) + { + return getChannel((ledc_mode_t)mode); + }; /** * @brief free an unspecified channel @@ -303,7 +308,7 @@ class Channel : public Singleton */ void freeChannel(const int channel) { - freeLSChannel((ledc_channel_t) channel); + freeLSChannel((ledc_channel_t)channel); }; /** @@ -327,7 +332,7 @@ class Channel : public Singleton */ void freeLSChannel(const int channel) { - freeChannel(LEDC_LOW_SPEED_MODE, (ledc_channel_t) channel); + freeChannel(LEDC_LOW_SPEED_MODE, (ledc_channel_t)channel); }; /** @@ -351,7 +356,7 @@ class Channel : public Singleton */ void freeHSChannel(const int channel) { - freeChannel(LEDC_HIGH_SPEED_MODE, (ledc_channel_t) channel); + freeChannel(LEDC_HIGH_SPEED_MODE, (ledc_channel_t)channel); }; /** @@ -370,9 +375,10 @@ class Channel : public Singleton * @note if you are mixing low speed and high speed channels, this is the safest call to use * @retval none */ - void freeChannel(const int mode, ledc_channel_t channel){ - freeChannel((ledc_mode_t ) mode, channel); - }; + void freeChannel(const int mode, ledc_channel_t channel) + { + freeChannel((ledc_mode_t)mode, channel); + }; /** * @brief free a channel @@ -383,7 +389,7 @@ class Channel : public Singleton */ void freeChannel(ledc_mode_t mode, const int channel) { - freeChannel( mode, (ledc_chanel_t)channel); + freeChannel(mode, (ledc_channel_t)channel); }; /** @@ -395,7 +401,7 @@ class Channel : public Singleton */ void freeChannel(const int mode, const int channel) { - freeChannel((ledc_mode_t)mode, (ledc_channel_t) channel); + freeChannel((ledc_mode_t)mode, (ledc_channel_t)channel); }; protected: From 09419fdcd932a58e6e40c78d48f2ac3405c73f27 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Thu, 30 Mar 2023 22:42:37 +0200 Subject: [PATCH 17/28] singleton test branch --- samples/Singleton_test/Makefile | 9 ++++ samples/Singleton_test/README.rst | 4 ++ samples/Singleton_test/app/application.cpp | 50 ++++++++++++++++++++++ samples/Singleton_test/component.mk | 5 +++ 4 files changed, 68 insertions(+) create mode 100644 samples/Singleton_test/Makefile create mode 100644 samples/Singleton_test/README.rst create mode 100644 samples/Singleton_test/app/application.cpp create mode 100644 samples/Singleton_test/component.mk diff --git a/samples/Singleton_test/Makefile b/samples/Singleton_test/Makefile new file mode 100644 index 0000000000..ff51b6c3a7 --- /dev/null +++ b/samples/Singleton_test/Makefile @@ -0,0 +1,9 @@ +##################################################################### +#### Please don't change this file. Use component.mk instead #### +##################################################################### + +ifndef SMING_HOME +$(error SMING_HOME is not set: please configure it as an environment variable) +endif + +include $(SMING_HOME)/project.mk diff --git a/samples/Singleton_test/README.rst b/samples/Singleton_test/README.rst new file mode 100644 index 0000000000..053e6d5ebb --- /dev/null +++ b/samples/Singleton_test/README.rst @@ -0,0 +1,4 @@ +Basic Hardware PWM +================== + +Demonstrates how to generate PWM signals on multiple pins using Sming's *HardwarePWM* class. diff --git a/samples/Singleton_test/app/application.cpp b/samples/Singleton_test/app/application.cpp new file mode 100644 index 0000000000..6efc8e92c6 --- /dev/null +++ b/samples/Singleton_test/app/application.cpp @@ -0,0 +1,50 @@ +/* + * File: Esp SDK Hardware PWM demo + * 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 Duty as per the formulae give in ESP8266 SDK + * 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 + */ +#include +//#include +#include "/opt/sming/Sming/Core/HardwarePWM.h" +//#include "singleton.h" +using namespace HardwarePWM; +void setup() { + // put your setup code here, to run once: + + Serial.begin(115200); + delay(2000); + Serial.println("starting"); + Serial.println("========"); + for(int i=1;i<=4;i++){ + Serial.printf("gettig timer %i\n", Timer::instance()->getTimer(0)); + } + Timer::instance()->freeTimer(0,3); + Serial.printf("freed timer 3\n"); + Serial.printf("gettig timer %i\n", Timer::instance()->getTimer(0)); + Timer::instance()->freeTimer(0,2); + Timer::instance()->freeTimer(0,4); + Serial.printf("freed timer 2 and 4\n"); + + for(int i=1;i<=4;i++){ + Serial.printf("gettig timer %i\n", Timer::instance()->getTimer(0)); + } +} + +void loop() { + // put your main code here, to run repeatedly: + +} + + diff --git a/samples/Singleton_test/component.mk b/samples/Singleton_test/component.mk new file mode 100644 index 0000000000..ec78f78162 --- /dev/null +++ b/samples/Singleton_test/component.mk @@ -0,0 +1,5 @@ +COMPONENT_SOC := esp8266 + +# Uncomment the line below if you want to use Espressif's PWM library. +#ENABLE_CUSTOM_PWM=0 + From 6eceda35892fa649e93f8092df5514311e32ab3e Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Mon, 3 Apr 2023 19:51:38 +0200 Subject: [PATCH 18/28] added class to abstractly manage timers --- Sming/Arch/Esp32/Core/ledc_timers.h | 32 ++++++++++ Sming/Arch/Esp32/Core/ledc_timers_cpp | 86 +++++++++++++++++++++++++++ 2 files changed, 118 insertions(+) create mode 100644 Sming/Arch/Esp32/Core/ledc_timers.h create mode 100644 Sming/Arch/Esp32/Core/ledc_timers_cpp diff --git a/Sming/Arch/Esp32/Core/ledc_timers.h b/Sming/Arch/Esp32/Core/ledc_timers.h new file mode 100644 index 0000000000..5159df5bf1 --- /dev/null +++ b/Sming/Arch/Esp32/Core/ledc_timers.h @@ -0,0 +1,32 @@ +#ifndef LEDC_TIMERS_H +#define LEDC_TIMERS_H +#include +#include "driver/ledc.h" +#include "esp_err.h" +#include "singleton.h" +#include + +class ledc_timer { + public: + esp_err_t ledc_timer(ledc_mode_t mode, ledc_timer_bit_t duty_resolution, ledc_timer_bit_t bit_num, uint32_t freq, ledc_clk_cfg_t clk_cfg); + esp_err_t ~ledc_timer(void); + esp_err_t setTimer(uint32_t clock_divider, uint32_t duty_resolution, ledc_clk_src_t clk_src); + esp_err_t setTimerFrequency(uint32_t freq); + esp_err_t timerReset(void); + esp_err_t timerPause(void); + esp_err_t timerResume(void); + esp_err_t timerBindChannel(ledc_channel_t channel); + + ledc_timer_t getTimerNumber(void); + ledc_mode_t getTimerMode(void); + + uint32_t getDutyResolution(void); + uint32_t getTimerFrequency(void); + uint32_t getClockDivider(void); + private: + ledc_timer_config_t timer_conf; + ledc_timer_t timer; + uint32_t clock_divider; +} + +#endif \ No newline at end of file diff --git a/Sming/Arch/Esp32/Core/ledc_timers_cpp b/Sming/Arch/Esp32/Core/ledc_timers_cpp new file mode 100644 index 0000000000..62472df9df --- /dev/null +++ b/Sming/Arch/Esp32/Core/ledc_timers_cpp @@ -0,0 +1,86 @@ +#ifndef LEDC_TIMERS_H +#include "ledc_timers.h" +#define LEDC_TIMERS_H +ledc_timer::ledc_timer(ledc_mode_t mode, ledc_timer_bit_t duty_resolution, ledc_timer_bit_t bit_num, uint32_t freq, ledc_clk_cfg_t clk_cfg){ + timer_conf={ + .speed_mode = mode, + .timer_num = Timer::instance()->getTimer(mode), + .duty_resolution = duty_resolution, + .bit_num = bit_num, + .freq_hz = freq, + .clk_cfg = clk_cfg + }; + return ledc_timer_config(*timer_conf); +} + +ledc_timer::~ledc_timer(void){ + ledc_timer_pause(timer_conf.speed_mode, timer_conf.timer_num); + Timer::instance()->freeTimer(timer_conf.speed_mode, timer_conf.timer_num); +} + +ledc_timer::setTimer(uint32_t clock_divider, uint32_t clock_div, uint32_t duty_resolution, ledc_clk_src_t clk_src){ + esp_err_t err=ledc_timer_set( + timer_conf.speed_mode, + timer_conf.timer_num, + clock_div, + duty_resolution, + clk_src + ); + if(err==ESP_OK){ + timer_conf={ + .duty_resolution = duty_resolution, + .clk_src = clk_src + }; + clock_divider=clock_div; + } + return err; +} + +ledc_timer::setTimerFrequency(uint32_t freq){ + esp_err_t err=ledc_set_freq( + timer_conf.speed_mode, + timer_conf.timer_num, + freq + ); + if(err==ESP_OK){ + timer_conf.freq_hz = freq; + } + return err; +} + +ledc_timer::timerReset(void){ + return ledc_timer_rst(timer_conf.speed_mode, timer_conf.timer_num); +} + +ledc_timer::timerPause(void){ + return ledc_timer_pause(timer_conf.speed_mode, timer_conf.timer_num); +} + +ledc_timer::timerResume(void){ + return ledc_timer_resume(timer_conf.speed_mode, timer_conf.timer_num); +} +/* +ledc_timer::timerBindChannel(ledc_channel_t channel){ + esp_err_t err=ledc_bind_channel_timer(timer_conf.speed_mode, channel, timer_conf.timer_num); + if(err==ESP_OK) +} +*/ +ledc_timer::getTimerNumber(void){ + return timer_conf.timer_num; +} + +ledc_timer::getTimerMode(void){ + return timer_conf.speed_mode; +} + +ledc_timer::getDutyResolution(void){ + return timer_conf.duty_resolution; +} + +getTimerFrequency(void){ + return timer_conf.freq_hz; +} + +ledc_timer::getClockDivider(void){ + return clock_divider; +} \ No newline at end of file From 9371b1ac2a875893db48d7d0f4223007790ab6ae Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Mon, 3 Apr 2023 20:35:36 +0200 Subject: [PATCH 19/28] some channels work --- Sming/Arch/Esp32/Core/ledc_channel.cpp | 65 ++++++++++++++++++++++++++ Sming/Arch/Esp32/Core/ledc_channel.h | 55 ++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 Sming/Arch/Esp32/Core/ledc_channel.cpp create mode 100644 Sming/Arch/Esp32/Core/ledc_channel.h diff --git a/Sming/Arch/Esp32/Core/ledc_channel.cpp b/Sming/Arch/Esp32/Core/ledc_channel.cpp new file mode 100644 index 0000000000..559263fa72 --- /dev/null +++ b/Sming/Arch/Esp32/Core/ledc_channel.cpp @@ -0,0 +1,65 @@ +#ifndef LEDC_CHANNEL_H +#include "ledc_channel.h" +ledc_channel::ledc_channel(ledc_speedmode_t mode, int gpio, ledc_timer_t timer, uint32_t duty, int hpoint){ + periph_module_enable(PERIPH_LEDC_MODULE); + channel_config={ + .speed_mode = mode, + .gpio_num = gpio, + .channel = Channel::instance()->getChannel(mode), + .intr_type = LEDC_INTR_DISABLE, + .timer_sel = timer, + .duty = duty, + .hpoint = hpoint + } + esp_err_t err=ledc_channel_config(&channel_config); + if(err==ESP_OK){ + return bindChannelTimer(timer); + } + return err; +} + +ledc_channel::ledc_channel(ledc_speedmode_t mode, ledc_timer_t timer, uint32_t duty){ + return ledc_channel(mode, timer, duty, (int) 0); +} + +ledc_channel::ledc_channel(ledc_speedmode_t mode, ledc_timer_t timer){ + return ledc_channel(mode, timer, (uint32_t) 0, (int) 0); +} + +ledc_channel::ledc_channel(ledc_speedmode_t mode){ + return ledc_channel(mode, Timer::instance()->getTimer(mode), (uint32_t) 0, (int) 0); +} +ledc_channel::~ledc_channel(){ + stop((uint32_t)0); +} + +ledc_channel::channelConfig(const ledc_channel_config_t *ledc_conf){ + +} + +ledc_channel::updateDuty(){ + +} +ledc_channel::setPin(int gpio_num){ + +} + +ledc_channel::stop(uint32_t idle_level){ + +} + +ledc_channel::setFreq(uint32_t freq){ + +} + +ledc_channel::setDutyWithHpoint(uint32_t duty, uint32_t hpoint){ + +} + +ledc_channel::setDuty(uint32_t duty){ + +} + +ledc_channel::bindChannelTimer(ledc_timer_t timer){ + +} \ No newline at end of file diff --git a/Sming/Arch/Esp32/Core/ledc_channel.h b/Sming/Arch/Esp32/Core/ledc_channel.h new file mode 100644 index 0000000000..11ffa2ff2c --- /dev/null +++ b/Sming/Arch/Esp32/Core/ledc_channel.h @@ -0,0 +1,55 @@ +#ifndef LEDC_CHANNEL_H +#define LEDC_CHANNEL_H +#include +#include "driver/ledc.h" +#include "esp_err.h" +#include "singleton.h" +#include + +#ifndef LEDC_TIMERS_H +#import "ledc_timers.h" +#endif + +class ledc_channel{ + public: + esp_err_t ledc_channel(ledc_speedmode_t mode, int gpio, ledc_timer_t timer, uint32_t duty, int hpoint); + esp_err_t ledc_channel(ledc_speedmode_t mode, ledc_timer_t timer, uint32_t duty); + esp_err_t ledc_channel(ledc_speedmode_t mode, ledc_timer_t timer); + esp_err_t ledc_channel(ledc_speedmode_t mode); + ~ledc_channel(); + + esp_err_t channelConfig(const ledc_channel_config_t *ledc_conf); + esp_err_t updateDuty(); + esp_err_t setPin(int gpio_num); + esp_err_t stop(){ + return stop(0); + }; + esp_err_t stop(uint32_t idle_level); + esp_err_t setFreq(uint32_t freq); + uint32_t getFreq(){ + return channel_config.freq_hz; + }; + esp_err_t setDutyWithHpoint(uint32_t duty, uint32_t hpoint); + esp_err_t setDuty(uint32_t duty); + int getHpoint(){ + return channel_config.hpoint; + }; + uint32_t getDuty(){ + return channel_config.duty; + }; + esp_err_t bindChannelTimer(ledc_timer_t timer); + /* omit fades for now... + esp_err_t setFade(uint32_t duty, ledc_duty_direction_t fade_direction, uint32_t step_num, uint32_t duty_cycle_num, uint32_t duty_scale); + esp_err_t setFadeWithStep(uint32_t target_duty, uint32_t scale, uint32_t cycle_num); + esp_err_t setFadeWithTime(uint32_t target_duty, int max_fade_time_ms); + esp_err_t fadeStart(ledc_fade_mode_t fade_mode) + esp_err_t fadeFuncInstall(int intr_alloc_flags); + esp_err_t fadeFuncUninstall(); + esp_err_t ledc_isr_register(void (*fn)(void *), void *arg, int intr_alloc_flags, ledc_isr_handle_t *handle ); + */ + private: + ledc_channel_config_t channel_config; + +}; + +#endif \ No newline at end of file From 7cca2669c73b977f00dcdd29590aa9334329dd58 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Mon, 3 Apr 2023 20:37:29 +0200 Subject: [PATCH 20/28] some channels work --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 3 +++ Sming/Arch/Esp32/Core/ledc_timers.h | 6 +++++- Sming/Arch/Esp32/Core/ledc_timers_cpp | 2 +- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index 94ad6f2a8f..99dbf071d8 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -163,6 +163,9 @@ namespace{ uint32_t periodToFrequency(uint32_t period); uint32_t frequencyToPeriod(uint32_t freq); uint32_t maxDuty(ledc_timer_bit_t bits); + + //ledc_channel_t getChannel(ledc_mode_t); + //ledc_timer_t getTimer(ledc_mode_t); } //namespace #define DEFAULT_RESOLUTION static_cast(10) diff --git a/Sming/Arch/Esp32/Core/ledc_timers.h b/Sming/Arch/Esp32/Core/ledc_timers.h index 5159df5bf1..76294209ba 100644 --- a/Sming/Arch/Esp32/Core/ledc_timers.h +++ b/Sming/Arch/Esp32/Core/ledc_timers.h @@ -6,6 +6,10 @@ #include "singleton.h" #include +#ifndef LEDC_CHANNEL_H +#import "ledc_channel.h" +#endif + class ledc_timer { public: esp_err_t ledc_timer(ledc_mode_t mode, ledc_timer_bit_t duty_resolution, ledc_timer_bit_t bit_num, uint32_t freq, ledc_clk_cfg_t clk_cfg); @@ -27,6 +31,6 @@ class ledc_timer { ledc_timer_config_t timer_conf; ledc_timer_t timer; uint32_t clock_divider; -} +}; #endif \ No newline at end of file diff --git a/Sming/Arch/Esp32/Core/ledc_timers_cpp b/Sming/Arch/Esp32/Core/ledc_timers_cpp index 62472df9df..05813d89d1 100644 --- a/Sming/Arch/Esp32/Core/ledc_timers_cpp +++ b/Sming/Arch/Esp32/Core/ledc_timers_cpp @@ -10,7 +10,7 @@ ledc_timer::ledc_timer(ledc_mode_t mode, ledc_timer_bit_t duty_resolution, ledc_ .freq_hz = freq, .clk_cfg = clk_cfg }; - return ledc_timer_config(*timer_conf); + return ledc_timer_config(&timer_conf); } ledc_timer::~ledc_timer(void){ From 1025cd83d2f4aed1d996850ebbc08068af2ff4a5 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Tue, 4 Apr 2023 07:44:07 +0200 Subject: [PATCH 21/28] more channel work --- Sming/Arch/Esp32/Core/ledc_channel.cpp | 37 +++++++++++++++++--------- 1 file changed, 25 insertions(+), 12 deletions(-) diff --git a/Sming/Arch/Esp32/Core/ledc_channel.cpp b/Sming/Arch/Esp32/Core/ledc_channel.cpp index 559263fa72..42b9a66e3f 100644 --- a/Sming/Arch/Esp32/Core/ledc_channel.cpp +++ b/Sming/Arch/Esp32/Core/ledc_channel.cpp @@ -32,34 +32,47 @@ ledc_channel::ledc_channel(ledc_speedmode_t mode){ ledc_channel::~ledc_channel(){ stop((uint32_t)0); } - +/* ledc_channel::channelConfig(const ledc_channel_config_t *ledc_conf){ } - +*/ ledc_channel::updateDuty(){ - + return ledc_update_duty(); } -ledc_channel::setPin(int gpio_num){ +ledc_channel::setPin(int gpio_num){ + esp_err_t err = ledc_set_pin(gpio_num, channel_config.mode, channel_config.channel); + if(esp_err==ESP_OK){ + channel_config.gpio_num=gpio_num; + } + return err; } ledc_channel::stop(uint32_t idle_level){ - -} - -ledc_channel::setFreq(uint32_t freq){ - + return ledc_stop(channel_config.mode, channel_config.channel, idle_level); } ledc_channel::setDutyWithHpoint(uint32_t duty, uint32_t hpoint){ - + esp_err_t err = ledc_set_duty_with_hpoint(channel_config.mode, chanel_config.channel, duty, hpoint); + if(err==ESP_OK){ + channel_config.duty=duty; + channel_config.hpoint=hpoint; + } + return err; } ledc_channel::setDuty(uint32_t duty){ - + esp_err_t err=ledc_set_duty(channel_config.mode, channel_config.channel, duty); + if(err==ESP_OK){ + channel_config.duty=duty; + } + return err; } ledc_channel::bindChannelTimer(ledc_timer_t timer){ - + esp_err_t err = ledc_bind_channel_timer(channel_config.mode, channel_config.channel, timer); + if(err==ESP_OK){ + channel_config.timer=timer; + } } \ No newline at end of file From 5b70f33fa03bec760928b2ddeaada22e62c8073b Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Tue, 4 Apr 2023 08:56:02 +0200 Subject: [PATCH 22/28] starting to move HardwarePWM to the new channel/timer architecture --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 71 +++++++------------ Sming/Arch/Esp32/Core/ledc_channel.cpp | 1 + Sming/Arch/Esp32/Core/ledc_channel.h | 8 +-- .../Core/{ledc_timers_cpp => ledc_timer.cpp} | 6 +- .../Core/{ledc_timers.h => ledc_timer.h} | 4 +- Sming/Arch/Esp32/Core/singelton.cpp | 10 +++ Sming/Arch/Esp32/Core/singleton.h | 8 +++ 7 files changed, 55 insertions(+), 53 deletions(-) rename Sming/Arch/Esp32/Core/{ledc_timers_cpp => ledc_timer.cpp} (96%) rename Sming/Arch/Esp32/Core/{ledc_timers.h => ledc_timer.h} (95%) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index 99dbf071d8..d7af8be93a 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -155,6 +155,8 @@ #include #include #include "./singleton.h" +#include "ledc_channel.h" +#include "ledc_timers.h" namespace{ ledc_mode_t pinToGroup(uint8_t pin); @@ -173,57 +175,38 @@ namespace{ 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; + //ledc_timer_config_t ledc_timer; + //ledc_channel_config_t ledc_channel; + ledc_speedmode_t mode; + if(SOC_LEDC_SUPPORT_HS_MODE){ + mode=LEDC_HIGH_SPEED_MODE; + }else{ + mode=LEDC_LOW_SPEED_MODE; + } + debug_d("starting HardwarePWM init"); periph_module_enable(PERIPH_LEDC_MODULE); if((no_of_pins == 0) || (no_of_pins > SOC_LEDC_CHANNEL_NUM)) { - return; + return PWM_BAD_CHANNEL; } + - 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)); + if(Channel::instance()=>getFreeChannels(mode)getFreeChannels(mode) #ifndef LEDC_TIMERS_H -#import "ledc_timers.h" +#import "ledc_timer.h" #endif class ledc_channel{ @@ -18,15 +18,15 @@ class ledc_channel{ esp_err_t ledc_channel(ledc_speedmode_t mode); ~ledc_channel(); - esp_err_t channelConfig(const ledc_channel_config_t *ledc_conf); + //esp_err_t channelConfig(const ledc_channel_config_t *ledc_conf); esp_err_t updateDuty(); esp_err_t setPin(int gpio_num); esp_err_t stop(){ return stop(0); }; esp_err_t stop(uint32_t idle_level); - esp_err_t setFreq(uint32_t freq); - uint32_t getFreq(){ + //esp_err_t setFreq(uint32_t freq); + //uint32_t getFreq(){ return channel_config.freq_hz; }; esp_err_t setDutyWithHpoint(uint32_t duty, uint32_t hpoint); diff --git a/Sming/Arch/Esp32/Core/ledc_timers_cpp b/Sming/Arch/Esp32/Core/ledc_timer.cpp similarity index 96% rename from Sming/Arch/Esp32/Core/ledc_timers_cpp rename to Sming/Arch/Esp32/Core/ledc_timer.cpp index 05813d89d1..f7f9dd995f 100644 --- a/Sming/Arch/Esp32/Core/ledc_timers_cpp +++ b/Sming/Arch/Esp32/Core/ledc_timer.cpp @@ -1,6 +1,6 @@ -#ifndef LEDC_TIMERS_H -#include "ledc_timers.h" -#define LEDC_TIMERS_H +#ifndef LEDC_TIMER_H +#include "ledc_timer.h" +#define LEDC_TIMER_H ledc_timer::ledc_timer(ledc_mode_t mode, ledc_timer_bit_t duty_resolution, ledc_timer_bit_t bit_num, uint32_t freq, ledc_clk_cfg_t clk_cfg){ timer_conf={ .speed_mode = mode, diff --git a/Sming/Arch/Esp32/Core/ledc_timers.h b/Sming/Arch/Esp32/Core/ledc_timer.h similarity index 95% rename from Sming/Arch/Esp32/Core/ledc_timers.h rename to Sming/Arch/Esp32/Core/ledc_timer.h index 76294209ba..9b8c225292 100644 --- a/Sming/Arch/Esp32/Core/ledc_timers.h +++ b/Sming/Arch/Esp32/Core/ledc_timer.h @@ -1,5 +1,5 @@ -#ifndef LEDC_TIMERS_H -#define LEDC_TIMERS_H +#ifndef LEDC_TIMER_H +#define LEDC_TIMER_H #include #include "driver/ledc.h" #include "esp_err.h" diff --git a/Sming/Arch/Esp32/Core/singelton.cpp b/Sming/Arch/Esp32/Core/singelton.cpp index 67749a2e0e..e5a7edfe79 100644 --- a/Sming/Arch/Esp32/Core/singelton.cpp +++ b/Sming/Arch/Esp32/Core/singelton.cpp @@ -53,6 +53,16 @@ void Channel::freeChannel(ledc_mode_t mode, ledc_channel_t channel) isUsed[(uint8_t)mode][(uint8_t)channel] = false; } +uint8_t getFreeChannels(ledc_speedmode_t mode){ + uint8_t count=0; + for(uit8_t i=0 i<(uint8_t)LEDC_CHANNEL_MAX;i++){ + if(!isUsed[(uint8_t)mode][i]){ + count++; + } + return count; + } +} + Channel::Channel() { for(int i = 0; i < LEDC_CHANNEL_MAX; i++) { diff --git a/Sming/Arch/Esp32/Core/singleton.h b/Sming/Arch/Esp32/Core/singleton.h index 3f55dadaf6..d7010a9953 100644 --- a/Sming/Arch/Esp32/Core/singleton.h +++ b/Sming/Arch/Esp32/Core/singleton.h @@ -404,6 +404,14 @@ class Channel : public Singleton freeChannel((ledc_mode_t)mode, (ledc_channel_t)channel); }; + /** + * @brief return number of free channels in mode + * @param const int + * @note this is in no way thread safe. + * @retval number of unassigned channels + */ + uint8_t getFreeChannels(ledc_speedmode_t mode); + protected: Channel(); bool isUsed[LEDC_SPEED_MODE_MAX][LEDC_CHANNEL_MAX]; From 4302970e053feb877d89b432ff104be826900dcd Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Tue, 4 Apr 2023 10:03:42 +0200 Subject: [PATCH 23/28] changes to make it build. removed the namespace{} for now --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 33 +++---- Sming/Arch/Esp32/Core/ledc_channel.cpp | 14 +-- Sming/Arch/Esp32/Core/ledc_channel.h | 14 ++- Sming/Arch/Esp32/Core/ledc_timer.cpp | 2 + Sming/Arch/Esp32/Core/ledc_timer.h | 6 +- Sming/Arch/Esp32/Core/singelton.cpp | 18 ++-- Sming/Arch/Esp32/Core/singleton.h | 114 +++++++++++++------------ 7 files changed, 99 insertions(+), 102 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index d7af8be93a..06fb57d0d2 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -156,7 +156,7 @@ #include #include "./singleton.h" #include "ledc_channel.h" -#include "ledc_timers.h" +#include "ledc_timer.h" namespace{ ledc_mode_t pinToGroup(uint8_t pin); @@ -177,12 +177,12 @@ 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; - ledc_speedmode_t mode; - if(SOC_LEDC_SUPPORT_HS_MODE){ - mode=LEDC_HIGH_SPEED_MODE; - }else{ - mode=LEDC_LOW_SPEED_MODE; - } + ledc_mode_t mode; + #ifdef LEDC_HIGH_SPEED_MODE + mode=LEDC_HIGH_SPEED_MODE + #else + mode=LEDC_LOW_SPEED_MODE + #endif debug_d("starting HardwarePWM init"); periph_module_enable(PERIPH_LEDC_MODULE); @@ -191,16 +191,19 @@ HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_o return PWM_BAD_CHANNEL; } - - if(Channel::instance()=>getFreeChannels(mode)getFreeChannels(mode)getFreeChannels(mode)getFreeChannels(mode)getTimer(mode), (uint32_t) 0, (int) 0); } ledc_channel::~ledc_channel(){ @@ -76,4 +75,5 @@ ledc_channel::bindChannelTimer(ledc_timer_t timer){ channel_config.timer=timer; } -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/Sming/Arch/Esp32/Core/ledc_channel.h b/Sming/Arch/Esp32/Core/ledc_channel.h index 2210ff34e1..fb53febebf 100644 --- a/Sming/Arch/Esp32/Core/ledc_channel.h +++ b/Sming/Arch/Esp32/Core/ledc_channel.h @@ -7,15 +7,15 @@ #include #ifndef LEDC_TIMERS_H -#import "ledc_timer.h" +#include "ledc_timer.h" #endif class ledc_channel{ public: - esp_err_t ledc_channel(ledc_speedmode_t mode, int gpio, ledc_timer_t timer, uint32_t duty, int hpoint); - esp_err_t ledc_channel(ledc_speedmode_t mode, ledc_timer_t timer, uint32_t duty); - esp_err_t ledc_channel(ledc_speedmode_t mode, ledc_timer_t timer); - esp_err_t ledc_channel(ledc_speedmode_t mode); + ledc_channel(ledc_mode_t mode, int gpio, ledc_timer_t timer, uint32_t duty, int hpoint); + ledc_channel(ledc_mode_t mode, ledc_timer_t timer, uint32_t duty); + ledc_channel(ledc_mode_t mode, ledc_timer_t timer); + ledc_channel(ledc_mode_t mode); ~ledc_channel(); //esp_err_t channelConfig(const ledc_channel_config_t *ledc_conf); @@ -25,10 +25,6 @@ class ledc_channel{ return stop(0); }; esp_err_t stop(uint32_t idle_level); - //esp_err_t setFreq(uint32_t freq); - //uint32_t getFreq(){ - return channel_config.freq_hz; - }; esp_err_t setDutyWithHpoint(uint32_t duty, uint32_t hpoint); esp_err_t setDuty(uint32_t duty); int getHpoint(){ diff --git a/Sming/Arch/Esp32/Core/ledc_timer.cpp b/Sming/Arch/Esp32/Core/ledc_timer.cpp index f7f9dd995f..be4277c56a 100644 --- a/Sming/Arch/Esp32/Core/ledc_timer.cpp +++ b/Sming/Arch/Esp32/Core/ledc_timer.cpp @@ -1,6 +1,8 @@ #ifndef LEDC_TIMER_H #include "ledc_timer.h" #define LEDC_TIMER_H +#endif + ledc_timer::ledc_timer(ledc_mode_t mode, ledc_timer_bit_t duty_resolution, ledc_timer_bit_t bit_num, uint32_t freq, ledc_clk_cfg_t clk_cfg){ timer_conf={ .speed_mode = mode, diff --git a/Sming/Arch/Esp32/Core/ledc_timer.h b/Sming/Arch/Esp32/Core/ledc_timer.h index 9b8c225292..d4ccd9c026 100644 --- a/Sming/Arch/Esp32/Core/ledc_timer.h +++ b/Sming/Arch/Esp32/Core/ledc_timer.h @@ -7,13 +7,13 @@ #include #ifndef LEDC_CHANNEL_H -#import "ledc_channel.h" +#include "ledc_channel.h" #endif class ledc_timer { public: - esp_err_t ledc_timer(ledc_mode_t mode, ledc_timer_bit_t duty_resolution, ledc_timer_bit_t bit_num, uint32_t freq, ledc_clk_cfg_t clk_cfg); - esp_err_t ~ledc_timer(void); + ledc_timer(ledc_mode_t mode, ledc_timer_bit_t duty_resolution, ledc_timer_bit_t bit_num, uint32_t freq, ledc_clk_cfg_t clk_cfg); + ~ledc_timer(void); esp_err_t setTimer(uint32_t clock_divider, uint32_t duty_resolution, ledc_clk_src_t clk_src); esp_err_t setTimerFrequency(uint32_t freq); esp_err_t timerReset(void); diff --git a/Sming/Arch/Esp32/Core/singelton.cpp b/Sming/Arch/Esp32/Core/singelton.cpp index e5a7edfe79..f747a28422 100644 --- a/Sming/Arch/Esp32/Core/singelton.cpp +++ b/Sming/Arch/Esp32/Core/singelton.cpp @@ -1,10 +1,5 @@ #include "./singleton.h" -/** - * @namespace HardwarePWM - stuffing away my HardwarePWM specific functions in a separate Namespace - * -*/ -namespace HardwarePWM -{ + /** * @brief get a pwm timer * @@ -53,23 +48,22 @@ void Channel::freeChannel(ledc_mode_t mode, ledc_channel_t channel) isUsed[(uint8_t)mode][(uint8_t)channel] = false; } -uint8_t getFreeChannels(ledc_speedmode_t mode){ +uint8_t Channel::getFreeChannels(ledc_mode_t mode){ uint8_t count=0; - for(uit8_t i=0 i<(uint8_t)LEDC_CHANNEL_MAX;i++){ + for(uint8_t i=0; i<(uint8_t)LEDC_CHANNEL_MAX;i++){ if(!isUsed[(uint8_t)mode][i]){ count++; } - return count; } + return count; } Channel::Channel() { for(int i = 0; i < LEDC_CHANNEL_MAX; i++) { - isUsed[0][i] = false; + isUsed[LEDC_LOW_SPEED_MODE][i] = false; #ifdef SOC_LEDC_SUPPORT_HS_MODE - isUsed[1][i] = false; + isUsed[LEDC_HIGH_SPEED_MODE][i] = false; #endif } } -} // end namespace HardwarePWM \ No newline at end of file diff --git a/Sming/Arch/Esp32/Core/singleton.h b/Sming/Arch/Esp32/Core/singleton.h index d7010a9953..487c77b70d 100644 --- a/Sming/Arch/Esp32/Core/singleton.h +++ b/Sming/Arch/Esp32/Core/singleton.h @@ -1,11 +1,9 @@ +#pragma once #include -#ifndef LEDC_TYPES_H_ #include -#endif #define PWM_MAX_TIMER 8 -namespace HardwarePWM -{ + template class Singleton { public: @@ -60,7 +58,7 @@ class Timer : public Singleton { return getTimer(LEDC_LOW_SPEED_MODE); }; - + #ifdef LEDC_HIGH_SPEED_MODE /** * @brief get a high speed timer * @param none @@ -73,6 +71,32 @@ class Timer : public Singleton return getTimer(LEDC_HIGH_SPEED_MODE); }; + + /** + * @brief free a high speed timer + * @param ledc_timer_t + * @note using this to free a low speed mode timer will lead to unexpected effects + * because the seemingly freed timer might be re-used and thus re-configured later on + * @retval none + */ + void freeHSTimer(ledc_timer_t timer) + { + freeTimer(LEDC_HIGH_SPEED_MODE, timer); + }; + + /** + * @brief free a high speed timer + * @param const int + * @note using this to free a low speed mode timer will lead to unexpected effects + * because the seemingly freed timer might be re-used and thus re-configured later on + * @retval none + */ + void freeHSTimer(const int timer) + { + freeTimer(LEDC_HIGH_SPEED_MODE, timer); + }; + #endif + /** * @brief get a timer of specified speed mode * @param ledc_mode_t (LEDC_LOW_SPEED_MODE or LEDC_HIGH_SPEED_MODE) @@ -146,30 +170,6 @@ class Timer : public Singleton freeTimer(LEDC_LOW_SPEED_MODE, (ledc_timer_t)timer); }; - /** - * @brief free a high speed timer - * @param ledc_timer_t - * @note using this to free a low speed mode timer will lead to unexpected effects - * because the seemingly freed timer might be re-used and thus re-configured later on - * @retval none - */ - void freeHSTimer(ledc_timer_t timer) - { - freeTimer(LEDC_HIGH_SPEED_MODE, timer); - }; - - /** - * @brief free a high speed timer - * @param const int - * @note using this to free a low speed mode timer will lead to unexpected effects - * because the seemingly freed timer might be re-used and thus re-configured later on - * @retval none - */ - void freeHSTimer(const int timer) - { - freeTimer(LEDC_HIGH_SPEED_MODE, timer); - }; - /** * @brief free a timer * @param ledc_timer_t @@ -250,6 +250,7 @@ class Channel : public Singleton return getChannel(LEDC_LOW_SPEED_MODE); }; + #ifdef LEDC_HIGH_SPEED_MODE /** * @brief get a high speed channel * @param none @@ -262,9 +263,35 @@ class Channel : public Singleton return getChannel(LEDC_HIGH_SPEED_MODE); }; + /** + * @brief free a high speed channel + * @param ledc_channel_t + * @note using this to free a low speed channel will lead to unexpected effects + * because the seemingly freed channel might be re-used and thus re-configured later on + * @retval none + */ + void freeHSChannel(ledc_channel_t channel) + { + freeChannel(LEDC_HIGH_SPEED_MODE, channel); + }; + + /** + * @brief free a high speed channel + * @param const int + * @note using this to free a low speed channel will lead to unexpected effects + * because the seemingly freed channel might be re-used and thus re-configured later on + * @retval none + */ + void freeHSChannel(const int channel) + { + freeChannel(LEDC_HIGH_SPEED_MODE, (ledc_channel_t)channel); + }; + + #endif + /** * @brief get a channel with a defined speed mode - * @param ledc_speedmode_t mode + * @param ledc_mode_t mode * @note you are not guaranteed that the SoC has high speed channels * also, you are required to keep track of the speed mode of the channels yourself. * @retval ledc_channel_t , @@ -335,30 +362,6 @@ class Channel : public Singleton freeChannel(LEDC_LOW_SPEED_MODE, (ledc_channel_t)channel); }; - /** - * @brief free a high speed channel - * @param ledc_channel_t - * @note using this to free a low speed channel will lead to unexpected effects - * because the seemingly freed channel might be re-used and thus re-configured later on - * @retval none - */ - void freeHSChannel(ledc_channel_t channel) - { - freeChannel(LEDC_HIGH_SPEED_MODE, channel); - }; - - /** - * @brief free a high speed channel - * @param const int - * @note using this to free a low speed channel will lead to unexpected effects - * because the seemingly freed channel might be re-used and thus re-configured later on - * @retval none - */ - void freeHSChannel(const int channel) - { - freeChannel(LEDC_HIGH_SPEED_MODE, (ledc_channel_t)channel); - }; - /** * @brief free a channel * @param ledc_mode_t @@ -410,10 +413,9 @@ class Channel : public Singleton * @note this is in no way thread safe. * @retval number of unassigned channels */ - uint8_t getFreeChannels(ledc_speedmode_t mode); + uint8_t getFreeChannels(ledc_mode_t mode); protected: Channel(); bool isUsed[LEDC_SPEED_MODE_MAX][LEDC_CHANNEL_MAX]; }; -} //end namespace HardwarePWM \ No newline at end of file From a554b005cf8fe667d5e7b100be4678768a5dff76 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Wed, 5 Apr 2023 21:59:56 +0200 Subject: [PATCH 24/28] more fixes --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 8 ++--- Sming/Arch/Esp32/Core/ledc_channel.cpp | 30 ++++++++-------- Sming/Arch/Esp32/Core/ledc_channel.h | 6 ++-- Sming/Arch/Esp32/Core/ledc_timer.cpp | 48 ++++++++++++-------------- Sming/Arch/Esp32/Core/ledc_timer.h | 10 +++--- 5 files changed, 48 insertions(+), 54 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index 06fb57d0d2..290b871839 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -145,13 +145,11 @@ * */ -// orig #include -// orig #include "ESP8266EX.h" + #include //#include #include "driver/ledc.h" #include "esp_err.h" -//#include "hal/ledc_types.h" #include #include #include "./singleton.h" @@ -179,9 +177,9 @@ HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_o //ledc_channel_config_t ledc_channel; ledc_mode_t mode; #ifdef LEDC_HIGH_SPEED_MODE - mode=LEDC_HIGH_SPEED_MODE + mode=LEDC_HIGH_SPEED_MODE; #else - mode=LEDC_LOW_SPEED_MODE + mode=LEDC_LOW_SPEED_MODE; #endif debug_d("starting HardwarePWM init"); diff --git a/Sming/Arch/Esp32/Core/ledc_channel.cpp b/Sming/Arch/Esp32/Core/ledc_channel.cpp index 5e27517d2f..e4531b2ee0 100644 --- a/Sming/Arch/Esp32/Core/ledc_channel.cpp +++ b/Sming/Arch/Esp32/Core/ledc_channel.cpp @@ -2,32 +2,32 @@ #include "ledc_channel.h" ledc_channel::ledc_channel(ledc_mode_t mode, int gpio, ledc_timer_t timer, uint32_t duty, int hpoint){ periph_module_enable(PERIPH_LEDC_MODULE); - channel_config={ - .speed_mode = mode, - .gpio_num = gpio, - .channel = Channel::instance()->getChannel(mode), - .intr_type = LEDC_INTR_DISABLE, - .timer_sel = timer, - .duty = duty, - .hpoint = hpoint - } + channel_config.speed_mode = mode; + channel_config.gpio_num = gpio; + channel_config.channel = Channel::instance()->getChannel(mode); + channel_config.intr_type = LEDC_INTR_DISABLE; + channel_config.timer_sel = timer; + channel_config.duty = duty; + channel_config.hpoint = hpoint; + esp_err_t err=ledc_channel_config(&channel_config); if(err==ESP_OK){ bindChannelTimer(timer); } } -ledc_channel::ledc_channel(ledc_mode_t mode, ledc_timer_t timer, uint32_t duty){ - return ledc_channel(mode, timer, duty, (int) 0); +ledc_channel::ledc_channel(ledc_mode_t mode, int gpio, ledc_timer_t timer, uint32_t duty){ + ledc_channel(mode, gpio, timer, duty, (int) 0); } -ledc_channel::ledc_channel(ledc_mode_t mode, ledc_timer_t timer){ - return ledc_channel(mode, timer, (uint32_t) 0, (int) 0); +ledc_channel::ledc_channel(ledc_mode_t mode, int gpio, ledc_timer_t timer){ + return ledc_channel(mode, gpio, timer, (uint32_t) 0, (int) 0); } -ledc_channel::ledc_channel(ledc_mode_t mode){ - return ledc_channel(mode, Timer::instance()->getTimer(mode), (uint32_t) 0, (int) 0); +ledc_channel::ledc_channel(ledc_mode_t mode, int gpio){ + return ledc_channel(mode, gpio, Timer::instance()->getTimer(mode), (uint32_t) 0, (int) 0); } + ledc_channel::~ledc_channel(){ stop((uint32_t)0); } diff --git a/Sming/Arch/Esp32/Core/ledc_channel.h b/Sming/Arch/Esp32/Core/ledc_channel.h index fb53febebf..65aff97003 100644 --- a/Sming/Arch/Esp32/Core/ledc_channel.h +++ b/Sming/Arch/Esp32/Core/ledc_channel.h @@ -13,9 +13,9 @@ class ledc_channel{ public: ledc_channel(ledc_mode_t mode, int gpio, ledc_timer_t timer, uint32_t duty, int hpoint); - ledc_channel(ledc_mode_t mode, ledc_timer_t timer, uint32_t duty); - ledc_channel(ledc_mode_t mode, ledc_timer_t timer); - ledc_channel(ledc_mode_t mode); + ledc_channel(ledc_mode_t mode, int gpio, ledc_timer_t timer, uint32_t duty); + ledc_channel(ledc_mode_t mode, int gpio, ledc_timer_t timer); + ledc_channel(ledc_mode_t mode, int gpio); ~ledc_channel(); //esp_err_t channelConfig(const ledc_channel_config_t *ledc_conf); diff --git a/Sming/Arch/Esp32/Core/ledc_timer.cpp b/Sming/Arch/Esp32/Core/ledc_timer.cpp index be4277c56a..fec5848d00 100644 --- a/Sming/Arch/Esp32/Core/ledc_timer.cpp +++ b/Sming/Arch/Esp32/Core/ledc_timer.cpp @@ -3,16 +3,14 @@ #define LEDC_TIMER_H #endif -ledc_timer::ledc_timer(ledc_mode_t mode, ledc_timer_bit_t duty_resolution, ledc_timer_bit_t bit_num, uint32_t freq, ledc_clk_cfg_t clk_cfg){ - timer_conf={ - .speed_mode = mode, - .timer_num = Timer::instance()->getTimer(mode), - .duty_resolution = duty_resolution, - .bit_num = bit_num, - .freq_hz = freq, - .clk_cfg = clk_cfg - }; - return ledc_timer_config(&timer_conf); +ledc_timer::ledc_timer(ledc_mode_t mode, ledc_timer_bit_t duty_resolution, uint32_t freq, ledc_clk_cfg_t clk_cfg){ + timer_conf.speed_mode = mode; + timer_conf.timer_num = Timer::instance()->getTimer(mode); + timer_conf.duty_resolution = duty_resolution; + timer_conf.freq_hz = freq; + timer_conf.clk_cfg = clk_cfg; + + ledc_timer_config(&timer_conf); } ledc_timer::~ledc_timer(void){ @@ -20,25 +18,23 @@ ledc_timer::~ledc_timer(void){ Timer::instance()->freeTimer(timer_conf.speed_mode, timer_conf.timer_num); } -ledc_timer::setTimer(uint32_t clock_divider, uint32_t clock_div, uint32_t duty_resolution, ledc_clk_src_t clk_src){ +esp_err_t ledc_timer::setTimer(uint32_t clock_divider, ledc_timer_bit_t duty_resolution, ledc_clk_cfg_t clk_cfg){ esp_err_t err=ledc_timer_set( timer_conf.speed_mode, timer_conf.timer_num, - clock_div, + clock_divider, duty_resolution, - clk_src + clk_cfg ); if(err==ESP_OK){ - timer_conf={ - .duty_resolution = duty_resolution, - .clk_src = clk_src - }; + timer_conf.duty_resolution = duty_resolution; + timer_conf.clk_cfg = clk_src; clock_divider=clock_div; } return err; } -ledc_timer::setTimerFrequency(uint32_t freq){ +esp_err_t ledc_timer::setTimerFrequency(uint32_t freq){ esp_err_t err=ledc_set_freq( timer_conf.speed_mode, timer_conf.timer_num, @@ -50,15 +46,15 @@ ledc_timer::setTimerFrequency(uint32_t freq){ return err; } -ledc_timer::timerReset(void){ +esp_err_t ledc_timer::timerReset(void){ return ledc_timer_rst(timer_conf.speed_mode, timer_conf.timer_num); } -ledc_timer::timerPause(void){ +esp_err_t ledc_timer::timerPause(void){ return ledc_timer_pause(timer_conf.speed_mode, timer_conf.timer_num); } -ledc_timer::timerResume(void){ +esp_err_t ledc_timer::timerResume(void){ return ledc_timer_resume(timer_conf.speed_mode, timer_conf.timer_num); } /* @@ -67,22 +63,22 @@ ledc_timer::timerBindChannel(ledc_channel_t channel){ if(err==ESP_OK) } */ -ledc_timer::getTimerNumber(void){ +uint32_t ledc_timer::getTimerNumber(void){ return timer_conf.timer_num; } -ledc_timer::getTimerMode(void){ +ledc_mode_t ledc_timer::getTimerMode(void){ return timer_conf.speed_mode; } -ledc_timer::getDutyResolution(void){ +ledc_timer_bit_t ledc_timer::getDutyResolution(void){ return timer_conf.duty_resolution; } -getTimerFrequency(void){ +uint32_t getTimerFrequency(void){ return timer_conf.freq_hz; } -ledc_timer::getClockDivider(void){ +uint32_t ledc_timer::getClockDivider(void){ return clock_divider; } \ No newline at end of file diff --git a/Sming/Arch/Esp32/Core/ledc_timer.h b/Sming/Arch/Esp32/Core/ledc_timer.h index d4ccd9c026..9117f29ab6 100644 --- a/Sming/Arch/Esp32/Core/ledc_timer.h +++ b/Sming/Arch/Esp32/Core/ledc_timer.h @@ -12,9 +12,9 @@ class ledc_timer { public: - ledc_timer(ledc_mode_t mode, ledc_timer_bit_t duty_resolution, ledc_timer_bit_t bit_num, uint32_t freq, ledc_clk_cfg_t clk_cfg); + ledc_timer(ledc_mode_t mode, ledc_timer_bit_t duty_resolution, uint32_t freq, ledc_clk_cfg_t clk_cfg); ~ledc_timer(void); - esp_err_t setTimer(uint32_t clock_divider, uint32_t duty_resolution, ledc_clk_src_t clk_src); + esp_err_t setTimer(uint32_t clock_divider, ledc_timer_bit_t duty_resolution, ledc_clk_cfg_t clk_cfg); esp_err_t setTimerFrequency(uint32_t freq); esp_err_t timerReset(void); esp_err_t timerPause(void); @@ -28,9 +28,9 @@ class ledc_timer { uint32_t getTimerFrequency(void); uint32_t getClockDivider(void); private: - ledc_timer_config_t timer_conf; - ledc_timer_t timer; - uint32_t clock_divider; + ledc_timer_config_t timer_conf; + ledc_timer_t timer; + uint32_t clock_divider; }; #endif \ No newline at end of file From 5195abcd7c0dac3ef33c8771e2ecb3052cc47497 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Wed, 5 Apr 2023 23:19:02 +0200 Subject: [PATCH 25/28] rewrote the HardwarePWM constructor to use the new ledc_timer/ledc_channel classes which in turn use the channel/timer singleton classes that marshal access to the hardware instances. The actual member functions have not been rewritten yet and still use the esp ledc_ interface (and thus will fail). --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 25 ++++++++++--------- Sming/Arch/Esp32/Core/ledc_channel.cpp | 34 +++++++++++++------------- Sming/Arch/Esp32/Core/ledc_channel.h | 2 +- Sming/Arch/Esp32/Core/ledc_timer.cpp | 12 ++++----- Sming/Arch/Esp32/Core/ledc_timer.h | 4 +-- 5 files changed, 39 insertions(+), 38 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index 290b871839..169e6f6a7d 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -152,7 +152,7 @@ #include "esp_err.h" #include #include -#include "./singleton.h" +#include "singleton.h" #include "ledc_channel.h" #include "ledc_timer.h" @@ -169,7 +169,8 @@ namespace{ } //namespace #define DEFAULT_RESOLUTION static_cast(10) -#define DEFAULT_PERIOD 200 +#define DEFAULT_FREQ 400000 +#define DEFAULT_CLOCK_SOURCE LEDC_AUTO_CLK HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_of_pins) { @@ -186,26 +187,26 @@ HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_o periph_module_enable(PERIPH_LEDC_MODULE); if((no_of_pins == 0) || (no_of_pins > SOC_LEDC_CHANNEL_NUM)) { - return PWM_BAD_CHANNEL; + return; } #ifdef LEDC_HIGH_SPEED_MODE - if(Channel::instance()=>getFreeChannels(mode)getFreeChannels(mode)getFreeChannels(mode)getFreeChannels(mode)getFreeChannels(mode)getFreeChannels(mode)getTimerNumber(), 0); } } diff --git a/Sming/Arch/Esp32/Core/ledc_channel.cpp b/Sming/Arch/Esp32/Core/ledc_channel.cpp index e4531b2ee0..2884afefe6 100644 --- a/Sming/Arch/Esp32/Core/ledc_channel.cpp +++ b/Sming/Arch/Esp32/Core/ledc_channel.cpp @@ -21,11 +21,11 @@ ledc_channel::ledc_channel(ledc_mode_t mode, int gpio, ledc_timer_t timer, uint3 } ledc_channel::ledc_channel(ledc_mode_t mode, int gpio, ledc_timer_t timer){ - return ledc_channel(mode, gpio, timer, (uint32_t) 0, (int) 0); + ledc_channel(mode, gpio, timer, (uint32_t) 0, (int) 0); } ledc_channel::ledc_channel(ledc_mode_t mode, int gpio){ - return ledc_channel(mode, gpio, Timer::instance()->getTimer(mode), (uint32_t) 0, (int) 0); + ledc_channel(mode, gpio, Timer::instance()->getTimer(mode), (uint32_t) 0, (int) 0); } ledc_channel::~ledc_channel(){ @@ -36,24 +36,24 @@ ledc_channel::channelConfig(const ledc_channel_config_t *ledc_conf){ } */ -ledc_channel::updateDuty(){ - return ledc_update_duty(); +esp_err_t ledc_channel::updateDuty(void){ + return ledc_update_duty(channel_config.speed_mode, channel_config.channel); } -ledc_channel::setPin(int gpio_num){ - esp_err_t err = ledc_set_pin(gpio_num, channel_config.mode, channel_config.channel); - if(esp_err==ESP_OK){ +esp_err_t ledc_channel::setPin(int gpio_num){ + esp_err_t err = ledc_set_pin(gpio_num, channel_config.speed_mode, channel_config.channel); + if(err==ESP_OK){ channel_config.gpio_num=gpio_num; } return err; } -ledc_channel::stop(uint32_t idle_level){ - return ledc_stop(channel_config.mode, channel_config.channel, idle_level); +esp_err_t ledc_channel::stop(uint32_t idle_level){ + return ledc_stop(channel_config.speed_mode, channel_config.channel, idle_level); } -ledc_channel::setDutyWithHpoint(uint32_t duty, uint32_t hpoint){ - esp_err_t err = ledc_set_duty_with_hpoint(channel_config.mode, chanel_config.channel, duty, hpoint); +esp_err_t ledc_channel::setDutyWithHpoint(uint32_t duty, uint32_t hpoint){ + esp_err_t err = ledc_set_duty_with_hpoint(channel_config.speed_mode, channel_config.channel, duty, hpoint); if(err==ESP_OK){ channel_config.duty=duty; channel_config.hpoint=hpoint; @@ -61,19 +61,19 @@ ledc_channel::setDutyWithHpoint(uint32_t duty, uint32_t hpoint){ return err; } -ledc_channel::setDuty(uint32_t duty){ - esp_err_t err=ledc_set_duty(channel_config.mode, channel_config.channel, duty); +esp_err_t ledc_channel::setDuty(uint32_t duty){ + esp_err_t err=ledc_set_duty(channel_config.speed_mode, channel_config.channel, duty); if(err==ESP_OK){ channel_config.duty=duty; } return err; } -ledc_channel::bindChannelTimer(ledc_timer_t timer){ - esp_err_t err = ledc_bind_channel_timer(channel_config.mode, channel_config.channel, timer); +esp_err_t ledc_channel::bindChannelTimer(ledc_timer_t timer){ + esp_err_t err = ledc_bind_channel_timer(channel_config.speed_mode, channel_config.channel, timer); if(err==ESP_OK){ - channel_config.timer=timer; + channel_config.timer_sel=timer; } - + return err; } #endif \ No newline at end of file diff --git a/Sming/Arch/Esp32/Core/ledc_channel.h b/Sming/Arch/Esp32/Core/ledc_channel.h index 65aff97003..929fcb522f 100644 --- a/Sming/Arch/Esp32/Core/ledc_channel.h +++ b/Sming/Arch/Esp32/Core/ledc_channel.h @@ -19,7 +19,7 @@ class ledc_channel{ ~ledc_channel(); //esp_err_t channelConfig(const ledc_channel_config_t *ledc_conf); - esp_err_t updateDuty(); + esp_err_t updateDuty(void); esp_err_t setPin(int gpio_num); esp_err_t stop(){ return stop(0); diff --git a/Sming/Arch/Esp32/Core/ledc_timer.cpp b/Sming/Arch/Esp32/Core/ledc_timer.cpp index fec5848d00..0becc6ccdc 100644 --- a/Sming/Arch/Esp32/Core/ledc_timer.cpp +++ b/Sming/Arch/Esp32/Core/ledc_timer.cpp @@ -18,18 +18,18 @@ ledc_timer::~ledc_timer(void){ Timer::instance()->freeTimer(timer_conf.speed_mode, timer_conf.timer_num); } -esp_err_t ledc_timer::setTimer(uint32_t clock_divider, ledc_timer_bit_t duty_resolution, ledc_clk_cfg_t clk_cfg){ +esp_err_t ledc_timer::setTimer(uint32_t clock_divider, ledc_timer_bit_t duty_resolution, ledc_clk_src_t clk_src){ esp_err_t err=ledc_timer_set( timer_conf.speed_mode, timer_conf.timer_num, clock_divider, duty_resolution, - clk_cfg + clk_src ); if(err==ESP_OK){ timer_conf.duty_resolution = duty_resolution; - timer_conf.clk_cfg = clk_src; - clock_divider=clock_div; + //timer_conf.clk_cfg = clk_cfg; + clock_divider=clock_divider; } return err; } @@ -63,7 +63,7 @@ ledc_timer::timerBindChannel(ledc_channel_t channel){ if(err==ESP_OK) } */ -uint32_t ledc_timer::getTimerNumber(void){ +ledc_timer_t ledc_timer::getTimerNumber(void){ return timer_conf.timer_num; } @@ -75,7 +75,7 @@ ledc_timer_bit_t ledc_timer::getDutyResolution(void){ return timer_conf.duty_resolution; } -uint32_t getTimerFrequency(void){ +uint32_t ledc_timer::getTimerFrequency(void){ return timer_conf.freq_hz; } diff --git a/Sming/Arch/Esp32/Core/ledc_timer.h b/Sming/Arch/Esp32/Core/ledc_timer.h index 9117f29ab6..8d1f0072f2 100644 --- a/Sming/Arch/Esp32/Core/ledc_timer.h +++ b/Sming/Arch/Esp32/Core/ledc_timer.h @@ -14,7 +14,7 @@ class ledc_timer { public: ledc_timer(ledc_mode_t mode, ledc_timer_bit_t duty_resolution, uint32_t freq, ledc_clk_cfg_t clk_cfg); ~ledc_timer(void); - esp_err_t setTimer(uint32_t clock_divider, ledc_timer_bit_t duty_resolution, ledc_clk_cfg_t clk_cfg); + esp_err_t setTimer(uint32_t clock_divider, ledc_timer_bit_t duty_resolution, ledc_clk_src_t clk_src); esp_err_t setTimerFrequency(uint32_t freq); esp_err_t timerReset(void); esp_err_t timerPause(void); @@ -24,7 +24,7 @@ class ledc_timer { ledc_timer_t getTimerNumber(void); ledc_mode_t getTimerMode(void); - uint32_t getDutyResolution(void); + ledc_timer_bit_t getDutyResolution(void); uint32_t getTimerFrequency(void); uint32_t getClockDivider(void); private: From 4e5cea76bcfc0ce55e8e1f41d6f0b78bf58a6b16 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Fri, 7 Apr 2023 11:14:51 +0200 Subject: [PATCH 26/28] added doxygen for the ledc_channel and ledc_timer class, rewrote the member functions for HardwarePWM. --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 43 ++++----- Sming/Arch/Esp32/Core/ledc_channel.h | 120 +++++++++++++++++++++++++- Sming/Arch/Esp32/Core/ledc_timer.h | 85 +++++++++++++++++- Sming/Core/HardwarePWM.h | 4 + 4 files changed, 220 insertions(+), 32 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index 169e6f6a7d..fecf1859ae 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -162,7 +162,6 @@ namespace{ 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); //ledc_channel_t getChannel(ledc_mode_t); //ledc_timer_t getTimer(ledc_mode_t); @@ -203,7 +202,7 @@ HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_o } #endif - ledc_timer* timer = new ledc_timer(mode, (ledc_timer_bit_t) DEFAULT_RESOLUTION, DEFAULT_FREQ, (ledc_clk_cfg_t) DEFAULT_CLOCK_SOURCE ); + timer = new ledc_timer(mode, (ledc_timer_bit_t) DEFAULT_RESOLUTION, DEFAULT_FREQ, (ledc_clk_cfg_t) DEFAULT_CLOCK_SOURCE ); ledc_channel* channel[no_of_pins]; for(uint8_t i=0;igetTimerNumber(), 0); @@ -215,7 +214,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); + channel[i]->stop(); } } @@ -226,12 +225,12 @@ HardwarePWM::~HardwarePWM() uint8_t HardwarePWM::getChannel(uint8_t pin) { for(uint8_t i = 0; i < channel_count; i++) { - if(channels[i] == pin) { + if(channel[i]->getPin()==pin) { //debug_d("getChannel %d is %d", pin, i); return i; } } - return -1; + return PWM_BAD_PIN; } /* Function Name: getDutyChan @@ -243,9 +242,8 @@ uint32_t HardwarePWM::getDutyChan(uint8_t chan) if(chan == PWM_BAD_CHANNEL) { return 0; } else { - return ledc_get_duty(pinToGroup(chan),pinToChannel(chan)); + return channel[chan]->getDuty(); } - // esp32 defines the frequency / period per timer, } /* Function Name: setDutyChan @@ -260,14 +258,10 @@ 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(); - * } - */ + channel[chan]->setDuty(duty); + if(update) { + channel[chan]->updateDuty(); + } ESP_ERROR_CHECK(ledc_update_duty(pinToGroup(chan), pinToChannel(chan))); return true; } else { @@ -285,7 +279,7 @@ 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))); + return frequencyToPeriod(frequencyToPeriod(timer->getTimerFrequency())); } /* Function Name: setPeriod @@ -294,12 +288,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 - for(uint8_t i = 0; i < channel_count; i++) { - ESP_ERROR_CHECK(ledc_set_freq(pinToGroup(i), pinToTimer(i), periodToFrequency(period))); - } - //sledc_update_duty(); + timer->setTimerFrequency(periodToFrequency(period)); update(); } @@ -308,12 +297,14 @@ void HardwarePWM::setPeriod(uint32_t period) */ void HardwarePWM::update() { - //ledc_update_duty(); + for(uint8_t i=0;iupdateDuty(); + } } uint32_t HardwarePWM::getFrequency(uint8_t pin) { - return ledc_get_freq(pinToGroup(pin), pinToTimer(pin)); + return timer->getTimerFrequency(); } namespace{ @@ -345,8 +336,4 @@ namespace{ } } - uint32_t maxDuty(ledc_timer_bit_t bits){ - return (1<<(uint32_t)bits) - 1; - } - } \ No newline at end of file diff --git a/Sming/Arch/Esp32/Core/ledc_channel.h b/Sming/Arch/Esp32/Core/ledc_channel.h index 929fcb522f..cf030918a5 100644 --- a/Sming/Arch/Esp32/Core/ledc_channel.h +++ b/Sming/Arch/Esp32/Core/ledc_channel.h @@ -9,31 +9,147 @@ #ifndef LEDC_TIMERS_H #include "ledc_timer.h" #endif - +/** + * @brief ledc_channel class + * this class is primarily intended to be used by the Sming HardwarePWM implementation for ESP32 class MCUs. + * while it should be possible to use it directly in user code, it is not generally advised as the plan is to + * expose the full esp32 ledc_ interface through the HardwarePWM interface. + * + * If you chose to use this directly, though, the singleton class at the heart of this *should* at least make sure + * that hardware is correctly shared between HardwarePWM use and direct use as both channels and timers are + * managed by the Timer and Channel classes, respectively. + */ class ledc_channel{ public: + /** + * @brief Construct a new ledc channel object + * + * @param mode speed mode (see ledc_mode_t typedef for options) + * @param gpio the pin to attach to the channel + * @param timer the timer to use for this channel. Get the timer by instanciating a ledc_timer object and calling the ledc_timer.getTimer() member function + * @param duty the intial duty cycle + * @param hpoint the hpoint as defined by the esp-idf (max: 0xfff) + */ ledc_channel(ledc_mode_t mode, int gpio, ledc_timer_t timer, uint32_t duty, int hpoint); + + /** + * @brief Construct a new ledc channel object - reduced interface using default hpoint + * + * @param mode speed mode (see ledc_mode_t typedef for options) + * @param gpio the pin to attach to the channel + * @param timer the timer to use for this channel. Get the timer by instanciating a ledc_timer object and calling the ledc_timer.getTimer() member function + * @param duty the intial duty cycle + */ ledc_channel(ledc_mode_t mode, int gpio, ledc_timer_t timer, uint32_t duty); + + /** + * @brief Construct a new ledc channel object - reduced interface using default hpoint and duty + * + * @param mode speed mode (see ledc_mode_t typedef for options) + * @param gpio the pin to attach to the channel + * @param timer the timer to use for this channel. Get the timer by instanciating a ledc_timer object and calling the ledc_timer.getTimer() member function + */ ledc_channel(ledc_mode_t mode, int gpio, ledc_timer_t timer); + + /** + * @brief Construct a new ledc channel object - reduced interface using default hpoint, duty and timer. + * The timer will be automatically assigned using the ledc_timer.getTimer() function. + * Caution: if you are using this to extend the sming HardwarePWM function, this constructor will use a new timer + * for every channel generated which will break the functionality of HardwarePWM to, for example, setting the PWM + * frequency for all channels. + * + * @param mode speed mode (see ledc_mode_t typedef for options) + * @param gpio the pin to attach to the channel + */ ledc_channel(ledc_mode_t mode, int gpio); + + /** + * @brief destructor for the channel. Frees the channel to be then re-used by other instances + * + */ ~ledc_channel(); - + //esp_err_t channelConfig(const ledc_channel_config_t *ledc_conf); + + /** + * @brief update the duty cycle of the channel + * call this function after you have set the duty cycle either by calling setDutyWithHpoint or setDuty as those two functions + * only set the duty cycle value but not enact it yet. This allows multiple channels to be synchronized. + * + * @return esp_err_t + */ + esp_err_t updateDuty(void); + + /** + * @brief Set the Pin + * if the channel has been created with one of the reduced interface constructors without setting the actual pin, the pin can be configured here + * + * @param gpio_num + * @return esp_err_t + */ esp_err_t setPin(int gpio_num); + + /** + * @brief stop the PWM on this channel and set the output to 0 + * + * @return esp_err_t + */ esp_err_t stop(){ return stop(0); }; + + /** + * @brief stop the PWM on this channel and set the output to idle_level + * + * @param idle_level + * @return esp_err_t + */ esp_err_t stop(uint32_t idle_level); + + /** + * @brief Set the Duty cycle With Hpoint + * + * @param duty + * @param hpoint + * @return esp_err_t + */ esp_err_t setDutyWithHpoint(uint32_t duty, uint32_t hpoint); + + /** + * @brief Set the Duty cycle + * + * @param duty + * @return esp_err_t + */ esp_err_t setDuty(uint32_t duty); + + /** + * @brief Get the Hpoint configured for this channel + * + * @return int + */ int getHpoint(){ return channel_config.hpoint; }; + + /** + * @brief Get the current Duty cycle + * + * @return uint32_t + */ uint32_t getDuty(){ return channel_config.duty; }; + + /** + * @brief bind the channel object to an existing timer + * + * @param timer + * @return esp_err_t + */ esp_err_t bindChannelTimer(ledc_timer_t timer); + /* omit fades for now... esp_err_t setFade(uint32_t duty, ledc_duty_direction_t fade_direction, uint32_t step_num, uint32_t duty_cycle_num, uint32_t duty_scale); esp_err_t setFadeWithStep(uint32_t target_duty, uint32_t scale, uint32_t cycle_num); diff --git a/Sming/Arch/Esp32/Core/ledc_timer.h b/Sming/Arch/Esp32/Core/ledc_timer.h index 8d1f0072f2..c08b2f198d 100644 --- a/Sming/Arch/Esp32/Core/ledc_timer.h +++ b/Sming/Arch/Esp32/Core/ledc_timer.h @@ -12,20 +12,101 @@ class ledc_timer { public: + /** + * @brief Construct a new ledc timer object + * + * @param mode + * @param duty_resolution + * @param freq + * @param clk_cfg + */ ledc_timer(ledc_mode_t mode, ledc_timer_bit_t duty_resolution, uint32_t freq, ledc_clk_cfg_t clk_cfg); + /** + * @brief Destroy the ledc timer object + * + */ ~ledc_timer(void); - esp_err_t setTimer(uint32_t clock_divider, ledc_timer_bit_t duty_resolution, ledc_clk_src_t clk_src); + + /** + * @brief Set the Timer object + * + * @param clock_divider + * @param duty_resolution + * @param clk_src + * + * @return esp_err_t + */ + esp_err_t setTimer(uint32_t clock_divider, ledc_timer_bit_t duty_resolution, ledc_clk_src_t clk_src); + + /** + * @brief set the Frequency for this timer object + * + * @param frequency + * @return esp_err_t + */ esp_err_t setTimerFrequency(uint32_t freq); + + /** + * @brief reset this timer + * + * @return esp_err_t + */ esp_err_t timerReset(void); + + /** + * @brief pause this timer + * + * @return esp_err_t + */ esp_err_t timerPause(void); + + /** + * @brief resume this timer + * + * @return esp_err_t + */ esp_err_t timerResume(void); - esp_err_t timerBindChannel(ledc_channel_t channel); + /* + * esp_err_t timerBindChannel(ledc_channel_t channel); + */ + + /** + * @brief Get the Timer Number + * use this to get the timer number to use in the ledc_channel constructor or ledc_channel.bindTimer() + * + * @return ledc_timer_t Timer Number + */ ledc_timer_t getTimerNumber(void); + + /** + * @brief Get the Timer Mode + * return the timer mode + * + * @return ledc_mode_t Timer Mode + */ ledc_mode_t getTimerMode(void); + /** + * @brief Get the Duty Resolution + * get the resolution in bits configured for this timer + * + * @return ledc_timer_bit_t Timer Resolution + */ ledc_timer_bit_t getDutyResolution(void); + + /** + * @brief Get the Timer Frequency + * + * @return uint32_t Frequency + */ uint32_t getTimerFrequency(void); + + /** + * @brief Get the Clock Divider object + * + * @return uint32_t clock divider + */ uint32_t getClockDivider(void); private: ledc_timer_config_t timer_conf; diff --git a/Sming/Core/HardwarePWM.h b/Sming/Core/HardwarePWM.h index 2f895b478d..a395eda6f3 100644 --- a/Sming/Core/HardwarePWM.h +++ b/Sming/Core/HardwarePWM.h @@ -31,6 +31,7 @@ #include #define PWM_BAD_CHANNEL 0xff ///< Invalid PWM channel +#define PWM_BAD_PIN 0xff ///< Invalid Pin / Pin is not attached to any channel /// Hardware pulse width modulation class HardwarePWM @@ -131,6 +132,9 @@ class HardwarePWM uint8_t channel_count; uint8_t channels[PWM_CHANNEL_NUM_MAX]; uint32_t maxduty; + + ledc_channel* channel[]; + ledc_timer* timer[]; }; /** @} */ From 3b08563e6e705d29c7b029cb652160ec7c7ba095 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sat, 8 Apr 2023 11:45:36 +0200 Subject: [PATCH 27/28] first version that successfully compiles, now of to testing --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 29 ++++++++------------------ Sming/Arch/Esp32/Core/ledc_channel.cpp | 6 ++++-- Sming/Arch/Esp32/Core/ledc_channel.h | 8 +++++++ Sming/Arch/Esp32/Core/ledc_timer.cpp | 11 +++++++--- Sming/Arch/Esp32/Core/singelton.cpp | 2 ++ Sming/Core/HardwarePWM.h | 11 ++++++++-- 6 files changed, 40 insertions(+), 27 deletions(-) diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index fecf1859ae..fe8575e526 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -157,9 +157,6 @@ #include "ledc_timer.h" 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); @@ -189,21 +186,25 @@ HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_o return; } + /* ToDo: this logic seems broken. What needs to happen: + * if LEDC_HIGH_SPEED_MODE, we'll need to check both, HS and LS mode, + * else only LS mode + */ #ifdef LEDC_HIGH_SPEED_MODE if(Channel::instance()->getFreeChannels(mode)getFreeChannels(mode)getFreeChannels(mode)getFreeChannels(mode)getFreeChannels(mode)getTimerNumber(), 0); } @@ -262,7 +263,6 @@ bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) if(update) { channel[chan]->updateDuty(); } - ESP_ERROR_CHECK(ledc_update_duty(pinToGroup(chan), pinToChannel(chan))); return true; } else { debug_d("Duty cycle value too high for current period."); @@ -279,7 +279,7 @@ 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(frequencyToPeriod(timer->getTimerFrequency())); + return frequencyToPeriod(timer->getTimerFrequency()); } /* Function Name: setPeriod @@ -308,17 +308,6 @@ uint32_t HardwarePWM::getFrequency(uint8_t 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){ diff --git a/Sming/Arch/Esp32/Core/ledc_channel.cpp b/Sming/Arch/Esp32/Core/ledc_channel.cpp index 2884afefe6..f96ccfb8bb 100644 --- a/Sming/Arch/Esp32/Core/ledc_channel.cpp +++ b/Sming/Arch/Esp32/Core/ledc_channel.cpp @@ -1,10 +1,11 @@ #ifndef LEDC_CHANNEL_H #include "ledc_channel.h" + ledc_channel::ledc_channel(ledc_mode_t mode, int gpio, ledc_timer_t timer, uint32_t duty, int hpoint){ periph_module_enable(PERIPH_LEDC_MODULE); channel_config.speed_mode = mode; channel_config.gpio_num = gpio; - channel_config.channel = Channel::instance()->getChannel(mode); + channel_config.channel = ledc_singleton::Channel::instance()->getChannel(mode); channel_config.intr_type = LEDC_INTR_DISABLE; channel_config.timer_sel = timer; channel_config.duty = duty; @@ -13,6 +14,7 @@ ledc_channel::ledc_channel(ledc_mode_t mode, int gpio, ledc_timer_t timer, uint3 esp_err_t err=ledc_channel_config(&channel_config); if(err==ESP_OK){ bindChannelTimer(timer); + debug_d("configured channel %d pin %d to user timer %d", channel_config.channel, channel_config.gpio_num, timer->getTimerNum()); } } @@ -25,7 +27,7 @@ ledc_channel::ledc_channel(ledc_mode_t mode, int gpio, ledc_timer_t timer){ } ledc_channel::ledc_channel(ledc_mode_t mode, int gpio){ - ledc_channel(mode, gpio, Timer::instance()->getTimer(mode), (uint32_t) 0, (int) 0); + ledc_channel(mode, gpio, ledc_singleton::Timer::instance()->getTimer(mode), (uint32_t) 0, (int) 0); } ledc_channel::~ledc_channel(){ diff --git a/Sming/Arch/Esp32/Core/ledc_channel.h b/Sming/Arch/Esp32/Core/ledc_channel.h index cf030918a5..a265573219 100644 --- a/Sming/Arch/Esp32/Core/ledc_channel.h +++ b/Sming/Arch/Esp32/Core/ledc_channel.h @@ -142,6 +142,14 @@ class ledc_channel{ return channel_config.duty; }; + /** + * @brief Get the assigned gpio Pin + * + * @return int the Pin + */ + int getPin(){ + return channel_config.gpio_num; + } /** * @brief bind the channel object to an existing timer * diff --git a/Sming/Arch/Esp32/Core/ledc_timer.cpp b/Sming/Arch/Esp32/Core/ledc_timer.cpp index 0becc6ccdc..6878368bc7 100644 --- a/Sming/Arch/Esp32/Core/ledc_timer.cpp +++ b/Sming/Arch/Esp32/Core/ledc_timer.cpp @@ -1,11 +1,14 @@ + #ifndef LEDC_TIMER_H #include "ledc_timer.h" #define LEDC_TIMER_H #endif +//namespace ledc_singleton{ + ledc_timer::ledc_timer(ledc_mode_t mode, ledc_timer_bit_t duty_resolution, uint32_t freq, ledc_clk_cfg_t clk_cfg){ timer_conf.speed_mode = mode; - timer_conf.timer_num = Timer::instance()->getTimer(mode); + timer_conf.timer_num = ledc_singleton::Timer::instance()->getTimer(mode); timer_conf.duty_resolution = duty_resolution; timer_conf.freq_hz = freq; timer_conf.clk_cfg = clk_cfg; @@ -15,7 +18,7 @@ ledc_timer::ledc_timer(ledc_mode_t mode, ledc_timer_bit_t duty_resolution, uint3 ledc_timer::~ledc_timer(void){ ledc_timer_pause(timer_conf.speed_mode, timer_conf.timer_num); - Timer::instance()->freeTimer(timer_conf.speed_mode, timer_conf.timer_num); + ledc_singleton::Timer::instance()->freeTimer(timer_conf.speed_mode, timer_conf.timer_num); } esp_err_t ledc_timer::setTimer(uint32_t clock_divider, ledc_timer_bit_t duty_resolution, ledc_clk_src_t clk_src){ @@ -31,6 +34,7 @@ esp_err_t ledc_timer::setTimer(uint32_t clock_divider, ledc_timer_bit_t duty_res //timer_conf.clk_cfg = clk_cfg; clock_divider=clock_divider; } + debug_d("configured timer %i",timer_conf.timer_num); return err; } @@ -81,4 +85,5 @@ uint32_t ledc_timer::getTimerFrequency(void){ uint32_t ledc_timer::getClockDivider(void){ return clock_divider; -} \ No newline at end of file +} +// } // end namespace ledc_singleton \ No newline at end of file diff --git a/Sming/Arch/Esp32/Core/singelton.cpp b/Sming/Arch/Esp32/Core/singelton.cpp index f747a28422..4acbe5ff68 100644 --- a/Sming/Arch/Esp32/Core/singelton.cpp +++ b/Sming/Arch/Esp32/Core/singelton.cpp @@ -7,6 +7,7 @@ * */ +namespace ledc_singleton{ ledc_timer_t Timer::getTimer(ledc_mode_t mode) { for(uint8_t i = 0; i < (uint8_t)LEDC_TIMER_MAX; i++) { @@ -67,3 +68,4 @@ Channel::Channel() #endif } } +} //end namespace ledc_singleton \ No newline at end of file diff --git a/Sming/Core/HardwarePWM.h b/Sming/Core/HardwarePWM.h index a395eda6f3..8342142d40 100644 --- a/Sming/Core/HardwarePWM.h +++ b/Sming/Core/HardwarePWM.h @@ -33,6 +33,11 @@ #define PWM_BAD_CHANNEL 0xff ///< Invalid PWM channel #define PWM_BAD_PIN 0xff ///< Invalid Pin / Pin is not attached to any channel +#if SMING_ARCH==Esp32 +#include "../Arch/Esp32/Core/ledc_timer.h" +#include "../Arch/Esp32/Core/ledc_channel.h" +#endif + /// Hardware pulse width modulation class HardwarePWM { @@ -133,8 +138,10 @@ class HardwarePWM uint8_t channels[PWM_CHANNEL_NUM_MAX]; uint32_t maxduty; - ledc_channel* channel[]; - ledc_timer* timer[]; + #if SMING_ARCH==Esp32 + ledc_channel* channel[PWM_CHANNEL_NUM_MAX]; + ledc_timer* timer; + #endif }; /** @} */ From a81b1d42c632d1eaa5472adfa1f099c9a9f1145b Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Sat, 8 Apr 2023 11:45:59 +0200 Subject: [PATCH 28/28] first version that successfully compiles, now of to testing --- Sming/Arch/Esp32/Core/singleton.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Sming/Arch/Esp32/Core/singleton.h b/Sming/Arch/Esp32/Core/singleton.h index 487c77b70d..656f197603 100644 --- a/Sming/Arch/Esp32/Core/singleton.h +++ b/Sming/Arch/Esp32/Core/singleton.h @@ -3,7 +3,7 @@ #include #define PWM_MAX_TIMER 8 - +namespace ledc_singleton{ template class Singleton { public: @@ -419,3 +419,4 @@ class Channel : public Singleton Channel(); bool isUsed[LEDC_SPEED_MODE_MAX][LEDC_CHANNEL_MAX]; }; +} // end namespace ledc_singleton \ No newline at end of file