diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index 0e7f716929..9a62e1774d 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -17,10 +17,43 @@ extern "C" { #endif -#define LEDC_APB_CLK_HZ (APB_CLK_FREQ) -#define LEDC_REF_CLK_HZ (REF_CLK_FREQ) -#define LEDC_ERR_DUTY (0xFFFFFFFF) -#define LEDC_ERR_VAL (-1) +#define LEDC_APB_CLK_HZ (APB_CLK_FREQ) +#define LEDC_REF_CLK_HZ (REF_CLK_FREQ) +#define LEDC_ERR_DUTY (0xFFFFFFFF) +#define LEDC_ERR_VAL (-1) + +/** + * @brief Configuration parameters of LEDC channel for ledc_channel_config function + */ +typedef struct { + int gpio_num; /*!< the LEDC output gpio_num, if you want to use gpio16, gpio_num = 16 */ + ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */ + ledc_channel_t channel; /*!< LEDC channel (0 - 7) */ + ledc_intr_type_t intr_type; /*!< configure interrupt, Fade interrupt enable or Fade interrupt disable */ + ledc_timer_t timer_sel; /*!< Select the timer source of channel (0 - 3) */ + uint32_t duty; /*!< LEDC channel duty, the range of duty setting is [0, (2**duty_resolution)] */ + int hpoint; /*!< LEDC channel hpoint value, the max value is 0xfffff */ + struct { + unsigned int output_invert: 1;/*!< Enable (1) or disable (0) gpio output invert */ + } flags; /*!< LEDC flags */ + +} ledc_channel_config_t; + +/** + * @brief Configuration parameters of LEDC Timer timer for ledc_timer_config function + */ +typedef struct { + ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */ + union { + ledc_timer_bit_t duty_resolution; /*!< LEDC channel duty resolution */ + ledc_timer_bit_t bit_num __attribute__((deprecated)); /*!< Deprecated in ESP-IDF 3.0. This is an alias to 'duty_resolution' for backward compatibility with ESP-IDF 2.1 */ + }; + ledc_timer_t timer_num; /*!< The timer source of channel (0 - 3) */ + uint32_t freq_hz; /*!< LEDC timer frequency (Hz) */ + ledc_clk_cfg_t clk_cfg; /*!< Configure LEDC source clock. + For low speed channels and high speed channels, you can specify the source clock using LEDC_USE_REF_TICK, LEDC_USE_APB_CLK or LEDC_AUTO_CLK. + For low speed channels, you can also specify the source clock using LEDC_USE_RTC8M_CLK, in this case, all low speed channel's source clock must be RTC8M_CLK*/ +} ledc_timer_config_t; typedef intr_handle_t ledc_isr_handle_t; diff --git a/components/driver/ledc.c b/components/driver/ledc.c index 3044ab3e15..f2069fed4e 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -62,6 +62,8 @@ static portMUX_TYPE ledc_spinlock = portMUX_INITIALIZER_UNLOCKED; #define SLOW_CLK_CYC_CALIBRATE (13) #define LEDC_FADE_TOO_SLOW_STR "LEDC FADE TOO SLOW" #define LEDC_FADE_TOO_FAST_STR "LEDC FADE TOO FAST" +#define DIM(array) (sizeof(array)/sizeof(*array)) +#define LEDC_IS_DIV_INVALID(div) ((div) <= LEDC_LL_FRACTIONAL_MAX || (div) > LEDC_TIMER_DIV_NUM_MAX) static __attribute__((unused)) const char *LEDC_NOT_INIT = "LEDC is not initialized"; static __attribute__((unused)) const char *LEDC_FADE_SERVICE_ERR_STR = "LEDC fade service not installed"; @@ -101,10 +103,12 @@ static uint32_t ledc_get_src_clk_freq(ledc_clk_cfg_t clk_cfg) uint32_t src_clk_freq = 0; if (clk_cfg == LEDC_USE_APB_CLK) { src_clk_freq = LEDC_APB_CLK_HZ; - } else if (clk_cfg == LEDC_USE_REF_TICK) { - src_clk_freq = LEDC_REF_CLK_HZ; } else if (clk_cfg == LEDC_USE_RTC8M_CLK) { src_clk_freq = s_ledc_slow_clk_8M; +#if SOC_LEDC_SUPPORT_REF_TICK + } else if (clk_cfg == LEDC_USE_REF_TICK) { + src_clk_freq = LEDC_REF_CLK_HZ; +#endif #if SOC_LEDC_SUPPORT_XTAL_CLOCK } else if (clk_cfg == LEDC_USE_XTAL_CLK) { src_clk_freq = rtc_clk_xtal_freq_get() * 1000000; @@ -174,7 +178,11 @@ esp_err_t ledc_timer_set(ledc_mode_t speed_mode, ledc_timer_t timer_sel, uint32_ LEDC_CHECK(p_ledc_obj[speed_mode] != NULL, LEDC_NOT_INIT, ESP_ERR_INVALID_STATE); portENTER_CRITICAL(&ledc_spinlock); ledc_hal_set_clock_divider(&(p_ledc_obj[speed_mode]->ledc_hal), timer_sel, clock_divider); +#if SOC_LEDC_HAS_TIMER_SPECIFIC_MUX + /* Clock source can only be configured on boards which support timer-specific + * source clock. */ ledc_hal_set_clock_source(&(p_ledc_obj[speed_mode]->ledc_hal), timer_sel, clk_src); +#endif ledc_hal_set_duty_resolution(&(p_ledc_obj[speed_mode]->ledc_hal), timer_sel, duty_resolution); ledc_ls_timer_update(speed_mode, timer_sel); portEXIT_CRITICAL(&ledc_spinlock); @@ -258,52 +266,211 @@ esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, return ret; } -// Setting the LEDC timer divisor with the given source clock, frequency and resolution. +static inline uint32_t ledc_calculate_divisor(uint32_t src_clk_freq, int freq_hz, uint32_t precision) +{ + /** + * In order to find the right divisor, we need to divide the source clock + * frequency by the desired frequency. However, two things to note here: + * - The lowest LEDC_LL_FRACTIONAL_BITS bits of the result are the FRACTIONAL + * part. The higher bits represent the integer part, this is why we need + * to right shift the source frequency. + * - The `precision` parameter represents the granularity of the clock. It + * **must** be a power of 2. It means that the resulted divisor is + * a multiplier of `precision`. + * + * Let's take a concrete example, we need to generate a 5KHz clock out of + * a 80MHz clock (APB). + * If the precision is 1024 (10 bits), the resulted multiplier is: + * (80000000 << 8) / (5000 * 1024) = 4000 (0xfa0) + * Let's ignore the fractional part to simplify the exaplanation, so we get + * a result of 15 (0xf). + * This can be interpreted as: every 15 "precision" ticks, the resulted + * clock will go high, where one precision tick is made out of 1024 source + * clock ticks. + * Thus, every `15 * 1024` source clock ticks, the resulted clock will go + * high. */ + return ( (uint64_t) src_clk_freq << LEDC_LL_FRACTIONAL_BITS ) / (freq_hz * precision); +} + +static inline uint32_t ledc_auto_global_clk_divisor(int freq_hz, uint32_t precision, ledc_clk_cfg_t* clk_target) +{ + uint32_t div_param = 0; + uint32_t i = 0; + uint32_t clk_freq = 0; + /* This function will go through all the following clock sources to look + * for a valid divisor which generates the requested frequency. */ + const ledc_clk_cfg_t glb_clks[] = LEDC_LL_GLOBAL_CLOCKS; + + for (i = 0; i < DIM(glb_clks); i++) { + /* Before calculating the divisor, we need to have the RTC frequency. + * If it hasn't been mesured yet, try calibrating it now. */ + if (glb_clks[i] == LEDC_SLOW_CLK_RTC8M && s_ledc_slow_clk_8M == 0 && !ledc_slow_clk_calibrate()) { + ESP_LOGD(LEDC_TAG, "Unable to retrieve RTC clock frequency, skipping it\n"); + continue; + } + + clk_freq = ledc_get_src_clk_freq(glb_clks[i]); + div_param = ledc_calculate_divisor(clk_freq, freq_hz, precision); + + /* If the divisor is valid, we can return this value. */ + if (!LEDC_IS_DIV_INVALID(div_param)) { + *clk_target = glb_clks[i]; + break; + } + } + + return div_param; + +} + +#if SOC_LEDC_HAS_TIMER_SPECIFIC_MUX +static inline uint32_t ledc_auto_timer_specific_clk_divisor(ledc_mode_t speed_mode, int freq_hz, uint32_t precision, + ledc_clk_src_t* clk_source) +{ + uint32_t div_param = 0; + uint32_t i = 0; + + /* Use an anonymous structure, only this function requires it. + * Get the list of the timer-specific clocks, try to find one for the reuested frequency. */ + const struct { ledc_clk_src_t clk; uint32_t freq; } specific_clks[] = LEDC_LL_TIMER_SPECIFIC_CLOCKS; + + for (i = 0; i < DIM(specific_clks); i++) { + div_param = ledc_calculate_divisor(specific_clks[i].freq, freq_hz, precision); + + /* If the divisor is valid, we can return this value. */ + if (!LEDC_IS_DIV_INVALID(div_param)) { + *clk_source = specific_clks[i].clk; + break; + } + } + +#if SOC_LEDC_SUPPORT_HS_MODE + /* On board that support LEDC high-speed mode, APB clock becomes a timer- + * specific clock when in high speed mode. Check if it is necessary here + * to test APB. */ + if (speed_mode == LEDC_HIGH_SPEED_MODE && i == DIM(specific_clks)) { + /* No divider was found yet, try with APB! */ + div_param = ledc_calculate_divisor(LEDC_APB_CLK_HZ, freq_hz, precision); + + if (!LEDC_IS_DIV_INVALID(div_param)) { + *clk_source = LEDC_APB_CLK; + } + } +#endif + + return div_param; +} +#endif + +/** + * @brief Try to find the clock with its divisor giving the frequency requested + * by the caller. + */ +static uint32_t ledc_auto_clk_divisor(ledc_mode_t speed_mode, int freq_hz, uint32_t precision, + ledc_clk_src_t* clk_source, ledc_clk_cfg_t* clk_target) +{ + uint32_t div_param = 0; + +#if SOC_LEDC_HAS_TIMER_SPECIFIC_MUX + /* If the SoC presents timer-specific clock(s), try to achieve the given frequency + * thanks to it/them. + * clk_source parameter will returned by this function. */ + div_param = ledc_auto_timer_specific_clk_divisor(speed_mode, freq_hz, precision, clk_source); + + if (!LEDC_IS_DIV_INVALID(div_param)) { + /* The dividor is valid, no need try any other clock, return directly. */ + return div_param; + } +#endif + + /* On ESP32, only low speed channel can use the global clocks. For other + * chips, there are no high speed channels. */ + if (speed_mode == LEDC_LOW_SPEED_MODE) { + div_param = ledc_auto_global_clk_divisor(freq_hz, precision, clk_target); + } + + return div_param; +} + +/** + * @brief Function setting the LEDC timer divisor with the given source clock, + * frequency and resolution. If the clock configuration passed is + * LEDC_AUTO_CLK, the clock will be determined automatically (if possible). + */ static esp_err_t ledc_set_timer_div(ledc_mode_t speed_mode, ledc_timer_t timer_num, ledc_clk_cfg_t clk_cfg, int freq_hz, int duty_resolution) { uint32_t div_param = 0; - uint32_t precision = ( 0x1 << duty_resolution ); - ledc_clk_src_t timer_clk_src = LEDC_APB_CLK; - // Calculate the divisor - // User specified source clock(RTC8M_CLK) for low speed channel - if ((speed_mode == LEDC_LOW_SPEED_MODE) && (clk_cfg == LEDC_USE_RTC8M_CLK)) { - if(s_ledc_slow_clk_8M == 0) { - if (ledc_slow_clk_calibrate() == false) { - goto error; - } + const uint32_t precision = ( 0x1 << duty_resolution ); + /* This variable represents the timer's mux value. It will be overwritten + * if a timer-specific clock is used. */ + ledc_clk_src_t timer_clk_src = LEDC_SCLK; + uint32_t src_clk_freq = 0; + + if (clk_cfg == LEDC_AUTO_CLK) { + /* User hasn't specified the speed, we should try to guess it. */ + div_param = ledc_auto_clk_divisor(speed_mode, freq_hz, precision, &timer_clk_src, &clk_cfg); + + } else if (clk_cfg == LEDC_USE_RTC8M_CLK) { + /* User specified source clock(RTC8M_CLK) for low speed channel. + * Make sure the speed mode is correct. */ + ESP_RETURN_ON_FALSE((speed_mode == LEDC_LOW_SPEED_MODE), ESP_ERR_INVALID_ARG, LEDC_TAG, "RTC clock can only be used in low speed mode"); + + /* Before calculating the divisor, we need to have the RTC frequency. + * If it hasn't been mesured yet, try calibrating it now. */ + if(s_ledc_slow_clk_8M == 0 && ledc_slow_clk_calibrate() == false) { + goto error; } - div_param = ( (uint64_t) s_ledc_slow_clk_8M << 8 ) / freq_hz / precision; + + /* We have the RTC clock frequency now. */ + div_param = ledc_calculate_divisor(s_ledc_slow_clk_8M, freq_hz, precision); + } else { - // Automatically select APB or REF_TICK as the source clock. - if (clk_cfg == LEDC_AUTO_CLK) { - // Try calculating divisor based on LEDC_APB_CLK - div_param = ( (uint64_t) LEDC_APB_CLK_HZ << 8 ) / freq_hz / precision; - if (div_param > LEDC_TIMER_DIV_NUM_MAX) { - // APB_CLK results in divisor which too high. Try using REF_TICK as clock source. - timer_clk_src = LEDC_REF_TICK; - div_param = ((uint64_t) LEDC_REF_CLK_HZ << 8) / freq_hz / precision; - } else if (div_param < 256) { - // divisor is too low - goto error; - } - // User specified source clock(LEDC_APB_CLK_HZ or LEDC_REF_TICK) - } else { - timer_clk_src = (clk_cfg == LEDC_USE_REF_TICK) ? LEDC_REF_TICK : LEDC_APB_CLK; - uint32_t src_clk_freq = ledc_get_src_clk_freq(clk_cfg); - div_param = ( (uint64_t) src_clk_freq << 8 ) / freq_hz / precision; + +#if SOC_LEDC_HAS_TIMER_SPECIFIC_MUX + if (LEDC_LL_IS_TIMER_SPECIFIC_CLOCK(speed_mode, clk_cfg)) { + /* Currently we can convert a timer specific clock to a source clock that + * easily because their values are identical in the enumerations (on purpose) + * If we decide to change the values in the future, we should consider defining + * a macro/function to convert timer-specific clock to clock source .*/ + timer_clk_src = (ledc_clk_src_t) clk_cfg; } +#endif + src_clk_freq = ledc_get_src_clk_freq(clk_cfg); + div_param = ledc_calculate_divisor(src_clk_freq, freq_hz, precision); } - if (div_param < 256 || div_param > LEDC_TIMER_DIV_NUM_MAX) { + + if (LEDC_IS_DIV_INVALID(div_param)) { goto error; } + + /* The following debug message makes more sense for AUTO mode. */ + ESP_LOGD(LEDC_TAG, "Using clock source %d (in %s mode), divisor: 0x%x\n", + timer_clk_src, (speed_mode == LEDC_LOW_SPEED_MODE ? "slow" : "fast"), div_param); + + /* The following block configures the global clock. + * Thus, in theory, this only makes sense when the source clock is LEDC_SCLK + * and in LOW_SPEED_MODE (as FAST_SPEED_MODE doesn't present any global clock) + * + * However, in practice, on modules that support high-speed mode, no matter + * whether the source clock is a timer-specific one (e.g. REF_TICK) or not, + * the global clock MUST be configured when in low speed mode. + * When using high-speed mode, this is not necessary. + */ +#if SOC_LEDC_SUPPORT_HS_MODE if (speed_mode == LEDC_LOW_SPEED_MODE) { +#else + if (timer_clk_src == LEDC_SCLK) { +#endif + ESP_LOGD(LEDC_TAG, "In slow speed mode, using clock %d", clk_cfg); portENTER_CRITICAL(&ledc_spinlock); ledc_hal_set_slow_clk(&(p_ledc_obj[speed_mode]->ledc_hal), clk_cfg); portEXIT_CRITICAL(&ledc_spinlock); } - //Set the divisor + + /* The divisor is correct, we can write in the hardware. */ ledc_timer_set(speed_mode, timer_num, div_param, duty_resolution, timer_clk_src); - // reset the timer + + /* Reset the timer. */ ledc_timer_rst(speed_mode, timer_num); return ESP_OK; error: diff --git a/components/hal/esp32/include/hal/ledc_ll.h b/components/hal/esp32/include/hal/ledc_ll.h index 1682c7c410..0631f087de 100644 --- a/components/hal/esp32/include/hal/ledc_ll.h +++ b/components/hal/esp32/include/hal/ledc_ll.h @@ -22,6 +22,27 @@ #include "soc/ledc_struct.h" #define LEDC_LL_GET_HW() &LEDC +#define LEDC_LL_FRACTIONAL_BITS (8) +#define LEDC_LL_FRACTIONAL_MAX ((1 << LEDC_LL_FRACTIONAL_BITS) - 1) + +#define LEDC_LL_GLOBAL_CLOCKS { \ + LEDC_USE_APB_CLK, \ + LEDC_USE_RTC8M_CLK, \ + } +#define LEDC_LL_TIMER_SPECIFIC_CLOCKS \ + {\ + { \ + .clk = LEDC_REF_TICK, \ + .freq = LEDC_REF_CLK_HZ, \ + } \ + } + +/* On ESP32, APB clock is a timer-specific clock only in fast clock mode */ +#define LEDC_LL_IS_TIMER_SPECIFIC_CLOCK(SPEED, CLK) (\ + ((CLK) == LEDC_USE_REF_TICK) || \ + ((SPEED) == LEDC_HIGH_SPEED_MODE && (CLK) == LEDC_USE_APB_CLK) \ + ) + #ifdef __cplusplus extern "C" { diff --git a/components/hal/esp32c3/include/hal/ledc_ll.h b/components/hal/esp32c3/include/hal/ledc_ll.h index fb495b0319..9976532974 100644 --- a/components/hal/esp32c3/include/hal/ledc_ll.h +++ b/components/hal/esp32c3/include/hal/ledc_ll.h @@ -26,6 +26,13 @@ extern "C" { #endif #define LEDC_LL_GET_HW() &LEDC +#define LEDC_LL_FRACTIONAL_BITS (8) +#define LEDC_LL_FRACTIONAL_MAX ((1 << LEDC_LL_FRACTIONAL_BITS) - 1) +#define LEDC_LL_GLOBAL_CLOCKS { \ + LEDC_USE_APB_CLK, \ + LEDC_USE_XTAL_CLK, \ + LEDC_USE_RTC8M_CLK, \ + } /** * @brief Set LEDC low speed timer clock @@ -155,27 +162,6 @@ static inline void ledc_ll_get_clock_divider(ledc_dev_t *hw, ledc_mode_t speed_m *clock_divider = hw->timer_group[speed_mode].timer[timer_sel].conf.clock_divider; } -/** - * @brief Set LEDC timer clock source - * - * @param hw Beginning address of the peripheral registers - * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode - * @param timer_sel LEDC timer index (0-3), select from ledc_timer_t - * @param clk_src Timer clock source - * - * @return None - */ -static inline void ledc_ll_set_clock_source(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_timer_t timer_sel, ledc_clk_src_t clk_src) -{ - if (clk_src == LEDC_REF_TICK) { - //REF_TICK can only be used when APB is selected. - hw->timer_group[speed_mode].timer[timer_sel].conf.tick_sel = 1; - hw->conf.apb_clk_sel = 1; - } else { - hw->timer_group[speed_mode].timer[timer_sel].conf.tick_sel = 0; - } -} - /** * @brief Get LEDC timer clock source * @@ -188,11 +174,7 @@ static inline void ledc_ll_set_clock_source(ledc_dev_t *hw, ledc_mode_t speed_mo */ static inline void ledc_ll_get_clock_source(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_timer_t timer_sel, ledc_clk_src_t *clk_src) { - if (hw->timer_group[speed_mode].timer[timer_sel].conf.tick_sel == 1) { - *clk_src = LEDC_REF_TICK; - } else { - *clk_src = LEDC_APB_CLK; - } + *clk_src = LEDC_APB_CLK; } /** diff --git a/components/hal/esp32h2/include/hal/ledc_ll.h b/components/hal/esp32h2/include/hal/ledc_ll.h index fb495b0319..9976532974 100644 --- a/components/hal/esp32h2/include/hal/ledc_ll.h +++ b/components/hal/esp32h2/include/hal/ledc_ll.h @@ -26,6 +26,13 @@ extern "C" { #endif #define LEDC_LL_GET_HW() &LEDC +#define LEDC_LL_FRACTIONAL_BITS (8) +#define LEDC_LL_FRACTIONAL_MAX ((1 << LEDC_LL_FRACTIONAL_BITS) - 1) +#define LEDC_LL_GLOBAL_CLOCKS { \ + LEDC_USE_APB_CLK, \ + LEDC_USE_XTAL_CLK, \ + LEDC_USE_RTC8M_CLK, \ + } /** * @brief Set LEDC low speed timer clock @@ -155,27 +162,6 @@ static inline void ledc_ll_get_clock_divider(ledc_dev_t *hw, ledc_mode_t speed_m *clock_divider = hw->timer_group[speed_mode].timer[timer_sel].conf.clock_divider; } -/** - * @brief Set LEDC timer clock source - * - * @param hw Beginning address of the peripheral registers - * @param speed_mode LEDC speed_mode, high-speed mode or low-speed mode - * @param timer_sel LEDC timer index (0-3), select from ledc_timer_t - * @param clk_src Timer clock source - * - * @return None - */ -static inline void ledc_ll_set_clock_source(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_timer_t timer_sel, ledc_clk_src_t clk_src) -{ - if (clk_src == LEDC_REF_TICK) { - //REF_TICK can only be used when APB is selected. - hw->timer_group[speed_mode].timer[timer_sel].conf.tick_sel = 1; - hw->conf.apb_clk_sel = 1; - } else { - hw->timer_group[speed_mode].timer[timer_sel].conf.tick_sel = 0; - } -} - /** * @brief Get LEDC timer clock source * @@ -188,11 +174,7 @@ static inline void ledc_ll_set_clock_source(ledc_dev_t *hw, ledc_mode_t speed_mo */ static inline void ledc_ll_get_clock_source(ledc_dev_t *hw, ledc_mode_t speed_mode, ledc_timer_t timer_sel, ledc_clk_src_t *clk_src) { - if (hw->timer_group[speed_mode].timer[timer_sel].conf.tick_sel == 1) { - *clk_src = LEDC_REF_TICK; - } else { - *clk_src = LEDC_APB_CLK; - } + *clk_src = LEDC_APB_CLK; } /** diff --git a/components/hal/esp32s2/include/hal/ledc_ll.h b/components/hal/esp32s2/include/hal/ledc_ll.h index 677a2e36d3..3727395ef1 100644 --- a/components/hal/esp32s2/include/hal/ledc_ll.h +++ b/components/hal/esp32s2/include/hal/ledc_ll.h @@ -26,6 +26,22 @@ extern "C" { #endif #define LEDC_LL_GET_HW() &LEDC +#define LEDC_LL_FRACTIONAL_BITS (8) +#define LEDC_LL_FRACTIONAL_MAX ((1 << LEDC_LL_FRACTIONAL_BITS) - 1) +#define LEDC_LL_GLOBAL_CLOCKS { \ + LEDC_USE_APB_CLK, \ + LEDC_USE_XTAL_CLK, \ + LEDC_USE_RTC8M_CLK, \ + } +#define LEDC_LL_TIMER_SPECIFIC_CLOCKS \ + {\ + { \ + .clk = LEDC_REF_TICK, \ + .freq = LEDC_REF_CLK_HZ, \ + } \ + } + +#define LEDC_LL_IS_TIMER_SPECIFIC_CLOCK(SPEED, CLK) ((CLK) == LEDC_USE_REF_TICK) /** * @brief Set LEDC low speed timer clock diff --git a/components/hal/esp32s3/include/hal/ledc_ll.h b/components/hal/esp32s3/include/hal/ledc_ll.h index fb495b0319..aabf342492 100644 --- a/components/hal/esp32s3/include/hal/ledc_ll.h +++ b/components/hal/esp32s3/include/hal/ledc_ll.h @@ -26,6 +26,22 @@ extern "C" { #endif #define LEDC_LL_GET_HW() &LEDC +#define LEDC_LL_FRACTIONAL_BITS (8) +#define LEDC_LL_FRACTIONAL_MAX ((1 << LEDC_LL_FRACTIONAL_BITS) - 1) +#define LEDC_LL_GLOBAL_CLOCKS { \ + LEDC_USE_APB_CLK, \ + LEDC_USE_XTAL_CLK, \ + LEDC_USE_RTC8M_CLK, \ + } +#define LEDC_LL_TIMER_SPECIFIC_CLOCKS \ + {\ + { \ + .clk = LEDC_REF_TICK, \ + .freq = LEDC_REF_CLK_HZ, \ + } \ + } + +#define LEDC_LL_IS_TIMER_SPECIFIC_CLOCK(SPEED, CLK) ((CLK) == LEDC_USE_REF_TICK) /** * @brief Set LEDC low speed timer clock diff --git a/components/hal/include/hal/ledc_types.h b/components/hal/include/hal/ledc_types.h index 0ddcf18c63..04cb1d13dd 100644 --- a/components/hal/include/hal/ledc_types.h +++ b/components/hal/include/hal/ledc_types.h @@ -1,16 +1,8 @@ -// Copyright 2019 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #pragma once @@ -50,11 +42,19 @@ typedef enum { #endif } ledc_slow_clk_sel_t; +/** + * In theory, the following enumeration shall be placed in LEDC driver's header. + * However, as the next enumeration, `ledc_clk_src_t`, makes the use of some of + * these values and to avoid mutual inclusion of the headers, we must define it + * here. + */ typedef enum { LEDC_AUTO_CLK = 0, /*!< The driver will automatically select the source clock(REF_TICK or APB) based on the giving resolution and duty parameter when init the timer*/ - LEDC_USE_REF_TICK, /*!< LEDC timer select REF_TICK clock as source clock*/ LEDC_USE_APB_CLK, /*!< LEDC timer select APB clock as source clock*/ LEDC_USE_RTC8M_CLK, /*!< LEDC timer select RTC8M_CLK as source clock. Only for low speed channels and this parameter must be the same for all low speed channels*/ +#if SOC_LEDC_SUPPORT_REF_TICK + LEDC_USE_REF_TICK, /*!< LEDC timer select REF_TICK clock as source clock*/ +#endif #if SOC_LEDC_SUPPORT_XTAL_CLOCK LEDC_USE_XTAL_CLK, /*!< LEDC timer select XTAL clock as source clock*/ #endif @@ -64,11 +64,13 @@ typedef enum { LEDC_AUTO_CLK in the driver, as these enums have very similar names and user may pass one of these by mistake. */ typedef enum { +#if SOC_LEDC_SUPPORT_REF_TICK LEDC_REF_TICK = LEDC_USE_REF_TICK, /*!< LEDC timer clock divided from reference tick (1Mhz) */ +#endif LEDC_APB_CLK = LEDC_USE_APB_CLK, /*!< LEDC timer clock divided from APB clock (80Mhz) */ + LEDC_SCLK = LEDC_USE_APB_CLK /*!< Selecting this value for LEDC_TICK_SEL_TIMER let the hardware take its source clock from LEDC_APB_CLK_SEL */ } ledc_clk_src_t; - typedef enum { LEDC_TIMER_0 = 0, /*!< LEDC timer 0 */ LEDC_TIMER_1, /*!< LEDC timer 1 */ @@ -123,39 +125,6 @@ typedef enum { LEDC_FADE_MAX, } ledc_fade_mode_t; -/** - * @brief Configuration parameters of LEDC channel for ledc_channel_config function - */ -typedef struct { - int gpio_num; /*!< the LEDC output gpio_num, if you want to use gpio16, gpio_num = 16 */ - ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */ - ledc_channel_t channel; /*!< LEDC channel (0 - 7) */ - ledc_intr_type_t intr_type; /*!< configure interrupt, Fade interrupt enable or Fade interrupt disable */ - ledc_timer_t timer_sel; /*!< Select the timer source of channel (0 - 3) */ - uint32_t duty; /*!< LEDC channel duty, the range of duty setting is [0, (2**duty_resolution)] */ - int hpoint; /*!< LEDC channel hpoint value, the max value is 0xfffff */ - struct { - unsigned int output_invert: 1;/*!< Enable (1) or disable (0) gpio output invert */ - } flags; /*!< LEDC flags */ - -} ledc_channel_config_t; - -/** - * @brief Configuration parameters of LEDC Timer timer for ledc_timer_config function - */ -typedef struct { - ledc_mode_t speed_mode; /*!< LEDC speed speed_mode, high-speed mode or low-speed mode */ - union { - ledc_timer_bit_t duty_resolution; /*!< LEDC channel duty resolution */ - ledc_timer_bit_t bit_num __attribute__((deprecated)); /*!< Deprecated in ESP-IDF 3.0. This is an alias to 'duty_resolution' for backward compatibility with ESP-IDF 2.1 */ - }; - ledc_timer_t timer_num; /*!< The timer source of channel (0 - 3) */ - uint32_t freq_hz; /*!< LEDC timer frequency (Hz) */ - ledc_clk_cfg_t clk_cfg; /*!< Configure LEDC source clock. - For low speed channels and high speed channels, you can specify the source clock using LEDC_USE_REF_TICK, LEDC_USE_APB_CLK or LEDC_AUTO_CLK. - For low speed channels, you can also specify the source clock using LEDC_USE_RTC8M_CLK, in this case, all low speed channel's source clock must be RTC8M_CLK*/ -} ledc_timer_config_t; - #ifdef __cplusplus } #endif diff --git a/components/hal/ledc_hal.c b/components/hal/ledc_hal.c index 6c00dabd39..2409babc62 100644 --- a/components/hal/ledc_hal.c +++ b/components/hal/ledc_hal.c @@ -1,16 +1,8 @@ -// Copyright 2019 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ // The HAL layer for LEDC (common part) @@ -25,37 +17,73 @@ void ledc_hal_init(ledc_hal_context_t *hal, ledc_mode_t speed_mode) hal->speed_mode = speed_mode; } +static inline ledc_clk_cfg_t ledc_hal_get_slow_clock_helper(ledc_hal_context_t *hal) +{ + ledc_slow_clk_sel_t slow_clk = LEDC_SLOW_CLK_APB; + + ledc_hal_get_slow_clk_sel(hal, &slow_clk); + + switch (slow_clk) { + case LEDC_SLOW_CLK_RTC8M: + return LEDC_USE_RTC8M_CLK; +#if SOC_LEDC_SUPPORT_XTAL_CLOCK + case LEDC_SLOW_CLK_XTAL: + return LEDC_USE_XTAL_CLK; +#endif + default: + return LEDC_USE_APB_CLK; + } +} + void ledc_hal_get_clk_cfg(ledc_hal_context_t *hal, ledc_timer_t timer_sel, ledc_clk_cfg_t *clk_cfg) { - ledc_clk_src_t clk_src = LEDC_APB_CLK; + /* Use the following variable to retrieve the clock source used by the LEDC + * hardware controler. */ + ledc_clk_src_t clk_src; + + /* Clock configuration to return to the driver. */ + ledc_clk_cfg_t driver_clk = LEDC_USE_APB_CLK; + + /* Get the timer-specific mux value. */ ledc_hal_get_clock_source(hal, timer_sel, &clk_src); +#if SOC_LEDC_SUPPORT_REF_TICK if (clk_src == LEDC_REF_TICK) { - *clk_cfg = LEDC_USE_REF_TICK; - } else { - *clk_cfg = LEDC_USE_APB_CLK; - if (hal->speed_mode == LEDC_LOW_SPEED_MODE) { - ledc_slow_clk_sel_t slow_clk = LEDC_SLOW_CLK_APB; - ledc_hal_get_slow_clk_sel(hal, &slow_clk); - if (slow_clk == LEDC_SLOW_CLK_RTC8M) { - *clk_cfg = LEDC_USE_RTC8M_CLK; -#if SOC_LEDC_SUPPORT_XTAL_CLOCK - } else if (slow_clk == LEDC_SLOW_CLK_XTAL) { - *clk_cfg = LEDC_USE_XTAL_CLK; + driver_clk = LEDC_USE_REF_TICK; + } else #endif - } - } + /* If the timer-specific mux is not set to REF_TICK, it either means that: + * - The controler is in fast mode, and thus using APB clock (driver_clk + * variable's default value) + * - The controler is in slow mode and so, using a global clock, + * so we have to retrieve that clock here. + */ + if (hal->speed_mode == LEDC_LOW_SPEED_MODE) { + /* If the source clock used by LEDC hardware is not REF_TICKS, it is + * necessary to retrieve the global clock source used. */ + driver_clk = ledc_hal_get_slow_clock_helper(hal); } + + *clk_cfg = driver_clk; } void ledc_hal_set_slow_clk(ledc_hal_context_t *hal, ledc_clk_cfg_t clk_cfg) { // For low speed channels, if RTC_8MCLK is used as the source clock, the `slow_clk_sel` register should be cleared, otherwise it should be set. - ledc_slow_clk_sel_t slow_clk_sel = LEDC_SLOW_CLK_APB; + ledc_slow_clk_sel_t slow_clk_sel; + + switch (clk_cfg) { + case LEDC_USE_RTC8M_CLK: + slow_clk_sel = LEDC_SLOW_CLK_RTC8M; + break; #if SOC_LEDC_SUPPORT_XTAL_CLOCK - slow_clk_sel = (clk_cfg == LEDC_USE_RTC8M_CLK) ? LEDC_SLOW_CLK_RTC8M : - ((clk_cfg == LEDC_USE_XTAL_CLK) ? LEDC_SLOW_CLK_XTAL : LEDC_SLOW_CLK_APB); -#else - slow_clk_sel = (clk_cfg == LEDC_USE_RTC8M_CLK) ? LEDC_SLOW_CLK_RTC8M : LEDC_SLOW_CLK_APB; + case LEDC_USE_XTAL_CLK: + slow_clk_sel = LEDC_SLOW_CLK_XTAL; + break; #endif + default: + slow_clk_sel = LEDC_SLOW_CLK_APB; + break; + } + ledc_hal_set_slow_clk_sel(hal, slow_clk_sel); } diff --git a/components/soc/esp32/include/soc/soc_caps.h b/components/soc/esp32/include/soc/soc_caps.h index 3449129ad0..31eca576a4 100644 --- a/components/soc/esp32/include/soc/soc_caps.h +++ b/components/soc/esp32/include/soc/soc_caps.h @@ -167,7 +167,8 @@ #define SOC_LCD_I80_BUS_WIDTH (24) /*!< Intel 8080 bus width */ /*-------------------------- LEDC CAPS ---------------------------------------*/ -#define SOC_LEDC_SUPPORT_HS_MODE (1) +#define SOC_LEDC_HAS_TIMER_SPECIFIC_MUX (1) +#define SOC_LEDC_SUPPORT_REF_TICK (1) #define SOC_LEDC_SUPPORT_HS_MODE (1) #define SOC_LEDC_CHANNEL_NUM (8) #define SOC_LEDC_TIMER_BIT_WIDE_NUM (20) diff --git a/components/soc/esp32c3/include/soc/ledc_struct.h b/components/soc/esp32c3/include/soc/ledc_struct.h index 0eb8c18268..da17ba966c 100644 --- a/components/soc/esp32c3/include/soc/ledc_struct.h +++ b/components/soc/esp32c3/include/soc/ledc_struct.h @@ -1,16 +1,9 @@ -// Copyright 2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + #ifndef _SOC_LEDC_STRUCT_H_ #define _SOC_LEDC_STRUCT_H_ #ifdef __cplusplus @@ -84,7 +77,7 @@ typedef volatile struct ledc_dev_s { uint32_t clock_divider: 18; uint32_t pause: 1; uint32_t rst: 1; - uint32_t tick_sel: 1; + uint32_t reserved24: 1; uint32_t low_speed_update: 1; uint32_t reserved26: 6; }; diff --git a/components/soc/esp32h2/include/soc/ledc_struct.h b/components/soc/esp32h2/include/soc/ledc_struct.h index 0eb8c18268..503c7dd77a 100644 --- a/components/soc/esp32h2/include/soc/ledc_struct.h +++ b/components/soc/esp32h2/include/soc/ledc_struct.h @@ -1,16 +1,8 @@ -// Copyright 2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2020-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #ifndef _SOC_LEDC_STRUCT_H_ #define _SOC_LEDC_STRUCT_H_ #ifdef __cplusplus @@ -84,7 +76,7 @@ typedef volatile struct ledc_dev_s { uint32_t clock_divider: 18; uint32_t pause: 1; uint32_t rst: 1; - uint32_t tick_sel: 1; + uint32_t reserved24: 1; uint32_t low_speed_update: 1; uint32_t reserved26: 6; }; diff --git a/components/soc/esp32s2/include/soc/soc_caps.h b/components/soc/esp32s2/include/soc/soc_caps.h index 10d2af25da..6dbe4a75c5 100644 --- a/components/soc/esp32s2/include/soc/soc_caps.h +++ b/components/soc/esp32s2/include/soc/soc_caps.h @@ -157,6 +157,8 @@ #define SOC_LCD_I80_BUS_WIDTH (24) /*!< Intel 8080 bus width */ /*-------------------------- LEDC CAPS ---------------------------------------*/ +#define SOC_LEDC_HAS_TIMER_SPECIFIC_MUX (1) +#define SOC_LEDC_SUPPORT_REF_TICK (1) #define SOC_LEDC_SUPPORT_XTAL_CLOCK (1) #define SOC_LEDC_CHANNEL_NUM (8) #define SOC_LEDC_TIMER_BIT_WIDE_NUM (14) diff --git a/components/soc/esp32s3/include/soc/ledc_caps.h b/components/soc/esp32s3/include/soc/ledc_caps.h index 5f7a49704c..674facfb07 100644 --- a/components/soc/esp32s3/include/soc/ledc_caps.h +++ b/components/soc/esp32s3/include/soc/ledc_caps.h @@ -1,15 +1,8 @@ -// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. +/* + * SPDX-FileCopyrightText: 2019-2021 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ #pragma once @@ -17,6 +10,8 @@ extern "C" { #endif +#define SOC_LEDC_HAS_TIMER_SPECIFIC_MUX (1) +#define SOC_LEDC_SUPPORT_REF_TICK (1) #define SOC_LEDC_SUPPORT_XTAL_CLOCK (1) #define SOC_LEDC_CHANNEL_NUM 8 #define SOC_LEDC_TIMER_BIT_WIDE_NUM (14)