Skip to content

Commit 245074e

Browse files
committed
feat(ledc): Improve timer management with frequency/resolution matching
1 parent f3ae2a6 commit 245074e

File tree

2 files changed

+119
-12
lines changed

2 files changed

+119
-12
lines changed

cores/esp32/esp32-hal-ledc.c

Lines changed: 117 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,96 @@ typedef struct {
4545

4646
ledc_periph_t ledc_handle = {0};
4747

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

50140
static ledc_clk_cfg_t clock_source = LEDC_DEFAULT_CLK;
@@ -81,6 +171,8 @@ static bool ledcDetachBus(void *bus) {
81171
}
82172
pinMatrixOutDetach(handle->pin, false, false);
83173
if (!channel_found) {
174+
uint8_t group = (handle->channel / 8);
175+
remove_channel_from_timer(group, handle->timer_num, handle->channel % 8);
84176
ledc_handle.used_channels &= ~(1UL << handle->channel);
85177
}
86178
free(handle);
@@ -117,26 +209,37 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c
117209
return false;
118210
}
119211

120-
uint8_t group = (channel / 8), timer = ((channel / 2) % 4);
212+
uint8_t group = (channel / 8);
213+
uint8_t timer;
121214
bool channel_used = ledc_handle.used_channels & (1UL << channel);
215+
122216
if (channel_used) {
123217
log_i("Channel %u is already set up, given frequency and resolution will be ignored", channel);
124218
if (ledc_set_pin(pin, group, channel % 8) != ESP_OK) {
125219
log_e("Attaching pin to already used channel failed!");
126220
return false;
127221
}
128222
} else {
129-
ledc_timer_config_t ledc_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;
136-
137-
if (ledc_timer_config(&ledc_timer) != ESP_OK) {
138-
log_e("ledc setup failed!");
139-
return false;
223+
// Find a timer with matching frequency and resolution, or a free timer
224+
if (!find_matching_timer(group, freq, resolution, &timer)) {
225+
if (!find_free_timer(group, &timer)) {
226+
log_e("No free timers available for speed mode %u", group);
227+
return false;
228+
}
229+
230+
// Configure the timer if we're using a new one
231+
ledc_timer_config_t ledc_timer;
232+
memset((void *)&ledc_timer, 0, sizeof(ledc_timer_config_t));
233+
ledc_timer.speed_mode = group;
234+
ledc_timer.timer_num = timer;
235+
ledc_timer.duty_resolution = resolution;
236+
ledc_timer.freq_hz = freq;
237+
ledc_timer.clk_cfg = clock_source;
238+
239+
if (ledc_timer_config(&ledc_timer) != ESP_OK) {
240+
log_e("ledc setup failed!");
241+
return false;
242+
}
140243
}
141244

142245
uint32_t duty = ledc_get_duty(group, (channel % 8));
@@ -157,6 +260,8 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c
157260
ledc_channel_handle_t *handle = (ledc_channel_handle_t *)malloc(sizeof(ledc_channel_handle_t));
158261
handle->pin = pin;
159262
handle->channel = channel;
263+
handle->timer_num = timer;
264+
handle->freq_hz = freq;
160265
#ifndef SOC_LEDC_SUPPORT_FADE_STOP
161266
handle->lock = NULL;
162267
#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_t pin; // Pin assigned to channel
5252
uint8_t channel; // Channel number
5353
uint8_t channel_resolution; // Resolution of channel
54+
uint8_t timer_num; // Timer number used by this channel
55+
uint32_t freq_hz; // Frequency configured for this channel
5456
voidFuncPtr fn;
5557
void *arg;
5658
#ifndef SOC_LEDC_SUPPORT_FADE_STOP

0 commit comments

Comments
 (0)