@@ -45,6 +45,93 @@ typedef struct {
4545
4646ledc_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 && 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+
48135static bool fade_initialized = false;
49136
50137static ledc_clk_cfg_t clock_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_t group = (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_t group = (channel / 8 ), timer = ((channel / 2 ) % 4 );
209+ uint8_t group = (channel / 8 );
210+ uint8_t timer = 0 ;
121211 bool channel_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_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+ }
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_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+ }
140240 }
141241
142242 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
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#ifndef SOC_LEDC_SUPPORT_FADE_STOP
161263 handle -> lock = NULL ;
162264#endif
0 commit comments