SimpleFOClibrary 2.4.0
Loading...
Searching...
No Matches
esp32_ledc_mcu.cpp
Go to the documentation of this file.
1#include "../../hardware_api.h"
2
3#if defined(ESP_H) && defined(ARDUINO_ARCH_ESP32) && ( !defined(SOC_MCPWM_SUPPORTED) || defined(SIMPLEFOC_ESP32_USELEDC) )
4
5#pragma message("")
6#pragma message("SimpleFOC: compiling for ESP32 LEDC driver")
7#pragma message("")
8
9#include "driver/ledc.h"
10#include "esp_idf_version.h"
11
12
13// version check - this ledc driver is specific for ESP-IDF 5.x and arduino-esp32 3.x
14#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
15#error SimpleFOC: ESP-IDF version 4 or lower detected. Please update to ESP-IDF 5.x and Arduino-esp32 3.0 (or higher)
16#endif
17
18#define _PWM_FREQUENCY 25000 // 25khz
19#define _PWM_FREQUENCY_MAX 38000 // 38khz max to be able to have 10 bit pwm resolution
20#define _PWM_RES_BIT 10 // 10 bir resolution
21#define _PWM_RES 1023 // 2^10-1 = 1024-1
22
23
24// figure out how many ledc channels are available
25// esp32 - 2x8=16
26// esp32s2 - 8
27// esp32c3 - 6
28#include "soc/soc_caps.h"
29#ifdef SOC_LEDC_SUPPORT_HS_MODE
30#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM<<1)
31#else
32#define LEDC_CHANNELS (SOC_LEDC_CHANNEL_NUM)
33#endif
34
35#define LEDC_CHANNELS_GROUP0 (LEDC_CHANNELS < 8 ? LEDC_CHANNELS : 8)
36#define LEDC_CHANNELS_GROUP1 (LEDC_CHANNELS < 8 ? 0 : LEDC_CHANNELS - 8)
37
38
39// currently used ledc channels
40// support for multiple motors
41// esp32 has 16 channels
42// esp32s2 has 8 channels
43// esp32c3 has 6 channels
44// channels from 0-7 are in group 0 and 8-15 in group 1
45// - only esp32 as of mid 2024 has the second group, all the s versions don't
46int group_channels_used[2] = {0};
47
48
49typedef struct ESP32LEDCDriverParams {
50 ledc_channel_t channels[6];
51 ledc_mode_t groups[6];
52 long pwm_frequency;
53 float dead_zone;
54} ESP32LEDCDriverParams;
55
56
57
58int esp32_gpio_nr(int pin) {
59 #if defined(BOARD_HAS_PIN_REMAP) && !defined(BOARD_USES_HW_GPIO_NUMBERS)
60 return digitalPinToGPIONumber(pin);
61 #else
62 return pin;
63 #endif
64}
65
66
67/*
68 Function to attach a channel to a pin with advanced settings
69 - freq - pwm frequency
70 - resolution - pwm resolution
71 - channel - ledc channel
72 - inverted - output inverted
73 - group - ledc group
74
75 This function is a workaround for the ledcAttachPin function that is not available in the ESP32 Arduino core, in which the
76 PWM signals are synchronized in pairs, while the simplefoc requires a bit more flexible configuration.
77 This function sets also allows configuring a channel as inverted, which is not possible with the ledcAttachPin function.
78
79 Function returns true if the channel was successfully attached, false otherwise.
80*/
81bool _ledcAttachChannelAdvanced(uint8_t pin, int _channel, int _group, uint32_t freq, uint8_t resolution, bool inverted) {
82
83
84 ledc_channel_t channel = static_cast<ledc_channel_t>(_channel);
85 ledc_mode_t group = static_cast<ledc_mode_t>(_group);
86
87 ledc_timer_bit_t res = static_cast<ledc_timer_bit_t>(resolution);
88 ledc_timer_config_t ledc_timer;
89 memset(&ledc_timer, 0, sizeof(ledc_timer));
90 ledc_timer.speed_mode = group;
91 ledc_timer.timer_num = LEDC_TIMER_0;
92 ledc_timer.duty_resolution = res;
93 ledc_timer.freq_hz = freq;
94 ledc_timer.clk_cfg = LEDC_AUTO_CLK;
95 if (ledc_timer_config(&ledc_timer) != ESP_OK) {
96 SIMPLEFOC_DEBUG("EP32-DRV: ERROR - Failed to configure the timer:", LEDC_TIMER_0);
97 return false;
98 }
99
100 // if active high is false invert
101 int pin_high_level = SIMPLEFOC_PWM_ACTIVE_HIGH ? 0 : 1;
102 if (inverted) pin_high_level = !pin_high_level;
103
104 uint32_t duty = ledc_get_duty(group, channel);
105 ledc_channel_config_t ledc_channel;
106 ledc_channel.speed_mode = group;
107 ledc_channel.sleep_mode = LEDC_SLEEP_MODE_KEEP_ALIVE;
108 ledc_channel.channel = channel;
109 ledc_channel.timer_sel = LEDC_TIMER_0;
110 ledc_channel.intr_type = LEDC_INTR_DISABLE;
111 ledc_channel.gpio_num = esp32_gpio_nr(pin);
112 ledc_channel.duty = duty;
113 ledc_channel.hpoint = 0;
114 ledc_channel.flags.output_invert = pin_high_level; // 0 is active high, 1 is active low
115 if (ledc_channel_config(&ledc_channel)!= ESP_OK) {
116 SIMPLEFOC_DEBUG("EP32-DRV: ERROR - Failed to attach channel:", _channel);
117 return false;
118 }
119
120 return true;
121}
122
123
124// returns the number of available channels in the group
125int _availableGroupChannels(int group){
126 if(group == 0) return LEDC_CHANNELS_GROUP0 - group_channels_used[0];
127 else if(group == 1) return LEDC_CHANNELS_GROUP1 - group_channels_used[1];
128 return 0;
129}
130
131// returns the number of the group that has enough channels available
132// returns -1 if no group has enough channels
133//
134// NOT IMPLEMENTED BUT COULD BE USEFUL
135// returns 2 if no group has enough channels but combined they do
136int _findGroupWithChannelsAvailable(int no_channels){
137 if(no_channels <= _availableGroupChannels(0)) return 0;
138 if(no_channels <= _availableGroupChannels(1)) return 1;
139 return -1;
140}
141
142
143void* _configure1PWM(long pwm_frequency, const int pinA) {
144 if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
145 else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
146
147 SIMPLEFOC_DEBUG("EP32-DRV: Configuring 1PWM");
148 // check if enough channels available
149 int group = _findGroupWithChannelsAvailable(1);
150 if (group < 0){
151 SIMPLEFOC_DEBUG("EP32-DRV: ERROR - Not enough channels available!");
153 }
154 SIMPLEFOC_DEBUG("EP32-DRV: 1PWM setup in group: ", (group));
155
156 // configure the channel
157 group_channels_used[group] += 1;
158 if(!_ledcAttachChannelAdvanced(pinA, group_channels_used[group], group, pwm_frequency, _PWM_RES_BIT, false)){
159 SIMPLEFOC_DEBUG("EP32-DRV: ERROR - Failed to configure pin:", pinA);
161 }
162
163
164 ESP32LEDCDriverParams* params = new ESP32LEDCDriverParams {
165 .channels = { static_cast<ledc_channel_t>(group_channels_used[group]) },
166 .groups = { (ledc_mode_t)group },
167 .pwm_frequency = pwm_frequency
168 };
169 SIMPLEFOC_DEBUG("EP32-DRV: 1PWM setup successful in group: ", (group));
170 return params;
171}
172
173
174
175
176
177
178
179
180void* _configure2PWM(long pwm_frequency, const int pinA, const int pinB) {
181 if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
182 else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
183
184 SIMPLEFOC_DEBUG("EP32-DRV: Configuring 2PWM");
185
186 // check if enough channels available
187 int group = _findGroupWithChannelsAvailable(2);
188 if (group < 0) {
189 SIMPLEFOC_DEBUG("EP32-DRV: ERROR - Not enough channels available!");
191 }
192 SIMPLEFOC_DEBUG("EP32-DRV: 2PWM setup in group: ", (group));
193
194 ESP32LEDCDriverParams* params = new ESP32LEDCDriverParams {
195 .channels = { static_cast<ledc_channel_t>(0)},
196 .groups = { (ledc_mode_t)0 },
197 .pwm_frequency = pwm_frequency
198 };
199
200 int pins[2] = {pinA, pinB};
201 for(int i = 0; i < 2; i++){
202 group_channels_used[group]++;
203 if(!_ledcAttachChannelAdvanced(pins[i], group_channels_used[group], group, pwm_frequency, _PWM_RES_BIT, false)){
204 SIMPLEFOC_DEBUG("EP32-DRV: ERROR - Failed to configure pin:", pins[i]);
206 }
207
208 params->channels[i] = static_cast<ledc_channel_t>(group_channels_used[group]);
209 params->groups[i] = (ledc_mode_t)group;
210 }
211 SIMPLEFOC_DEBUG("EP32-DRV: 2PWM setup successful in group: ", (group));
212 return params;
213}
214
215
216
217void* _configure3PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC) {
218 if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
219 else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
220
221 SIMPLEFOC_DEBUG("EP32-DRV: Configuring 3PWM");
222
223 // check if enough channels available
224 int group = _findGroupWithChannelsAvailable(3);
225 if (group < 0) {
226 SIMPLEFOC_DEBUG("EP32-DRV: ERROR - Not enough channels available!");
228 }
229 SIMPLEFOC_DEBUG("EP32-DRV: 3PWM setup in group: ", (group));
230
231 ESP32LEDCDriverParams* params = new ESP32LEDCDriverParams {
232 .channels = { static_cast<ledc_channel_t>(0)},
233 .groups = { (ledc_mode_t)0 },
234 .pwm_frequency = pwm_frequency
235 };
236
237 int pins[3] = {pinA, pinB, pinC};
238 for(int i = 0; i < 3; i++){
239 if(!_ledcAttachChannelAdvanced(pins[i], group_channels_used[group], group, pwm_frequency, _PWM_RES_BIT, false)){
240 SIMPLEFOC_DEBUG("EP32-DRV: ERROR - Failed to configure pin:", pins[i]);
242 }
243
244 params->channels[i] = static_cast<ledc_channel_t>(group_channels_used[group]);
245 params->groups[i] = (ledc_mode_t)group;
246 group_channels_used[group]++;
247 }
248 SIMPLEFOC_DEBUG("EP32-DRV: 3PWM setup successful in group: ", (group));
249 return params;
250}
251
252
253
254void* _configure4PWM(long pwm_frequency,const int pinA, const int pinB, const int pinC, const int pinD) {
255 if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
256 else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
257
258
259 ESP32LEDCDriverParams* params = new ESP32LEDCDriverParams {
260 .channels = { static_cast<ledc_channel_t>(0)},
261 .groups = { (ledc_mode_t)0 },
262 .pwm_frequency = pwm_frequency
263 };
264
265 SIMPLEFOC_DEBUG("EP32-DRV: Configuring 4PWM");
266 // check if enough channels available
267 int group = _findGroupWithChannelsAvailable(4);
268 if (group < 0){
269 // not enough channels available on any individual group
270 // check if their combined number is enough (two channels per group)
271 if(_availableGroupChannels(0) >=2 && _availableGroupChannels(1) >=2){
272 group = 2;
273 SIMPLEFOC_DEBUG("EP32-DRV: WARNING: Not enough available ledc channels for 4pwm in a single group! Using two groups!");
274 SIMPLEFOC_DEBUG("EP32-DRV: 4PWM setup in groups: 0 and 1!");
275 params->groups[2] = (ledc_mode_t)1;
276 params->groups[3] = (ledc_mode_t)1;
277 }else{
278 SIMPLEFOC_DEBUG("EP32-DRV: ERROR - Not enough available ledc channels for 4pwm!");
280 }
281 }else{
282 SIMPLEFOC_DEBUG("EP32-DRV: 4PWM setup in group: ", (group));
283 params->groups[0] = (ledc_mode_t)group;
284 params->groups[1] = (ledc_mode_t)group;
285 params->groups[2] = (ledc_mode_t)group;
286 params->groups[3] = (ledc_mode_t)group;
287 }
288
289
290
291 int pins[4] = {pinA, pinB, pinC, pinD};
292 for(int i = 0; i < 4; i++){
293 group_channels_used[params->groups[i]]++;
294 if(!_ledcAttachChannelAdvanced(pins[i], group_channels_used[params->groups[i]], params->groups[i], pwm_frequency, _PWM_RES_BIT, false)){
295 SIMPLEFOC_DEBUG("EP32-DRV: ERROR - Failed to configure pin:", pins[i]);
297 }
298 params->channels[i] = static_cast<ledc_channel_t>(group_channels_used[params->groups[i]]);
299 }
300 SIMPLEFOC_DEBUG("EP32-DRV: 4PWM setup successful!");
301 return params;
302}
303
304
305void IRAM_ATTR _writeDutyCycle(float dc, void* params, int index){
306 ledc_set_duty_with_hpoint(((ESP32LEDCDriverParams*)params)->groups[index],((ESP32LEDCDriverParams*)params)->channels[index], _PWM_RES*dc, _PWM_RES/2.0*(1.0-dc));
307 ledc_update_duty(((ESP32LEDCDriverParams*)params)->groups[index],((ESP32LEDCDriverParams*)params)->channels[index]);
308}
309
310void IRAM_ATTR _writeDutyCycle1PWM(float dc_a, void* params){
311 _writeDutyCycle(dc_a, params, 0);
312}
313
314
315
316void IRAM_ATTR _writeDutyCycle2PWM(float dc_a, float dc_b, void* params){
317 _writeDutyCycle(dc_a, params, 0);
318 _writeDutyCycle(dc_b, params, 1);
319}
320
321
322
323void IRAM_ATTR _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void* params){
324 _writeDutyCycle(dc_a, params, 0);
325 _writeDutyCycle(dc_b, params, 1);
326 _writeDutyCycle(dc_c, params, 2);
327}
328
329
330
331void IRAM_ATTR _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void* params){
332 _writeDutyCycle(dc_1a, params, 0);
333 _writeDutyCycle(dc_1b, params, 1);
334 _writeDutyCycle(dc_2a, params, 2);
335 _writeDutyCycle(dc_2b, params, 3);
336}
337
338
339void* _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l){
340 if(!pwm_frequency || !_isset(pwm_frequency) ) pwm_frequency = _PWM_FREQUENCY; // default frequency 25khz
341 else pwm_frequency = _constrain(pwm_frequency, 0, _PWM_FREQUENCY_MAX); // constrain to 50kHz max
342
343 SIMPLEFOC_DEBUG("EP32-DRV: Configuring 6PWM");
344 SIMPLEFOC_DEBUG("EP32-DRV: WARNING - 6PWM on LEDC is poorly supported and not tested, consider using MCPWM driver instead!");
345 // check if enough channels available
346 int group = _findGroupWithChannelsAvailable(6);
347 if (group < 0){
348 SIMPLEFOC_DEBUG("EP32-DRV: ERROR - Not enough channels available!");
350 }
351 SIMPLEFOC_DEBUG("EP32-DRV: 6PWM setup in group: ", (group));
352 ESP32LEDCDriverParams* params = new ESP32LEDCDriverParams {
353 .channels = { static_cast<ledc_channel_t>(0)},
354 .groups = { (ledc_mode_t)group },
355 .pwm_frequency = pwm_frequency,
356 .dead_zone = dead_zone
357 };
358
359 int high_side_invert = SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH ? false : true;
360 int low_side_invert = SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH ? true : false;
361
362 int pin_pairs[6][2] = {
363 {pinA_h, pinA_l},
364 {pinB_h, pinB_l},
365 {pinC_h, pinC_l}
366 };
367
368 for(int i = 0; i < 3; i++){
369 group_channels_used[group]++;
370 if(!_ledcAttachChannelAdvanced(pin_pairs[i][0], group_channels_used[group], group, pwm_frequency, _PWM_RES_BIT, high_side_invert)){
371 SIMPLEFOC_DEBUG("EP32-DRV: ERROR - Failed to configure pin:", pin_pairs[i][0]);
373 }
374
375 params->channels[2*i] = static_cast<ledc_channel_t>(group_channels_used[group]);
376 params->groups[2*i] = (ledc_mode_t)group;
377
378 group_channels_used[group]++;
379 if(!_ledcAttachChannelAdvanced(pin_pairs[i][1], group_channels_used[group], group, pwm_frequency, _PWM_RES_BIT, low_side_invert)){
380 SIMPLEFOC_DEBUG("EP32-DRV: ERROR - Failed to configure pin:", pin_pairs[i][0]);
382 }
383
384 params->channels[2*i+1] = static_cast<ledc_channel_t>(group_channels_used[group]);
385 params->groups[2*i+1] = (ledc_mode_t)group;
386 }
387
388 SIMPLEFOC_DEBUG("EP32-DRV: 6PWM setup successful in group: ", (group));
389 return params;
390}
391
392void IRAM_ATTR _setPwmPairDutyCycle( void* params, int ind_h, int ind_l, float val, float dead_time, PhaseState ps){
393 float pwm_h = _constrain(val - (dead_time * 0.5), 0, 1.0);
394 float pwm_l = _constrain(val + (dead_time * 0.5), 0, 1.0);
395
396 // determine the phase state and set the pwm accordingly
397 // deactivate phases if needed
398 if((ps == PhaseState::PHASE_OFF) || (ps == PhaseState::PHASE_LO)){
399 _writeDutyCycle(0, params, ind_h);
400 }else{
401 _writeDutyCycle(pwm_h, params, ind_h);
402 }
403 if((ps == PhaseState::PHASE_OFF) || (ps == PhaseState::PHASE_HI)){
404 _writeDutyCycle(0, params, ind_l);
405 }else{
406 _writeDutyCycle(pwm_l, params, ind_l);
407 }
408}
409
410void IRAM_ATTR _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, PhaseState *phase_state, void* params){
411 _setPwmPairDutyCycle(params, 0, 1, dc_a, ((ESP32LEDCDriverParams*)params)->dead_zone, phase_state[0]);
412 _setPwmPairDutyCycle(params, 2, 3, dc_b, ((ESP32LEDCDriverParams*)params)->dead_zone, phase_state[1]);
413 _setPwmPairDutyCycle(params, 4, 5, dc_c, ((ESP32LEDCDriverParams*)params)->dead_zone, phase_state[2]);
414}
415
416#endif
PhaseState
Definition FOCDriver.h:7
@ PHASE_HI
Definition FOCDriver.h:10
@ PHASE_LO
Definition FOCDriver.h:11
@ PHASE_OFF
Definition FOCDriver.h:8
#define SIMPLEFOC_DEBUG(msg,...)
const int const int const int pinC
GenericCurrentSenseParams * params
#define SIMPLEFOC_PWM_ACTIVE_HIGH
void * _configure4PWM(long pwm_frequency, const int pin1A, const int pin1B, const int pin2A, const int pin2B)
void * _configure6PWM(long pwm_frequency, float dead_zone, const int pinA_h, const int pinA_l, const int pinB_h, const int pinB_l, const int pinC_h, const int pinC_l)
void * _configure2PWM(long pwm_frequency, const int pinA, const int pinB)
void _writeDutyCycle2PWM(float dc_a, float dc_b, void *params)
void _writeDutyCycle1PWM(float dc_a, void *params)
#define SIMPLEFOC_DRIVER_INIT_FAILED
void _writeDutyCycle4PWM(float dc_1a, float dc_1b, float dc_2a, float dc_2b, void *params)
void * _configure3PWM(long pwm_frequency, const int pinA, const int pinB, const int pinC)
#define SIMPLEFOC_PWM_LOWSIDE_ACTIVE_HIGH
void _writeDutyCycle6PWM(float dc_a, float dc_b, float dc_c, PhaseState *phase_state, void *params)
void * _configure1PWM(long pwm_frequency, const int pinA)
#define SIMPLEFOC_PWM_HIGHSIDE_ACTIVE_HIGH
void _writeDutyCycle3PWM(float dc_a, float dc_b, float dc_c, void *params)
float const int const int const int pinB_h
float const int const int const int const int pinB_l
float const int const int const int const int const int pinC_h
float float float dc_2b
float float PhaseState * phase_state
float const int const int const int const int const int const int pinC_l
float const int const int pinA_l
#define _isset(a)
Definition foc_utils.h:13
#define _constrain(amt, low, high)
Definition foc_utils.h:11