@@ -85,17 +85,17 @@ STATIC ledc_timer_config_t timers[PWM_TIMER_MAX];
8585// duty_u16() and duty_ns() use 16-bit resolution or less
8686
8787// Possible highest resolution in device
88- #if CONFIG_IDF_TARGET_ESP32
89- #define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 bit in fact, but 16 bit is used
88+ #if ( LEDC_TIMER_BIT_MAX - 1 ) < LEDC_TIMER_16_BIT
89+ #define HIGHEST_PWM_RES (LEDC_TIMER_BIT_MAX - 1)
9090#else
91- #define HIGHEST_PWM_RES (LEDC_TIMER_14_BIT)
91+ #define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 bit for ESP32, but 16 bit is used
9292#endif
9393// Duty resolution of user interface in `duty_u16()` and `duty_u16` parameter in constructor/initializer
9494#define UI_RES_16_BIT (16)
9595// Maximum duty value on highest user interface resolution
9696#define UI_MAX_DUTY ((1 << UI_RES_16_BIT) - 1)
9797// How much to shift from the HIGHEST_PWM_RES duty resolution to the user interface duty resolution UI_RES_16_BIT
98- #define UI_RES_SHIFT (16 - HIGHEST_PWM_RES) // 0 for ESP32, 2 for S2, S3, C3
98+ #define UI_RES_SHIFT (UI_RES_16_BIT - HIGHEST_PWM_RES) // 0 for ESP32, 2 for S2, S3, C3
9999
100100// If the PWM frequency is less than EMPIRIC_FREQ, then LEDC_REF_CLK_HZ(1 MHz) source is used, else LEDC_APB_CLK_HZ(80 MHz) source is used
101101#define EMPIRIC_FREQ (10) // Hz
@@ -205,24 +205,19 @@ STATIC void configure_channel(machine_pwm_obj_t *self) {
205205}
206206
207207STATIC void set_freq (machine_pwm_obj_t * self , unsigned int freq , ledc_timer_config_t * timer ) {
208- // Even if the timer frequency is already set,
209- // the set_duty_x() is required to reconfigure the channel duty anyway
210208 if (freq != timer -> freq_hz ) {
211- PWM_DBG ("set_freq(%d)" , freq )
212-
213209 // Find the highest bit resolution for the requested frequency
214210 unsigned int i = LEDC_APB_CLK_HZ ; // 80 MHz
215211 if (freq < EMPIRIC_FREQ ) {
216212 i = LEDC_REF_CLK_HZ ; // 1 MHz
217213 }
218214
219- #if 1
215+ #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL ( 5 , 0 , 0 )
220216 // original code
221217 i /= freq ;
222218 #else
223219 // See https://github.com/espressif/esp-idf/issues/7722
224- unsigned int divider = i / freq ; // truncated
225- // int divider = (i + freq / 2) / freq; // rounded
220+ int divider = (i + freq / 2 ) / freq ; // rounded
226221 if (divider == 0 ) {
227222 divider = 1 ;
228223 }
@@ -245,6 +240,7 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf
245240 }
246241
247242 // Configure the new resolution and frequency
243+ unsigned int save_duty_resolution = timer -> duty_resolution ;
248244 timer -> duty_resolution = res ;
249245 timer -> freq_hz = freq ;
250246 timer -> clk_cfg = LEDC_USE_APB_CLK ;
@@ -256,7 +252,6 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf
256252 esp_err_t err = ledc_timer_config (timer );
257253 if (err != ESP_OK ) {
258254 if (err == ESP_FAIL ) {
259- PWM_DBG (" (timer timer->speed_mode %d, timer->timer_num %d, timer->clk_cfg %d, timer->freq_hz %d, timer->duty_resolution %d) " , timer -> speed_mode , timer -> timer_num , timer -> clk_cfg , timer -> freq_hz , timer -> duty_resolution );
260255 mp_raise_msg_varg (& mp_type_ValueError , MP_ERROR_TEXT ("unreachable frequency %d" ), freq );
261256 } else {
262257 check_esp_err (err );
@@ -266,15 +261,17 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf
266261 if (self -> mode == LEDC_LOW_SPEED_MODE ) {
267262 check_esp_err (ledc_timer_rst (self -> mode , self -> timer ));
268263 }
269- }
270264
271- // Save the same duty cycle when frequency or channel are changed
272- if (self -> duty_x == HIGHEST_PWM_RES ) {
273- set_duty_u16 (self , self -> duty_u16 );
274- } else if (self -> duty_x == PWRES ) {
275- set_duty_u10 (self , self -> duty_u10 );
276- } else if (self -> duty_x == - HIGHEST_PWM_RES ) {
277- set_duty_ns (self , self -> duty_ns );
265+ // Save the same duty cycle when frequency is changed
266+ if (save_duty_resolution != timer -> duty_resolution ) {
267+ if (self -> duty_x == HIGHEST_PWM_RES ) {
268+ set_duty_u16 (self , self -> duty_u16 );
269+ } else if (self -> duty_x == PWRES ) {
270+ set_duty_u10 (self , self -> duty_u10 );
271+ } else if (self -> duty_x == - HIGHEST_PWM_RES ) {
272+ set_duty_ns (self , self -> duty_ns );
273+ }
274+ }
278275 }
279276}
280277
@@ -287,14 +284,12 @@ STATIC int ns_to_duty(machine_pwm_obj_t *self, int ns) {
287284 } else if (duty > UI_MAX_DUTY ) {
288285 duty = UI_MAX_DUTY ;
289286 }
290- // PWM_DBG(" ns_to_duty(UI_MAX_DUTY=%d freq_hz=%d duty=%d=%f <- ns=%d) ", UI_MAX_DUTY, timer.freq_hz, duty, (float)ns * UI_MAX_DUTY * timer.freq_hz / 1000000000.0, ns);
291287 return duty ;
292288}
293289
294290STATIC int duty_to_ns (machine_pwm_obj_t * self , int duty ) {
295291 ledc_timer_config_t timer = timers [TIMER_IDX (self -> mode , self -> timer )];
296292 int64_t ns = ((int64_t )duty * 1000000000LL + (int64_t )timer .freq_hz * UI_MAX_DUTY / 2 ) / ((int64_t )timer .freq_hz * UI_MAX_DUTY );
297- // PWM_DBG(" duty_to_ns(UI_MAX_DUTY=%d freq_hz=%d duty=%d -> ns=%f=%d) ", UI_MAX_DUTY, timer.freq_hz, duty, (float)duty * 1000000000.0 / ((float)timer.freq_hz * UI_MAX_DUTY), ns);
298293 return ns ;
299294}
300295
@@ -316,23 +311,27 @@ STATIC void set_duty_u16(machine_pwm_obj_t *self, int duty) {
316311 if ((duty < 0 ) || (duty > UI_MAX_DUTY )) {
317312 mp_raise_msg_varg (& mp_type_ValueError , MP_ERROR_TEXT ("duty_u16 must be from 0 to %d" ), UI_MAX_DUTY );
318313 }
319- duty >>= HIGHEST_PWM_RES + UI_RES_SHIFT - timers [TIMER_IDX (self -> mode , self -> timer )].duty_resolution ;
320- int max_duty = (1 << timers [TIMER_IDX (self -> mode , self -> timer )].duty_resolution ) - 1 ;
321- if (duty < 0 ) {
322- duty = 0 ;
323- } else if (duty > max_duty ) {
324- duty = max_duty ;
325- }
326- check_esp_err (ledc_set_duty (self -> mode , self -> channel , duty ));
314+ ledc_timer_config_t timer = timers [TIMER_IDX (self -> mode , self -> timer )];
315+ int channel_duty = duty >> (HIGHEST_PWM_RES + UI_RES_SHIFT - timer .duty_resolution );
316+ int max_duty = (1 << timer .duty_resolution ) - 1 ;
317+ if (channel_duty < 0 ) {
318+ channel_duty = 0 ;
319+ } else if (channel_duty > max_duty ) {
320+ channel_duty = max_duty ;
321+ }
322+ check_esp_err (ledc_set_duty (self -> mode , self -> channel , channel_duty ));
327323 check_esp_err (ledc_update_duty (self -> mode , self -> channel ));
328324
329325 /*
330326 // Bug: Sometimes duty is not set right now.
327+ // Not a bug. It's a feature. The duty is applied at the beginning of the next signal period.
328+ // Bug: It has been experimentally established that the duty is setted during 2 signal periods, but 1 period is expected.
331329 // See https://github.com/espressif/esp-idf/issues/7288
332330 if (duty != get_duty_u16(self)) {
333- ets_delay_us(100);
331+ PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, channel_duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), channel_duty, timer.duty_resolution, timer.freq_hz);
332+ ets_delay_us(2 * 1000000 / timer.freq_hz);
334333 if (duty != get_duty_u16(self)) {
335- PWM_DBG(" ( set_duty_u16(%u) get_duty_u16()=%u duty_resolution=%d) ", duty, get_duty_u16(self), timers[TIMER_IDX(self->mode, self-> timer)] .duty_resolution);
334+ PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, channel_duty:%d, duty_resolution:%d, freq_hz:%d ", duty, get_duty_u16(self), channel_duty, timer.duty_resolution, timer.freq_hz );
336335 }
337336 }
338337 */
0 commit comments