Skip to content

Commit faf745a

Browse files
authored
Fix gauges by refactoring PWM code to use esp-idf instead of arduino-esp32; fix linker issue with rgb leds; (#46)
* refactor
1 parent 0c3f1af commit faf745a

9 files changed

Lines changed: 558 additions & 142 deletions

File tree

lib/EspSimHub/EspSimHubPwm.h

Lines changed: 312 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,312 @@
1+
#pragma once
2+
#include <Arduino.h>
3+
4+
#ifdef ESP32
5+
#include "driver/ledc.h"
6+
7+
ledc_timer_bit_t getResolutionFromBits(uint8_t dutyCycleRangeInBits) {
8+
if (dutyCycleRangeInBits > SOC_LEDC_TIMER_BIT_WIDE_NUM || dutyCycleRangeInBits < 1) {
9+
throw -1;
10+
}
11+
12+
switch(dutyCycleRangeInBits) {
13+
case 1:
14+
return LEDC_TIMER_1_BIT;
15+
case 2:
16+
return LEDC_TIMER_2_BIT;
17+
case 3:
18+
return LEDC_TIMER_3_BIT;
19+
case 4:
20+
return LEDC_TIMER_4_BIT;
21+
case 5:
22+
return LEDC_TIMER_5_BIT;
23+
case 6:
24+
return LEDC_TIMER_6_BIT;
25+
case 7:
26+
return LEDC_TIMER_7_BIT;
27+
case 8:
28+
return LEDC_TIMER_8_BIT;
29+
case 9:
30+
return LEDC_TIMER_9_BIT;
31+
case 10:
32+
return LEDC_TIMER_10_BIT;
33+
case 11:
34+
return LEDC_TIMER_11_BIT;
35+
case 12:
36+
return LEDC_TIMER_12_BIT;
37+
case 13:
38+
return LEDC_TIMER_13_BIT;
39+
case 14:
40+
return LEDC_TIMER_14_BIT;
41+
#if SOC_LEDC_TIMER_BIT_WIDE_NUM > 14
42+
case 15:
43+
return LEDC_TIMER_15_BIT;
44+
case 16:
45+
return LEDC_TIMER_16_BIT;
46+
case 17:
47+
return LEDC_TIMER_17_BIT;
48+
case 18:
49+
return LEDC_TIMER_18_BIT;
50+
case 19:
51+
return LEDC_TIMER_19_BIT;
52+
case 20:
53+
return LEDC_TIMER_20_BIT;
54+
#endif
55+
default:
56+
return LEDC_TIMER_8_BIT;
57+
}
58+
}
59+
60+
ledc_channel_t getChannelFromNumber(uint8_t channel) {
61+
if (channel > SOC_LEDC_CHANNEL_NUM || channel < 0) {
62+
throw -1;
63+
}
64+
65+
switch(channel) {
66+
case 0:
67+
return LEDC_CHANNEL_0;
68+
case 1:
69+
return LEDC_CHANNEL_1;
70+
case 2:
71+
return LEDC_CHANNEL_2;
72+
case 3:
73+
return LEDC_CHANNEL_3;
74+
case 4:
75+
return LEDC_CHANNEL_4;
76+
case 5:
77+
return LEDC_CHANNEL_5;
78+
#if SOC_LEDC_CHANNEL_NUM > 6
79+
case 6:
80+
return LEDC_CHANNEL_6;
81+
case 7:
82+
return LEDC_CHANNEL_7;
83+
#endif
84+
#if SOC_LEDC_CHANNEL_NUM > 8
85+
case 8:
86+
return LEDC_CHANNEL_8;
87+
case 9:
88+
return LEDC_CHANNEL_9;
89+
case 10:
90+
return LEDC_CHANNEL_10;
91+
case 11:
92+
return LEDC_CHANNEL_11;
93+
case 12:
94+
return LEDC_CHANNEL_12;
95+
case 13:
96+
return LEDC_CHANNEL_13;
97+
case 14:
98+
return LEDC_CHANNEL_14;
99+
case 15:
100+
return LEDC_CHANNEL_15;
101+
#endif
102+
default:
103+
return LEDC_CHANNEL_0;
104+
}
105+
}
106+
107+
ledc_timer_t getTimerFromNumber(uint8_t timer) {
108+
if (timer > (LEDC_TIMER_MAX - 1) || timer < 0) {
109+
throw -1;
110+
}
111+
112+
switch(timer) {
113+
case 0:
114+
return LEDC_TIMER_0;
115+
case 1:
116+
return LEDC_TIMER_1;
117+
case 2:
118+
return LEDC_TIMER_2;
119+
case 3:
120+
return LEDC_TIMER_3;
121+
default:
122+
return LEDC_TIMER_0;
123+
}
124+
}
125+
126+
/**
127+
* A struct that contains timer and channel structs from LEDC
128+
*/
129+
struct ledc_components {
130+
uint8_t pin;
131+
ledc_timer_config_t timer;
132+
ledc_channel_config_t channel;
133+
};
134+
135+
136+
/**
137+
* Reuses the timer and creates a new channel using it
138+
*/
139+
ledc_components makePwmFromTimer(
140+
ledc_timer_config_t timerConfig,
141+
ledc_channel_t channel,
142+
uint32_t initialDutyCycleForResolution,
143+
uint8_t outputPin
144+
) {
145+
// Prepare and then apply the LEDC PWM channel configuration
146+
ledc_channel_config_t ledc_channel = {
147+
.gpio_num = outputPin,
148+
.speed_mode = LEDC_LOW_SPEED_MODE,
149+
.channel = channel,
150+
.intr_type = LEDC_INTR_DISABLE,
151+
.timer_sel = timerConfig.timer_num,
152+
.duty = initialDutyCycleForResolution,
153+
.hpoint = 0,
154+
};
155+
if(ledc_channel_config(&ledc_channel) != ESP_OK) {
156+
throw -2;
157+
}
158+
159+
ledc_components components = {
160+
.pin = outputPin,
161+
.timer = timerConfig,
162+
.channel = ledc_channel,
163+
};
164+
165+
return components;
166+
}
167+
168+
169+
/**
170+
* Configure a new timer and channel for PWM
171+
*/
172+
ledc_components makePwm(
173+
ledc_timer_bit_t resolution,
174+
ledc_timer_t timer,
175+
ledc_channel_t channel,
176+
uint32_t freq,
177+
uint32_t initialDutyCycleForResolution,
178+
uint8_t outputPin
179+
) {
180+
// Prepare and then apply the LEDC PWM timer configuration
181+
ledc_timer_config_t ledc_timer = {
182+
.speed_mode = LEDC_LOW_SPEED_MODE, // the old ESP32 is the only one able to use non-low speed modes
183+
.duty_resolution = resolution,
184+
.timer_num = timer,
185+
.freq_hz = freq,
186+
.clk_cfg = LEDC_AUTO_CLK
187+
};
188+
if (ledc_timer_config(&ledc_timer) != ESP_OK) {
189+
throw -1;
190+
}
191+
192+
// Prepare and then apply the LEDC PWM channel configuration
193+
ledc_channel_config_t ledc_channel = {
194+
.gpio_num = outputPin,
195+
.speed_mode = LEDC_LOW_SPEED_MODE, // the old ESP32 is the only one able to use non-low speed modes
196+
.channel = channel,
197+
.intr_type = LEDC_INTR_DISABLE,
198+
.timer_sel = timer,
199+
.duty = initialDutyCycleForResolution,
200+
.hpoint = 0,
201+
};
202+
if(ledc_channel_config(&ledc_channel) != ESP_OK) {
203+
throw -2;
204+
}
205+
206+
ledc_components components = {
207+
.pin = outputPin,
208+
.timer = ledc_timer,
209+
.channel = ledc_channel,
210+
};
211+
212+
return components;
213+
}
214+
215+
char currentTimer = 0;
216+
char currentChannel = 0;
217+
218+
219+
/**
220+
* Allocate a number of channels for static frequency and varying the duty cycle.
221+
*
222+
* This will create a single timer attached to multiple channels, and each channel
223+
* can vary its duty cycle independently.
224+
*
225+
* dutyCycleRangeInBits is how many steps will the system divide duty cycle into, 8 bits -> 0-255; 10 bits; 0-1023 and so on.
226+
* We need to tweak this number based on how we get our duty cycle updates (number range), but also it's necessary to be tweaked
227+
* in order to achieve different ranges of frequencies due to dividers in the device, clock etc.
228+
* We need higher bits resolution for lower frequencies and lower bits for higher frequencies, or LEDC won't work at all.
229+
* This isn't matter of YOU requiring more resolution, but the hardware timers being able to count properly to achieve the target.
230+
*
231+
* This function will throw if you've allocated more channels or timers than available in the hardware, so make sure you don't have
232+
* many pwm features on at the same time (fans, motors, gauges, meters).
233+
*
234+
* This function modifies the passed array of ledc_components (pointer) with outputPinSize valid elements
235+
* with a maximum of SOC_LEDC_CHANNEL_NUM elements (6-16):
236+
* https://espressif-docs.readthedocs-hosted.com/projects/arduino-esp32/en/latest/api/ledc.html
237+
* (please notice that we're NOT using Arduino framework for esp32 to handle PWM)
238+
*/
239+
void createPwmForDutyCycleControl(
240+
struct ledc_components *components,
241+
uint8_t outputPinSize,
242+
uint32_t staticFrequency,
243+
uint8_t dutyCycleResolutionBits
244+
) {
245+
if (currentChannel + outputPinSize > SOC_LEDC_CHANNEL_NUM) {
246+
// We can't allocate the requested number of channels
247+
throw -1;
248+
}
249+
if (currentTimer + 1 > (LEDC_TIMER_MAX - 1)) {
250+
// We can't allocate the requested number of timers (1 more)
251+
throw -2;
252+
}
253+
254+
for (int i = 0; i < outputPinSize; i++) {
255+
if (i == 0) {
256+
components[i] = makePwm(
257+
getResolutionFromBits(dutyCycleResolutionBits),
258+
getTimerFromNumber(currentTimer++),
259+
getChannelFromNumber(currentChannel++),
260+
staticFrequency,
261+
0,
262+
components[i].pin
263+
);
264+
} else {
265+
ledc_timer_config_t timer = components[0].timer;
266+
components[i] = makePwmFromTimer(
267+
timer,
268+
getChannelFromNumber(currentChannel++),
269+
0,
270+
components[i].pin
271+
);
272+
}
273+
}
274+
}
275+
276+
/**
277+
* Create a channel and timer for each of the passed pins
278+
*
279+
* ESPs have a maximum of (LEDC_TIMER_MAX - 1)[usually 4] timers, and those are shared among all LEDC outputs.
280+
* Thus function will take 1 to (LEDC_TIMER_MAX - 1) pins for varying frequency and create a channel and timer per each
281+
* You will deplete the timers quickly with this function, so plan ahead which features will be enabled
282+
*/
283+
void createPwmForFrequencyControl(
284+
struct ledc_components *components,
285+
uint8_t outputPinSize,
286+
uint32_t initialFrequency,
287+
uint8_t dutyCycleResolutionBits,
288+
uint32_t initialDutyCycleForResolution // if resolution is 8 bits this parameter is expected to be between 0-255; if 10 bits then 0-1023, etc
289+
) {
290+
if (currentChannel + outputPinSize > SOC_LEDC_CHANNEL_NUM) {
291+
// We can't allocate the requested number of channels
292+
throw -1;
293+
}
294+
if (currentTimer + outputPinSize > (LEDC_TIMER_MAX - 1)) {
295+
// We can't allocate the requested number of timers
296+
throw -2;
297+
}
298+
299+
for (int i = 0; i < outputPinSize; i++) {
300+
auto component = makePwm(
301+
getResolutionFromBits(dutyCycleResolutionBits),
302+
getTimerFromNumber(currentTimer++),
303+
getChannelFromNumber(currentChannel++),
304+
initialFrequency,
305+
initialDutyCycleForResolution,
306+
components[i].pin
307+
);
308+
components[i].channel = component.channel;
309+
components[i].timer = component.timer;
310+
}
311+
}
312+
#endif

lib/EspSimHub/TimerOne.h

Lines changed: 0 additions & 57 deletions
This file was deleted.

platformio.ini

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,20 +71,21 @@ build_unflags = -fno-rtti # comment this out if you need more ram, but you'll ne
7171
#############################################
7272
;[env:esp32]
7373
;platform = espressif32@^6.2
74-
;# this should be set to your flavor of esp32, for instance wemos_d1_mini32
74+
;# this should be set to your flavor of esp32, for instance wemos_d1_mini32, esp32doit-devkit-v1, seeed_xiao_esp32c3
7575
;# BOARD LIST: https://docs.platformio.org/en/latest/boards/index.html#espressif-32
7676
;board = esp32doit-devkit-v1
77-
;board_build.f_cpu = 240000000L
7877
;framework = arduino
7978
;lib_deps =
8079
; ${common.lib_deps}
8180
; fastled/FastLED@3.6
8281
; # add any libraries that are specific for the esp32
8382

83+
;# this sets your board to 240MHz, not all boards support this, but if yours does, uncomment.
84+
;; board_build.f_cpu = 240000000L
85+
8486
;build_flags =
8587
; -w -DESP32=true
8688
;monitor_speed = 115200
8789
;; build_type = debug # set this to debug only for debugging, as it's slower.
8890
;monitor_filters = esp32_exception_decoder
8991
;;upload_port = COM4
90-

0 commit comments

Comments
 (0)