-
-
Notifications
You must be signed in to change notification settings - Fork 349
added optional usePhaseShift
parameter to HardwarePWM constructor. …
#2955
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
PR Summary
|
oh heck, adding the optional parameter breaks all other arch's ? update: I have updated the signatures of esp8266/Host/rp2040, but leave the optional parameter unhandled there. |
d6a1c9f
to
8a1b61c
Compare
…This uses the esp32 ledc hpoint setting to stagger the starting point of pwm channels evenly across the pwm period to improve EMI of multi-channel high current PWM applications
8a1b61c
to
da69d82
Compare
6132f3b
to
aee88c6
Compare
I had a similar problem extending the UART driver. This is the original call: smg_uart_t* smg_uart_init(
uint8_t uart_nr,
uint32_t baudrate,
smg_uart_format_t format,
smg_uart_mode_t mode,
uint8_t tx_pin,
size_t rx_size,
size_t tx_size = 0); This already has way too many parameters for a simple function call, and it's also too easy to pass them in the wrong order. So I added a second constructor function: struct smg_uart_config_t {
uint8_t uart_nr;
uint8_t tx_pin;
uint8_t rx_pin;
smg_uart_mode_t mode;
uart_options_t options;
uint32_t baudrate;
smg_uart_format_t format;
size_t rx_size;
size_t tx_size;
};
smg_uart_t* smg_uart_init_ex(const smg_uart_config_t& cfg); I'd suggest a similar approach with class HardwarePWM
{
/**
* @brief Configuration for extended constructor
*
* Allows use of additional hardware feature for those platforms which support it.
*/
struct Config {
/**
* @brief Array of pins to control
*/
const uint8_t* pins;
/**
* @brief Number of pins in array
*/
uint8_t numberOfPins;
/**
* @brief ESP32 (and possibly RP2040) support phase shifting to control EMI
*/
bool usePhaseShift{false};
/**
* @brief Enable minimal spread spectrum modulation to further reduce EMI
*/
bool useSpreadSpectrum{false};
};
/**
* @brief Constructor to access advanced hardware capabilities
*/
HardwarePWM(const Config& config);
// ... etc.
}; And in user code: HardwarePWM pwm({
.pins = ...,
.numberOfPins = ...,
.usePhaseShift = ...,
}); If we only implement the ESP32 version (for now) then other architectures will fail to build if the extended constructor is used. There is the question of how to handle unsupported configurations. |
It might even be worth defining architecture-specific configuration; that could go in |
good points. |
The Rp2040 hardware abstraction is straightforward and adds no bloat so I just use it directly rather than trying to abstract it. If I ever did anything serious with the Esp32 I'd probably end up rewriting the driver code, just to get things closer to the metal. |
With this approach perhaps adding |
I was just pondering this, my LED controller firmware uses https://github.com/pljakobs/RGBWWLed which implements 10 bit PWM fades for Esp8266 and does so pretty well (along with color space conversion and color correction). |
I'd prefer not to introduce swathes of feature flags if possible. In C++ I think we'd probably define a separate interface for advanced functions, and that would be on a per-architecture basis. The esp32 IDF includes many useful capability flags (soc_caps.h) which can be useful. |
The So with that in mind perhaps a slight revision: class HardwarePWM
{
/**
* @brief Configuration for extended constructor
*
* Allows use of additional hardware feature for those platforms which support it.
*/
struct Options {
/**
* @brief ESP32 (and possible RP2040) support phase shifting to control EMI
*/
bool usePhaseShift{false};
/**
* @brief Enable minimal spread spectrum modulation to further reduce EMI
*/
bool useSpreadSpectrum{false};
};
/** @brief Instantiate hardware PWM object
* @param pins Pointer to array of pins to control
* @param no_of_pins Quantity of elements in array of pins
* @param options Archtecture-specific options to further refine operation
*/
HardwarePWM(const uint8_t* pins, uint8_t no_of_pins, const Options& options);
}; One other question is whether we might want to adjust these options whilst PWM is running? |
passing an architecture-specific options structure is a lean way to keep the current interface intact. |
so what I'm thinking is: class IHardwarePWM
{
//common interface definition for all platforms - essentially the current ESP8266 function set
} Arch//Components/driver/include/driver/pwm.h struct PWM_Options{
bool usePhaseShift = false; // Use ledc's hpoint phase shift feature to stagger the rising edge of the PWM signals evenly across the period. This is useful for driving multiple LEDs connected to the same power rail to reduce the peak current draw and help with electromagnetic interference.
bool useSpreadSpectrum = false; // implement minimal spread spectrum modulation to further reduce EMI. This feature uses a hardware timer to modulate the base frequency of the PWM signal. Depending on the base frequency, this may cause quite some system load, so choosing a good balance between CPU resources and acceptable EMI is important.
}; architecture specific class definition and implementation: class HardwarePWM : public IHardwarePWM
{
public:
/** @brief Instantiate hardware PWM object
* @param pins Pointer to array of pins to control
* @param no_of_pins Quantity of elements in array of pins
* @param usePhaseShift Parameter ignored on ESP8266 (present for API compatibility)
*/
HardwarePWM(const uint8_t* pins, uint8_t no_of_pins); This would, I guess, on one hand maintain compatibility with all current code while providing the freedom to extend the interface for specific hardware capabilities and/or software based implementations. Or is it too large a change? |
I will close this and continue in #2956 which is based on a larger rewrite |
this commit adds an optional third parameter to the HardwarePWM constructor to enable or disable pwm phase shifting. It should not break existing code and will just not enable the feature if the parameter is omitted.
HardwarePWM(const uint8_t* pins, uint8_t no_of_pins, bool usePhaseShift=false);
Instantiating HardwarePWM with
usePhaseShift=true
will use the Esp32's built in hpoint feature to delay the rising edge of a pwm signal, avoiding a big single transient at the beginning of every pwm cycle. In high current PWM environments, this should help reduce electromagnetic interferrence and transient load on the power supply.The current implementation equally delays the rising edge across the PWM cycle time, if you're running 1kHz PWM on five channels, meaning your cycle time is 1ms or 1000µs, the first channel will start at 0µs, the second at 200µs, the third at 400µs, 600µs and the last at 800µs. This does not impact the PWM setpoint, or duty cycle, it just pushes the whole signal "to the right".