Select one of the symbols to view example projects that use it.
 
Outline
#include <sys/lock.h>
#include "sdkconfig.h"
#define LOG_LOCAL_LEVEL
#include "esp_log.h"
#include "esp_check.h"
#include "rmt_private.h"
#include "clk_ctrl_os.h"
#include "soc/rtc.h"
#include "soc/soc_caps.h"
#include "soc/rmt_periph.h"
#include "hal/rmt_ll.h"
#include "driver/gpio.h"
#include "esp_private/esp_clk_tree_common.h"
#include "esp_private/periph_ctrl.h"
TAG
#define RMT_CLOCK_SRC_ATOMIC
#define RMT_CLOCK_SRC_ATOMIC
#define RMT_RCC_ATOMIC
#define RMT_RCC_ATOMIC
rmt_platform_t
s_platform
rmt_acquire_group_handle(int)
rmt_release_group_handle(rmt_group_t *)
rmt_select_periph_clock(rmt_channel_handle_t, rmt_clock_source_t)
rmt_get_channel_id(rmt_channel_handle_t, int *)
rmt_get_channel_resolution(rmt_channel_handle_t, uint32_t *)
rmt_apply_carrier(rmt_channel_handle_t, const rmt_carrier_config_t *)
rmt_del_channel(rmt_channel_handle_t)
rmt_enable(rmt_channel_handle_t)
rmt_disable(rmt_channel_handle_t)
rmt_set_intr_priority_to_group(rmt_group_t *, int)
rmt_get_isr_flags(rmt_group_t *)
Files
loading...
SourceVuESP-IDF Framework and ExamplesESP-IDFcomponents/esp_driver_rmt/src/rmt_common.c
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
/* * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 *//* ... */ #include <sys/lock.h> #include "sdkconfig.h" #if CONFIG_RMT_ENABLE_DEBUG_LOG // The local log level must be defined before including esp_log.h // Set the maximum log level for this source file #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG/* ... */ #endif #include "esp_log.h" #include "esp_check.h" #include "rmt_private.h" #include "clk_ctrl_os.h" #include "soc/rtc.h" #include "soc/soc_caps.h" #include "soc/rmt_periph.h" #include "hal/rmt_ll.h" #include "driver/gpio.h" #include "esp_private/esp_clk_tree_common.h" #include "esp_private/periph_ctrl.h"11 includes static const char *TAG = "rmt"; #if SOC_PERIPH_CLK_CTRL_SHARED #define RMT_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC() #else #define RMT_CLOCK_SRC_ATOMIC() #endif #if !SOC_RCC_IS_INDEPENDENT #define RMT_RCC_ATOMIC() PERIPH_RCC_ATOMIC() #else #define RMT_RCC_ATOMIC() #endif typedef struct rmt_platform_t { _lock_t mutex; // platform level mutex lock rmt_group_t *groups[SOC_RMT_GROUPS]; // array of RMT group instances int group_ref_counts[SOC_RMT_GROUPS]; // reference count used to protect group install/uninstall }{ ... } rmt_platform_t; static rmt_platform_t s_platform; // singleton platform #if RMT_USE_RETENTION_LINK static esp_err_t rmt_create_sleep_retention_link_cb(void *arg); #endif rmt_group_t *rmt_acquire_group_handle(int group_id) { bool new_group = false; rmt_group_t *group = NULL; // prevent install rmt group concurrently _lock_acquire(&s_platform.mutex); if (!s_platform.groups[group_id]) { group = heap_caps_calloc(1, sizeof(rmt_group_t), RMT_MEM_ALLOC_CAPS); if (group) { new_group = true; s_platform.groups[group_id] = group; group->group_id = group_id; group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; // initial occupy_mask: 1111...100...0 group->occupy_mask = UINT32_MAX & ~((1 << SOC_RMT_CHANNELS_PER_GROUP) - 1); // group clock won't be configured at this stage, it will be set when allocate the first channel group->clk_src = 0; // group interrupt priority is shared between all channels, it will be set when allocate the first channel group->intr_priority = RMT_GROUP_INTR_PRIORITY_UNINITIALIZED; // enable the bus clock for the RMT peripheral RMT_RCC_ATOMIC() { rmt_ll_enable_bus_clock(group_id, true); rmt_ll_reset_register(group_id); }{...} #if RMT_USE_RETENTION_LINK sleep_retention_module_t module = rmt_reg_retention_info[group_id].module; sleep_retention_module_init_param_t init_param = { .cbs = { .create = { .handle = rmt_create_sleep_retention_link_cb, .arg = group, }{...}, }{...}, .depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM) }{...}; if (sleep_retention_module_init(module, &init_param) != ESP_OK) { // even though the sleep retention module init failed, RMT driver should still work, so just warning here ESP_LOGW(TAG, "init sleep retention failed %d, power domain may be turned off during sleep", group_id); }{...} #endif/* ... */ // RMT_USE_RETENTION_LINK // hal layer initialize rmt_hal_init(&group->hal); }{...} }{...} else { // group already install group = s_platform.groups[group_id]; }{...} if (group) { // someone acquired the group handle means we have a new object that refer to this group s_platform.group_ref_counts[group_id]++; }{...} _lock_release(&s_platform.mutex); if (new_group) { ESP_LOGD(TAG, "new group(%d) at %p, occupy=%"PRIx32, group_id, group, group->occupy_mask); }{...} return group; }{ ... } void rmt_release_group_handle(rmt_group_t *group) { int group_id = group->group_id; rmt_clock_source_t clk_src = group->clk_src; bool do_deinitialize = false; rmt_hal_context_t *hal = &group->hal; _lock_acquire(&s_platform.mutex); s_platform.group_ref_counts[group_id]--; if (s_platform.group_ref_counts[group_id] == 0) { do_deinitialize = true; s_platform.groups[group_id] = NULL; // disable core clock RMT_CLOCK_SRC_ATOMIC() { rmt_ll_enable_group_clock(hal->regs, false); }{...} // hal layer deinitialize rmt_hal_deinit(hal); // disable bus clock RMT_RCC_ATOMIC() { rmt_ll_enable_bus_clock(group_id, false); }{...} }{...} _lock_release(&s_platform.mutex); switch (clk_src) { #if SOC_RMT_SUPPORT_RC_FAST case RMT_CLK_SRC_RC_FAST: periph_rtc_dig_clk8m_disable(); break;/* ... */ #endif // SOC_RMT_SUPPORT_RC_FAST default: break;... }{...} if (do_deinitialize) { #if RMT_USE_RETENTION_LINK sleep_retention_module_t module = rmt_reg_retention_info[group_id].module; if (sleep_retention_is_module_created(module)) { sleep_retention_module_free(module); }{...} if (sleep_retention_is_module_inited(module)) { sleep_retention_module_deinit(module); }{...} #endif/* ... */ free(group); ESP_LOGD(TAG, "del group(%d)", group_id); }{...} }{ ... } esp_err_t rmt_select_periph_clock(rmt_channel_handle_t chan, rmt_clock_source_t clk_src) { esp_err_t ret = ESP_OK; rmt_group_t *group = chan->group; int channel_id = chan->channel_id; uint32_t periph_src_clk_hz = 0; bool clock_selection_conflict = false; // check if we need to update the group clock source, group clock source is shared by all channels portENTER_CRITICAL(&group->spinlock); if (group->clk_src == 0) { group->clk_src = clk_src; }{...} else { clock_selection_conflict = (group->clk_src != clk_src); }{...} portEXIT_CRITICAL(&group->spinlock); ESP_RETURN_ON_FALSE(!clock_selection_conflict, ESP_ERR_INVALID_STATE, TAG, "group clock conflict, already is %d but attempt to %d", group->clk_src, clk_src); // TODO: [clk_tree] to use a generic clock enable/disable or acquire/release function for all clock source #if SOC_RMT_SUPPORT_RC_FAST if (clk_src == RMT_CLK_SRC_RC_FAST) { // RC_FAST clock is not enabled automatically on start up, we enable it here manually. // Note there's a ref count in the enable/disable function, we must call them in pair in the driver. periph_rtc_dig_clk8m_enable(); }{...} #endif/* ... */ // SOC_RMT_SUPPORT_RC_FAST // get clock source frequency 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), TAG, "get clock source frequency failed"); #if CONFIG_PM_ENABLE // if DMA is not used, we're using CPU to push the data to the RMT FIFO // if the CPU frequency goes down, the transfer+encoding scheme could be unstable because CPU can't fill the data in time // so, choose ESP_PM_CPU_FREQ_MAX lock for non-dma mode // otherwise, chose lock type based on the clock source esp_pm_lock_type_t pm_lock_type = chan->dma_chan ? ESP_PM_NO_LIGHT_SLEEP : ESP_PM_CPU_FREQ_MAX; #if SOC_RMT_SUPPORT_APB if (clk_src == RMT_CLK_SRC_APB) { // APB clock frequency can be changed during DFS pm_lock_type = ESP_PM_APB_FREQ_MAX; }{...} #endif/* ... */ // SOC_RMT_SUPPORT_APB sprintf(chan->pm_lock_name, "rmt_%d_%d", group->group_id, channel_id); // e.g. rmt_0_0 ret = esp_pm_lock_create(pm_lock_type, 0, chan->pm_lock_name, &chan->pm_lock); ESP_RETURN_ON_ERROR(ret, TAG, "create pm lock failed");/* ... */ #endif // CONFIG_PM_ENABLE esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true); // no division for group clock source, to achieve highest resolution RMT_CLOCK_SRC_ATOMIC() { rmt_ll_set_group_clock_src(group->hal.regs, channel_id, clk_src, 1, 1, 0); rmt_ll_enable_group_clock(group->hal.regs, true); }{...} group->resolution_hz = periph_src_clk_hz; ESP_LOGD(TAG, "group clock resolution:%"PRIu32, group->resolution_hz); return ret; }{ ... } esp_err_t rmt_get_channel_id(rmt_channel_handle_t channel, int *ret_id) { ESP_RETURN_ON_FALSE(channel && ret_id, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); *ret_id = channel->channel_id; return ESP_OK; }{ ... } esp_err_t rmt_get_channel_resolution(rmt_channel_handle_t channel, uint32_t *ret_resolution_hz) { ESP_RETURN_ON_FALSE(channel && ret_resolution_hz, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); *ret_resolution_hz = channel->resolution_hz; return ESP_OK; }{ ... } esp_err_t rmt_apply_carrier(rmt_channel_handle_t channel, const rmt_carrier_config_t *config) { // specially, we allow config to be NULL, means to disable the carrier submodule ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); return channel->set_carrier_action(channel, config); }{ ... } esp_err_t rmt_del_channel(rmt_channel_handle_t channel) { ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); return channel->del(channel); }{ ... } esp_err_t rmt_enable(rmt_channel_handle_t channel) { ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); return channel->enable(channel); }{ ... } esp_err_t rmt_disable(rmt_channel_handle_t channel) { ESP_RETURN_ON_FALSE(channel, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); return channel->disable(channel); }{ ... } bool rmt_set_intr_priority_to_group(rmt_group_t *group, int intr_priority) { bool priority_conflict = false; portENTER_CRITICAL(&group->spinlock); if (group->intr_priority == RMT_GROUP_INTR_PRIORITY_UNINITIALIZED) { // intr_priority never allocated, accept user's value unconditionally // intr_priority could only be set once here group->intr_priority = intr_priority; }{...} else { // group intr_priority already specified // If interrupt priority specified before, it CANNOT BE CHANGED until `rmt_release_group_handle()` called // So we have to check if the new priority specified conflicts with the old one if (intr_priority) { // User specified intr_priority, check if conflict or not // Even though the `group->intr_priority` is 0, an intr_priority must have been specified automatically too, // although we do not know it exactly now, so specifying the intr_priority again might also cause conflict. // So no matter if `group->intr_priority` is 0 or not, we have to check. // Value `0` of `group->intr_priority` means "unknown", NOT "unspecified"! if (intr_priority != (group->intr_priority)) { // intr_priority conflicts! priority_conflict = true; }{...} }{...} // else do nothing // user did not specify intr_priority, then keep the old priority // We'll use the `RMT_INTR_ALLOC_FLAG | RMT_ALLOW_INTR_PRIORITY_MASK`, which should always success }{...} // The `group->intr_priority` will not change any longer, even though another task tries to modify it. // So we could exit critical here safely. portEXIT_CRITICAL(&group->spinlock); return priority_conflict; }{ ... } int rmt_get_isr_flags(rmt_group_t *group) { int isr_flags = RMT_INTR_ALLOC_FLAG; if (group->intr_priority) { // Use user-specified priority bit isr_flags |= (1 << (group->intr_priority)); }{...} else { // Allow all LOWMED priority bits isr_flags |= RMT_ALLOW_INTR_PRIORITY_MASK; }{...} return isr_flags; }{ ... } #if RMT_USE_RETENTION_LINK static esp_err_t rmt_create_sleep_retention_link_cb(void *arg) { rmt_group_t *group = (rmt_group_t *)arg; int group_id = group->group_id; esp_err_t err = sleep_retention_entries_create(rmt_reg_retention_info[group_id].regdma_entry_array, rmt_reg_retention_info[group_id].array_size, REGDMA_LINK_PRI_RMT, rmt_reg_retention_info[group_id].module); return err; }{...} void rmt_create_retention_module(rmt_group_t *group) { int group_id = group->group_id; sleep_retention_module_t module = rmt_reg_retention_info[group_id].module; _lock_acquire(&s_platform.mutex); if (sleep_retention_is_module_inited(module) && !sleep_retention_is_module_created(module)) { if (sleep_retention_module_allocate(module) != ESP_OK) { // even though the sleep retention module create failed, RMT driver should still work, so just warning here ESP_LOGW(TAG, "create retention link failed, power domain won't be turned off during sleep"); }{...} }{...} _lock_release(&s_platform.mutex); }{...} /* ... */#endif // RMT_USE_RETENTION_LINK
Details
Show:
from
Types: Columns:
This file uses the notable symbols shown below. Click anywhere in the file to view more details.