From f8b561216bf9b15750f4029b00903e5ec3dcafed Mon Sep 17 00:00:00 2001 From: cjwhoishe Date: Tue, 5 Aug 2025 10:03:46 +0100 Subject: [PATCH 1/3] Update pwm_stream.c --- components/audio_stream/pwm_stream.c | 50 +++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/components/audio_stream/pwm_stream.c b/components/audio_stream/pwm_stream.c index 9e351dafa..98029aed4 100644 --- a/components/audio_stream/pwm_stream.c +++ b/components/audio_stream/pwm_stream.c @@ -217,14 +217,22 @@ static esp_err_t pwm_data_list_wait_flushed(pwm_data_handle_t data, TickType_t t return ESP_FAIL; } -static inline void ledc_set_left_duty_fast(uint32_t duty_val) +// +// ensure this is put into IRAM when the compiler does not inline the code +// because of optimisation options or it chooses not to +// +static inline IRAM_ATTR void ledc_set_left_duty_fast(uint32_t duty_val) { *g_ledc_left_duty_val = (duty_val) << 4; *g_ledc_left_conf0_val |= 0x00000014; *g_ledc_left_conf1_val |= 0x80000000; } -static inline void ledc_set_right_duty_fast(uint32_t duty_val) +// +// ensure this is put into IRAM when the compiler does not inline the code +// because of optimisation options or it chooses not to +// +static inline IRAM_ATTR void ledc_set_right_duty_fast(uint32_t duty_val) { *g_ledc_right_duty_val = (duty_val) << 4; *g_ledc_right_conf0_val |= 0x00000014; @@ -239,7 +247,11 @@ static void IRAM_ATTR timer_group_isr(void *para) return; } -#ifdef CONFIG_IDF_TARGET_ESP32S2 +// +// add support for ESP32C3 and ESP32C6 +// use same settings as ESP32S2 +// +#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined (CONFIG_IDF_TARGET_ESP32C6) #if ((ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 0))) if (handle->timg_dev->int_st.val & BIT(handle->config.timer_num)) { handle->timg_dev->int_clr.val |= (1UL << handle->config.timer_num); @@ -383,14 +395,27 @@ static esp_err_t audio_pwm_init(const audio_pwm_config_t *cfg) } AUDIO_CHECK(TAG, 0 != handle->channel_mask, goto init_error, "AUDIO PWM CHANNEL MASK IS 0"); -#ifdef CONFIG_IDF_TARGET_ESP32S2 +// +// add support for ESP32C3 and ESPS32C6 +// by choosing Clock that is equivilant to 80Mhz +// which the other calculations are based upon +// +#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) handle->ledc_timer.clk_cfg = LEDC_USE_APB_CLK; +#elif defined(CONFIG_IDF_TARGET_ESP32C6) + handle->ledc_timer.clk_cfg = TIMER_SRC_CLK_PLL_F80M; #endif handle->ledc_timer.speed_mode = LEDC_LOW_SPEED_MODE; handle->ledc_timer.duty_resolution = handle->config.duty_resolution; handle->ledc_timer.timer_num = handle->config.ledc_timer_sel; +#if defined(CONFIG_IDF_TARGET_ESP32C6) + // for the esp32C6 the APB_CLK_FREQ is 40M but we cannot use that as + // we are forcing it to use the 80Mhz PLL clock so do the frequency calulation based on that + uint32_t freq = ((80 * 1000000) / (1 << handle->ledc_timer.duty_resolution)); +#else uint32_t freq = (APB_CLK_FREQ / (1 << handle->ledc_timer.duty_resolution)); +#endif handle->ledc_timer.freq_hz = freq - (freq % 1000); res = ledc_timer_config(&handle->ledc_timer); AUDIO_CHECK(TAG, ESP_OK == res, goto init_error, "AUDIO PWM TIMER ERROR"); @@ -454,7 +479,13 @@ esp_err_t audio_pwm_set_param(int rate, ledc_timer_bit_t bits, int ch) timer_init(handle->config.tg_num, handle->config.timer_num, &config); timer_set_counter_value(handle->config.tg_num, handle->config.timer_num, 0x00000000ULL); +#if defined(CONFIG_IDF_TARGET_ESP32C6) + // for the esp32C6 the APB_CLK_FREQ is 40M but we cannot use that as + // we are forcing it to use the 80Mhz PLL clock so do the frequency calulation based on that + timer_set_alarm_value(handle->config.tg_num, handle->config.timer_num, ((80 * 1000000) / config.divider) / handle->framerate); +#else timer_set_alarm_value(handle->config.tg_num, handle->config.timer_num, (APB_CLK_FREQ / config.divider) / handle->framerate); +#endif timer_enable_intr(handle->config.tg_num, handle->config.timer_num); timer_isr_register(handle->config.tg_num, handle->config.timer_num, timer_group_isr, NULL, ESP_INTR_FLAG_IRAM, NULL); return res; @@ -471,7 +502,10 @@ esp_err_t audio_pwm_set_sample_rate(int rate) audio_pwm_handle_t handle = g_audio_pwm_handle; handle->framerate = rate; uint16_t div = 1; -#ifdef CONFIG_IDF_TARGET_ESP32S2 +// +// Add support for ESP32C3 and ESP32C6 +// +#if defined(CONFIG_IDF_TARGET_ESP32S2) || defined(CONFIG_IDF_TARGET_ESP32C3) || defined(CONFIG_IDF_TARGET_ESP32C6) #if ((ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 2, 0)) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4, 4, 0))) div = (uint16_t)handle->timg_dev->hw_timer[handle->config.timer_num].config.divider; #else @@ -487,7 +521,13 @@ esp_err_t audio_pwm_set_sample_rate(int rate) div = (uint16_t)handle->timg_dev->hw_timer[handle->config.timer_num].config.tn_divider; #endif +#if defined(CONFIG_IDF_TARGET_ESP32C6) + // for the esp32C6 the APB_CLK_FREQ is 40M but we cannot use that as + // we are forcing it to use the 80Mhz PLL clock so do the frequency calulation based on that + res = timer_set_alarm_value(handle->config.tg_num, handle->config.timer_num, ((80 * 1000000) / div) / handle->framerate); +#else res = timer_set_alarm_value(handle->config.tg_num, handle->config.timer_num, (APB_CLK_FREQ / div) / handle->framerate); +#endif return res; } From a645f58e8af99be94d3a4afe372a056aba911103 Mon Sep 17 00:00:00 2001 From: cjwhoishe Date: Mon, 11 Aug 2025 17:36:04 +0100 Subject: [PATCH 2/3] Update pwm_stream.c --- components/audio_stream/pwm_stream.c | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/components/audio_stream/pwm_stream.c b/components/audio_stream/pwm_stream.c index 98029aed4..55c767103 100644 --- a/components/audio_stream/pwm_stream.c +++ b/components/audio_stream/pwm_stream.c @@ -432,7 +432,8 @@ static esp_err_t audio_pwm_init(const audio_pwm_config_t *cfg) g_ledc_right_conf0_val = &LEDC.channel_group[handle->ledc_timer.speed_mode].channel[handle->ledc_channel[CHANNEL_RIGHT_INDEX].channel].conf0.val; g_ledc_right_conf1_val = &LEDC.channel_group[handle->ledc_timer.speed_mode].channel[handle->ledc_channel[CHANNEL_RIGHT_INDEX].channel].conf1.val; - handle->status = AUDIO_PWM_STATUS_IDLE; +// handle->status = AUDIO_PWM_STATUS_IDLE; + handle->status = AUDIO_PWM_STATUS_UNINIT; return res; init_error: @@ -787,8 +788,22 @@ audio_element_handle_t pwm_stream_init(pwm_stream_cfg_t *config) esp_err_t pwm_stream_set_clk(audio_element_handle_t pwm_stream, int rate, int bits, int ch) { + audio_pwm_handle_t _handle = g_audio_pwm_handle; esp_err_t res = ESP_OK; + if (_handle->status == AUDIO_PWM_STATUS_UNINIT) + { res |= audio_pwm_set_param(rate, bits, ch); + _handle->status = AUDIO_PWM_STATUS_IDLE; + res |= audio_pwm_start(); + } + else + { + audio_pwm_stop(); + _handle->channel_set_num = ch; + _handle->bits_per_sample = bits; +// res |= audio_pwm_set_param(rate, bits, ch); + res |= audio_pwm_set_sample_rate(rate); res |= audio_pwm_start(); + } return res; } From ae5e394e22adc4c9428198f914e9f51f903d3ff0 Mon Sep 17 00:00:00 2001 From: cjwhoishe Date: Mon, 11 Aug 2025 18:10:02 +0100 Subject: [PATCH 3/3] Update pwm_stream.c --- components/audio_stream/pwm_stream.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/components/audio_stream/pwm_stream.c b/components/audio_stream/pwm_stream.c index 55c767103..617ed51f2 100644 --- a/components/audio_stream/pwm_stream.c +++ b/components/audio_stream/pwm_stream.c @@ -433,7 +433,7 @@ static esp_err_t audio_pwm_init(const audio_pwm_config_t *cfg) g_ledc_right_conf1_val = &LEDC.channel_group[handle->ledc_timer.speed_mode].channel[handle->ledc_channel[CHANNEL_RIGHT_INDEX].channel].conf1.val; // handle->status = AUDIO_PWM_STATUS_IDLE; - handle->status = AUDIO_PWM_STATUS_UNINIT; + handle->status = AUDIO_PWM_STATUS_UNINIT; // set the first time in ie timers not setup return res; init_error: @@ -788,13 +788,18 @@ audio_element_handle_t pwm_stream_init(pwm_stream_cfg_t *config) esp_err_t pwm_stream_set_clk(audio_element_handle_t pwm_stream, int rate, int bits, int ch) { + // This is the only way that the audio_pwm is started and could not be called if the audio sample rate/bits or channels altered because + // a different source was changed to eg 44100 to 48000 sample rate change + // this is overcome by setting the status to AUDIO_PWM_STATUS_UNINIT in audio_pwm_init() such that the first run can be detected + // then the first run calls audio_pwm_set_param() which sets the timer properties , ISR etc. + // subsequent calls stop the timers etc by calling audio_pwm_stop + // then changes only the bits, and ch directly and uses audio_pwm_set_sample_rate() to calculate timer settings audio_pwm_handle_t _handle = g_audio_pwm_handle; esp_err_t res = ESP_OK; if (_handle->status == AUDIO_PWM_STATUS_UNINIT) - { + { // set parameters, configure the timers, ISR and start res |= audio_pwm_set_param(rate, bits, ch); _handle->status = AUDIO_PWM_STATUS_IDLE; - res |= audio_pwm_start(); } else { @@ -802,8 +807,9 @@ esp_err_t pwm_stream_set_clk(audio_element_handle_t pwm_stream, int rate, int bi _handle->channel_set_num = ch; _handle->bits_per_sample = bits; // res |= audio_pwm_set_param(rate, bits, ch); + // cannot call audio_pwm_set_param() as the timers are already setup res |= audio_pwm_set_sample_rate(rate); - res |= audio_pwm_start(); } + res |= audio_pwm_start(); return res; }