diff --git a/cores/esp32/esp32-hal-ledc.c b/cores/esp32/esp32-hal-ledc.c index 76d522c2313..2005777d7bd 100644 --- a/cores/esp32/esp32-hal-ledc.c +++ b/cores/esp32/esp32-hal-ledc.c @@ -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) { diff --git a/cores/esp32/esp32-hal-ledc.h b/cores/esp32/esp32-hal-ledc.h index 2d2c42e9369..94dcf7509d1 100644 --- a/cores/esp32/esp32-hal-ledc.h +++ b/cores/esp32/esp32-hal-ledc.h @@ -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 diff --git a/cores/esp32/io_pin_remap.h b/cores/esp32/io_pin_remap.h index 9f09e2b8de2..853cdfa7e81 100644 --- a/cores/esp32/io_pin_remap.h +++ b/cores/esp32/io_pin_remap.h @@ -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) diff --git a/docs/en/api/ledc.rst b/docs/en/api/ledc.rst index 1abeea2f6ee..200e19f81e5 100644 --- a/docs/en/api/ledc.rst +++ b/docs/en/api/ledc.rst @@ -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 ******** diff --git a/docs/en/migration_guides/2.x_to_3.0.rst b/docs/en/migration_guides/2.x_to_3.0.rst index 452a7fb21b4..b07af795f77 100644 --- a/docs/en/migration_guides/2.x_to_3.0.rst +++ b/docs/en/migration_guides/2.x_to_3.0.rst @@ -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 *************** diff --git a/libraries/ESP32/examples/AnalogOut/LEDCFade/LEDCFade.ino b/libraries/ESP32/examples/AnalogOut/LEDCFade/LEDCFade.ino index 002df746014..19c2be3428d 100644 --- a/libraries/ESP32/examples/AnalogOut/LEDCFade/LEDCFade.ino +++ b/libraries/ESP32/examples/AnalogOut/LEDCFade/LEDCFade.ino @@ -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) diff --git a/libraries/ESP32/examples/AnalogOut/ledcWrite_RGB/ledcWrite_RGB.ino b/libraries/ESP32/examples/AnalogOut/ledcWrite_RGB/ledcWrite_RGB.ino index 1104a6b4c63..0135ab1d0c1 100644 --- a/libraries/ESP32/examples/AnalogOut/ledcWrite_RGB/ledcWrite_RGB.ino +++ b/libraries/ESP32/examples/AnalogOut/ledcWrite_RGB/ledcWrite_RGB.ino @@ -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);