11/*
2- * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
2+ * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
33 *
44 * SPDX-License-Identifier: Apache-2.0
55 */
@@ -158,12 +158,64 @@ void rmt_release_group_handle(rmt_group_t *group)
158158 }
159159}
160160
161- esp_err_t rmt_select_periph_clock (rmt_channel_handle_t chan , rmt_clock_source_t clk_src )
161+ #if !SOC_RMT_CHANNEL_CLK_INDEPENDENT
162+ static esp_err_t s_rmt_set_group_prescale (rmt_channel_t * chan , uint32_t expect_resolution_hz , uint32_t * ret_channel_prescale )
163+ {
164+ uint32_t periph_src_clk_hz = 0 ;
165+ rmt_group_t * group = chan -> group ;
166+ int group_id = group -> group_id ;
167+
168+ ESP_RETURN_ON_ERROR (esp_clk_tree_src_get_freq_hz (group -> clk_src , ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED , & periph_src_clk_hz ), TAG , "get clock source freq failed" );
169+
170+ uint32_t group_resolution_hz = 0 ;
171+ uint32_t group_prescale = 0 ;
172+ uint32_t channel_prescale = 0 ;
173+
174+ if (group -> resolution_hz == 0 ) {
175+ while (++ group_prescale <= RMT_LL_GROUP_CLOCK_MAX_INTEGER_PRESCALE ) {
176+ group_resolution_hz = periph_src_clk_hz / group_prescale ;
177+ channel_prescale = (group_resolution_hz + expect_resolution_hz / 2 ) / expect_resolution_hz ;
178+ // use the first value found during the search that satisfies the division requirement (highest frequency)
179+ if (channel_prescale > 0 && channel_prescale <= RMT_LL_CHANNEL_CLOCK_MAX_PRESCALE ) {
180+ break ;
181+ }
182+ }
183+ } else {
184+ group_prescale = periph_src_clk_hz / group -> resolution_hz ;
185+ channel_prescale = (group -> resolution_hz + expect_resolution_hz / 2 ) / expect_resolution_hz ;
186+ }
187+
188+ ESP_RETURN_ON_FALSE (group_prescale > 0 && group_prescale <= RMT_LL_GROUP_CLOCK_MAX_INTEGER_PRESCALE , ESP_ERR_INVALID_ARG , TAG ,
189+ "group prescale out of the range" );
190+ ESP_RETURN_ON_FALSE (channel_prescale > 0 && channel_prescale <= RMT_LL_CHANNEL_CLOCK_MAX_PRESCALE , ESP_ERR_INVALID_ARG , TAG ,
191+ "channel prescale out of the range" );
192+
193+ // group prescale is shared by all rmt_channel, only set once. use critical section to avoid race condition.
194+ bool prescale_conflict = false;
195+ group_resolution_hz = periph_src_clk_hz / group_prescale ;
196+ portENTER_CRITICAL (& group -> spinlock );
197+ if (group -> resolution_hz == 0 ) {
198+ group -> resolution_hz = group_resolution_hz ;
199+ RMT_CLOCK_SRC_ATOMIC () {
200+ rmt_ll_set_group_clock_src (group -> hal .regs , chan -> channel_id , group -> clk_src , group_prescale , 1 , 0 );
201+ rmt_ll_enable_group_clock (group -> hal .regs , true);
202+ }
203+ } else {
204+ prescale_conflict = (group -> resolution_hz != group_resolution_hz );
205+ }
206+ portEXIT_CRITICAL (& group -> spinlock );
207+ ESP_RETURN_ON_FALSE (!prescale_conflict , ESP_ERR_INVALID_ARG , TAG ,
208+ "group resolution conflict, already is %" PRIu32 " but attempt to %" PRIu32 "" , group -> resolution_hz , group_resolution_hz );
209+ ESP_LOGD (TAG , "group (%d) clock resolution:%" PRIu32 "Hz" , group_id , group -> resolution_hz );
210+ * ret_channel_prescale = channel_prescale ;
211+ return ESP_OK ;
212+ }
213+ #endif // SOC_RMT_CHANNEL_CLK_INDEPENDENT
214+
215+ esp_err_t rmt_select_periph_clock (rmt_channel_handle_t chan , rmt_clock_source_t clk_src , uint32_t expect_channel_resolution )
162216{
163217 esp_err_t ret = ESP_OK ;
164218 rmt_group_t * group = chan -> group ;
165- int channel_id = chan -> channel_id ;
166- uint32_t periph_src_clk_hz = 0 ;
167219 bool clock_selection_conflict = false;
168220 // check if we need to update the group clock source, group clock source is shared by all channels
169221 portENTER_CRITICAL (& group -> spinlock );
@@ -173,7 +225,7 @@ esp_err_t rmt_select_periph_clock(rmt_channel_handle_t chan, rmt_clock_source_t
173225 clock_selection_conflict = (group -> clk_src != clk_src );
174226 }
175227 portEXIT_CRITICAL (& group -> spinlock );
176- ESP_RETURN_ON_FALSE (!clock_selection_conflict , ESP_ERR_INVALID_STATE , TAG ,
228+ ESP_RETURN_ON_FALSE (!clock_selection_conflict , ESP_ERR_INVALID_ARG , TAG ,
177229 "group clock conflict, already is %d but attempt to %d" , group -> clk_src , clk_src );
178230
179231 // TODO: [clk_tree] to use a generic clock enable/disable or acquire/release function for all clock source
@@ -185,10 +237,6 @@ esp_err_t rmt_select_periph_clock(rmt_channel_handle_t chan, rmt_clock_source_t
185237 }
186238#endif // SOC_RMT_SUPPORT_RC_FAST
187239
188- // get clock source frequency
189- ESP_RETURN_ON_ERROR (esp_clk_tree_src_get_freq_hz ((soc_module_clk_t )clk_src , ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED , & periph_src_clk_hz ),
190- TAG , "get clock source frequency failed" );
191-
192240#if CONFIG_PM_ENABLE
193241 // if DMA is not used, we're using CPU to push the data to the RMT FIFO
194242 // if the CPU frequency goes down, the transfer+encoding scheme could be unstable because CPU can't fill the data in time
@@ -203,19 +251,40 @@ esp_err_t rmt_select_periph_clock(rmt_channel_handle_t chan, rmt_clock_source_t
203251 }
204252#endif // SOC_RMT_SUPPORT_APB
205253
206- sprintf (chan -> pm_lock_name , "rmt_%d_%d" , group -> group_id , channel_id ); // e.g. rmt_0_0
254+ sprintf (chan -> pm_lock_name , "rmt_%d_%d" , group -> group_id , chan -> channel_id ); // e.g. rmt_0_0
207255 ret = esp_pm_lock_create (pm_lock_type , 0 , chan -> pm_lock_name , & chan -> pm_lock );
208256 ESP_RETURN_ON_ERROR (ret , TAG , "create pm lock failed" );
209257#endif // CONFIG_PM_ENABLE
210258
211259 esp_clk_tree_enable_src ((soc_module_clk_t )clk_src , true);
212- // no division for group clock source, to achieve highest resolution
260+ uint32_t real_div ;
261+ #if SOC_RMT_CHANNEL_CLK_INDEPENDENT
262+ uint32_t periph_src_clk_hz = 0 ;
263+ // get clock source frequency
264+ ESP_RETURN_ON_ERROR (esp_clk_tree_src_get_freq_hz ((soc_module_clk_t )clk_src , ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED , & periph_src_clk_hz ),
265+ TAG , "get clock source frequency failed" );
213266 RMT_CLOCK_SRC_ATOMIC () {
214- rmt_ll_set_group_clock_src (group -> hal .regs , channel_id , clk_src , 1 , 1 , 0 );
267+ rmt_ll_set_group_clock_src (group -> hal .regs , chan -> channel_id , clk_src , 1 , 1 , 0 );
215268 rmt_ll_enable_group_clock (group -> hal .regs , true);
216269 }
217270 group -> resolution_hz = periph_src_clk_hz ;
218271 ESP_LOGD (TAG , "group clock resolution:%" PRIu32 , group -> resolution_hz );
272+ real_div = (group -> resolution_hz + expect_channel_resolution / 2 ) / expect_channel_resolution ;
273+ #else
274+ // set division for group clock source, to achieve highest resolution while guaranteeing the channel resolution.
275+ ESP_RETURN_ON_ERROR (s_rmt_set_group_prescale (chan , expect_channel_resolution , & real_div ), TAG , "set rmt group prescale failed" );
276+ #endif // SOC_RMT_CHANNEL_CLK_INDEPENDENT
277+
278+ if (chan -> direction == RMT_CHANNEL_DIRECTION_TX ) {
279+ rmt_ll_tx_set_channel_clock_div (group -> hal .regs , chan -> channel_id , real_div );
280+ } else {
281+ rmt_ll_rx_set_channel_clock_div (group -> hal .regs , chan -> channel_id , real_div );
282+ }
283+ // resolution lost due to division, calculate the real resolution
284+ chan -> resolution_hz = group -> resolution_hz / real_div ;
285+ if (chan -> resolution_hz != expect_channel_resolution ) {
286+ ESP_LOGW (TAG , "channel resolution loss, real=%" PRIu32 , chan -> resolution_hz );
287+ }
219288 return ret ;
220289}
221290
0 commit comments