diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c old mode 100755 new mode 100644 index 9308fcd3cb..5baaa693c6 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -74,7 +74,8 @@ STATIC ledc_timer_config_t timers[PWM_TIMER_MAX]; // Params for PWM operation // 5khz is default frequency #define PWM_FREQ (5000) -// default 10-bit resolution (compatible with esp8266 PWM) + +// 10-bit resolution (compatible with esp8266 PWM) #define PWM_RES_10_BIT (LEDC_TIMER_10_BIT) // Maximum duty value on 10-bit resolution @@ -84,15 +85,17 @@ STATIC ledc_timer_config_t timers[PWM_TIMER_MAX]; // duty_u16() and duty_ns() use 16-bit resolution or less // Possible highest resolution in device -#if CONFIG_IDF_TARGET_ESP32 -#define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 bit for ESP32, but 16 bit is used +#if (LEDC_TIMER_BIT_MAX - 1) < LEDC_TIMER_16_BIT +#define HIGHEST_PWM_RES (LEDC_TIMER_BIT_MAX - 1) #else -#define HIGHEST_PWM_RES (LEDC_TIMER_BIT_MAX - 1) // 14 bit is used +#define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 bit for ESP32, but 16 bit is used #endif // Duty resolution of user interface in `duty_u16()` and `duty_u16` parameter in constructor/initializer #define UI_RES_16_BIT (16) // Maximum duty value on highest user interface resolution #define UI_MAX_DUTY ((1 << UI_RES_16_BIT) - 1) +// How much to shift from the HIGHEST_PWM_RES duty resolution to the user interface duty resolution UI_RES_16_BIT +#define UI_RES_SHIFT (UI_RES_16_BIT - HIGHEST_PWM_RES) // 0 for ESP32, 2 for S2, S3, C3 // 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 #define EMPIRIC_FREQ (10) // Hz @@ -108,7 +111,7 @@ typedef struct _machine_pwm_obj_t { int mode; int channel; int timer; - int duty_x; // PWM_RES_10_BIT if duty(), UI_RES_16_BIT if duty_u16(), -UI_RES_16_BIT if duty_ns() + int duty_x; // PWM_RES_10_BIT if duty(), HIGHEST_PWM_RES if duty_u16(), -HIGHEST_PWM_RES if duty_ns() int duty_u10; // stored values from previous duty setters int duty_u16; // - / - int duty_ns; // - / - @@ -237,7 +240,6 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf } // Configure the new resolution and frequency - unsigned int save_duty_resolution = timer->duty_resolution; timer->duty_resolution = res; timer->freq_hz = freq; timer->clk_cfg = LEDC_USE_APB_CLK; @@ -258,17 +260,15 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf if (self->mode == LEDC_LOW_SPEED_MODE) { check_esp_err(ledc_timer_rst(self->mode, self->timer)); } + } - // Save the same duty cycle when frequency is changed - if (save_duty_resolution != timer->duty_resolution) { - if (self->duty_x == UI_RES_16_BIT) { - set_duty_u16(self, self->duty_u16); - } else if (self->duty_x == PWM_RES_10_BIT) { - set_duty_u10(self, self->duty_u10); - } else if (self->duty_x == -UI_RES_16_BIT) { - set_duty_ns(self, self->duty_ns); - } - } + // Save the same duty cycle when frequency is changed + if (self->duty_x == HIGHEST_PWM_RES) { + set_duty_u16(self, self->duty_u16); + } else if (self->duty_x == PWM_RES_10_BIT) { + set_duty_u10(self, self->duty_u10); + } else if (self->duty_x == -HIGHEST_PWM_RES) { + set_duty_ns(self, self->duty_ns); } } @@ -293,11 +293,18 @@ STATIC int duty_to_ns(machine_pwm_obj_t *self, int duty) { #define get_duty_raw(self) ledc_get_duty(self->mode, self->channel) STATIC uint32_t get_duty_u16(machine_pwm_obj_t *self) { - return ledc_get_duty(self->mode, self->channel) << (UI_RES_16_BIT - timers[TIMER_IDX(self->mode, self->timer)].duty_resolution); + int resolution = timers[TIMER_IDX(self->mode, self->timer)].duty_resolution; + int duty = ledc_get_duty(self->mode, self->channel); + if (resolution <= UI_RES_16_BIT) { + duty <<= (UI_RES_16_BIT - resolution); + } else { + duty >>= (resolution - UI_RES_16_BIT); + } + return duty; } STATIC uint32_t get_duty_u10(machine_pwm_obj_t *self) { - return get_duty_u16(self) >> (UI_RES_16_BIT - LEDC_TIMER_10_BIT); + return get_duty_u16(self) >> 6; // Scale down from 16 bit to 10 bit resolution } STATIC uint32_t get_duty_ns(machine_pwm_obj_t *self) { @@ -309,7 +316,12 @@ STATIC void set_duty_u16(machine_pwm_obj_t *self, int duty) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_u16 must be from 0 to %d"), UI_MAX_DUTY); } ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)]; - int channel_duty = duty >> (UI_RES_16_BIT - timer.duty_resolution); + int channel_duty; + if (timer.duty_resolution <= UI_RES_16_BIT) { + channel_duty = duty >> (UI_RES_16_BIT - timer.duty_resolution); + } else { + channel_duty = duty << (timer.duty_resolution - UI_RES_16_BIT); + } int max_duty = (1 << timer.duty_resolution) - 1; if (channel_duty < 0) { channel_duty = 0; @@ -333,7 +345,7 @@ STATIC void set_duty_u16(machine_pwm_obj_t *self, int duty) { } */ - self->duty_x = UI_RES_16_BIT; + self->duty_x = HIGHEST_PWM_RES; self->duty_u16 = duty; } @@ -341,7 +353,7 @@ STATIC void set_duty_u10(machine_pwm_obj_t *self, int duty) { if ((duty < 0) || (duty > MAX_DUTY_U10)) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty must be from 0 to %u"), MAX_DUTY_U10); } - set_duty_u16(self, duty << (UI_RES_16_BIT - LEDC_TIMER_10_BIT)); + set_duty_u16(self, duty << (UI_RES_16_BIT - PWM_RES_10_BIT)); self->duty_x = PWM_RES_10_BIT; self->duty_u10 = duty; } @@ -351,7 +363,7 @@ STATIC void set_duty_ns(machine_pwm_obj_t *self, int ns) { mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_ns must be from 0 to %d ns"), duty_to_ns(self, UI_MAX_DUTY)); } set_duty_u16(self, ns_to_duty(self, ns)); - self->duty_x = -UI_RES_16_BIT; + self->duty_x = -HIGHEST_PWM_RES; self->duty_ns = ns; } @@ -424,7 +436,7 @@ STATIC void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_p if (self->duty_x == PWM_RES_10_BIT) { mp_printf(print, ", duty=%d", get_duty_u10(self)); - } else if (self->duty_x == -UI_RES_16_BIT) { + } else if (self->duty_x == -HIGHEST_PWM_RES) { mp_printf(print, ", duty_ns=%d", get_duty_ns(self)); } else { mp_printf(print, ", duty_u16=%d", get_duty_u16(self));