From 5eb7426773123da0b459a066f67fa8bd097206a7 Mon Sep 17 00:00:00 2001 From: Peter Jakobs Date: Fri, 28 Apr 2023 17:06:15 +0200 Subject: [PATCH] Initial functioning version of ESP32 HardwarePWM. (#2599) * initial functioning version of ESP32 HardwarePWM. Tested to run on the ESP32C3 with up to 5kHz pwm frequency. * some documentation updates * more documentation * more documentation * implemented requested changes from initial PR * implemented requested changes from initial PR (now *with* changes. Sigh) * Revert "more documentation" This reverts commit 7279c66287e6a3be238052687ad3a0e3318a9c01. * implemented requested changes from initial PR (now *with* changes. after goofing up with git Sigh) * fixed some things I didn't understand at first. * HardwarePWM.h was not part of the last commit * .cpp was also missing. why? * Apply coding style * Rebase, tidy * Tidy Esp8266 implementation, add new 'getFrequency' method * Add missing include * Tidy driver/pwm.h * Use static initializers * minor updates as suggested by mikee47 * minor updates as suggested by mikee47 * removed unused variables * missed one occurrence and also messed up in a merge - cleaned out now * removed a piece of code that was doubled * fixed typos and coding style * removed ToDos from the comments that are to be implemented in the next iteraton as they are already underway * fixed return values * changed some coding style issues in esp8266 code * removed unnecessary and improved useful debug output * corrected the PWM_CHANNEL_NUM_MAX define in pwm.h to reflect the correct SoC config * Small coding style fixes. --------- Co-authored-by: mikee47 Co-authored-by: Slavey Karadzhov --- .../Components/driver/include/driver/pwm.h | 71 +---- Sming/Arch/Esp32/Core/HardwarePWM.cpp | 275 ++++++++++++++++++ .../Components/driver/include/driver/pwm.h | 4 +- .../Components/driver/new-pwm/README.md | 6 +- Sming/Arch/Esp8266/Core/HardwarePWM.cpp | 89 +++--- Sming/Core/HardwarePWM.h | 6 + 6 files changed, 332 insertions(+), 119 deletions(-) create mode 100644 Sming/Arch/Esp32/Core/HardwarePWM.cpp diff --git a/Sming/Arch/Esp32/Components/driver/include/driver/pwm.h b/Sming/Arch/Esp32/Components/driver/include/driver/pwm.h index b003ec121a..ea069f1cbf 100644 --- a/Sming/Arch/Esp32/Components/driver/include/driver/pwm.h +++ b/Sming/Arch/Esp32/Components/driver/include/driver/pwm.h @@ -1,65 +1,18 @@ -/* - * ESPRESSIF MIT License +/**** + * 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. * - * Copyright (c) 2016 + * pwm.h * - * Permission is hereby granted for use on ESPRESSIF SYSTEMS ESP8266 only, in which case, - * it is free of charge, to any person obtaining a copy of this software and associated - * documentation files (the "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the Software is furnished - * to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all copies or - * substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS - * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR - * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - * - */ - -#ifndef __PWM_H__ -#define __PWM_H__ + ****/ #pragma once -#if defined(__cplusplus) -extern "C" { -#endif - -/*pwm.h: function and macro definition of PWM API , driver level */ -/*user_light.h: user interface for light API, user level*/ -/*user_light_adj: API for color changing and lighting effects, user level*/ - -/*NOTE!! : DO NOT CHANGE THIS FILE*/ - -/*SUPPORT UP TO 8 PWM CHANNEL*/ -#define PWM_CHANNEL_NUM_MAX 8 - -//struct pwm_param { -// uint32_t period; -// uint32_t freq; -// uint32_t duty[PWM_CHANNEL_NUM_MAX]; //PWM_CHANNEL<=8 -//}; -// -///* pwm_init should be called only once, for now */ -//void pwm_init(uint32_t period, uint32_t* duty, uint32_t pwm_channel_num, uint32_t (*pin_info_list)[3]); -//void pwm_start(void); -// -//void pwm_set_duty(uint32_t duty, uint8_t channel); -//uint32_t pwm_get_duty(uint8_t channel); -//void pwm_set_period(uint32_t period); -//uint32_t pwm_get_period(void); -// -//uint32_t get_pwm_version(void); -//void set_pwm_debug_en(uint8_t print_en); - -#if defined(__cplusplus) -} -#endif - +#ifdef SOC_LEDC_CHANNEL_NUM + #define PWM_CHANNEL_NUM_MAX SOC_LEDC_CHANNEL_NUM +#else + // this should not happen if the correct esp32 includes are used, just to be absolutely sure + #define PWM_CHANNEL_NUM_MAX 8 #endif diff --git a/Sming/Arch/Esp32/Core/HardwarePWM.cpp b/Sming/Arch/Esp32/Core/HardwarePWM.cpp new file mode 100644 index 0000000000..fcc387dc34 --- /dev/null +++ b/Sming/Arch/Esp32/Core/HardwarePWM.cpp @@ -0,0 +1,275 @@ +/**** + * 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. + * + * 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) + * + * 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 + * + */ + +#include +#include +#include +#include +#include +#include + +#define DEFAULT_RESOLUTION ledc_timer_bit_t(10) +#define DEFAULT_PERIOD 200 +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) +{ + return (period == 0) ? 0 : (1000000 / period); +} + +uint32_t frequencyToPeriod(uint32_t freq) +{ + return (freq == 0) ? 0 : (1000000 / freq); +} + +uint32_t maxDuty(ledc_timer_bit_t bits) +{ + return (1U << bits) - 1; +} + +} //namespace + +HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_of_pins) +{ + debug_d("starting HardwarePWM init"); + periph_module_enable(PERIPH_LEDC_MODULE); + if((no_of_pins == 0) || (no_of_pins > SOC_LEDC_CHANNEL_NUM)) { + return; + } + + for(uint8_t i = 0; i < no_of_pins; i++) { + channels[i] = pins[i]; + + /* + * Prepare and then apply the LEDC PWM timer configuration. + * This may configure the same timer more than once (in fact up to 8 times) + * which should not be an issue, though, since the values should be the same for all timers + */ + // The two groups (if available) are operating in different speed modes, hence speed mode is an alias for group or vice versa + ledc_timer_config_t ledc_timer{ + .speed_mode = pinToGroup(i), + .duty_resolution = LEDC_TIMER_10_BIT, // todo: make configurable later + .timer_num = pinToTimer(i), + .freq_hz = periodToFrequency(DEFAULT_PERIOD), // todo: make configurable later + .clk_cfg = LEDC_AUTO_CLK, + }; + + debug_d("ledc_timer.\r\n" + "\tspeed_mode: %i\r\n" + "\ttimer_num: %i\r\n" + "\tduty_resolution: %i\r\n" + "\tfreq: %i\n\tclk_cfg: %i\r\n\n", + ledc_timer.speed_mode, ledc_timer.timer_num, ledc_timer.duty_resolution, ledc_timer.freq_hz, + ledc_timer.clk_cfg); + ESP_ERROR_CHECK(ledc_timer_config(&ledc_timer)); + + /* + * Prepare and then apply the LEDC PWM channel configuration + */ + ledc_channel_config_t ledc_channel{ + .gpio_num = pins[i], + .speed_mode = pinToGroup(i), + .channel = pinToChannel(i), + .intr_type = LEDC_INTR_DISABLE, + .timer_sel = pinToTimer(i), + .duty = 0, + .hpoint = 0, + }; + debug_d("ledc_channel\n" + "\tspeed_mode: %i\r\n" + "\tchannel: %i\r\n" + "\ttimer_sel %i\r\n" + "\tintr_type: %i\r\n" + "\tgpio_num: %i\r\n" + "\tduty: %i\r\n" + "\thpoint: %i\r\n\n", + pinToGroup(i), pinToChannel(i), pinToTimer(i), ledc_channel.intr_type, pins[i], 0, 0); + ESP_ERROR_CHECK(ledc_channel_config(&ledc_channel)); + ledc_bind_channel_timer(pinToGroup(i), pinToChannel(i), pinToTimer(i)); + } + maxduty = maxDuty(DEFAULT_RESOLUTION); +} + +HardwarePWM::~HardwarePWM() +{ + // Stop pwm for all pins and set idle level to 0 + for(uint8_t i = 0; i < channel_count; i++) { + ledc_stop(pinToGroup(i), pinToChannel(i), 0); + } +} + +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) +{ + // esp32 defines the frequency / period per timer + return (chan == PWM_BAD_CHANNEL) ? 0 : ledc_get_duty(pinToGroup(chan), pinToChannel(chan)); +} + +bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) +{ + if(chan == PWM_BAD_CHANNEL) { + return false; + } + + if(duty <= maxduty) { + ESP_ERROR_CHECK(ledc_set_duty(pinToGroup(chan), pinToChannel(chan), duty)); + /* + * ignoring the update flag in this release, ToDo: implement a synchronized update mechanism + * if(update) { + * ESP_ERROR_CHECK(ledc_update_duty(pinToGroup(chan), pinToChannel(chan))); + * //update(); + * } + */ + ESP_ERROR_CHECK(ledc_update_duty(pinToGroup(chan), pinToChannel(chan))); + return true; + } + + debug_d("Duty cycle value too high for current period, max duty cycle is %d", maxduty); + return false; +} + +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(ledc_mode_t(0), ledc_timer_t(0))); +} + +void HardwarePWM::setPeriod(uint32_t period) +{ + // Set the frequency globally, will add per timer functions later. + // Also, this can be done smarter. + for(uint8_t i = 0; i < channel_count; i++) { + ESP_ERROR_CHECK(ledc_set_freq(pinToGroup(i), pinToTimer(i), periodToFrequency(period))); + } + // ledc_update_duty(); + update(); +} + +void HardwarePWM::update() +{ + // ledc_update_duty(); +} + +uint32_t HardwarePWM::getFrequency(uint8_t pin) +{ + return ledc_get_freq(pinToGroup(pin), pinToTimer(pin)); +} diff --git a/Sming/Arch/Esp8266/Components/driver/include/driver/pwm.h b/Sming/Arch/Esp8266/Components/driver/include/driver/pwm.h index e47435fa3a..2424ae320a 100644 --- a/Sming/Arch/Esp8266/Components/driver/include/driver/pwm.h +++ b/Sming/Arch/Esp8266/Components/driver/include/driver/pwm.h @@ -23,13 +23,13 @@ extern "C" { * * Example: * - * uint32 io_info[][3] = { + * uint32 ioInfo[][3] = { * {PWM_0_OUT_IO_MUX, PWM_0_OUT_IO_FUNC, PWM_0_OUT_IO_NUM}, * {PWM_1_OUT_IO_MUX, PWM_1_OUT_IO_FUNC, PWM_1_OUT_IO_NUM}, * {PWM_2_OUT_IO_MUX, PWM_2_OUT_IO_FUNC, PWM_2_OUT_IO_NUM} * }; * - * pwm_init(light_param.pwm_period, light_param.pwm_duty, 3, io_info); + * pwm_init(light_param.pwm_period, light_param.pwm_duty, 3, ioInfo); * */ diff --git a/Sming/Arch/Esp8266/Components/driver/new-pwm/README.md b/Sming/Arch/Esp8266/Components/driver/new-pwm/README.md index d9391a597e..8881d1f3c0 100644 --- a/Sming/Arch/Esp8266/Components/driver/new-pwm/README.md +++ b/Sming/Arch/Esp8266/Components/driver/new-pwm/README.md @@ -37,7 +37,7 @@ Example usage: const uint32_t period = 5000; // * 200ns ^= 1 kHz // PWM setup - uint32 io_info[PWM_CHANNELS][3] = { + uint32 ioInfo[PWM_CHANNELS][3] = { // MUX, FUNC, PIN {PERIPHS_IO_MUX_MTDI_U, FUNC_GPIO12, 12}, {PERIPHS_IO_MUX_MTDO_U, FUNC_GPIO15, 15}, @@ -47,9 +47,9 @@ Example usage: }; // initial duty: all off - uint32 pwm_duty_init[PWM_CHANNELS] = {0, 0, 0, 0, 0}; + uint32 pwmDutyInit[PWM_CHANNELS] = {0, 0, 0, 0, 0}; - pwm_init(period, pwm_duty_init, PWM_CHANNELS, io_info); + pwm_init(period, pwmDutyInit, PWM_CHANNELS, ioInfo); pwm_start(); // do something like this whenever you want to change duty diff --git a/Sming/Arch/Esp8266/Core/HardwarePWM.cpp b/Sming/Arch/Esp8266/Core/HardwarePWM.cpp index a1cbb10689..45a33d560f 100644 --- a/Sming/Arch/Esp8266/Core/HardwarePWM.cpp +++ b/Sming/Arch/Esp8266/Core/HardwarePWM.cpp @@ -23,31 +23,32 @@ * */ +#include #include #include "ESP8266EX.h" #include -#include - #define PERIOD_TO_MAX_DUTY(x) (x * 25) -HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t no_of_pins) : channel_count(no_of_pins) +HardwarePWM::HardwarePWM(uint8_t* pins, uint8_t noOfPins) : channel_count(noOfPins) { - if(no_of_pins > 0) { - uint32_t io_info[PWM_CHANNEL_NUM_MAX][3]; // pin information - uint32_t pwm_duty_init[PWM_CHANNEL_NUM_MAX]; // pwm duty - for(uint8_t i = 0; i < no_of_pins; i++) { - io_info[i][0] = EspDigitalPins[pins[i]].mux; - io_info[i][1] = EspDigitalPins[pins[i]].gpioFunc; - io_info[i][2] = EspDigitalPins[pins[i]].id; - pwm_duty_init[i] = 0; // Start with zero output - channels[i] = pins[i]; - } - const int initial_period = 1000; - pwm_init(initial_period, pwm_duty_init, no_of_pins, io_info); - update(); - maxduty = PERIOD_TO_MAX_DUTY(initial_period); // for period of 1000 + if(noOfPins == 0) { + return; + } + + uint32_t ioInfo[PWM_CHANNEL_NUM_MAX][3]; // pin information + uint32_t pwmDutyInit[PWM_CHANNEL_NUM_MAX]; // pwm duty + for(uint8_t i = 0; i < noOfPins; i++) { + ioInfo[i][0] = EspDigitalPins[pins[i]].mux; + ioInfo[i][1] = EspDigitalPins[pins[i]].gpioFunc; + ioInfo[i][2] = EspDigitalPins[pins[i]].id; + pwmDutyInit[i] = 0; // Start with zero output + channels[i] = pins[i]; } + const int initialPeriod = 1000; + pwm_init(initialPeriod, pwmDutyInit, noOfPins, ioInfo); + update(); + maxduty = PERIOD_TO_MAX_DUTY(initialPeriod); // for period of 1000 } HardwarePWM::~HardwarePWM() @@ -55,72 +56,46 @@ 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; } } - //debugf("getChannel: can't find pin %d", pin); + + debug_d("getChannel: can't find pin %d", pin); return PWM_BAD_CHANNEL; } -/* Function Name: getDutyChan - * Description: This function is used to get the duty cycle number for a given channel - * Parameters: chan -Esp8266 channel number - */ uint32_t HardwarePWM::getDutyChan(uint8_t chan) { - if(chan == PWM_BAD_CHANNEL) { - return 0; - } else { - return pwm_get_duty(chan); - } + return (chan == PWM_BAD_CHANNEL) ? 0 : pwm_get_duty(chan); } -/* Function Name: setDutyChan - * Description: This function is used to set the pwm duty cycle for a given channel. If parameter 'update' is false - * then you have to call update() later to update duties. - * Parameters: chan - channel number - * duty - duty cycle value - * update - update PWM output - */ bool HardwarePWM::setDutyChan(uint8_t chan, uint32_t duty, bool update) { if(chan == PWM_BAD_CHANNEL) { return false; - } else if(duty <= maxduty) { + } + + if(duty <= maxduty) { pwm_set_duty(duty, chan); if(update) { this->update(); } return true; - } else { - debugf("Duty cycle value too high for current period."); - return false; } + + debug_e("Duty cycle value too high for current period. max duty is %d.",maxduty); + return false; } -/* Function Name: getPeriod - * Description: This function is used to get Period of PWM. - * Period / frequency will remain same for all pins. - * - */ uint32_t HardwarePWM::getPeriod() { return pwm_get_period(); } -/* Function Name: setPeriod - * Description: This function is used to set Period of PWM. - * Period / frequency will remain same for all pins. - */ void HardwarePWM::setPeriod(uint32_t period) { maxduty = PERIOD_TO_MAX_DUTY(period); @@ -128,10 +103,14 @@ void HardwarePWM::setPeriod(uint32_t period) update(); } -/* Function Name: update - * Description: This function is used to actually update the PWM. - */ void HardwarePWM::update() { pwm_start(); } + +uint32_t HardwarePWM::getFrequency(uint8_t pin) +{ + (void)pin; + auto period = pwm_get_period(); + return (period == 0) ? 0 : 1000000U / period; +} diff --git a/Sming/Core/HardwarePWM.h b/Sming/Core/HardwarePWM.h index 78b27476fb..101f692899 100644 --- a/Sming/Core/HardwarePWM.h +++ b/Sming/Core/HardwarePWM.h @@ -122,6 +122,12 @@ class HardwarePWM */ void update(); + /** @brief Get PWM Frequency + * @param pin GPIO to get frequency for + * @retval uint32_t Value of Frequency + */ + uint32_t getFrequency(uint8_t pin); + private: uint8_t channel_count; uint8_t channels[PWM_CHANNEL_NUM_MAX];