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