Skip to content

Commit 7bf1bc8

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

File tree

1 file changed

+112
-34
lines changed

1 file changed

+112
-34
lines changed

ports/esp32/machine_pwm.c

Lines changed: 112 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,55 @@ 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 free_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+
// A timer already use the same freq. Use it now.
126+
return timer;
127+
}
128+
if ((free_timer_found == -1) && (timers[timer].freq_hz == -1)) {
129+
free_timer_found = timer;
130+
// Continue to check if a channel with the same freq is in used.
131+
}
132+
}
133+
134+
return free_timer_found;
135+
}
136+
137+
// If the timer is no longer used, clean it
138+
STATIC void cleanup_timer(int prev_channel, int timer) {
139+
bool used = false;
140+
int i;
141+
for (i = 0; i < LEDC_CHANNEL_MAX; ++i) {
142+
if (i != prev_channel && chan_timer[i] == timer) {
143+
used = true;
144+
break;
145+
}
146+
}
147+
148+
// If timer is not used, clean it
149+
if (!used) {
150+
ledc_timer_pause(PWMODE, timer);
151+
152+
// Flag it unused
153+
timers[timer].freq_hz = -1;
154+
chan_timer[prev_channel] = -1;
155+
}
156+
}
157+
108158
/******************************************************************************/
109159

110160
// MicroPython bindings for PWM
@@ -113,7 +163,7 @@ STATIC void esp32_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_
113163
esp32_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in);
114164
mp_printf(print, "PWM(%u", self->pin);
115165
if (self->active) {
116-
mp_printf(print, ", freq=%u, duty=%u", timer_cfg.freq_hz,
166+
mp_printf(print, ", freq=%u, duty=%u", timers[chan_timer[self->channel]].freq_hz,
117167
ledc_get_duty(PWMODE, self->channel));
118168
}
119169
mp_printf(print, ")");
@@ -151,38 +201,47 @@ STATIC void esp32_pwm_init_helper(esp32_pwm_obj_t *self,
151201
}
152202
self->channel = channel;
153203

204+
int timer;
205+
int freq = args[ARG_freq].u_int;
206+
207+
if (freq == -1) {
208+
freq = PWFREQ;
209+
}
210+
211+
timer = found_timer(freq);
212+
213+
if (timer == -1) {
214+
mp_raise_ValueError(MP_ERROR_TEXT("out of PWM timers"));
215+
}
216+
chan_timer[channel] = timer;
217+
154218
// New PWM assignment
155219
self->active = 1;
156220
if (chan_gpio[channel] == -1) {
157221
ledc_channel_config_t cfg = {
158222
.channel = channel,
159-
.duty = (1 << timer_cfg.duty_resolution) / 2,
223+
.duty = (1 << timers[timer].duty_resolution) / 2,
160224
.gpio_num = self->pin,
161225
.intr_type = LEDC_INTR_DISABLE,
162226
.speed_mode = PWMODE,
163-
.timer_sel = PWTIMER,
227+
.timer_sel = timer,
164228
};
165229
if (ledc_channel_config(&cfg) != ESP_OK) {
166230
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("PWM not supported on pin %d"), self->pin);
167231
}
168232
chan_gpio[channel] = self->pin;
169233
}
170234

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-
}
235+
// Set timer frequency
236+
if (!set_freq(freq, &timers[timer])) {
237+
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("bad frequency %d"), freq);
179238
}
180239

181240
// Set duty cycle?
182241
int dval = args[ARG_duty].u_int;
183242
if (dval != -1) {
184243
dval &= ((1 << PWRES) - 1);
185-
dval >>= PWRES - timer_cfg.duty_resolution;
244+
dval >>= PWRES - timers[timer].duty_resolution;
186245
ledc_set_duty(PWMODE, channel, dval);
187246
ledc_update_duty(PWMODE, channel);
188247
}
@@ -227,6 +286,9 @@ STATIC mp_obj_t esp32_pwm_deinit(mp_obj_t self_in) {
227286

228287
// Valid channel?
229288
if ((chan >= 0) && (chan < LEDC_CHANNEL_MAX)) {
289+
// Clean up timer if necessary
290+
cleanup_timer(chan, chan_timer[self->channel]);
291+
230292
// Mark it unused, and tell the hardware to stop routing
231293
chan_gpio[chan] = -1;
232294
ledc_stop(PWMODE, chan, 0);
@@ -239,14 +301,30 @@ STATIC mp_obj_t esp32_pwm_deinit(mp_obj_t self_in) {
239301
STATIC MP_DEFINE_CONST_FUN_OBJ_1(esp32_pwm_deinit_obj, esp32_pwm_deinit);
240302

241303
STATIC mp_obj_t esp32_pwm_freq(size_t n_args, const mp_obj_t *args) {
304+
esp32_pwm_obj_t *self = MP_OBJ_TO_PTR(args[0]);
242305
if (n_args == 1) {
243306
// get
244-
return MP_OBJ_NEW_SMALL_INT(timer_cfg.freq_hz);
307+
return MP_OBJ_NEW_SMALL_INT(timers[chan_timer[self->channel]].freq_hz);
245308
}
246309

247310
// set
248311
int tval = mp_obj_get_int(args[1]);
249-
if (!set_freq(tval)) {
312+
cleanup_timer(self->channel, chan_timer[self->channel]);
313+
314+
// Check if a timer already use the new freq, or grab a new one
315+
int new_timer = found_timer(tval);
316+
317+
if (new_timer == -1) {
318+
mp_raise_ValueError(MP_ERROR_TEXT("out of PWM timers"));
319+
}
320+
321+
chan_timer[self->channel] = new_timer;
322+
323+
if (ledc_bind_channel_timer(PWMODE, self->channel, new_timer) != ESP_OK) {
324+
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Failed to bind timer to channel"));
325+
}
326+
327+
if (!set_freq(tval, &timers[new_timer])) {
250328
mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("bad frequency %d"), tval);
251329
}
252330
return mp_const_none;
@@ -261,14 +339,14 @@ STATIC mp_obj_t esp32_pwm_duty(size_t n_args, const mp_obj_t *args) {
261339
if (n_args == 1) {
262340
// get
263341
duty = ledc_get_duty(PWMODE, self->channel);
264-
duty <<= PWRES - timer_cfg.duty_resolution;
342+
duty <<= PWRES - timers[chan_timer[self->channel]].duty_resolution;
265343
return MP_OBJ_NEW_SMALL_INT(duty);
266344
}
267345

268346
// set
269347
duty = mp_obj_get_int(args[1]);
270348
duty &= ((1 << PWRES) - 1);
271-
duty >>= PWRES - timer_cfg.duty_resolution;
349+
duty >>= PWRES - timers[chan_timer[self->channel]].duty_resolution;
272350
ledc_set_duty(PWMODE, self->channel, duty);
273351
ledc_update_duty(PWMODE, self->channel);
274352

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