8000 esp32: Always set the duty cycle when setting the frequency. · robert-hh/micropython@7d0bb77 · GitHub
[go: up one dir, main page]

Skip to content

Commit 7d0bb77

Browse files
committed
esp32: 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 micropython#8306 as well.
1 parent 665f0e2 commit 7d0bb77

File tree

1 file changed

+43
-32
lines changed

1 file changed

+43
-32
lines changed

ports/esp32/machine_pwm.c

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -71,28 +71,31 @@ STATIC ledc_timer_config_t timers[PWM_TIMER_MAX];
7171
#define TIMER_IDX_TO_MODE(timer_idx) (timer_idx / LEDC_TIMER_MAX)
7272
#define TIMER_IDX_TO_TIMER(timer_idx) (timer_idx % LEDC_TIMER_MAX)
7373

74-
// Params for PWM operation
74+
// Params for PW operation
7575
// 5khz is default frequency
76-
#define PWM_FREQ (5000)
77-
// default 10-bit resolution (compatible with esp8266 PWM)
78-
#define PWM_RES_10_BIT (LEDC_TIMER_10_BIT)
76+
#define PWFREQ (5000)
77+
78+
// 10-bit resolution (compatible with esp8266 PWM)
79+
#define PWRES (LEDC_TIMER_10_BIT)
7980

8081
// Maximum duty value on 10-bit resolution
81-
#define MAX_DUTY_U10 ((1 << PWM_RES_10_BIT) - 1)
82+
#define MAX_DUTY_U10 ((1 << PWRES) - 1)
8283
// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html#supported-range-of-frequency-and-duty-resolutions
8384
// duty() uses 10-bit resolution or less
8485
// duty_u16() and duty_ns() use 16-bit resolution or less
8586

8687
// Possible highest resolution in device
87-
#if CONFIG_IDF_TARGET_ESP32
88-
#define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 bit for ESP32, 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)
8990
#else
90-
#define HIGHEST_PWM_RES (LEDC_TIMER_BIT_MAX - 1) // 14 bit is used
91+
#define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 bit for ESP32, but 16 bit is used
9192
#endif
9293
// Duty resolution of user interface in `duty_u16()` and `duty_u16` parameter in constructor/initializer
9394
#define UI_RES_16_BIT (16)
9495
// Maximum duty value on highest user interface resolution
9596
#define UI_MAX_DUTY ((1 << UI_RES_16_BIT) - 1)
97+
// 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 (UI_RES_16_BIT - HIGHEST_PWM_RES) // 0 for ESP32, 2 for S2, S3, C3
9699

97100
// 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
98101
#define EMPIRIC_FREQ (10) // Hz
@@ -108,7 +111,7 @@ typedef struct _machine_pwm_obj_t {
108111
int mode;
109112
int channel;
110113
int timer;
111-
int duty_x; // PWM_RES_10_BIT if duty(), UI_RES_16_BIT if duty_u16(), -UI_RES_16_BIT if duty_ns()
114+
int duty_x; // PWRES if duty(), HIGHEST_PWM_RES if duty_u16(), -HIGHEST_PWM_RES if duty_ns()
112115
int duty_u10; // stored values from previous duty setters
113116
int duty_u16; // - / -
114117
int duty_ns; // - / -
@@ -208,7 +211,6 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf
208211
if (freq < EMPIRIC_FREQ) {
209212
i = LEDC_REF_CLK_HZ; // 1 MHz
210213
}
211-
212214
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
213215
// original code
214216
i /= freq;
@@ -237,7 +239,6 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf
237239
}
238240

239241
// Configure the new resolution and frequency
240-
unsigned int save_duty_resolution = timer->duty_resolution;
241242
timer->duty_resolution = res;
242243
timer->freq_hz = freq;
243244
timer->clk_cfg = LEDC_USE_APB_CLK;
@@ -258,17 +259,15 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf
258259
if (self->mode == LEDC_LOW_SPEED_MODE) {
259260
check_esp_err(ledc_timer_rst(self->mode, self->timer));
260261
}
262+
}
261263

262-
// Save the same duty cycle when frequency is changed
263-
if (save_duty_resolution != timer->duty_resolution) {
264-
if (self->duty_x == UI_RES_16_BIT) {
265-
set_duty_u16(self, self->duty_u16);
266-
} else if (self->duty_x == PWM_RES_10_BIT) {
267-
set_duty_u10(self, self->duty_u10);
268-
} else if (self->duty_x == -UI_RES_16_BIT) {
269-
set_duty_ns(self, self->duty_ns);
270-
}
271-
}
264+
// Save the same duty cycle when frequency is changed
265+
if (self->duty_x == HIGHEST_PWM_RES) {
266+
set_duty_u16(self, self->duty_u16);
267+
} else if (self->duty_x == PWRES) {
268+
set_duty_u10(self, self->duty_u10);
269+
} else if (self->duty_x == -HIGHEST_PWM_RES) {
270+
set_duty_ns(self, self->duty_ns);
272271
}
273272
}
274273

293292
#define get_duty_raw(self) ledc_get_duty(self->mode, self->channel)
294293

295294
STATIC uint32_t get_duty_u16(machine_pwm_obj_t *self) {
296-
return ledc_get_duty(self->mode, self->channel) << (UI_RES_16_BIT - timers[TIMER_IDX(self->mode, self->timer)].duty_resolution);
295+
int resolution = timers[TIMER_IDX(self->mode, self->timer)].duty_resolution;
296+
int duty = ledc_get_duty(self->mode, self->channel);
297+
if (resolution <= UI_RES_16_BIT) {
298+
duty <<= (UI_RES_16_BIT - resolution);
299+
} else {
300+
duty >>= (resolution - UI_RES_16_BIT);
301+
}
302+
return duty;
297303
}
298304

299305
STATIC uint32_t get_duty_u10(machine_pwm_obj_t *self) {
300-
return get_duty_u16(self) >> (UI_RES_16_BIT - LEDC_TIMER_10_BIT);
306+
return get_duty_u16(self) >> 6; // Scale down from 16 bit to 10 bit resolution
301307
}
302308

303309
STATIC uint32_t get_duty_ns(machine_pwm_obj_t *self) {
@@ -309,7 +315,12 @@ STATIC void set_duty_u16(machine_pwm_obj_t *self, int duty) {
309315
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_u16 must be from 0 to %d"), UI_MAX_DUTY);
310316
}
311317
ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)];
312-
int channel_duty = duty >> (UI_RES_16_BIT - timer.duty_resolution);
318+
int channel_duty;
319+
if (timer.duty_resolution <= UI_RES_16_BIT) {
320+
channel_duty = duty >> (UI_RES_16_BIT - timer.duty_resolution);
321+
} else {
322+
channel_duty = duty << (timer.duty_resolution - UI_RES_16_BIT);
323+
}
313324
int max_duty = (1 << timer.duty_resolution) - 1;
314325
if (channel_duty < 0) {
315326
channel_duty = 0;
@@ -333,16 +344,16 @@ STATIC void set_duty_u16(machine_pwm_obj_t *self, int duty) {
333344
}
334345
*/
335346

336-
self->duty_x = UI_RES_16_BIT;
347+
self->duty_x = HIGHEST_PWM_RES;
337348
self->duty_u16 = duty;
338349
}
339350

340351
STATIC void set_duty_u10(machine_pwm_obj_t *self, int duty) {
341352
if ((duty < 0) || (duty > MAX_DUTY_U10)) {
342353
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty must be from 0 to %u"), MAX_DUTY_U10);
343354
}
344-
set_duty_u16(self, duty << (UI_RES_16_BIT - LEDC_TIMER_10_BIT));
345-
self->duty_x = PWM_RES_10_BIT;
355+
set_duty_u16(self, duty << (HIGHEST_PWM_RES + UI_RES_SHIFT - PWRES));
356+
self->duty_x = PWRES;
346357
self->duty_u10 = duty;
347358
}
348359

@@ -351,7 +362,7 @@ STATIC void set_duty_ns(machine_pwm_obj_t *self, int ns) {
351362
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));
352363
}
353364
set_duty_u16(self, ns_to_duty(self, ns));
354-
self->duty_x = -UI_RES_16_BIT;
365+
self->duty_x = -HIGHEST_PWM_RES;
355366
self->duty_ns = ns;
356367
}
357368

@@ -422,9 +433,9 @@ STATIC void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_p
422433
if (self->active) {
423434
mp_printf(print, ", freq=%u", ledc_get_freq(self->mode, self->timer));
424435

425-
if (self->duty_x == PWM_RES_10_BIT) {
436+
if (self->duty_x == PWRES) {
426437
mp_printf(print, ", duty=%d", get_duty_u10(self));
427-
} else if (self->duty_x == -UI_RES_16_BIT) {
438+
} else if (self->duty_x == -HIGHEST_PWM_RES) {
428439
mp_printf(print, ", duty_ns=%d", get_duty_ns(self));
429440
} else {
430441
mp_printf(print, ", duty_u16=%d", get_duty_u16(self));
@@ -476,7 +487,7 @@ STATIC void mp_machine_pwm_init_helper(machine_pwm_obj_t *self,
476487
freq = timers[chans[channel_idx].timer_idx].freq_hz;
477488
}
478489
if (freq <= 0) {
479-
freq = PWM_FREQ;
490+
freq = PWFREQ;
480491
}
481492
}
482493
if ((freq <= 0) || (freq > 40000000)) {
@@ -533,7 +544,7 @@ STATIC void mp_machine_pwm_init_helper(machine_pwm_obj_t *self,
533544
} else if (duty != -1) {
534545
set_duty_u10(self, duty);
535546
} else if (self->duty_x == 0) {
536-
set_duty_u10(self, (1 << PWM_RES_10_BIT) / 2); // 50%
547+
set_duty_u10(self, (1 << PWRES) / 2); // 50%
537548
}
538549
}
539550

0 commit comments

Comments
 (0)
0