3434#include "py/mphal.h"
3535#include "driver/ledc.h"
3636#include "esp_err.h"
37- #include "esp_clk_tree.h"
3837#include "soc/gpio_sig_map.h"
3938
39+ #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL (5 , 1 , 0 )
40+ #include "esp_clk_tree.h"
41+ #endif
42+
4043#define PWM_DBG (...)
4144// #define PWM_DBG(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, "\n");
4245
@@ -208,7 +211,41 @@ static void configure_channel(machine_pwm_obj_t *self) {
208211 }
209212}
210213
214+ // Temporary workaround for ledc_find_suitable_duty_resolution function only being added in IDF V5.2
215+ #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL (5 , 2 , 0 )
216+ static uint32_t ledc_find_suitable_duty_resolution (uint32_t src_clk_freq , uint32_t timer_freq ) {
217+ // This implementation is based on the one used in Micropython v1.23
218+
219+ // Find the highest bit resolution for the requested frequency
220+ unsigned int freq = src_clk_freq ;
221+
222+ int divider = (freq + timer_freq / 2 ) / timer_freq ; // rounded
223+ if (divider == 0 ) {
224+ divider = 1 ;
225+ }
226+ float f = (float )freq / divider ; // actual frequency
227+ if (f <= 1.0 ) {
228+ f = 1.0 ;
229+ }
230+ freq = (unsigned int )roundf ((float )freq / f );
231+
232+ unsigned int res = 0 ;
233+ for (; freq > 1 ; freq >>= 1 ) {
234+ ++ res ;
235+ }
236+ if (res == 0 ) {
237+ res = 1 ;
238+ } else if (res > HIGHEST_PWM_RES ) {
239+ // Limit resolution to HIGHEST_PWM_RES to match units of our duty
240+ res = HIGHEST_PWM_RES ;
241+ }
242+
243+ return res ;
244+ }
245+ #endif
246+
211247static void set_freq (machine_pwm_obj_t * self , unsigned int freq , ledc_timer_config_t * timer ) {
248+ esp_err_t err ;
212249 if (freq != timer -> freq_hz ) {
213250 // Configure the new frequency and resolution
214251 timer -> freq_hz = freq ;
@@ -228,10 +265,21 @@ static void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf
228265 }
229266 #endif
230267 uint32_t src_clk_freq = 0 ;
231- esp_err_t err = esp_clk_tree_src_get_freq_hz (timer -> clk_cfg , ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED , & src_clk_freq );
268+ #if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL (5 , 1 , 0 )
269+ err = esp_clk_tree_src_get_freq_hz (timer -> clk_cfg , ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED , & src_clk_freq );
232270 if (err != ESP_OK ) {
233271 mp_raise_msg_varg (& mp_type_ValueError , MP_ERROR_TEXT ("unable to query source clock frequency %d" ), (int )timer -> clk_cfg );
234272 }
273+ #else
274+ // Simplified fallback logic for IDF V5.0.x, for targets with APB only.
275+ src_clk_freq = APB_CLK_FREQ ; // 80 MHz
276+ #if SOC_LEDC_SUPPORT_REF_TICK
277+ if (timer -> clk_cfg == LEDC_USE_REF_TICK ) {
278+ src_clk_freq = REF_CLK_FREQ ; // 1 MHz
279+ }
280+ #endif // SOC_LEDC_SUPPORT_REF_TICK
281+ #endif // ESP_IDF_VERSION
282+
235283 timer -> duty_resolution = ledc_find_suitable_duty_resolution (src_clk_freq , timer -> freq_hz );
236284
237285 // Set frequency
0 commit comments