Skip to content

Commit edfbe35

Browse files
committed
esp32/machine_pwm: handle multiple timers.
This commit allow to use all the available timer for the ledc subsystem, without affecting the API. If a new frequency is set, first it check if an other timer is using the same freq. If yes, then it use this timer, otherwise it create a new one. If all timers are used, the user should set an already used frequency, or de-init a channel.
1 parent 5fb276d commit edfbe35

File tree

1 file changed

+117
-34
lines changed

1 file changed

+117
-34
lines changed

ports/esp32/machine_pwm.c

Lines changed: 117 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -46,39 +46,50 @@ typedef struct _esp32_pwm_obj_t {
4646
// (-1 if not assigned)
4747
STATIC int chan_gpio[LEDC_CHANNEL_MAX];
4848

49+
// Which channel has which timer assigned?
50+
// (-1 if not assigned)
51+
STATIC int chan_timer[LEDC_CHANNEL_MAX];
52+
53+
// List of timer configs
54+
STATIC ledc_timer_config_t timers[LEDC_TIMER_MAX];
55+
4956
// Params for PW operation
5057
// 5khz
5158
#define PWFREQ (5000)
5259
// High speed mode
5360
#define PWMODE (LEDC_HIGH_SPEED_MODE)
5461
// 10-bit resolution (compatible with esp8266 PWM)
5562
#define PWRES (LEDC_TIMER_10_BIT)
56-
// Timer 1
57-
#define PWTIMER (LEDC_TIMER_1)
5863

5964
// Config of timer upon which we run all PWM'ed GPIO pins
6065
STATIC bool pwm_inited = false;
61-
STATIC ledc_timer_config_t timer_cfg = {
62-
.duty_resolution = PWRES,
63-
.freq_hz = PWFREQ,
64-
.speed_mode = PWMODE,
65-
.timer_num = PWTIMER
66-
};
6766

6867
STATIC void pwm_init(void) {
6968

7069
// Initial condition: no channels assigned
7170
for (int x = 0; x < LEDC_CHANNEL_MAX; ++x) {
7271
chan_gpio[x] = -1;
72+
chan_timer[x] = -1;
7373
}
7474

75-
// Init with default timer params
76-
ledc_timer_config(&timer_cfg);
75+
// Initial condition: no timers assigned
76+
for (int x = 0; x < LEDC_TIMER_MAX; ++x) {
77+
timers[x].duty_resolution = PWRES;
78+
// unset timer is -1
79+
timers[x].freq_hz = -1;
80+
timers[x].speed_mode = PWMODE;
81+
timers[x].timer_num = x;
82+
}
7783
}
7884

79-
STATIC int set_freq(int newval) {
80-
int ores = timer_cfg.duty_resolution;
81-
int oval = timer_cfg.freq_hz;
85+
STATIC int set_freq(int newval, ledc_timer_config_t *timer) {
86+
int ores = timer->duty_resolution;
87+
int oval = timer->freq_hz;
88+
89+
// If already set, do nothing
90+
if (newval == oval) {
91+
return 1;
92+
}
8293

8394
// Find the highest bit resolution for the requested frequency
8495
if (newval <= 0) {
@@ -95,16 +106,60 @@ STATIC int set_freq(int newval) {
95106
}
96107

97108
// Configure the new resolution and frequency
98-
timer_cfg.duty_resolution = res;
99-
timer_cfg.freq_hz = newval;
100-
if (ledc_timer_config(&timer_cfg) != ESP_OK) {
101-
timer_cfg.duty_resolution = ores;
102-
timer_cfg.freq_hz = oval;
109+
timer->duty_resolution = res;
110+
timer->freq_hz = newval;
111+
if (ledc_timer_config(timer) != ESP_OK) {
112+
timer->duty_resolution = ores;
113+
timer->freq_hz = oval;
103114
return 0;
104115
}
105116
return 1;
106117
}
107118

119+
STATIC int found_timer(int freq) {
120+
int timer_found = -1;
121+
int timer;
122+
// Find a free PWM Timer using the same freq
123+
for (timer = 0; timer < LEDC_TIMER_MAX; ++timer) {
124+
if (timers[timer].freq_hz == freq) {
125+
break;
126+
}
127+
if ((timer_found == -1) && (timers[timer].freq_hz == -1)) {
128+
timer_found = timer;
129+
}
130+
}
131+
if (timer >= LEDC_TIMER_MAX) {
132+
if (timer_found == -1) {
133+
timer = -1;
134+
} else {
135+
timer = timer_found;
136+
}
137+
}
138+
139+
return timer;
140+
}
141+
142+
// If the timer is no longer used, clean it
143+
STATIC void cleanup_timer(int prev_channel, int timer) {
144+
bool used = false;
145+
int i;
146+
for (i = 0; i < LEDC_CHANNEL_MAX; ++i) {
147+
if (i != prev_channel && chan_timer[i] == timer) {
148+
used = true;
149+
break;
150+
}
151+
}
152+
153+
// If timer is not used, clean it
154+
if (!used) {
155+
ledc_timer_pause(PWMODE, timer);
156+
157+
// Flag it unused
158+
timers[timer].freq_hz = -1;
159+
chan_timer[prev_channel] = -1;
160+
}
161+
}
162+
108163
/******************************************************************************/
109164

110165
// MicroPython bindings for PWM
@@ -113,7 +168,7 @@ STATIC void esp32_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_
113168
esp32_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in);
114169
mp_printf(print, "PWM(%u", self->pin);
115170
if (self->active) {
116-
mp_printf(print, ", freq=%u, duty=%u", timer_cfg.freq_hz,
171+
mp_printf(print, ", freq=%u, duty=%u", timers[chan_timer[self->channel]].freq_hz,
117172
ledc_get_duty(PWMODE, self->channel));
118173
}
119174
mp_printf(print, ")");
@@ -151,38 +206,47 @@ STATIC void esp32_pwm_init_helper(esp32_pwm_obj_t *self,
151206
}
152207
self->channel = channel;
153208

209+
int timer;
210+
int freq = args[ARG_freq].u_int;
211+
212+
if (freq == -1) {
213+
freq = PWFREQ;
214+
}
215+
216+
timer = found_timer(freq);
217+
218+
if (timer == -1) {
219+
mp_raise_ValueError(MP_ERROR_TEXT("out of PWM timers"));
220+
}
221+
chan_timer[channel] = timer;
222+
154223
// New PWM assignment
155224
self->active = 1;
156225
if (chan_gpio[channel] == -1) {
157226
ledc_channel_config_t cfg = {
158227
.channel = channel,
159-
.duty = (1 << timer_cfg.duty_resolution) / 2,
228+
.duty = (1 << timers[timer].duty_resolution) / 2,
160229
.gpio_num = self->pin,
161230
.intr_type = LEDC_INTR_DISABLE,
162231
.speed_mode = PWMODE,
163-
.timer_sel = PWTIMER,
232+
.timer_sel = timer,
164233
};
165234
if (ledc_channel_config(&cfg) != ESP_OK) {
166235
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("PWM not supported on pin %d"), self->pin);
167236
}
168237
chan_gpio[channel] = self->pin;
169238
}
170239

171-
// Maybe change PWM timer
172-
int tval = args[ARG_freq].u_int;
173-
if (tval != -1) {
174-
if (tval != timer_cfg.freq_hz) {
175-
if (!set_freq(tval)) {
176-
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("bad frequency %d"), tval);
177-
}
178-
}
240+
// Set timer frequency
241+
if (!set_freq(freq, &timers[timer])) {
242+
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("bad frequency %d"), freq);
179243
}
180244

181245
// Set duty cycle?
182246
int dval = args[ARG_duty].u_int;
183247
if (dval != -1) {
184248
dval &= ((1 << PWRES) - 1);
185-
dval >>= PWRES - timer_cfg.duty_resolution;
249+
dval >>= PWRES - timers[timer].duty_resolution;
186250
ledc_set_duty(PWMODE, channel, dval);
187251
ledc_update_duty(PWMODE, channel);
188252
}
@@ -227,6 +291,9 @@ STATIC mp_obj_t esp32_pwm_deinit(mp_obj_t self_in) {
227291

228292
// Valid channel?
229293
if ((chan >= 0) && (chan < LEDC_CHANNEL_MAX)) {
294+
// Clean up timer if necessary
295+
cleanup_timer(chan, chan_timer[self->channel]);
296+
230297
// Mark it unused, and tell the hardware to stop routing
231298
chan_gpio[chan] = -1;
232299
ledc_stop(PWMODE, chan, 0);
@@ -239,14 +306,30 @@ STATIC mp_obj_t esp32_pwm_deinit(mp_obj_t self_in) {
239306
STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_pwm_deinit_obj, esp32_pwm_deinit);
240307

241308
STATIC mp_obj_t esp32_pwm_freq(size_t n_args, const mp_obj_t *args) {
309+
esp32_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]);
242310
if (n_args == 1) {
243311
// get
244-
return MP_OBJ_NEW_SMALL_INT(timer_cfg.freq_hz);
312+
return MP_OBJ_NEW_SMALL_INT(timers[chan_timer[self->channel]].freq_hz);
245313
}
246314

247315
// set
248316
int tval = mp_obj_get_int(args[1]);
249-
if (!set_freq(tval)) {
317+
cleanup_timer(self->channel, chan_timer[self->channel]);
318+
319+
// Check if a timer already use the new freq, or grab a new one
320+
int new_timer = found_timer(tval);
321+
322+
if (new_timer == -1) {
323+
mp_raise_ValueError(MP_ERROR_TEXT("out of PWM timers"));
324+
}
325+
326+
chan_timer[self->channel] = new_timer;
327+
328+
if (ledc_bind_channel_timer(PWMODE, self->channel, new_timer) != ESP_OK) {
329+
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Failed to bind timer to channel"));
330+
}
331+
332+
if (!set_freq(tval, &timers[new_timer])) {
250333
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("bad frequency %d"), tval);
251334
}
252335
return mp_const_none;
@@ -261,14 +344,14 @@ STATIC mp_obj_t esp32_pwm_duty(size_t n_args, const mp_obj_t *args) {
261344
if (n_args == 1) {
262345
// get
263346
duty = ledc_get_duty(PWMODE, self->channel);
264-
duty <<= PWRES - timer_cfg.duty_resolution;
347+
duty <<= PWRES - timers[chan_timer[self->channel]].duty_resolution;
265348
return MP_OBJ_NEW_SMALL_INT(duty);
266349
}
267350

268351
// set
269352
duty = mp_obj_get_int(args[1]);
270353
duty &= ((1 << PWRES) - 1);
271-
duty >>= PWRES - timer_cfg.duty_resolution;
354+
duty >>= PWRES - timers[chan_timer[self->channel]].duty_resolution;
272355
ledc_set_duty(PWMODE, self->channel, duty);
273356
ledc_update_duty(PWMODE, self->channel);
274357

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