@@ -45,6 +45,96 @@ 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 ) {
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+
48138static bool fade_initialized = false;
49139
50140static 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
0 commit comments