8000 esp32/machine_pwm: handle multiple timers. · micropython/micropython@edfbe35 · GitHub
[go: up one dir, main page]

Skip to content {"props":{"docsUrl":"https://docs.github.com/get-started/accessibility/keyboard-shortcuts"}}

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)
0