Skip to content

Commit e3bc800

Browse files
authored
Merge pull request #4999 from hierophect/esp-timer-leak
ESP32S2: Fix PWM timer leak and variable frequency conflicts
2 parents e7c5165 + c504a85 commit e3bc800

File tree

3 files changed

+42
-19
lines changed

3 files changed

+42
-19
lines changed

locale/circuitpython.pot

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1270,6 +1270,7 @@ msgstr ""
12701270

12711271
#: ports/atmel-samd/common-hal/pwmio/PWMOut.c
12721272
#: ports/cxd56/common-hal/pwmio/PWMOut.c
1273+
#: ports/esp32s2/common-hal/pwmio/PWMOut.c
12731274
#: ports/mimxrt10xx/common-hal/pwmio/PWMOut.c
12741275
#: ports/nrf/common-hal/pwmio/PWMOut.c
12751276
#: ports/raspberrypi/common-hal/pwmio/PWMOut.c shared-bindings/pwmio/PWMOut.c
@@ -1329,7 +1330,7 @@ msgstr ""
13291330
msgid "Invalid format chunk size"
13301331
msgstr ""
13311332

1332-
#: ports/esp32s2/common-hal/busio/I2C.c ports/esp32s2/common-hal/pwmio/PWMOut.c
1333+
#: ports/esp32s2/common-hal/busio/I2C.c
13331334
msgid "Invalid frequency"
13341335
msgstr ""
13351336

ports/esp32s2/common-hal/pwmio/PWMOut.c

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,26 @@
3434

3535
STATIC bool not_first_reset = false;
3636
STATIC uint32_t reserved_timer_freq[LEDC_TIMER_MAX];
37+
STATIC bool varfreq_timers[LEDC_TIMER_MAX];
3738
STATIC uint8_t reserved_channels[LEDC_CHANNEL_MAX];
3839
STATIC bool never_reset_tim[LEDC_TIMER_MAX];
3940
STATIC bool never_reset_chan[LEDC_CHANNEL_MAX];
4041

42+
STATIC uint32_t calculate_duty_cycle(uint32_t frequency) {
43+
uint32_t duty_bits = 0;
44+
uint32_t interval = LEDC_APB_CLK_HZ / frequency;
45+
for (size_t i = 0; i < 32; i++) {
46+
if (!(interval >> i)) {
47+
duty_bits = i - 1;
48+
break;
49+
}
50+
}
51+
if (duty_bits >= LEDC_TIMER_14_BIT) {
52+
duty_bits = LEDC_TIMER_13_BIT;
53+
}
54+
return duty_bits;
55+
}
56+
4157
void pwmout_reset(void) {
4258
for (size_t i = 0; i < LEDC_CHANNEL_MAX; i++) {
4359
if (reserved_channels[i] != INDEX_EMPTY && not_first_reset) {
@@ -53,6 +69,7 @@ void pwmout_reset(void) {
5369
}
5470
if (!never_reset_tim[i]) {
5571
reserved_timer_freq[i] = 0;
72+
varfreq_timers[i] = false;
5673
}
5774
}
5875
not_first_reset = true;
@@ -70,25 +87,17 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t *self,
7087
}
7188

7289
// Calculate duty cycle
73-
uint32_t duty_bits = 0;
74-
uint32_t interval = LEDC_APB_CLK_HZ / frequency;
75-
for (size_t i = 0; i < 32; i++) {
76-
if (!(interval >> i)) {
77-
duty_bits = i - 1;
78-
break;
79-
}
80-
}
81-
if (duty_bits < 1) {
82-
mp_raise_ValueError(translate("Invalid frequency"));
83-
} else if (duty_bits >= LEDC_TIMER_14_BIT) {
84-
duty_bits = LEDC_TIMER_13_BIT;
90+
uint32_t duty_bits = calculate_duty_cycle(frequency);
91+
if (duty_bits == 0) {
92+
return PWMOUT_INVALID_FREQUENCY;
8593
}
8694

8795
// Find a viable timer
8896
size_t timer_index = INDEX_EMPTY;
8997
size_t channel_index = INDEX_EMPTY;
9098
for (size_t i = 0; i < LEDC_TIMER_MAX; i++) {
91-
if ((reserved_timer_freq[i] == frequency) && !variable_frequency) {
99+
// accept matching freq timers unless this instance is varfreq or a prior one was
100+
if ((reserved_timer_freq[i] == frequency) && !variable_frequency && !varfreq_timers[i]) {
92101
// prioritize matched frequencies so we don't needlessly take slots
93102
timer_index = i;
94103
break;
@@ -139,6 +148,9 @@ pwmout_result_t common_hal_pwmio_pwmout_construct(pwmio_pwmout_obj_t *self,
139148
reserved_timer_freq[timer_index] = frequency;
140149
reserved_channels[channel_index] = timer_index;
141150

151+
if (variable_frequency) {
152+
varfreq_timers[timer_index] = true;
153+
}
142154
self->variable_frequency = variable_frequency;
143155
self->pin_number = pin->number;
144156
self->deinited = false;
@@ -175,6 +187,7 @@ void common_hal_pwmio_pwmout_deinit(pwmio_pwmout_obj_t *self) {
175187
if (reserved_channels[self->chan_handle.channel] != INDEX_EMPTY) {
176188
ledc_stop(LEDC_LOW_SPEED_MODE, self->chan_handle.channel, 0);
177189
}
190+
reserved_channels[self->chan_handle.channel] = INDEX_EMPTY;
178191
// Search if any other channel is using the timer
179192
bool taken = false;
180193
for (size_t i = 0; i < LEDC_CHANNEL_MAX; i++) {
@@ -184,13 +197,12 @@ void common_hal_pwmio_pwmout_deinit(pwmio_pwmout_obj_t *self) {
184197
}
185198
// Variable frequency means there's only one channel on the timer
186199
if (!taken || self->variable_frequency) {
187-
if (reserved_timer_freq[self->tim_handle.timer_num] != 0) {
188-
ledc_timer_rst(LEDC_LOW_SPEED_MODE, self->tim_handle.timer_num);
189-
}
200+
ledc_timer_rst(LEDC_LOW_SPEED_MODE, self->tim_handle.timer_num);
190201
reserved_timer_freq[self->tim_handle.timer_num] = 0;
202+
// if timer isn't varfreq this will be off aleady
203+
varfreq_timers[self->tim_handle.timer_num] = false;
191204
}
192205
reset_pin_number(self->pin_number);
193-
reserved_channels[self->chan_handle.channel] = INDEX_EMPTY;
194206
self->deinited = true;
195207
}
196208

@@ -204,6 +216,12 @@ uint16_t common_hal_pwmio_pwmout_get_duty_cycle(pwmio_pwmout_obj_t *self) {
204216
}
205217

206218
void common_hal_pwmio_pwmout_set_frequency(pwmio_pwmout_obj_t *self, uint32_t frequency) {
219+
// Calculate duty cycle
220+
uint32_t duty_bits = calculate_duty_cycle(frequency);
221+
if (duty_bits == 0) {
222+
mp_raise_ValueError(translate("Invalid PWM frequency"));
223+
}
224+
self->duty_resolution = duty_bits;
207225
ledc_set_freq(LEDC_LOW_SPEED_MODE, self->tim_handle.timer_num, frequency);
208226
}
209227

shared-bindings/pwmio/PWMOut.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,11 @@ STATIC mp_obj_t pwmio_pwmout_obj_set_frequency(mp_obj_t self_in, mp_obj_t freque
231231
"PWM frequency not writable when variable_frequency is False on "
232232
"construction."));
233233
}
234-
common_hal_pwmio_pwmout_set_frequency(self, mp_obj_get_int(frequency));
234+
mp_int_t freq = mp_obj_get_int(frequency);
235+
if (freq == 0) {
236+
mp_raise_ValueError(translate("Invalid PWM frequency"));
237+
}
238+
common_hal_pwmio_pwmout_set_frequency(self, freq);
235239
return mp_const_none;
236240
}
237241
MP_DEFINE_CONST_FUN_OBJ_2(pwmio_pwmout_set_frequency_obj, pwmio_pwmout_obj_set_frequency);

0 commit comments

Comments
 (0)