@@ -45,6 +45,93 @@ typedef struct {
|
45 | 45 |
|
46 | 46 | ledc_periph_t ledc_handle = {0};
|
47 | 47 |
|
| 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 && 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 | +static bool find_free_timer(uint8_t speed_mode, uint8_t *timer_num) { |
| 72 | +// Check which timers are in use |
| 73 | +uint8_t used_timers = 0; |
| 74 | +for (uint8_t i = 0; i < SOC_GPIO_PIN_COUNT; i++) { |
| 75 | +if (!perimanPinIsValid(i)) { |
| 76 | +continue; |
| 77 | +} |
| 78 | +peripheral_bus_type_t type = 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_t i = 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 | +static void remove_channel_from_timer(uint8_t speed_mode, uint8_t timer_num, uint8_t channel) { |
| 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 | +bool timer_in_use = false; |
| 106 | +for (uint8_t i = 0; i < SOC_GPIO_PIN_COUNT; i++) { |
| 107 | +if (!perimanPinIsValid(i)) { |
| 108 | +continue; |
| 109 | +} |
| 110 | +peripheral_bus_type_t type = 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_t ledc_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 | + |
48 | 135 | static bool fade_initialized = false;
|
49 | 136 |
|
50 | 137 | static ledc_clk_cfg_t clock_source = LEDC_DEFAULT_CLK;
|
@@ -81,6 +168,8 @@ static bool ledcDetachBus(void *bus) {
|
81 | 168 | }
|
82 | 169 | pinMatrixOutDetach(handle->pin, false, false);
|
83 | 170 | if (!channel_found) {
|
| 171 | +uint8_t group = (handle->channel / 8); |
| 172 | +remove_channel_from_timer(group, handle->timer_num, handle->channel % 8); |
84 | 173 | ledc_handle.used_channels &= ~(1UL << handle->channel);
|
85 | 174 | }
|
86 | 175 | free(handle);
|
@@ -117,26 +206,37 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c
|
117 | 206 | return false;
|
118 | 207 | }
|
119 | 208 |
|
120 |
| -uint8_t group = (channel / 8), timer = ((channel / 2) % 4); |
| 209 | +uint8_t group = (channel / 8); |
| 210 | +uint8_t timer = 0; |
121 | 211 | bool channel_used = ledc_handle.used_channels & (1UL << channel);
|
| 212 | + |
122 | 213 | if (channel_used) {
|
123 | 214 | log_i("Channel %u is already set up, given frequency and resolution will be ignored", channel);
|
124 | 215 | if (ledc_set_pin(pin, group, channel % 8) != ESP_OK) {
|
125 | 216 | log_e("Attaching pin to already used channel failed!");
|
126 | 217 | return false;
|
127 | 218 | }
|
128 | 219 | } 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; |
| 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 | +} |
136 | 226 |
|
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_t ledc_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 | +} |
140 | 240 | }
|
141 | 241 |
|
142 | 242 | uint32_t duty = ledc_get_duty(group, (channel % 8));
|
@@ -157,6 +257,8 @@ bool ledcAttachChannel(uint8_t pin, uint32_t freq, uint8_t resolution, uint8_t c
|
157 | 257 | ledc_channel_handle_t *handle = (ledc_channel_handle_t *)malloc(sizeof(ledc_channel_handle_t));
|
158 | 258 | handle->pin = pin;
|
159 | 259 | handle->channel = channel;
|
| 260 | +handle->timer_num = timer; |
| 261 | +handle->freq_hz = freq; |
160 | 262 | #ifndef SOC_LEDC_SUPPORT_FADE_STOP
|
161 | 263 | handle->lock = NULL;
|
162 | 264 | #endif
|
|
0 commit comments