@@ -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,55 @@ 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 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
+
108
158
/******************************************************************************/
109
159
110
160
// 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_
113
163
esp32_pwm_obj_t * self = MP_OBJ_TO_PTR (self_in );
114
164
mp_printf (print , "PWM(%u" , self -> pin );
115
165
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 ,
117
167
ledc_get_duty (PWMODE , self -> channel ));
118
168
}
119
169
mp_printf (print , ")" );
@@ -151,38 +201,47 @@ STATIC void esp32_pwm_init_helper(esp32_pwm_obj_t *self,
151
201
}
152
202
self -> channel = channel ;
153
203
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
+
154
218
// New PWM assignment
155
219
self -> active = 1 ;
156
220
if (chan_gpio [channel ] == -1 ) {
157
221
ledc_channel_config_t cfg = {
158
222
.channel = channel ,
159
- .duty = (1 << timer_cfg .duty_resolution ) / 2 ,
223
+ .duty = (1 << timers [ timer ] .duty_resolution ) / 2 ,
160
224
.gpio_num = self -> pin ,
161
225
.intr_type = LEDC_INTR_DISABLE ,
162
226
.speed_mode = PWMODE ,
163
- .timer_sel = PWTIMER ,
227
+ .timer_sel = timer ,
164
228
};
165
229
if (ledc_channel_config (& cfg ) != ESP_OK ) {
166
230
mp_raise_msg_varg (& mp_type_ValueError , MP_ERROR_TEXT ("PWM not supported on pin %d" ), self -> pin );
167
231
}
168
232
chan_gpio [channel ] = self -> pin ;
169
233
}
170
234
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 );
179
238
}
180
239
181
240
// Set duty cycle?
182
241
int dval = args [ARG_duty ].u_int ;
183
242
if (dval != -1 ) {
184
243
dval &= ((1 << PWRES ) - 1 );
185
- dval >>= PWRES - timer_cfg .duty_resolution ;
244
+ dval >>= PWRES - timers [ timer ] .duty_resolution ;
186
245
ledc_set_duty (PWMODE , channel , dval );
187
246
ledc_update_duty (PWMODE , channel );
188
247
}
@@ -227,6 +286,9 @@ STATIC mp_obj_t esp32_pwm_deinit(mp_obj_t self_in) {
227
286
228
287
// Valid channel?
229
288
if ((chan >= 0 ) && (chan < LEDC_CHANNEL_MAX )) {
289
+ // Clean up timer if necessary
290
+ cleanup_timer (chan , chan_timer [self -> channel ]);
291
+
230
292
// Mark it unused, and tell the hardware to stop routing
231
293
chan_gpio [chan ] = -1 ;
232
294
ledc_stop (PWMODE , chan , 0 );
@@ -239,14 +301,30 @@ STATIC mp_obj_t esp32_pwm_deinit(mp_obj_t self_in) {
239
301
STATIC MP_DEFINE_CONST_FUN_OBJ_1 (esp32_pwm_deinit_obj , esp32_pwm_deinit );
240
302
241
303
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 ]);
242
305
if (n_args == 1 ) {
243
306
// 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 );
245
308
}
246
309
247
310
// set
248
311
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 ])) {
250
328
mp_raise_msg_varg (& mp_type_ValueError , MP_ERROR_TEXT ("bad frequency %d" ), tval );
251
329
}
252
330
return mp_const_none ;
@@ -261,14 +339,14 @@ STATIC mp_obj_t esp32_pwm_duty(size_t n_args, const mp_obj_t *args) {
261
339
if (n_args == 1 ) {
262
340
// get
263
341
duty = ledc_get_duty (PWMODE , self -> channel );
264
- duty <<= PWRES - timer_cfg .duty_resolution ;
342
+ duty <<= PWRES - timers [ chan_timer [ self -> channel ]] .duty_resolution ;
265
343
return MP_OBJ_NEW_SMALL_INT (duty );
266
344
}
267
345
268
346
// set
269
347
duty = mp_obj_get_int (args [1 ]);
270
348
duty &= ((1 << PWRES ) - 1 );
271
- duty >>= PWRES - timer_cfg .duty_resolution ;
349
+ duty >>= PWRES - timers [ chan_timer [ self -> channel ]] .duty_resolution ;
272
350
ledc_set_duty (PWMODE , self -> channel , duty );
273
351
ledc_update_duty (PWMODE , self -> channel );
274
352
0 commit comments