summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrobert-hh <robert@hammelrath.com>2022-02-27 15:22:21 +0100
committerDamien George <damien@micropython.org>2022-03-07 13:37:23 +1100
commit55a0125a154a9c13363d900e0caccd218bce0a54 (patch)
tree5c38cc401bae93bbd5e5ae25fd8d2f2ecf383107
parent665f0e2a68dfa4944f60e85a00d0543bb3d7fee8 (diff)
esp32/machine_pwm: Always set the duty cycle when setting the frequency.
If setting the frequency to a value used already by an existing timer, this timer will be used. But still, the duty cycle for that channel may have to be changed. Fixes issues #8306 and #8345.
-rw-r--r--[-rwxr-xr-x]ports/esp32/machine_pwm.c58
1 files changed, 35 insertions, 23 deletions
diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c
index 9308fcd3c..5baaa693c 100755..100644
--- 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));