feature(ledc): Add output invert option for LEDC pin + minor fixes (#9257)

* feat(ledc): Add output invert option for LEDC pin + minor fixes

* docs(ledc): Document LEDC functions in header file

* feat(ledc): Replace log2 with __builtin_ctz

* fix(ledc): Fixing free_channel for > 8 supported channels

* fix(ledc): Apply suggestions from code review

Co-authored-by: Rodrigo Garcia <rodrigo.garcia@espressif.com>

* fix(ledc): Added freq check to ledcChangeFrequency

* docs(ledc): Fix ledc documentation formatting

* docs(migration): Add new functions to the migration guide

* docs(ledc): Fix functions name and parameters

---------

Co-authored-by: Rodrigo Garcia <rodrigo.garcia@espressif.com>
Co-authored-by: Lucas Saavedra Vaz <32426024+lucasssvaz@users.noreply.github.com>
This commit is contained in:
Jan Procházka 2024-02-21 15:15:06 +01:00 committed by GitHub
parent 05e2abc58b
commit c4b55bb9f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 226 additions and 44 deletions

View File

@ -19,6 +19,8 @@
#include "esp32-hal-ledc.h"
#include "driver/ledc.h"
#include "esp32-hal-periman.h"
#include "soc/gpio_sig_map.h"
#include "esp_rom_gpio.h"
#ifdef SOC_LEDC_SUPPORT_HS_MODE
#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM<<1)
@ -40,7 +42,7 @@ typedef struct {
int used_channels : LEDC_CHANNELS; // Used channels as a bits
} ledc_periph_t;
ledc_periph_t ledc_handle;
ledc_periph_t ledc_handle = {0};
static bool fade_initialized = false;
@ -58,15 +60,28 @@ static bool ledcDetachBus(void * bus){
bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t channel)
{
if (channel >= LEDC_CHANNELS || resolution > LEDC_MAX_BIT_WIDTH)
{
log_e("Channel %u is not available! (maximum %u) or bit width too big (maximum %u)", channel, LEDC_CHANNELS, LEDC_MAX_BIT_WIDTH);
if(channel >= LEDC_CHANNELS || ledc_handle.used_channels & (1UL << channel)){
log_e("Channel %u is not available (maximum %u) or already used!", channel, LEDC_CHANNELS);
return false;
}
if (freq == 0) {
log_e("LEDC pin %u - frequency can't be zero.", pin);
return false;
}
if (resolution == 0 || resolution > LEDC_MAX_BIT_WIDTH){
log_e("LEDC pin %u - resolution is zero or it is too big (maximum %u)", pin, LEDC_MAX_BIT_WIDTH);
return false;
}
perimanSetBusDeinit(ESP32_BUS_TYPE_LEDC, ledcDetachBus);
ledc_channel_handle_t *bus = (ledc_channel_handle_t*)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
if(bus != NULL && !perimanClearPinBus(pin)){
if(bus != NULL){
log_e("Pin %u is already attached to LEDC (channel %u, resolution %u)", pin, bus->channel, bus->channel_resolution);
return false;
}
if(!perimanClearPinBus(pin)){
log_e("Pin %u is already attached to another bus and failed to detach", pin);
return false;
}
@ -119,12 +134,12 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c
bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution)
{
uint8_t free_channel = ~ledc_handle.used_channels & (ledc_handle.used_channels+1);
if (free_channel == 0 || resolution > LEDC_MAX_BIT_WIDTH){
log_e("No more LEDC channels available! (maximum %u) or bit width too big (maximum %u)", LEDC_CHANNELS, LEDC_MAX_BIT_WIDTH);
int free_channel = ~ledc_handle.used_channels & (ledc_handle.used_channels+1);
if (free_channel == 0){
log_e("No more LEDC channels available! (maximum is %u channels)", LEDC_CHANNELS);
return false;
}
int channel = log2(free_channel & -free_channel);
uint8_t channel = __builtin_ctz(free_channel); // Convert the free_channel bit to channel number
return ledcAttachChannel(pin, freq, resolution, channel);
}
@ -239,9 +254,12 @@ uint32_t ledcChangeFrequency(uint8_t pin, uint32_t freq, uint8_t resolution)
{
ledc_channel_handle_t *bus = (ledc_channel_handle_t*)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
if(bus != NULL){
if(resolution > LEDC_MAX_BIT_WIDTH){
log_e("LEDC resolution too big (maximum %u)", LEDC_MAX_BIT_WIDTH);
if (freq == 0) {
log_e("LEDC pin %u - frequency can't be zero.", pin);
return 0;
}
if (resolution == 0 || resolution > LEDC_MAX_BIT_WIDTH){
log_e("LEDC pin %u - resolution is zero or it is too big (maximum %u)", pin, LEDC_MAX_BIT_WIDTH);
return 0;
}
uint8_t group=(bus->channel/8), timer=((bus->channel/2)%4);
@ -265,6 +283,21 @@ uint32_t ledcChangeFrequency(uint8_t pin, uint32_t freq, uint8_t resolution)
return 0;
}
bool ledcOutputInvert(uint8_t pin, bool out_invert)
{
ledc_channel_handle_t *bus = (ledc_channel_handle_t*)perimanGetPinBus(pin, ESP32_BUS_TYPE_LEDC);
if(bus != NULL){
gpio_set_level(pin, out_invert);
#ifdef SOC_LEDC_SUPPORT_HS_MODE
esp_rom_gpio_connect_out_signal(pin, ((bus->channel/8 == 0) ? LEDC_HS_SIG_OUT0_IDX : LEDC_LS_SIG_OUT0_IDX) + ((bus->channel)%8), out_invert, 0);
#else
esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT0_IDX + ((bus->channel)%8), out_invert, 0);
#endif
return true;
}
return false;
}
static IRAM_ATTR bool ledcFnWrapper(const ledc_cb_param_t *param, void *user_arg)
{
if (param->event == LEDC_FADE_END_EVT) {

View File

@ -45,20 +45,150 @@ typedef struct {
#endif
} ledc_channel_handle_t;
//channel 0-15 resolution 1-16bits freq limits depend on resolution
bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution);
bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t channel);
bool ledcWrite(uint8_t pin, uint32_t duty);
uint32_t ledcWriteTone(uint8_t pin, uint32_t freq);
uint32_t ledcWriteNote(uint8_t pin, note_t note, uint8_t octave);
uint32_t ledcRead(uint8_t pin);
uint32_t ledcReadFreq(uint8_t pin);
bool ledcDetach(uint8_t pin);
uint32_t ledcChangeFrequency(uint8_t pin, uint32_t freq, uint8_t resolution);
/**
* @brief Attach a pin to the LEDC driver, with a given frequency and resolution.
* Channel is automatically assigned.
*
* @param pin GPIO pin
* @param freq frequency of PWM signal
* @param resolution resolution for LEDC pin
*
* @return true if configuration is successful and pin was successfully attached, false otherwise.
*/
bool ledcAttach(uint8_t pin, uint32_t freq, uint8_t resolution);
/**
* @brief Attach a pin to the LEDC driver, with a given frequency, resolution and channel.
*
* @param pin GPIO pin
* @param freq frequency of PWM signal
* @param resolution resolution for LEDC pin
* @param channel LEDC channel to attach to
*
* @return true if configuration is successful and pin was successfully attached, false otherwise.
*/
bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t channel);
/**
* @brief Set the duty cycle of a given pin.
*
* @param pin GPIO pin
* @param duty duty cycle to set
*
* @return true if duty cycle was successfully set, false otherwise.
*/
bool ledcWrite(uint8_t pin, uint32_t duty);
/**
* @brief Sets the duty to 50 % PWM tone on selected frequency.
*
* @param pin GPIO pin
* @param freq select frequency of pwm signal. If frequency is 0, duty will be set to 0.
*
* @return frequency if tone was successfully set.
* If ``0`` is returned, error occurs and LEDC pin was not configured.
*/
uint32_t ledcWriteTone(uint8_t pin, uint32_t freq);
/**
* @brief Sets the LEDC pin to specific note.
*
* @param pin GPIO pin
* @param note select note to be set (NOTE_C, NOTE_Cs, NOTE_D, NOTE_Eb, NOTE_E, NOTE_F, NOTE_Fs, NOTE_G, NOTE_Gs, NOTE_A, NOTE_Bb, NOTE_B).
* @param octave select octave for note.
*
* @return frequency if note was successfully set.
* If ``0`` is returned, error occurs and LEDC pin was not configured.
*/
uint32_t ledcWriteNote(uint8_t pin, note_t note, uint8_t octave);
/**
* @brief Read the duty cycle of a given LEDC pin.
*
* @param pin GPIO pin
*
* @return duty cycle of selected LEDC pin.
*/
uint32_t ledcRead(uint8_t pin);
/**
* @brief Read the frequency of a given LEDC pin.
*
* @param pin GPIO pin
*
* @return frequency of selected LEDC pin.
*/
uint32_t ledcReadFreq(uint8_t pin);
/**
* @brief Detach a pin from the LEDC driver.
*
* @param pin GPIO pin
*
* @return true if pin was successfully detached, false otherwise.
*/
bool ledcDetach(uint8_t pin);
/**
* @brief Change the frequency and resolution of a given LEDC pin.
*
* @param pin GPIO pin
* @param freq frequency of PWM signal
* @param resolution resolution for LEDC pin
*
* @return frequency configured for the LEDC channel.
* If ``0`` is returned, error occurs and LEDC pin was not configured.
*/
uint32_t ledcChangeFrequency(uint8_t pin, uint32_t freq, uint8_t resolution);
/**
* @brief Sets inverting of the output signal for a given LEDC pin.
*
* @param pin GPIO pin
* @param out_invert select, if output should be inverted (true = inverting output).
*
* @return true if output inverting was successfully set, false otherwise.
*/
bool ledcOutputInvert(uint8_t pin, bool out_invert);
//Fade functions
/**
* @brief Setup and start a fade on a given LEDC pin.
*
* @param pin GPIO pin
* @param start_duty initial duty cycle of the fade
* @param target_duty target duty cycle of the fade
* @param max_fade_time_ms maximum fade time in milliseconds
*
* @return true if fade was successfully set and started, false otherwise.
*/
bool ledcFade(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms);
/**
* @brief Setup and start a fade on a given LEDC pin with a callback function.
*
* @param pin GPIO pin
* @param start_duty initial duty cycle of the fade
* @param target_duty target duty cycle of the fade
* @param max_fade_time_ms maximum fade time in milliseconds
* @param userFunc callback function to be called after fade is finished
*
* @return true if fade was successfully set and started, false otherwise.
*/
bool ledcFadeWithInterrupt(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void));
/**
* @brief Setup and start a fade on a given LEDC pin with a callback function and argument.
*
* @param pin GPIO pin
* @param start_duty initial duty cycle of the fade
* @param target_duty target duty cycle of the fade
* @param max_fade_time_ms maximum fade time in milliseconds
* @param userFunc callback function to be called after fade is finished
* @param arg argument to be passed to the callback function
*
* @return true if fade was successfully set and started, false otherwise.
*/
bool ledcFadeWithInterruptArg(uint8_t pin, uint32_t start_duty, uint32_t target_duty, int max_fade_time_ms, void (*userFunc)(void*), void * arg);
#ifdef __cplusplus

View File

@ -53,14 +53,16 @@ int8_t gpioNumberToDigitalPin(int8_t gpioNumber);
#define i2cSlaveInit(num, sda, scl, slaveID, frequency, rx_len, tx_len) i2cSlaveInit(num, digitalPinToGPIONumber(sda), digitalPinToGPIONumber(scl), slaveID, frequency, rx_len, tx_len)
// cores/esp32/esp32-hal-ledc.h
#define ledcAttach(pin, freq, resolution) ledcAttach(digitalPinToGPIONumber(pin), freq, resolution)
#define ledcWrite(pin, duty) ledcWrite(digitalPinToGPIONumber(pin), duty)
#define ledcWriteTone(pin, freq) ledcWriteTone(digitalPinToGPIONumber(pin), freq)
#define ledcWriteNote(pin, note, octave) ledcWriteNote(digitalPinToGPIONumber(pin), note, octave)
#define ledcRead(pin) ledcRead(digitalPinToGPIONumber(pin))
#define ledcReadFreq(pin) ledcReadFreq(digitalPinToGPIONumber(pin))
#define ledcDetach(pin) ledcDetach(digitalPinToGPIONumber(pin))
#define ledcChangeFrequency(pin, freq, resolution) ledcChangeFrequency(digitalPinToGPIONumber(pin), freq, resolution)
#define ledcAttach(pin, freq, resolution) ledcAttach(digitalPinToGPIONumber(pin), freq, resolution)
#define ledcAttachChannel(pin, freq, resolution, channel) ledcAttachChannel(digitalPinToGPIONumber(pin), freq, resolution, channel)
#define ledcWrite(pin, duty) ledcWrite(digitalPinToGPIONumber(pin), duty)
#define ledcWriteTone(pin, freq) ledcWriteTone(digitalPinToGPIONumber(pin), freq)
#define ledcWriteNote(pin, note, octave) ledcWriteNote(digitalPinToGPIONumber(pin), note, octave)
#define ledcRead(pin) ledcRead(digitalPinToGPIONumber(pin))
#define ledcReadFreq(pin) ledcReadFreq(digitalPinToGPIONumber(pin))
#define ledcDetach(pin) ledcDetach(digitalPinToGPIONumber(pin))
#define ledcChangeFrequency(pin, freq, resolution) ledcChangeFrequency(digitalPinToGPIONumber(pin), freq, resolution)
#define ledcOutputInvert(pin, out_invert) ledcOutputInvert(digitalPinToGPIONumber(pin), out_invert)
#define ledcFade(pin, start_duty, target_duty, max_fade_time_ms) ledcFade(digitalPinToGPIONumber(pin), start_duty, target_duty, max_fade_time_ms)
#define ledcFadeWithInterrupt(pin, start_duty, target_duty, max_fade_time_ms, userFunc) ledcFadeWithInterrupt(digitalPinToGPIONumber(pin), start_duty, target_duty, max_fade_time_ms, userFunc)

View File

@ -26,8 +26,7 @@ Arduino-ESP32 LEDC API
ledcAttach
**********
This function is used to setup LEDC pin with given frequency and resolution.
LEDC channel will be selected automatically.
This function is used to setup LEDC pin with given frequency and resolution. LEDC channel will be selected automatically.
.. code-block:: arduino
@ -35,10 +34,10 @@ LEDC channel will be selected automatically.
* ``pin`` select LEDC pin.
* ``freq`` select frequency of pwm.
* ``resolution`` select resolution for LEDC channel.
* ``resolution`` select resolution for LEDC channel.
* range is 1-14 bits (1-20 bits for ESP32).
This function will return ``true`` if configuration is successful.
If ``false`` is returned, error occurs and LEDC channel was not configured.
@ -49,15 +48,16 @@ This function is used to setup LEDC pin with given frequency, resolution and cha
.. code-block:: arduino
bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t channel);
bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, int8_t channel);
* ``pin`` select LEDC pin.
* ``freq`` select frequency of pwm.
* ``resolution`` select resolution for LEDC channel.
* ``channel`` select LEDC channel.
* ``resolution`` select resolution for LEDC channel.
* range is 1-14 bits (1-20 bits for ESP32).
* ``channel`` select LEDC channel.
This function will return ``true`` if configuration is successful.
If ``false`` is returned, error occurs and LEDC channel was not configured.
@ -171,6 +171,21 @@ This function is used to set frequency for the LEDC pin.
This function will return ``frequency`` configured for the LEDC channel.
If ``0`` is returned, error occurs and the LEDC channel frequency was not set.
ledcOutputInvert
****************
This function is used to set inverting output for the LEDC pin.
.. code-block:: arduino
bool ledcOutputInvert(uint8_t pin, bool out_invert);
* ``pin`` select LEDC pin.
* ``out_invert`` select, if output should be inverted (true = inverting output).
This function returns ``true`` if setting inverting output was successful.
If ``false`` is returned, an error occurred and the inverting output was not set.
ledcFade
********

View File

@ -63,8 +63,10 @@ New APIs
********
* ``ledcAttach`` used to set up the LEDC pin (merged ``ledcSetup`` and ``ledcAttachPin`` functions).
* ``timerGetFrequency`` used to get the actual frequency of the timer.
* ``timerAttachInterruptArg`` used to attach the interrupt to a timer using arguments.
* ``ledcOutputInvert`` used to attach the interrupt to a timer using arguments.
* ``ledcFade`` used to set up and start a fade on a given LEDC pin.
* ``ledcFadeWithInterrupt`` used to set up and start a fade on a given LEDC pin with an interrupt.
* ``ledcFadeWithInterruptArg`` used to set up and start a fade on a given LEDC pin with an interrupt using arguments.
Changes in APIs
***************

View File

@ -33,7 +33,7 @@ void setup() {
Serial.begin(115200);
while(!Serial) delay(10);
// Setup timer and attach timer to a led pins
// Setup timer with given frequency, resolution and attach it to a led pin with auto-selected channel
ledcAttach(LED_PIN, LEDC_BASE_FREQ, LEDC_TIMER_12_BIT);
// Setup and start fade on led (duty from 0 to 4095)

View File

@ -26,7 +26,7 @@ void setup()
delay(10);
// Initialize pins as LEDC channels
// resolution 1-16 bits, freq limits depend on resolution
// resolution 1-16 bits, freq limits depend on resolution, channel is automatically selected
ledcAttach(ledR, 12000, 8); // 12 kHz PWM, 8-bit resolution
ledcAttach(ledG, 12000, 8);
ledcAttach(ledB, 12000, 8);