Select one of the symbols to view example projects that use it.
 
Outline
#include <stdint.h>
#include <sys/lock.h>
#include "sdkconfig.h"
#define LOG_LOCAL_LEVEL
#include "esp_log.h"
#include "esp_check.h"
#include "esp_clk_tree.h"
#include "esp_private/esp_clk_tree_common.h"
#include "esp_private/periph_ctrl.h"
#include "soc/mcpwm_periph.h"
#include "soc/soc_caps.h"
#include "hal/mcpwm_ll.h"
#include "mcpwm_private.h"
#include "esp_private/rtc_clk.h"
#define MCPWM_CLOCK_SRC_ATOMIC
#define MCPWM_CLOCK_SRC_ATOMIC
#define MCPWM_RCC_ATOMIC
#define MCPWM_RCC_ATOMIC
TAG
mcpwm_platform_t
s_platform
mcpwm_acquire_group_handle(int)
mcpwm_release_group_handle(mcpwm_group_t *)
mcpwm_check_intr_priority(mcpwm_group_t *, int)
mcpwm_get_intr_priority_flag(mcpwm_group_t *)
mcpwm_select_periph_clock(mcpwm_group_t *, soc_module_clk_t)
mcpwm_set_prescale(mcpwm_group_t *, uint32_t, uint32_t, uint32_t *)
Files
loading (4/5)...
SourceVuESP-IDF Framework and ExamplesESP-IDFcomponents/esp_driver_mcpwm/src/mcpwm_com.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
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
/* * SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 *//* ... */ #include <stdint.h> #include <sys/lock.h> #include "sdkconfig.h" #if CONFIG_MCPWM_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 "esp_clk_tree.h" #include "esp_private/esp_clk_tree_common.h" #include "esp_private/periph_ctrl.h" #include "soc/mcpwm_periph.h" #include "soc/soc_caps.h" #include "hal/mcpwm_ll.h" #include "mcpwm_private.h" #include "esp_private/rtc_clk.h"10 includes #if SOC_PERIPH_CLK_CTRL_SHARED #define MCPWM_CLOCK_SRC_ATOMIC() PERIPH_RCC_ATOMIC() #else #define MCPWM_CLOCK_SRC_ATOMIC() #endif #if !SOC_RCC_IS_INDEPENDENT #define MCPWM_RCC_ATOMIC() PERIPH_RCC_ATOMIC() #else #define MCPWM_RCC_ATOMIC() #endif #if MCPWM_USE_RETENTION_LINK static esp_err_t mcpwm_create_sleep_retention_link_cb(void *arg); #endif static const char *TAG = "mcpwm"; typedef struct { _lock_t mutex; // platform level mutex lock mcpwm_group_t *groups[SOC_MCPWM_GROUPS]; // array of MCPWM group instances int group_ref_counts[SOC_MCPWM_GROUPS]; // reference count used to protect group install/uninstall }{ ... } mcpwm_platform_t; static mcpwm_platform_t s_platform; // singleton platform mcpwm_group_t *mcpwm_acquire_group_handle(int group_id) { bool new_group = false; mcpwm_group_t *group = NULL; // prevent install mcpwm group concurrently _lock_acquire(&s_platform.mutex); if (!s_platform.groups[group_id]) { group = heap_caps_calloc(1, sizeof(mcpwm_group_t), MCPWM_MEM_ALLOC_CAPS); if (group) { new_group = true; s_platform.groups[group_id] = group; group->group_id = group_id; group->intr_priority = -1; group->spinlock = (portMUX_TYPE)portMUX_INITIALIZER_UNLOCKED; #if MCPWM_USE_RETENTION_LINK sleep_retention_module_t module = mcpwm_reg_retention_info[group_id].retention_module; sleep_retention_module_init_param_t init_param = { .cbs = { .create = { .handle = mcpwm_create_sleep_retention_link_cb, .arg = group, }{...}, }{...}, .depends = RETENTION_MODULE_BITMAP_INIT(CLOCK_SYSTEM) }{...}; // we only do retention init here. Allocate retention module in the unit initialization if (sleep_retention_module_init(module, &init_param) != ESP_OK) { // even though the sleep retention module init failed, MCPWM 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/* ... */ // MCPWM_USE_RETENTION_LINK // enable APB to access MCPWM registers MCPWM_RCC_ATOMIC() { mcpwm_ll_enable_bus_clock(group_id, true); mcpwm_ll_reset_register(group_id); }{...} // enable function clock before initialize HAL context // MCPWM registers are in the core clock domain, there's a bridge between APB and the Core clock domain // if the core clock is not enabled, then even the APB clock is enabled, the MCPWM registers are still not accessible MCPWM_CLOCK_SRC_ATOMIC() { mcpwm_ll_group_enable_clock(group_id, true); }{...} // initialize HAL context mcpwm_hal_init_config_t hal_config = { .group_id = group_id }{...}; mcpwm_hal_context_t *hal = &group->hal; mcpwm_hal_init(hal, &hal_config); // disable all interrupts and clear pending status mcpwm_ll_intr_enable(hal->dev, UINT32_MAX, false); mcpwm_ll_intr_clear_status(hal->dev, UINT32_MAX); }{...} }{...} 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", group_id, group); }{...} return group; }{ ... } void mcpwm_release_group_handle(mcpwm_group_t *group) { int group_id = group->group_id; bool do_deinitialize = false; _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; // deregister from platform MCPWM_CLOCK_SRC_ATOMIC() { mcpwm_ll_group_enable_clock(group_id, false); }{...} // hal layer deinitialize mcpwm_hal_deinit(&group->hal); MCPWM_RCC_ATOMIC() { mcpwm_ll_enable_bus_clock(group_id, false); }{...} if (group->pm_lock) { esp_pm_lock_delete(group->pm_lock); }{...} #if MCPWM_USE_RETENTION_LINK const periph_retention_module_t module_id = mcpwm_reg_retention_info[group_id].retention_module; if (sleep_retention_is_module_created(module_id)) { sleep_retention_module_free(module_id); }{...} if (sleep_retention_is_module_inited(module_id)) { sleep_retention_module_deinit(module_id); }{...} #endif/* ... */ // MCPWM_USE_RETENTION_LINK free(group); }{...} _lock_release(&s_platform.mutex); if (do_deinitialize) { ESP_LOGD(TAG, "del group(%d)", group_id); }{...} }{ ... } esp_err_t mcpwm_check_intr_priority(mcpwm_group_t *group, int intr_priority) { esp_err_t ret = ESP_OK; bool intr_priority_conflict = false; portENTER_CRITICAL(&group->spinlock); if (group->intr_priority == -1) { group->intr_priority = intr_priority; }{...} else if (intr_priority != 0) { intr_priority_conflict = (group->intr_priority != intr_priority); }{...} portEXIT_CRITICAL(&group->spinlock); ESP_RETURN_ON_FALSE(!intr_priority_conflict, ESP_ERR_INVALID_STATE, TAG, "intr_priority conflict, already is %d but attempt to %d", group->intr_priority, intr_priority); return ret; }{ ... } int mcpwm_get_intr_priority_flag(mcpwm_group_t *group) { int isr_flags = 0; if (group->intr_priority) { isr_flags |= 1 << (group->intr_priority); }{...} else { isr_flags |= MCPWM_ALLOW_INTR_PRIORITY_MASK; }{...} return isr_flags; }{ ... } esp_err_t mcpwm_select_periph_clock(mcpwm_group_t *group, soc_module_clk_t clk_src) { esp_err_t ret = ESP_OK; bool clock_selection_conflict = false; bool do_clock_init = false; // check if we need to update the group clock source, group clock source is shared by all mcpwm modules portENTER_CRITICAL(&group->spinlock); if (group->clk_src == 0) { group->clk_src = clk_src; do_clock_init = true; }{...} 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); if (do_clock_init) { #if CONFIG_PM_ENABLE sprintf(group->pm_lock_name, "mcpwm_%d", group->group_id); // e.g. mcpwm_0 ret = esp_pm_lock_create(ESP_PM_NO_LIGHT_SLEEP, 0, group->pm_lock_name, &group->pm_lock); ESP_RETURN_ON_ERROR(ret, TAG, "create pm lock failed"); ESP_LOGD(TAG, "install NO_LIGHT_SLEEP lock for MCPWM group(%d)", group->group_id);/* ... */ #endif // CONFIG_PM_ENABLE esp_clk_tree_enable_src((soc_module_clk_t)clk_src, true); MCPWM_CLOCK_SRC_ATOMIC() { mcpwm_ll_group_set_clock_source(group->group_id, clk_src); }{...} }{...} return ret; }{ ... } esp_err_t mcpwm_set_prescale(mcpwm_group_t *group, uint32_t expect_module_resolution_hz, uint32_t module_prescale_max, uint32_t *ret_module_prescale) { ESP_RETURN_ON_FALSE(group && expect_module_resolution_hz && module_prescale_max, ESP_ERR_INVALID_ARG, TAG, "invalid argument"); uint32_t periph_src_clk_hz = 0; int group_id = group->group_id; uint32_t group_resolution_hz = 0; uint32_t group_prescale = group->prescale > 0 ? group->prescale : MCPWM_GROUP_CLOCK_DEFAULT_PRESCALE; // range: 1~256, 0 means not calculated uint32_t module_prescale = 0; // range: 1~256 (for timer) or 1~16 (for carrier) or 1 (for capture) 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"); // calc the group prescale group_resolution_hz = periph_src_clk_hz / group_prescale; module_prescale = group_resolution_hz / expect_module_resolution_hz; // default prescale cannot match // try to ensure accurate division. If none of the division factors can be guaranteed to be integers, then allocate the clock frequency to the highest divisor uint32_t fit_module_prescale = 0; uint32_t fit_group_prescale = 0; if (!(module_prescale >= 1 && module_prescale <= module_prescale_max)) { group_prescale = 0; while (++group_prescale <= MCPWM_LL_MAX_GROUP_PRESCALE) { group_resolution_hz = periph_src_clk_hz / group_prescale; module_prescale = group_resolution_hz / expect_module_resolution_hz; if (module_prescale >= 1 && module_prescale <= module_prescale_max) { // maintain the first value found during the search that satisfies the division requirement (highest frequency), applicable for cases where integer division is not possible." fit_module_prescale = fit_module_prescale ? fit_module_prescale : module_prescale; fit_group_prescale = fit_group_prescale ? fit_group_prescale : group_prescale; // find accurate division if (group_resolution_hz == expect_module_resolution_hz * module_prescale) { fit_module_prescale = module_prescale; fit_group_prescale = group_prescale; break; }{...} }{...} }{...} module_prescale = fit_module_prescale; group_prescale = fit_group_prescale; group_resolution_hz = periph_src_clk_hz / group_prescale; }{...} ESP_LOGD(TAG, "group (%d) calc prescale:%"PRIu32", module calc prescale:%"PRIu32"", group_id, group_prescale, module_prescale); ESP_RETURN_ON_FALSE(group_prescale > 0 && group_prescale <= MCPWM_LL_MAX_GROUP_PRESCALE, ESP_ERR_INVALID_STATE, TAG, "set group prescale failed, group clock cannot match the resolution"); // check if we need to update the group prescale, group prescale is shared by all mcpwm modules bool prescale_conflict = false; portENTER_CRITICAL(&group->spinlock); if (group->prescale == 0) { group->prescale = group_prescale; group->resolution_hz = group_resolution_hz; MCPWM_CLOCK_SRC_ATOMIC() { mcpwm_ll_group_set_clock_prescale(group_id, group_prescale); }{...} }{...} else { prescale_conflict = (group->prescale != group_prescale); }{...} portEXIT_CRITICAL(&group->spinlock); ESP_RETURN_ON_FALSE(!prescale_conflict, ESP_ERR_INVALID_STATE, TAG, "group prescale conflict, already is %"PRIu32" but attempt to %"PRIu32"", group->prescale, group_prescale); ESP_LOGD(TAG, "group (%d) clock resolution:%"PRIu32"Hz", group_id, group->resolution_hz); // set module resolution if (ret_module_prescale) { *ret_module_prescale = module_prescale; }{...} return ESP_OK; }{ ... } #if MCPWM_USE_RETENTION_LINK static esp_err_t mcpwm_create_sleep_retention_link_cb(void *arg) { mcpwm_group_t *group = (mcpwm_group_t *)arg; int group_id = group->group_id; sleep_retention_module_t module_id = mcpwm_reg_retention_info[group_id].retention_module; esp_err_t err = sleep_retention_entries_create(mcpwm_reg_retention_info[group_id].regdma_entry_array, mcpwm_reg_retention_info[group_id].array_size, REGDMA_LINK_PRI_MCPWM, module_id); return err; }{...} void mcpwm_create_retention_module(mcpwm_group_t *group) { int group_id = group->group_id; sleep_retention_module_t module_id = mcpwm_reg_retention_info[group_id].retention_module; _lock_acquire(&s_platform.mutex); if (sleep_retention_is_module_inited(module_id) && !sleep_retention_is_module_created(module_id)) { if (sleep_retention_module_allocate(module_id) != ESP_OK) { // even though the sleep retention module create failed, MCPWM driver should still work, so just warning here ESP_LOGW(TAG, "create retention module failed, power domain can't turn off"); }{...} }{...} _lock_release(&s_platform.mutex); }{...} /* ... */#endif // MCPWM_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.