Skip to content

Commit 72d0f5b

Browse files
committed
mcpwm: can't apply the same delay module to multiple generators
This is a hardware limitation, one delay module can only be used by one generator at one time. Closes #11327
1 parent 66c999f commit 72d0f5b

File tree

9 files changed

+105
-12
lines changed

9 files changed

+105
-12
lines changed

components/driver/mcpwm/include/driver/mcpwm_gen.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,12 +215,17 @@ typedef struct {
215215
/**
216216
* @brief Set dead time for MCPWM generator
217217
*
218+
* @note Due to a hardware limitation, you can't set rising edge delay for both MCPWM generator 0 and 1 at the same time,
219+
* otherwise, there will be a conflict inside the dead time module. The same goes for the falling edge setting.
220+
* But you can set both the rising edge and falling edge delay for the same MCPWM generator.
221+
*
218222
* @param[in] in_generator MCPWM generator, before adding the dead time
219223
* @param[in] out_generator MCPWM generator, after adding the dead time
220224
* @param[in] config MCPWM dead time configuration
221225
* @return
222226
* - ESP_OK: Set dead time for MCPWM generator successfully
223227
* - ESP_ERR_INVALID_ARG: Set dead time for MCPWM generator failed because of invalid argument
228+
* - ESP_ERR_INVALID_STATE: Set dead time for MCPWM generator failed because of invalid state (e.g. delay module is already in use by other generator)
224229
* - ESP_FAIL: Set dead time for MCPWM generator failed because of other error
225230
*/
226231
esp_err_t mcpwm_generator_set_dead_time(mcpwm_gen_handle_t in_generator, mcpwm_gen_handle_t out_generator, const mcpwm_dead_time_config_t *config);

components/driver/mcpwm/mcpwm_gen.c

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,36 @@ esp_err_t mcpwm_generator_set_dead_time(mcpwm_gen_handle_t in_generator, mcpwm_g
268268
mcpwm_hal_context_t *hal = &group->hal;
269269
int oper_id = oper->oper_id;
270270

271+
// one delay module can only be used by one generator at a time
272+
bool delay_module_conflict = false;
273+
portENTER_CRITICAL(&oper->spinlock);
274+
if (config->posedge_delay_ticks) {
275+
if (oper->posedge_delay_owner && oper->posedge_delay_owner != in_generator) {
276+
delay_module_conflict = true;
277+
}
278+
}
279+
if (config->negedge_delay_ticks) {
280+
if (oper->negedge_delay_owner && oper->negedge_delay_owner != in_generator) {
281+
delay_module_conflict = true;
282+
}
283+
}
284+
if (!delay_module_conflict) {
285+
if (config->posedge_delay_ticks) {
286+
// set owner if delay module is used
287+
oper->posedge_delay_owner = in_generator;
288+
} else if (oper->posedge_delay_owner == in_generator) {
289+
// clear owner if delay module is previously used by in_generator, but now it is not used
290+
oper->posedge_delay_owner = NULL;
291+
}
292+
if (config->negedge_delay_ticks) {
293+
oper->negedge_delay_owner = in_generator;
294+
} else if (oper->negedge_delay_owner == in_generator) {
295+
oper->negedge_delay_owner = NULL;
296+
}
297+
}
298+
portEXIT_CRITICAL(&oper->spinlock);
299+
ESP_RETURN_ON_FALSE(!delay_module_conflict, ESP_ERR_INVALID_STATE, TAG, "delay module is in use by other generator");
300+
271301
// Note: to better understand the following code, you should read the deadtime module topology diagram in the TRM
272302
// check if we want to bypass the deadtime module
273303
bool bypass = (config->negedge_delay_ticks == 0) && (config->posedge_delay_ticks == 0);

components/driver/mcpwm/mcpwm_private.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -102,6 +102,8 @@ struct mcpwm_oper_t {
102102
mcpwm_operator_brake_mode_t brake_mode_on_soft_fault; // brake mode on software triggered fault
103103
mcpwm_operator_brake_mode_t brake_mode_on_gpio_fault[SOC_MCPWM_GPIO_FAULTS_PER_GROUP]; // brake mode on GPIO triggered faults
104104
uint32_t deadtime_resolution_hz; // resolution of deadtime submodule
105+
mcpwm_gen_t *posedge_delay_owner; // which generator owns the positive edge delay
106+
mcpwm_gen_t *negedge_delay_owner; // which generator owns the negative edge delay
105107
mcpwm_brake_event_cb_t on_brake_cbc; // callback function which would be invoked when mcpwm operator goes into trip zone
106108
mcpwm_brake_event_cb_t on_brake_ost; // callback function which would be invoked when mcpwm operator goes into trip zone
107109
void *user_data; // user data which would be passed to the trip zone callback

components/driver/test_apps/mcpwm/main/test_mcpwm_gen.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -591,6 +591,23 @@ static void redfedb_only_set_dead_time(mcpwm_gen_handle_t gena, mcpwm_gen_handle
591591
TEST_ESP_OK(mcpwm_generator_set_dead_time(genb, genb, &dead_time_config));
592592
}
593593

594+
static void invalid_reda_redb_set_dead_time(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb)
595+
{
596+
mcpwm_dead_time_config_t dead_time_config = {
597+
.posedge_delay_ticks = 10,
598+
};
599+
// generator_a adds delay on the posedge
600+
TEST_ESP_OK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
601+
// generator_b adds delay on the posedge as well, which is not allowed
602+
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, mcpwm_generator_set_dead_time(genb, genb, &dead_time_config));
603+
// bypass the delay module for generator_a
604+
dead_time_config.posedge_delay_ticks = 0;
605+
TEST_ESP_OK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
606+
// now generator_b can add delay on the posedge
607+
dead_time_config.posedge_delay_ticks = 10;
608+
TEST_ESP_OK(mcpwm_generator_set_dead_time(genb, genb, &dead_time_config));
609+
}
610+
594611
TEST_CASE("mcpwm_generator_deadtime_classical_configuration", "[mcpwm]")
595612
{
596613
printf("Active High Complementary\r\n");
@@ -613,6 +630,9 @@ TEST_CASE("mcpwm_generator_deadtime_classical_configuration", "[mcpwm]")
613630

614631
printf("Bypass A, RED + FED on B\r\n");
615632
mcpwm_deadtime_test_template(1000000, 500, 350, 350, 0, 2, redfedb_only_set_generator_actions, redfedb_only_set_dead_time);
633+
634+
printf("Can't apply one delay module to multiple generators\r\n");
635+
mcpwm_deadtime_test_template(1000000, 500, 350, 350, 0, 2, redfedb_only_set_generator_actions, invalid_reda_redb_set_dead_time);
616636
}
617637

618638
TEST_CASE("mcpwm_duty_empty_full", "[mcpwm]")

docs/_static/diagrams/mcpwm/deadtime_active_high_complementary.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
{
44
"name": "origin",
55
"wave": "0...1.....0...",
6-
"node": "....a.....b..."
6+
"node": "....a.e...b..."
77
},
88
{
99
"name": "pwm_A",
@@ -13,12 +13,13 @@
1313
{
1414
"name": "pwm_B",
1515
"wave": "1...0......1..",
16-
"node": "...........d.."
16+
"node": "......f....d.."
1717
}
1818
],
1919
"edge": [
2020
"a|->c RED",
21-
"b|->d FED"
21+
"b|->d FED",
22+
"e<->f Invert"
2223
],
2324
"head": {
2425
"text": "Active High, Complementary"

docs/_static/diagrams/mcpwm/deadtime_active_low.json

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,24 @@
33
{
44
"name": "origin",
55
"wave": "0...1.....0...",
6-
"node": "....a.....b..."
6+
"node": "....a..ef.b..."
77
},
88
{
99
"name": "pwm_A",
1010
"wave": "1....0....1...",
11-
"node": ".....c....."
11+
"node": ".....c.g..."
1212
},
1313
{
1414
"name": "pwm_B",
1515
"wave": "1...0......1..",
16-
"node": "...........d.."
16+
"node": "........h..d.."
1717
}
1818
],
1919
"edge": [
2020
"a|->c RED",
21-
"b|->d FED"
21+
"b|->d FED",
22+
"e<->g Invert",
23+
"f<->h Invert"
2224
],
2325
"head": {
2426
"text": "Active Low"

docs/_static/diagrams/mcpwm/deadtime_active_low_complementary.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
{
44
"name": "origin",
55
"wave": "0...1.....0...",
6-
"node": "....a.....b..."
6+
"node": "....a..e..b..."
77
},
88
{
99
"name": "pwm_A",
1010
"wave": "1....0....1...",
11-
"node": ".....c....."
11+
"node": ".....c.f..."
1212
},
1313
{
1414
"name": "pwm_B",
@@ -18,7 +18,8 @@
1818
],
1919
"edge": [
2020
"a|->c RED",
21-
"b|->d FED"
21+
"b|->d FED",
22+
"e<->f Invert"
2223
],
2324
"head": {
2425
"text": "Active Low, Complementary"

docs/en/api-reference/peripherals/mcpwm.rst

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -433,13 +433,29 @@ Dead Time
433433

434434
In power electronics, the rectifier and inverter are commonly used. This requires the use of a rectifier bridge and an inverter bridge. Each bridge arm has two power electronic devices, such as MOSFET, IGBT, etc. The two MOSFETs on the same arm can't conduct at the same time, otherwise there will be a short circuit. The fact is that, although the PWM wave shows it is turning off the switch, the MOSFET still needs a small time window to make that happen. This requires an extra delay to be added to the existing PWM wave generated by setting `Generator Actions on Events <#generator-actions-on-events>`__.
435435

436-
The dead time driver works like a *decorator*. This is also reflected in the function parameters of :cpp:func:`mcpwm_generator_set_dead_time`, where it takes the primary generator handle (``in_generator``), and returns a generator (``out_generator``) after applying the dead time. Please note, if the ``out_generator`` and ``in_generator`` are the same, it means we are adding the time delay to the PWM waveform in an "in-place" fashion. In turn, if the ``out_generator`` and ``in_generator`` are different, it means we're deriving a new PWM waveform from the existing ``in_generator``.
436+
The dead time driver works like a *decorator*. This is also reflected in the function parameters of :cpp:func:`mcpwm_generator_set_dead_time`, where it takes the primary generator handle (``in_generator``), and returns a new generator (``out_generator``) after applying the dead time. Please note, if the ``out_generator`` and ``in_generator`` are the same, it means we are adding the time delay to the PWM waveform in an "in-place" fashion. In turn, if the ``out_generator`` and ``in_generator`` are different, it means we're deriving a new PWM waveform from the existing ``in_generator``.
437437

438438
Dead time specific configuration is listed in the :cpp:type:`mcpwm_dead_time_config_t` structure:
439439

440440
- :cpp:member:`mcpwm_dead_time_config_t::posedge_delay_ticks` and :cpp:member:`mcpwm_dead_time_config_t::negedge_delay_ticks` set the number of ticks to delay the PWM waveform on the rising and falling edge. Specifically, setting both of them to zero means bypassing the dead time module. The resolution of the dead time tick is the same as the timer that is connected with the operator by :cpp:func:`mcpwm_operator_connect_timer`.
441441
- :cpp:member:`mcpwm_dead_time_config_t::invert_output` sets whether to invert the signal after applying the dead time, which can be used to control the delay edge polarity.
442442

443+
.. warning::
444+
445+
Due to the hardware limitation, one delay module (either `posedge delay` or `negedge delay`) can't be applied to multiple MCPWM generators at the same time. e.g. the following configuration is **invalid**:
446+
447+
.. code:: c
448+
449+
mcpwm_dead_time_config_t dt_config = {
450+
.posedge_delay_ticks = 10,
451+
};
452+
// Set posedge delay to generator A
453+
mcpwm_generator_set_dead_time(mcpwm_gen_a, mcpwm_gen_a, &dt_config);
454+
// NOTE: This is invalid, you can't apply the posedge delay to another generator
455+
mcpwm_generator_set_dead_time(mcpwm_gen_b, mcpwm_gen_b, &dt_config);
456+
457+
However, you can apply `posedge delay` to generator A and `negedge delay` to generator B. You can also set both `posedge delay` and `negedge delay` for generator A, while letting generator B bypass the dead time module.
458+
443459
.. note::
444460

445461
It is also possible to generate the required dead time by setting `Generator Actions on Events <#generator-actions-on-events>`__, especially by controlling edge placement using different comparators. However, if the more classical edge delay-based dead time with polarity control is required, then the dead time submodule should be used.

docs/zh_CN/api-reference/peripherals/mcpwm.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,22 @@ MCPWM 比较器可以在定时器计数器等于比较值时发送通知。若
440440
- :cpp:member:`mcpwm_dead_time_config_t::posedge_delay_ticks` 和 :cpp:member:`mcpwm_dead_time_config_t::negedge_delay_ticks` 设置 PWM 波形上升沿和下降沿上的延迟时间,以 Tick 为单位。若将这两个参数设置为 0,则代表绕过死区模块。死区的 Tick 分辨率与通过 :cpp:func:`mcpwm_operator_connect_timer` 连接操作器的定时器相同。
441441
- :cpp:member:`mcpwm_dead_time_config_t::invert_output` 设置是否在应用死区后取反信号,以控制延迟边沿的极性。
442442

443+
.. warning::
444+
445+
由于硬件限制,同一种 delay 模块(`posedge delay` 或者 `negedge delay`)不能同时被应用在不同的 MCPWM 生成器中。例如,以下配置是无效的:
446+
447+
.. code:: c
448+
449+
mcpwm_dead_time_config_t dt_config = {
450+
.posedge_delay_ticks = 10,
451+
};
452+
// 给 generator A 叠加上升沿 delay
453+
mcpwm_generator_set_dead_time(mcpwm_gen_a, mcpwm_gen_a, &dt_config);
454+
// NOTE: 下面的操作是无效的,不能将同一种 delay 应用于不同的 generator 上
455+
mcpwm_generator_set_dead_time(mcpwm_gen_b, mcpwm_gen_b, &dt_config);
456+
457+
然而,您可以为生成器 A 设置 `posedge delay`,为生成器 B 设置 `negedge delay`。另外,您也可以为生成器 A 同时设置 `posedge delay` 和 `negedge delay`,而让生成器 B 绕过死区模块。
458+
443459
.. note::
444460

445461
也可以通过设置 :ref:`mcpwm-generator-actions-on-events` 来生成所需的死区,通过不同的比较器来控制边沿位置。但是,如果需要使用经典的基于边沿延迟并附带极性控制的死区,则应使用死区子模块。

0 commit comments

Comments
 (0)