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

Skip to content

Commit d1e7ce0

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 caaaa2b commit d1e7ce0

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
@@ -40,45 +40,57 @@ typedef struct _esp32_pwm_obj_t {
4040
gpio_num_t pin;
4141
uint8_t active;
4242
uint8_t channel;
43+
uint8_t timer;
4344
} esp32_pwm_obj_t;
4445

4546
// Which channel has which GPIO pin assigned?
4647
// (-1 if not assigned)
4748
STATIC int chan_gpio[LEDC_CHANNEL_MAX];
4849

50+
// Which channel has which timer assigned?
51+
// (-1 if not assigned)
52+
STATIC int chan_timer[LEDC_CHANNEL_MAX];
53+
54+
// List of timer configs
55+
STATIC ledc_timer_config_t timers[LEDC_TIMER_MAX];
56+
4957
// Params for PW operation
5058
// 5khz
5159
#define PWFREQ (5000)
5260
// High speed mode
5361
#define PWMODE (LEDC_HIGH_SPEED_MODE)
5462
// 10-bit resolution (compatible with esp8266 PWM)
5563
#define PWRES (LEDC_TIMER_10_BIT)
56-
// Timer 1
57-
#define PWTIMER (LEDC_TIMER_1)
5864

5965
// Config of timer upon which we run all PWM'ed GPIO pins
6066
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-
};
6767

6868
STATIC void pwm_init(void) {
6969

7070
// Initial condition: no channels assigned
7171
for (int x = 0; x < LEDC_CHANNEL_MAX; ++x) {
7272
chan_gpio[x] = -1;
73+
chan_timer[x] = -1;
7374
}
7475

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

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

8395
// Find the highest bit resolution for the requested frequency
8496
if (newval <= 0) {
@@ -95,16 +107,39 @@ STATIC int set_freq(int newval) {
95107
}
96108

97109
// 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;
110+
timer->duty_resolution = res;
111+
timer->freq_hz = newval;
112+
if (ledc_timer_config(timer) != ESP_OK) {
113+
timer->duty_resolution = ores;
114+
timer->freq_hz = oval;
103115
return 0;
104116
}
105117
return 1;
106118
}
107119

120+
STATIC int found_timer(int freq) {
121+
int timer_found = -1;
122+
int timer;
123+
// Find a free PWM Timer using the same freq
124+
for (timer = 0; timer < LEDC_TIMER_MAX; ++timer) {
125+
if (timers[timer].freq_hz == freq) {
126+
break;
127+
}
128+
if ((timer_found == -1) && (timers[timer].freq_hz == -1)) {
129+
timer_found = timer;
130+
}
131+
}
132+
if (timer >= LEDC_TIMER_MAX) {
133+
if (timer_found == -1) {
134+
timer = -1;
135+
} else {
136+
timer = timer_found;
137+
}
138+
}
139+
140+
return timer;
141+
}
142+
108143
/******************************************************************************/
109144

110145
// MicroPython bindings for PWM
@@ -113,7 +148,7 @@ STATIC void esp32_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_
113148
esp32_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in);
114149
mp_printf(print, "PWM(%u", self->pin);
115150
if (self->active) {
116-
mp_printf(print, ", freq=%u, duty=%u", timer_cfg.freq_hz,
151+
mp_printf(print, ", freq=%u, duty=%u", timers[self->timer].freq_hz,
117152
ledc_get_duty(PWMODE, self->channel));
118153
}
119154
mp_printf(print, ")");
@@ -151,38 +186,48 @@ STATIC void esp32_pwm_init_helper(esp32_pwm_obj_t *self,
151186
}
152187
self->channel = channel;
153188

189+
int timer;
190+
int freq = args[ARG_freq].u_int;
191+
192+
if (freq == -1) {
193+
freq = PWFREQ;
194+
}
195+
196+
timer = found_timer(freq);
197+
198+
if (timer == -1) {
199+
mp_raise_ValueError(MP_ERROR_TEXT("out of PWM timers"));
200+
}
201+
self->timer = timer;
202+
chan_timer[channel] = timer;
203+
154204
// New PWM assignment
155205
self->active = 1;
156206
if (chan_gpio[channel] == -1) {
157207
ledc_channel_config_t cfg = {
158208
.channel = channel,
159-
.duty = (1 << timer_cfg.duty_resolution) / 2,
209+
.duty = (1 << timers[timer].duty_resolution) / 2,
160210
.gpio_num = self->pin,
161211
.intr_type = LEDC_INTR_DISABLE,
162212
.speed_mode = PWMODE,
163-
.timer_sel = PWTIMER,
213+
.timer_sel = timer,
164214
};
165215
if (ledc_channel_config(&cfg) != ESP_OK) {
166216
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("PWM not supported on pin %d"), self->pin);
167217
}
168218
chan_gpio[channel] = self->pin;
169219
}
170220

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-
}
221+
// Set timer frequency
222+
if (!set_freq(freq, &timers[timer])) {
223+
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("bad frequency %d"), freq);
179224
}
180225

181226
// Set duty cycle?
182227
int dval = args[ARG_duty].u_int;
183228
if (dval != -1) {
184229
dval &= ((1 << PWRES) - 1);
185-
dval >>= PWRES - timer_cfg.duty_resolution;
230+
dval >>= PWRES - timers[timer].duty_resolution;
186231
ledc_set_duty(PWMODE, channel, dval);
187232
ledc_update_duty(PWMODE, channel);
188233
}
@@ -229,6 +274,7 @@ STATIC mp_obj_t esp32_pwm_deinit(mp_obj_t self_in) {
229274
if ((chan >= 0) && (chan < LEDC_CHANNEL_MAX)) {
230275
// Mark it unused, and tell the hardware to stop routing
231276
chan_gpio[chan] = -1;
277+
chan_timer[chan] = -1;
232278
ledc_stop(PWMODE, chan, 0);
233279
self->active = 0;
234280
self->channel = -1;
@@ -239,14 +285,51 @@ STATIC mp_obj_t esp32_pwm_deinit(mp_obj_t self_in) {
239285
STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_pwm_deinit_obj, esp32_pwm_deinit);
240286

241287
STATIC mp_obj_t esp32_pwm_freq(size_t n_args, const mp_obj_t *args) {
288+
esp32_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]);
242289
if (n_args == 1) {
243290
// get
244-
return MP_OBJ_NEW_SMALL_INT(timer_cfg.freq_hz);
291+
return MP_OBJ_NEW_SMALL_INT(timers[self->timer].freq_hz);
245292
}
246293

247294
// set
248295
int tval = mp_obj_get_int(args[1]);
249-
if (!set_freq(tval)) {
296+
297+
// If the timer is no longer used, clean it
298+
int old_timer = self->timer;
299+
300+
bool used = false;
301+
int i;
302+
for (i = 0; i < LEDC_CHANNEL_MAX; ++i) {
303+
if (i != self->channel && chan_timer[i] == old_timer) {
304+
used = true;
305+
break;
306+
}
307+
}
308+
309+
// If timer is not used, clean it
310+
if (!used) {
311+
ledc_timer_pause(PWMODE, old_timer);
312+
313+
// Flag it unused
314+
timers[old_timer].freq_hz = -1;
315+
}
316+
317+
// Check if a timer already use the new freq, or grab a new one
318+
int new_timer = found_timer(tval);
319+
320+
if (new_timer == -1) {
321+
mp_raise_ValueError(MP_ERROR_TEXT("out of PWM timers"));
322+
}
323+
324+
self->timer = new_timer;
325+
chan_timer[self->channel] = new_timer;
326+
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[self->timer].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[self->timer].duty_resolution;
272355
ledc_set_duty(PWMODE, self->channel, duty);
273356
ledc_update_duty(PWMODE, self->channel);
274357

0 commit comments

Comments
 (0)
0