@@ -46,39 +46,50 @@ typedef struct _esp32_pwm_obj_t {
46
46
// (-1 if not assigned)
47
47
STATIC int chan_gpio [LEDC_CHANNEL_MAX ];
48
48
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
+
49
56
// Params for PW operation
50
57
// 5khz
51
58
#define PWFREQ (5000)
52
59
// High speed mode
53
60
#define PWMODE (LEDC_HIGH_SPEED_MODE)
54
61
// 10-bit resolution (compatible with esp8266 PWM)
55
62
#define PWRES (LEDC_TIMER_10_BIT)
56
- // Timer 1
57
- #define PWTIMER (LEDC_TIMER_1)
58
63
59
64
// Config of timer upon which we run all PWM'ed GPIO pins
60
65
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
- };
67
66
68
67
STATIC void pwm_init (void ) {
69
68
70
69
// Initial condition: no channels assigned
71
70
for (int x = 0 ; x < LEDC_CHANNEL_MAX ; ++ x ) {
72
71
chan_gpio [x ] = -1 ;
72
+ chan_timer [x ] = -1 ;
73
73
}
74
74
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
+ }
77
83
}
78
84
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
+ }
82
93
83
94
// Find the highest bit resolution for the requested frequency
84
95
if (newval <= 0 ) {
@@ -95,16 +106,60 @@ STATIC int set_freq(int newval) {
95
106
}
96
107
97
108
// 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 ;
103
114
return 0 ;
104
115
}
105
116
return 1 ;
106
117
}
107
118
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
+
108
163
/******************************************************************************/
109
164
110
165
// 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_
113
168
esp32_pwm_obj_t * self = MP_OBJ_TO_PTR (self_in );
114
169
mp_printf (print , "PWM(%u" , self -> pin );
115
170
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 ,
117
172
ledc_get_duty (PWMODE , self -> channel ));
118
173
}
119
174
mp_printf (print , ")" );
@@ -151,38 +206,47 @@ STATIC void esp32_pwm_init_helper(esp32_pwm_obj_t *self,
151
206
}
152
207
self -> channel = channel ;
153
208
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
+
154
223
// New PWM assignment
155
224
self -> active = 1 ;
156
225
if (chan_gpio [channel ] == -1 ) {
157
226
ledc_channel_config_t cfg = {
158
227
.channel = channel ,
159
- .duty = (1 << timer_cfg .duty_resolution ) / 2 ,
228
+ .duty = (1 << timers [ timer ] .duty_resolution ) / 2 ,
160
229
.gpio_num = self -> pin ,
161
230
.intr_type = LEDC_INTR_DISABLE ,
162
231
.speed_mode = PWMODE ,
163
- .timer_sel = PWTIMER ,
232
+ .timer_sel = timer ,
164
233
};
165
234
if (ledc_channel_config (& cfg ) != ESP_OK ) {
166
235
mp_raise_msg_varg (& mp_type_ValueError , MP_ERROR_TEXT ("PWM not supported on pin %d" ), self -> pin );
167
236
}
168
237
chan_gpio [channel ] = self -> pin ;
169
238
}
170
239
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 );
179
243
}
180
244
181
245
// Set duty cycle?
182
246
int dval = args [ARG_duty ].u_int ;
183
247
if (dval != -1 ) {
184
248
dval &= ((1 << PWRES ) - 1 );
185
- dval >>= PWRES - timer_cfg .duty_resolution ;
249
+ dval >>= PWRES - timers [ timer ] .duty_resolution ;
186
250
ledc_set_duty (PWMODE , channel , dval );
187
251
ledc_update_duty (PWMODE , channel );
188
252
}
@@ -227,6 +291,9 @@ STATIC mp_obj_t esp32_pwm_deinit(mp_obj_t self_in) {
227
291
228
292
// Valid channel?
229
293
if ((chan >= 0 ) && (chan < LEDC_CHANNEL_MAX )) {
294
+ // Clean up timer if necessary
295
+ cleanup_timer (chan , chan_timer [self -> channel ]);
296
+
230
297
// Mark it unused, and tell the hardware to stop routing
231
298
chan_gpio [chan ] = -1 ;
232
299
ledc_stop (PWMODE , chan , 0 );
@@ -239,14 +306,30 @@ STATIC mp_obj_t esp32_pwm_deinit(mp_obj_t self_in) {
239
306
STATIC MP_DEFINE_CONST_FUN_OBJ_1 (esp32_pwm_deinit_obj , esp32_pwm_deinit );
240
307
241
308
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 ]);
242
310
if (n_args == 1 ) {
243
311
// 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 );
245
313
}
246
314
247
315
// set
248
316
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 ])) {
250
333
mp_raise_msg_varg (& mp_type_ValueError , MP_ERROR_TEXT ("bad frequency %d" ), tval );
251
334
}
252
335
return mp_const_none ;
@@ -261,14 +344,14 @@ STATIC mp_obj_t esp32_pwm_duty(size_t n_args, const mp_obj_t *args) {
261
344
if (n_args == 1 ) {
262
345
// get
263
346
duty = ledc_get_duty (PWMODE , self -> channel );
264
- duty <<= PWRES - timer_cfg .duty_resolution ;
347
+ duty <<= PWRES - timers [ chan_timer [ self -> channel ]] .duty_resolution ;
265
348
return MP_OBJ_NEW_SMALL_INT (duty );
266
349
}
267
350
268
351
// set
269
352
duty = mp_obj_get_int (args [1 ]);
270
353
duty &= ((1 << PWRES ) - 1 );
271
- duty >>= PWRES - timer_cfg .duty_resolution ;
354
+ duty >>= PWRES - timers [ chan_timer [ self -> channel ]] .duty_resolution ;
272
355
ledc_set_duty (PWMODE , self -> channel , duty );
273
356
ledc_update_duty (PWMODE , self -> channel );
274
357
0 commit comments