From 6d94935b18801fa02fff35320fedc426b4993bd1 Mon Sep 17 00:00:00 2001 From: Mike Date: Tue, 5 Nov 2024 14:54:22 +0000 Subject: [PATCH] Implement HardwarePWM class for Rp2040 and update Basic PWM sample (#2908) --- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 18 +-- Sming/Arch/Esp8266/Core/HardwarePWM.cpp | 20 +-- Sming/Arch/Host/Core/HardwarePWM.cpp | 20 +-- .../Components/driver/include/driver/pwm.h | 86 +---------- Sming/Arch/Rp2040/Core/HardwarePWM.cpp | 135 +++++++++++++++++ Sming/Arch/Rp2040/Core/HardwarePWM.cpp.todo | 136 ------------------ Sming/Core/HardwarePWM.cpp | 26 ++++ Sming/Core/HardwarePWM.h | 50 +++++-- samples/Basic_HwPWM/app/application.cpp | 65 +++++---- samples/Basic_HwPWM/component.mk | 1 - 10 files changed, 259 insertions(+), 298 deletions(-) create mode 100644 Sming/Arch/Rp2040/Core/HardwarePWM.cpp delete mode 100644 Sming/Arch/Rp2040/Core/HardwarePWM.cpp.todo create mode 100644 Sming/Core/HardwarePWM.cpp diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp index 0b1a74f583..183accf984 100644 --- a/Sming/Arch/Esp32/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -138,7 +138,7 @@ uint32_t maxDuty(ledc_timer_bit_t bits) } //namespace -HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_of_pins) +HardwarePWM::HardwarePWM(const uint8_t* pins, uint8_t no_of_pins) : channel_count(no_of_pins) { assert(no_of_pins > 0 && no_of_pins <= SOC_LEDC_CHANNEL_NUM); no_of_pins = std::min(uint8_t(SOC_LEDC_CHANNEL_NUM), no_of_pins); @@ -206,17 +206,7 @@ HardwarePWM::~HardwarePWM() } } -uint8_t HardwarePWM::getChannel(uint8_t pin) -{ - for(uint8_t i = 0; i < channel_count; i++) { - if(channels[i] == pin) { - return i; - } - } - return -1; -} - -uint32_t HardwarePWM::getDutyChan(uint8_t chan) +uint32_t HardwarePWM::getDutyChan(uint8_t chan) const { // esp32 defines the frequency / period per timer return (chan == PWM_BAD_CHANNEL) ? 0 : ledc_get_duty(pinToGroup(chan), pinToChannel(chan)); @@ -245,7 +235,7 @@ bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) return false; } -uint32_t HardwarePWM::getPeriod() +uint32_t HardwarePWM::getPeriod() const { // 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. @@ -268,7 +258,7 @@ void HardwarePWM::update() // ledc_update_duty(); } -uint32_t HardwarePWM::getFrequency(uint8_t pin) +uint32_t HardwarePWM::getFrequency(uint8_t pin) const { return ledc_get_freq(pinToGroup(pin), pinToTimer(pin)); } diff --git a/Sming/Arch/Esp8266/Core/HardwarePWM.cpp b/Sming/Arch/Esp8266/Core/HardwarePWM.cpp index 1483df469e..50919441ad 100644 --- a/Sming/Arch/Esp8266/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp8266/Core/HardwarePWM.cpp @@ -52,7 +52,7 @@ static const uint8_t gpioPinFunc[]{ FUNC_GPIO15, // }; -HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t noOfPins) : channel_count(noOfPins) +HardwarePWM::HardwarePWM(const uint8_t* pins, uint8_t noOfPins) : channel_count(noOfPins) { if(noOfPins == 0) { return; @@ -85,19 +85,7 @@ HardwarePWM::~HardwarePWM() // There is no function in the SDK to stop PWM output, yet. } -uint8_t HardwarePWM::getChannel(uint8_t pin) -{ - for(uint8_t i = 0; i < channel_count; i++) { - if(channels[i] == pin) { - return i; - } - } - - debug_d("getChannel: can't find pin %d", pin); - return PWM_BAD_CHANNEL; -} - -uint32_t HardwarePWM::getDutyChan(uint8_t chan) +uint32_t HardwarePWM::getDutyChan(uint8_t chan) const { return (chan == PWM_BAD_CHANNEL) ? 0 : pwm_get_duty(chan); } @@ -120,7 +108,7 @@ bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) return false; } -uint32_t HardwarePWM::getPeriod() +uint32_t HardwarePWM::getPeriod() const { return pwm_get_period(); } @@ -137,7 +125,7 @@ void HardwarePWM::update() pwm_start(); } -uint32_t HardwarePWM::getFrequency(uint8_t pin) +uint32_t HardwarePWM::getFrequency(uint8_t pin) const { (void)pin; auto period = pwm_get_period(); diff --git a/Sming/Arch/Host/Core/HardwarePWM.cpp b/Sming/Arch/Host/Core/HardwarePWM.cpp index cf5d2de430..4c867c2ee4 100644 --- a/Sming/Arch/Host/Core/HardwarePWM.cpp +++ b/Sming/Arch/Host/Core/HardwarePWM.cpp @@ -25,36 +25,36 @@ #include -HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_of_pins) +HardwarePWM::HardwarePWM(const uint8_t*, uint8_t no_of_pins) : channel_count(no_of_pins) { } HardwarePWM::~HardwarePWM() = default; -uint8_t HardwarePWM::getChannel(uint8_t pin) -{ - return PWM_BAD_CHANNEL; -} - -uint32_t HardwarePWM::getDutyChan(uint8_t chan) +uint32_t HardwarePWM::getDutyChan(uint8_t) const { return 0; } -bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) +bool HardwarePWM::setDutyChan(uint8_t, uint32_t, bool) { return false; } -uint32_t HardwarePWM::getPeriod() +uint32_t HardwarePWM::getPeriod() const { return 0; } -void HardwarePWM::setPeriod(uint32_t period) +void HardwarePWM::setPeriod(uint32_t) { } void HardwarePWM::update() { } + +uint32_t HardwarePWM::getFrequency(uint8_t) const +{ + return 0; +} diff --git a/Sming/Arch/Rp2040/Components/driver/include/driver/pwm.h b/Sming/Arch/Rp2040/Components/driver/include/driver/pwm.h index 8eb9f2560e..b55397e352 100644 --- a/Sming/Arch/Rp2040/Components/driver/include/driver/pwm.h +++ b/Sming/Arch/Rp2040/Components/driver/include/driver/pwm.h @@ -1,88 +1,8 @@ #pragma once -#if defined(__cplusplus) -extern "C" { -#endif - -// #include - -#define PWM_CHANNEL_NUM_MAX 16 - -/** - * @defgroup pwm_driver PWM driver - * @ingroup drivers - * @{ - */ - -/** - * @fn void pwm_init(uint32 period, uint32 *duty,uint32 pwm_channel_num,uint32 (*pin_info_list)[3]) - * @brief Initialize PWM function, including GPIO selection, period and duty cycle - * @param period PWM period - * @param duty duty cycle of each output - * @param pwm_channel_num PWM channel number - * @param pin_info_list Array containing an entry for each channel giving - * @note This API can be called only once. - * - * Example: - * - * uint32 io_info[][3] = { - * {PWM_0_OUT_IO_MUX, PWM_0_OUT_IO_FUNC, PWM_0_OUT_IO_NUM}, - * {PWM_1_OUT_IO_MUX, PWM_1_OUT_IO_FUNC, PWM_1_OUT_IO_NUM}, - * {PWM_2_OUT_IO_MUX, PWM_2_OUT_IO_FUNC, PWM_2_OUT_IO_NUM} - * }; - * - * pwm_init(light_param.pwm_period, light_param.pwm_duty, 3, io_info); - * - */ - /** - * @fn void pwm_start(void) - * @brief Starts PWM + * @brief Maximum number of active PWM channels. * - * This function needs to be called after PWM configuration is changed. + * The Pico has 8 PWM 'slices', each of which can drive two outputs. */ - -/** - * @fn void pwm_set_duty(uint32 duty, uint8 channel) - * @brief Sets duty cycle of a PWM output - * @param duty The time that high-level single will last, duty cycle will be (duty*45)/(period*1000) - * @param channel PWM channel, which depends on how many PWM channels are used - * - * Set the time that high-level signal will last. - * The range of duty depends on PWM period. Its maximum value of which can be Period * 1000 / 45. - * - * For example, for 1-KHz PWM, the duty range is 0 ~ 22222. - */ - -/** - * @fn uint32 pwm_get_duty(uint8 channel) - * @brief Get duty cycle of PWM output - * @param channel PWM channel, which depends on how many PWM channels are used - * @retval uint32 Duty cycle of PWM output - * - * Duty cycle will be (duty*45) / (period*1000). - */ - -/** - * @fn void pwm_set_period(uint32 period) - * @brief Set PWM period - * @param period PWM period in us. For example, 1-KHz PWM period = 1000us. - */ - -/** - * @fn uint32 pwm_get_period(void) - * @brief Get PWM period - * @retval uint32 Return PWM period in us. - */ - -/** - * @fn uint32 get_pwm_version(void) - * @brief Get version information of PWM - * @retval uint32 PWM version - */ - -/** @} */ - -#if defined(__cplusplus) -} -#endif +#define PWM_CHANNEL_NUM_MAX 16 diff --git a/Sming/Arch/Rp2040/Core/HardwarePWM.cpp b/Sming/Arch/Rp2040/Core/HardwarePWM.cpp new file mode 100644 index 0000000000..ba92f59be2 --- /dev/null +++ b/Sming/Arch/Rp2040/Core/HardwarePWM.cpp @@ -0,0 +1,135 @@ +/**** + * 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. + * + * Rp2040/Core/HardwarePWM.cpp + * + */ + +#include +#include +#include +#include +#include +#include +#include + +/* + * Note on use of divisor + * ---------------------- + * + * Divisor is 8:4 fractional value, so 1 <= int <= 255, 0 <= frac <= 15 + * Simplest way to use full range is to factor calculations by 16. + * + * Using default CSR_PH_CORRECT=0: + * + * F_PWM = 16 * F_SYS / (TOP + 1) / DIV + * + */ + +#define PWM_FREQ_DEFAULT 1000 + +HardwarePWM::HardwarePWM(const uint8_t* pins, uint8_t noOfPins) : channel_count(noOfPins) +{ + assert(noOfPins > 0 && noOfPins <= PWM_CHANNEL_NUM_MAX); + noOfPins = std::min(uint8_t(PWM_CHANNEL_NUM_MAX), noOfPins); + std::copy_n(pins, noOfPins, channels); + setPeriod(1e6 / PWM_FREQ_DEFAULT); + + for(unsigned i = 0; i < noOfPins; ++i) { + auto pin = channels[i]; + gpio_set_function(pin, GPIO_FUNC_PWM); + gpio_set_dir(pin, GPIO_OUT); + } +} + +HardwarePWM::~HardwarePWM() +{ + for(unsigned i = 0; i < channel_count; ++i) { + auto slice_num = pwm_gpio_to_slice_num(channels[i]); + pwm_set_enabled(slice_num, false); + } +} + +uint32_t HardwarePWM::getDutyChan(uint8_t chan) const +{ + if(chan >= channel_count) { + return 0; + } + auto pin = channels[chan]; + auto slice_num = pwm_gpio_to_slice_num(pin); + auto value = pwm_hw->slice[slice_num].cc; + value >>= pwm_gpio_to_channel(pin) ? PWM_CH0_CC_B_LSB : PWM_CH0_CC_A_LSB; + return value & 0xffff; +} + +bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool) +{ + if(chan >= channel_count) { + return false; + } + auto pin = channels[chan]; + duty = std::min(duty, maxduty); + pwm_set_gpio_level(pin, duty); + return true; +} + +uint32_t HardwarePWM::getPeriod() const +{ + // All channels configured with same clock + auto slice_num = pwm_gpio_to_slice_num(channels[0]); + uint32_t top = pwm_hw->slice[slice_num].top; + uint32_t div = pwm_hw->slice[slice_num].div; + return muldiv(62500ULL, (top + 1) * div, clock_get_hz(clk_sys)); +} + +void HardwarePWM::setPeriod(uint32_t period) +{ + const uint32_t topMax{0xffff}; + const uint32_t divMin{0x10}; // 1.0 + const uint32_t divMax{0xfff}; // INT + FRAC + auto sysFreq = clock_get_hz(clk_sys); + // Calculate divisor assuming maximum value for TOP: ensure value is rounded UP + uint32_t div = ((uint64_t(period) * sysFreq / 62500) + topMax) / (topMax + 1); + uint32_t top; + if(div > divMax) { + // Period too big, set to maximum + top = topMax; + div = divMax; + } else { + if(div < divMin) { + // Period is very small, set div to minimum + div = divMin; + } + top = (uint64_t(period) * sysFreq / 62500 / div) - 1; + } + + debug_d("[PWM] %s(%u): div %u, top %u", __FUNCTION__, period, div, top); + + pwm_config cfg = pwm_get_default_config(); + cfg.div = div; + cfg.top = top; + + for(unsigned i = 0; i < channel_count; ++i) { + auto pin = channels[i]; + auto slice_num = pwm_gpio_to_slice_num(pin); + pwm_init(slice_num, &cfg, true); + } + + maxduty = top; +} + +void HardwarePWM::update() +{ + // Not implemented +} + +uint32_t HardwarePWM::getFrequency(uint8_t pin) const +{ + auto slice_num = pwm_gpio_to_slice_num(pin); + auto top = pwm_hw->slice[slice_num].top; + auto div = pwm_hw->slice[slice_num].div; + return 16UL * clock_get_hz(clk_sys) / (div * (top + 1)); +} diff --git a/Sming/Arch/Rp2040/Core/HardwarePWM.cpp.todo b/Sming/Arch/Rp2040/Core/HardwarePWM.cpp.todo deleted file mode 100644 index 6def2d36fd..0000000000 --- a/Sming/Arch/Rp2040/Core/HardwarePWM.cpp.todo +++ /dev/null @@ -1,136 +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 - * - */ - -#include -#include "ESP8266EX.h" - -#include - -#define PERIOD_TO_MAX_DUTY(x) (x * 25) - -HardwarePWM::HardwarePWM(uint8* pins, uint8 no_of_pins) : channel_count(no_of_pins) -{ - if(no_of_pins > 0) { - uint32 io_info[PWM_CHANNEL_NUM_MAX][3]; // pin information - uint32 pwm_duty_init[PWM_CHANNEL_NUM_MAX]; // pwm duty - for(uint8 i = 0; i < no_of_pins; i++) { - io_info[i][0] = EspDigitalPins[pins[i]].mux; - io_info[i][1] = EspDigitalPins[pins[i]].gpioFunc; - io_info[i][2] = EspDigitalPins[pins[i]].id; - pwm_duty_init[i] = 0; // Start with zero output - channels[i] = pins[i]; - } - const int initial_period = 1000; - pwm_init(initial_period, pwm_duty_init, no_of_pins, io_info); - update(); - maxduty = PERIOD_TO_MAX_DUTY(initial_period); // for period of 1000 - } -} - -HardwarePWM::~HardwarePWM() -{ - // 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 HardwarePWM::getChannel(uint8 pin) -{ - for(uint8 i = 0; i < channel_count; i++) { - if(channels[i] == pin) { - //debugf("getChannel %d is %d", pin, i); - return i; - } - } - //debugf("getChannel: can't find pin %d", pin); - return PWM_BAD_CHANNEL; -} - -/* Function Name: getDutyChan - * Description: This function is used to get the duty cycle number for a given channel - * Parameters: chan -Esp8266 channel number - */ -uint32 HardwarePWM::getDutyChan(uint8 chan) -{ - if(chan == PWM_BAD_CHANNEL) { - return 0; - } else { - return pwm_get_duty(chan); - } -} - -/* Function Name: setDutyChan - * Description: This function is used to set the pwm duty cycle for a given channel. If parameter 'update' is false - * then you have to call update() later to update duties. - * Parameters: chan - channel number - * duty - duty cycle value - * update - update PWM output - */ -bool HardwarePWM::setDutyChan(uint8 chan, uint32 duty, bool update) -{ - if(chan == PWM_BAD_CHANNEL) { - return false; - } else if(duty <= maxduty) { - pwm_set_duty(duty, chan); - if(update) { - this->update(); - } - return true; - } else { - debugf("Duty cycle value too high for current period."); - return false; - } -} - -/* Function Name: getPeriod - * Description: This function is used to get Period of PWM. - * Period / frequency will remain same for all pins. - * - */ -uint32 HardwarePWM::getPeriod() -{ - return pwm_get_period(); -} - -/* Function Name: setPeriod - * Description: This function is used to set Period of PWM. - * Period / frequency will remain same for all pins. - */ -void HardwarePWM::setPeriod(uint32 period) -{ - maxduty = PERIOD_TO_MAX_DUTY(period); - pwm_set_period(period); - update(); -} - -/* Function Name: update - * Description: This function is used to actually update the PWM. - */ -void HardwarePWM::update() -{ - pwm_start(); -} diff --git a/Sming/Core/HardwarePWM.cpp b/Sming/Core/HardwarePWM.cpp new file mode 100644 index 0000000000..72603e2907 --- /dev/null +++ b/Sming/Core/HardwarePWM.cpp @@ -0,0 +1,26 @@ +/**** + * 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 + * + * Common code for all architectures + * + */ + +#include "HardwarePWM.h" +#include + +uint8_t HardwarePWM::getChannel(uint8_t pin) const +{ + for(unsigned i = 0; i < channel_count; ++i) { + if(channels[i] == pin) { + return i; + } + } + + debug_d("[HWPWM] getChannel: can't find pin %u", pin); + return PWM_BAD_CHANNEL; +} diff --git a/Sming/Core/HardwarePWM.h b/Sming/Core/HardwarePWM.h index fe1d6d501d..77d7887c52 100644 --- a/Sming/Core/HardwarePWM.h +++ b/Sming/Core/HardwarePWM.h @@ -32,7 +32,30 @@ #define PWM_BAD_CHANNEL 0xff ///< Invalid PWM channel -/// Hardware pulse width modulation +/** + * @brief Basic pulse width modulation support. + * + * PERIOD: A full PWM cycle has a duration, measured in hardware timer counts + * DUTY: The 'ON' portion of a PWM cycle measured in hardware timer counts + * + * These values differ between architectures. + * + * DUTY is always <= PERIOD. + * If DUTY == 0 then output is OFF. + * If DUTY == PERIOD then output is ON. + * Intermediate values produce a proportional output, PERCENT = 100 * DUTY / PERIOD. + * + * Producing an analogue voltage from such a PWM output requires a suitable filter + * with characteristics tuned to the specific PWM frequency in use. + * A single-pole R/C filter with R*C = PERIOD (in seconds) is often sufficient. + * + * Note: Devices such as servo motors use the digital signal directly. + * + * Note: The Esp8266 has no PWM hardware and uses an interrupt-based approach (software PWM). + * + * Note: Esp32 and Rp2040 hardware offers more capability which requires use of SDK calls + * or direct hardware access. To avoid resource conflicts avoid using this class in those cases. + */ class HardwarePWM { public: @@ -40,7 +63,8 @@ class HardwarePWM * @param pins Pointer to array of pins to control * @param no_of_pins Quantity of elements in array of pins */ - HardwarePWM(uint8_t* pins, uint8_t no_of_pins); + HardwarePWM(const uint8_t* pins, uint8_t no_of_pins); + virtual ~HardwarePWM(); /** @brief Set PWM duty cycle @@ -64,7 +88,7 @@ class HardwarePWM /** @brief Set PWM duty cycle * @param pin GPIO to set - * @param duty Value of duty cycle to set pin to + * @param duty Value of duty cycle (timer ticks) to set pin to * @param update Update PWM output * @retval bool True on success * @note This function is used to set the pwm duty cycle for a given pin. If parameter 'update' is false @@ -78,13 +102,13 @@ class HardwarePWM /** @brief Get PWM duty cycle * @param chan Channel to get duty cycle for - * @retval uint32_t Value of PWM duty cycle + * @retval uint32_t Value of PWM duty cycle in timer ticks */ - uint32_t getDutyChan(uint8_t chan); + uint32_t getDutyChan(uint8_t chan) const; /** @brief Get PWM duty cycle * @param pin GPIO to get duty cycle for - * @retval uint32_t Value of PWM duty cycle + * @retval uint32_t Value of PWM duty cycle in timer ticks */ uint32_t getDuty(uint8_t pin) { @@ -93,27 +117,27 @@ class HardwarePWM } /** @brief Set PWM period - * @param period PWM period + * @param period PWM period in microseconds * @note All PWM pins share the same period */ void setPeriod(uint32_t period); /** @brief Get PWM period - * @retval uint32_t Value of PWM period + * @retval uint32_t Value of PWM period in microseconds */ - uint32_t getPeriod(); + uint32_t getPeriod() const; /** @brief Get channel number for a pin * @param pin GPIO to interrogate * @retval uint8_t Channel of GPIO */ - uint8_t getChannel(uint8_t pin); + uint8_t getChannel(uint8_t pin) const; /** @brief Get the maximum duty cycle value - * @retval uint32_t Maximum permissible duty cycle + * @retval uint32_t Maximum permissible duty cycle in timer ticks * @note Attempt to set duty of a pin above this value will fail */ - uint32_t getMaxDuty() + uint32_t getMaxDuty() const { return maxduty; } @@ -126,7 +150,7 @@ class HardwarePWM * @param pin GPIO to get frequency for * @retval uint32_t Value of Frequency */ - uint32_t getFrequency(uint8_t pin); + uint32_t getFrequency(uint8_t pin) const; private: uint8_t channel_count; diff --git a/samples/Basic_HwPWM/app/application.cpp b/samples/Basic_HwPWM/app/application.cpp index b1c29c4784..2ac094036a 100644 --- a/samples/Basic_HwPWM/app/application.cpp +++ b/samples/Basic_HwPWM/app/application.cpp @@ -18,26 +18,38 @@ #include #include -#define LED_PIN 2 - namespace { // List of pins that you want to connect to pwm -uint8_t pins[]{ +const uint8_t pins[] +{ +#if defined(ARCH_ESP32) +#define LED_PIN 3 + LED_PIN, 4, 5, 18, 19, 4, +#elif defined(ARCH_RP2040) +#define LED_PIN PICO_DEFAULT_LED_PIN + LED_PIN, 2, 3, 4, 5, 6, 7, 8, +#else +#define LED_PIN 2 LED_PIN, 4, 5, 0, 15, 13, 12, 14, +#endif }; -HardwarePWM HW_pwm(pins, ARRAY_SIZE(pins)); -SimpleTimer procTimer; +const uint8_t defaultDutyPercent[]{ + 50, 95, 50, 85, 10, 30, 60, 80, +}; -const int maxDuty = HW_pwm.getMaxDuty(); +HardwarePWM pwm(pins, ARRAY_SIZE(pins)); +uint32_t maxDuty; + +SimpleTimer procTimer; void doPWM() { static bool countUp = true; - static int duty; + static uint32_t duty; - const int increment = maxDuty / 50; + const uint32_t increment = maxDuty / 50; if(countUp) { duty += increment; @@ -45,15 +57,14 @@ void doPWM() duty = maxDuty; countUp = false; } + } else if(duty <= increment) { + duty = 0; + countUp = true; } else { duty -= increment; - if(duty <= 0) { - duty = 0; - countUp = true; - } } - HW_pwm.analogWrite(LED_PIN, duty); + pwm.analogWrite(LED_PIN, duty); } } // namespace @@ -69,17 +80,21 @@ void init() WifiAccessPoint.enable(false); #endif - // Setting PWM values on 8 different pins - HW_pwm.analogWrite(4, maxDuty); - HW_pwm.analogWrite(5, maxDuty / 2); - HW_pwm.analogWrite(0, maxDuty); - HW_pwm.analogWrite(2, maxDuty / 2); - HW_pwm.analogWrite(15, 0); - HW_pwm.analogWrite(13, maxDuty / 3); - HW_pwm.analogWrite(12, 2 * maxDuty / 3); - HW_pwm.analogWrite(14, maxDuty); - - Serial.println(_F("PWM output set on all 8 Pins. Kindly check...\r\n" - "Now LED_PIN will go from 0 to VCC to 0 in cycles.")); + // Change PWM frequency if required: period is in microseconds + // pwm.setPeriod(100); + + maxDuty = pwm.getMaxDuty(); + auto period = pwm.getPeriod(); + auto freq = pwm.getFrequency(LED_PIN); + + Serial << _F("PWM period = ") << period << _F("us, freq = ") << freq << _F(", max. duty = ") << maxDuty << endl; + + // Set default PWM values + for(unsigned i = 0; i < ARRAY_SIZE(pins); ++i) { + pwm.analogWrite(pins[i], maxDuty * defaultDutyPercent[i] / 100); + } + + Serial << _F("PWM output set on all ") << ARRAY_SIZE(pins) << _F(" Pins. Kindly check...") << endl + << _F("Now LED (pin ") << LED_PIN << _F(") will go from 0 to VCC to 0 in cycles.") << endl; procTimer.initializeMs<100>(doPWM).start(); } diff --git a/samples/Basic_HwPWM/component.mk b/samples/Basic_HwPWM/component.mk index be6ff9c63a..99a419fcc1 100644 --- a/samples/Basic_HwPWM/component.mk +++ b/samples/Basic_HwPWM/component.mk @@ -1,4 +1,3 @@ -COMPONENT_SOC := esp8266 DISABLE_NETWORK := 1 # Uncomment the line below if you want to use Espressif's PWM library.