Skip to content

Commit cbdaee6

Browse files
P-R-O-C-H-Ylucasssvazpre-commit-ci-lite[bot]
authored
feat(ledc): Improve timer management with frequency/resolution matching (espressif#11452)
* feat(ledc): Improve timer management with frequency/resolution matching * fix(ci): Fix uninitialized timer variable warning * Update cores/esp32/esp32-hal-ledc.c Co-authored-by: Lucas Saavedra Vaz <[email protected]> * ci(pre-commit): Apply automatic fixes --------- Co-authored-by: Lucas Saavedra Vaz <[email protected]> Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com>
1 parent d6a76da commit cbdaee6

File tree

2 files changed

+115
-11
lines changed

2 files changed

+115
-11
lines changed

‎cores/esp32/esp32-hal-ledc.c‎

Lines changed: 113 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,93 @@ typedef struct{
4545

4646
ledc_periph_tledc_handle={0};
4747

48+
// Helper function to find a timer with matching frequency and resolution
49+
staticboolfind_matching_timer(uint8_tspeed_mode, uint32_tfreq, uint8_tresolution, uint8_t*timer_num){
50+
log_d("Searching for timer with freq=%u, resolution=%u", freq, resolution);
51+
// Check all channels to find one with matching frequency and resolution
52+
for (uint8_ti=0; i<SOC_GPIO_PIN_COUNT; i++){
53+
if (!perimanPinIsValid(i)){
54+
continue;
55+
}
56+
peripheral_bus_type_ttype=perimanGetPinBusType(i);
57+
if (type==ESP32_BUS_TYPE_LEDC){
58+
ledc_channel_handle_t*bus= (ledc_channel_handle_t*)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC);
59+
if (bus!=NULL&& (bus->channel / 8) ==speed_mode&&bus->freq_hz==freq&&bus->channel_resolution==resolution){
60+
log_d("Found matching timer %u for freq=%u, resolution=%u", bus->timer_num, freq, resolution);
61+
*timer_num=bus->timer_num;
62+
return true;
63+
}
64+
}
65+
}
66+
log_d("No matching timer found for freq=%u, resolution=%u", freq, resolution);
67+
return false;
68+
}
69+
70+
// Helper function to find an unused timer
71+
staticboolfind_free_timer(uint8_tspeed_mode, uint8_t*timer_num){
72+
// Check which timers are in use
73+
uint8_tused_timers=0;
74+
for (uint8_ti=0; i<SOC_GPIO_PIN_COUNT; i++){
75+
if (!perimanPinIsValid(i)){
76+
continue;
77+
}
78+
peripheral_bus_type_ttype=perimanGetPinBusType(i);
79+
if (type==ESP32_BUS_TYPE_LEDC){
80+
ledc_channel_handle_t*bus= (ledc_channel_handle_t*)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC);
81+
if (bus!=NULL&& (bus->channel / 8) ==speed_mode){
82+
log_d("Timer %u is in use by channel %u", bus->timer_num, bus->channel);
83+
used_timers |= (1 << bus->timer_num);
84+
}
85+
}
86+
}
87+
88+
// Find first unused timer
89+
for (uint8_ti=0; i<SOC_LEDC_TIMER_NUM; i++){
90+
if (!(used_timers& (1 << i))){
91+
log_d("Found free timer %u", i);
92+
*timer_num=i;
93+
return true;
94+
}
95+
}
96+
log_e("No free timers available");
97+
return false;
98+
}
99+
100+
// Helper function to remove a channel from a timer and clear timer if no channels are using it
101+
staticvoidremove_channel_from_timer(uint8_tspeed_mode, uint8_ttimer_num, uint8_tchannel){
102+
log_d("Removing channel %u from timer %u in speed_mode %u", channel, timer_num, speed_mode);
103+
104+
// Check if any other channels are using this timer
105+
booltimer_in_use= false;
106+
for (uint8_ti=0; i<SOC_GPIO_PIN_COUNT; i++){
107+
if (!perimanPinIsValid(i)){
108+
continue;
109+
}
110+
peripheral_bus_type_ttype=perimanGetPinBusType(i);
111+
if (type==ESP32_BUS_TYPE_LEDC){
112+
ledc_channel_handle_t*bus= (ledc_channel_handle_t*)perimanGetPinBus(i, ESP32_BUS_TYPE_LEDC);
113+
if (bus!=NULL&& (bus->channel / 8) ==speed_mode&&bus->timer_num==timer_num&&bus->channel!=channel){
114+
log_d("Timer %u is still in use by channel %u", timer_num, bus->channel);
115+
timer_in_use= true;
116+
break;
117+
}
118+
}
119+
}
120+
121+
if (!timer_in_use){
122+
log_d("No other channels using timer %u, deconfiguring timer", timer_num);
123+
// Stop the timer
124+
ledc_timer_pause(speed_mode, timer_num);
125+
// Deconfigure the timer
126+
ledc_timer_config_tledc_timer;
127+
memset((void*)&ledc_timer, 0, sizeof(ledc_timer_config_t));
128+
ledc_timer.speed_mode=speed_mode;
129+
ledc_timer.timer_num=timer_num;
130+
ledc_timer.deconfigure= true;
131+
ledc_timer_config(&ledc_timer);
132+
}
133+
}
134+
48135
staticboolfade_initialized= false;
49136

50137
staticledc_clk_cfg_tclock_source=LEDC_DEFAULT_CLK;
@@ -81,6 +168,8 @@ static bool ledcDetachBus(void *bus){
81168
}
82169
pinMatrixOutDetach(handle->pin, false, false);
83170
if (!channel_found){
171+
uint8_tgroup= (handle->channel / 8);
172+
remove_channel_from_timer(group, handle->timer_num, handle->channel % 8);
84173
ledc_handle.used_channels &= ~(1UL << handle->channel);
85174
}
86175
free(handle);
@@ -117,26 +206,37 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c
117206
return false;
118207
}
119208

120-
uint8_tgroup= (channel / 8), timer= ((channel / 2) % 4);
209+
uint8_tgroup= (channel / 8);
210+
uint8_ttimer=0;
121211
boolchannel_used=ledc_handle.used_channels& (1UL << channel);
212+
122213
if (channel_used){
123214
log_i("Channel %u is already set up, given frequency and resolution will be ignored", channel);
124215
if (ledc_set_pin(pin, group, channel % 8) !=ESP_OK){
125216
log_e("Attaching pin to already used channel failed!");
126217
return false;
127218
}
128219
} else{
129-
ledc_timer_config_tledc_timer;
130-
memset((void*)&ledc_timer, 0, sizeof(ledc_timer_config_t));
131-
ledc_timer.speed_mode=group;
132-
ledc_timer.timer_num=timer;
133-
ledc_timer.duty_resolution=resolution;
134-
ledc_timer.freq_hz=freq;
135-
ledc_timer.clk_cfg=clock_source;
220+
// Find a timer with matching frequency and resolution, or a free timer
221+
if (!find_matching_timer(group, freq, resolution, &timer)){
222+
if (!find_free_timer(group, &timer)){
223+
log_e("No free timers available for speed mode %u", group);
224+
return false;
225+
}
136226

137-
if (ledc_timer_config(&ledc_timer) !=ESP_OK){
138-
log_e("ledc setup failed!");
139-
return false;
227+
// Configure the timer if we're using a new one
228+
ledc_timer_config_tledc_timer;
229+
memset((void*)&ledc_timer, 0, sizeof(ledc_timer_config_t));
230+
ledc_timer.speed_mode=group;
231+
ledc_timer.timer_num=timer;
232+
ledc_timer.duty_resolution=resolution;
233+
ledc_timer.freq_hz=freq;
234+
ledc_timer.clk_cfg=clock_source;
235+
236+
if (ledc_timer_config(&ledc_timer) !=ESP_OK){
237+
log_e("ledc setup failed!");
238+
return false;
239+
}
140240
}
141241

142242
uint32_tduty=ledc_get_duty(group, (channel % 8));
@@ -157,6 +257,8 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c
157257
ledc_channel_handle_t*handle= (ledc_channel_handle_t*)malloc(sizeof(ledc_channel_handle_t));
158258
handle->pin=pin;
159259
handle->channel=channel;
260+
handle->timer_num=timer;
261+
handle->freq_hz=freq;
160262
#ifndefSOC_LEDC_SUPPORT_FADE_STOP
161263
handle->lock=NULL;
162264
#endif

‎cores/esp32/esp32-hal-ledc.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ typedef struct{
5151
uint8_tpin; // Pin assigned to channel
5252
uint8_tchannel; // Channel number
5353
uint8_tchannel_resolution; // Resolution of channel
54+
uint8_ttimer_num; // Timer number used by this channel
55+
uint32_tfreq_hz; // Frequency configured for this channel
5456
voidFuncPtrfn;
5557
void*arg;
5658
#ifndefSOC_LEDC_SUPPORT_FADE_STOP

0 commit comments

Comments
(0)