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

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