Skip to content

Commit 55a0125

Browse files
robert-hhdpgeorge
authored andcommitted
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.
1 parent 665f0e2 commit 55a0125

File tree

1 file changed

+35
-23
lines changed

1 file changed

+35
-23
lines changed

ports/esp32/machine_pwm.c

100755100644
Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,8 @@ STATIC ledc_timer_config_t timers[PWM_TIMER_MAX];
7474
// Params for PWM operation
7575
// 5khz is default frequency
7676
#define PWM_FREQ (5000)
77-
// default 10-bit resolution (compatible with esp8266 PWM)
77+
78+
// 10-bit resolution (compatible with esp8266 PWM)
7879
#define PWM_RES_10_BIT (LEDC_TIMER_10_BIT)
7980

8081
// Maximum duty value on 10-bit resolution
@@ -84,15 +85,17 @@ STATIC ledc_timer_config_t timers[PWM_TIMER_MAX];
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; // PWM_RES_10_BIT 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; // - / -
@@ -237,7 +240,6 @@ STATIC void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_conf
237240
}
238241

239242
// Configure the new resolution and frequency
240-
unsigned int save_duty_resolution = timer->duty_resolution;
241243
timer->duty_resolution = res;
242244
timer->freq_hz = freq;
243245
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
258260
if (self->mode == LEDC_LOW_SPEED_MODE) {
259261
check_esp_err(ledc_timer_rst(self->mode, self->timer));
260262
}
263+
}
261264

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-
}
265+
// Save the same duty cycle when frequency is changed
266+
if (self->duty_x == HIGHEST_PWM_RES) {
267+
set_duty_u16(self, self->duty_u16);
268+
} else if (self->duty_x == PWM_RES_10_BIT) {
269+
set_duty_u10(self, self->duty_u10);
270+
} else if (self->duty_x == -HIGHEST_PWM_RES) {
271+
set_duty_ns(self, self->duty_ns);
272272
}
273273
}
274274

@@ -293,11 +293,18 @@ STATIC int duty_to_ns(machine_pwm_obj_t *self, int duty) {
293293
#define get_duty_raw(self) ledc_get_duty(self->mode, self->channel)
294294

295295
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);
296+
int resolution = timers[TIMER_IDX(self->mode, self->timer)].duty_resolution;
297+
int duty = ledc_get_duty(self->mode, self->channel);
298+
if (resolution <= UI_RES_16_BIT) {
299+
duty <<= (UI_RES_16_BIT - resolution);
300+
} else {
301+
duty >>= (resolution - UI_RES_16_BIT);
302+
}
303+
return duty;
297304
}
298305

299306
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);
307+
return get_duty_u16(self) >> 6; // Scale down from 16 bit to 10 bit resolution
301308
}
302309

303310
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) {
309316
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_u16 must be from 0 to %d"), UI_MAX_DUTY);
310317
}
311318
ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)];
312-
int channel_duty = duty >> (UI_RES_16_BIT - timer.duty_resolution);
319+
int channel_duty;
320+
if (timer.duty_resolution <= UI_RES_16_BIT) {
321+
channel_duty = duty >> (UI_RES_16_BIT - timer.duty_resolution);
322+
} else {
323+
channel_duty = duty << (timer.duty_resolution - UI_RES_16_BIT);
324+
}
313325
int max_duty = (1 << timer.duty_resolution) - 1;
314326
if (channel_duty < 0) {
315327
channel_duty = 0;
@@ -333,15 +345,15 @@ STATIC void set_duty_u16(machine_pwm_obj_t *self, int duty) {
333345
}
334346
*/
335347

336-
self->duty_x = UI_RES_16_BIT;
348+
self->duty_x = HIGHEST_PWM_RES;
337349
self->duty_u16 = duty;
338350
}
339351

340352
STATIC void set_duty_u10(machine_pwm_obj_t *self, int duty) {
341353
if ((duty < 0) || (duty > MAX_DUTY_U10)) {
342354
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty must be from 0 to %u"), MAX_DUTY_U10);
343355
}
344-
set_duty_u16(self, duty << (UI_RES_16_BIT - LEDC_TIMER_10_BIT));
356+
set_duty_u16(self, duty << (UI_RES_16_BIT - PWM_RES_10_BIT));
345357
self->duty_x = PWM_RES_10_BIT;
346358
self->duty_u10 = duty;
347359
}
@@ -351,7 +363,7 @@ STATIC void set_duty_ns(machine_pwm_obj_t *self, int ns) {
351363
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));
352364
}
353365
set_duty_u16(self, ns_to_duty(self, ns));
354-
self->duty_x = -UI_RES_16_BIT;
366+
self->duty_x = -HIGHEST_PWM_RES;
355367
self->duty_ns = ns;
356368
}
357369

@@ -424,7 +436,7 @@ STATIC void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_p
424436

425437
if (self->duty_x == PWM_RES_10_BIT) {
426438
mp_printf(print, ", duty=%d", get_duty_u10(self));
427-
} else if (self->duty_x == -UI_RES_16_BIT) {
439+
} else if (self->duty_x == -HIGHEST_PWM_RES) {
428440
mp_printf(print, ", duty_ns=%d", get_duty_ns(self));
429441
} else {
430442
mp_printf(print, ", duty_u16=%d", get_duty_u16(self));

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy