mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
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 https://github.com/espressif/esp-idf/issues/11327
This commit is contained in:
parent
f00c78b020
commit
49c83f112d
@ -215,12 +215,17 @@ typedef struct {
|
|||||||
/**
|
/**
|
||||||
* @brief Set dead time for MCPWM generator
|
* @brief Set dead time for MCPWM generator
|
||||||
*
|
*
|
||||||
|
* @note Due to a hardware limitation, you can't set rising edge delay for both MCPWM generator 0 and 1 at the same time,
|
||||||
|
* otherwise, there will be a conflict inside the dead time module. The same goes for the falling edge setting.
|
||||||
|
* But you can set both the rising edge and falling edge delay for the same MCPWM generator.
|
||||||
|
*
|
||||||
* @param[in] in_generator MCPWM generator, before adding the dead time
|
* @param[in] in_generator MCPWM generator, before adding the dead time
|
||||||
* @param[in] out_generator MCPWM generator, after adding the dead time
|
* @param[in] out_generator MCPWM generator, after adding the dead time
|
||||||
* @param[in] config MCPWM dead time configuration
|
* @param[in] config MCPWM dead time configuration
|
||||||
* @return
|
* @return
|
||||||
* - ESP_OK: Set dead time for MCPWM generator successfully
|
* - ESP_OK: Set dead time for MCPWM generator successfully
|
||||||
* - ESP_ERR_INVALID_ARG: Set dead time for MCPWM generator failed because of invalid argument
|
* - ESP_ERR_INVALID_ARG: Set dead time for MCPWM generator failed because of invalid argument
|
||||||
|
* - 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)
|
||||||
* - ESP_FAIL: Set dead time for MCPWM generator failed because of other error
|
* - ESP_FAIL: Set dead time for MCPWM generator failed because of other error
|
||||||
*/
|
*/
|
||||||
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);
|
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);
|
||||||
|
@ -268,6 +268,36 @@ esp_err_t mcpwm_generator_set_dead_time(mcpwm_gen_handle_t in_generator, mcpwm_g
|
|||||||
mcpwm_hal_context_t *hal = &group->hal;
|
mcpwm_hal_context_t *hal = &group->hal;
|
||||||
int oper_id = oper->oper_id;
|
int oper_id = oper->oper_id;
|
||||||
|
|
||||||
|
// one delay module can only be used by one generator at a time
|
||||||
|
bool delay_module_conflict = false;
|
||||||
|
portENTER_CRITICAL(&oper->spinlock);
|
||||||
|
if (config->posedge_delay_ticks) {
|
||||||
|
if (oper->posedge_delay_owner && oper->posedge_delay_owner != in_generator) {
|
||||||
|
delay_module_conflict = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (config->negedge_delay_ticks) {
|
||||||
|
if (oper->negedge_delay_owner && oper->negedge_delay_owner != in_generator) {
|
||||||
|
delay_module_conflict = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!delay_module_conflict) {
|
||||||
|
if (config->posedge_delay_ticks) {
|
||||||
|
// set owner if delay module is used
|
||||||
|
oper->posedge_delay_owner = in_generator;
|
||||||
|
} else if (oper->posedge_delay_owner == in_generator) {
|
||||||
|
// clear owner if delay module is previously used by in_generator, but now it is not used
|
||||||
|
oper->posedge_delay_owner = NULL;
|
||||||
|
}
|
||||||
|
if (config->negedge_delay_ticks) {
|
||||||
|
oper->negedge_delay_owner = in_generator;
|
||||||
|
} else if (oper->negedge_delay_owner == in_generator) {
|
||||||
|
oper->negedge_delay_owner = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
portEXIT_CRITICAL(&oper->spinlock);
|
||||||
|
ESP_RETURN_ON_FALSE(!delay_module_conflict, ESP_ERR_INVALID_STATE, TAG, "delay module is in use by other generator");
|
||||||
|
|
||||||
// Note: to better understand the following code, you should read the deadtime module topology diagram in the TRM
|
// Note: to better understand the following code, you should read the deadtime module topology diagram in the TRM
|
||||||
// check if we want to bypass the deadtime module
|
// check if we want to bypass the deadtime module
|
||||||
bool bypass = (config->negedge_delay_ticks == 0) && (config->posedge_delay_ticks == 0);
|
bool bypass = (config->negedge_delay_ticks == 0) && (config->posedge_delay_ticks == 0);
|
||||||
|
@ -102,6 +102,8 @@ struct mcpwm_oper_t {
|
|||||||
mcpwm_operator_brake_mode_t brake_mode_on_soft_fault; // brake mode on software triggered fault
|
mcpwm_operator_brake_mode_t brake_mode_on_soft_fault; // brake mode on software triggered fault
|
||||||
mcpwm_operator_brake_mode_t brake_mode_on_gpio_fault[SOC_MCPWM_GPIO_FAULTS_PER_GROUP]; // brake mode on GPIO triggered faults
|
mcpwm_operator_brake_mode_t brake_mode_on_gpio_fault[SOC_MCPWM_GPIO_FAULTS_PER_GROUP]; // brake mode on GPIO triggered faults
|
||||||
uint32_t deadtime_resolution_hz; // resolution of deadtime submodule
|
uint32_t deadtime_resolution_hz; // resolution of deadtime submodule
|
||||||
|
mcpwm_gen_t *posedge_delay_owner; // which generator owns the positive edge delay
|
||||||
|
mcpwm_gen_t *negedge_delay_owner; // which generator owns the negative edge delay
|
||||||
mcpwm_brake_event_cb_t on_brake_cbc; // callback function which would be invoked when mcpwm operator goes into trip zone
|
mcpwm_brake_event_cb_t on_brake_cbc; // callback function which would be invoked when mcpwm operator goes into trip zone
|
||||||
mcpwm_brake_event_cb_t on_brake_ost; // callback function which would be invoked when mcpwm operator goes into trip zone
|
mcpwm_brake_event_cb_t on_brake_ost; // callback function which would be invoked when mcpwm operator goes into trip zone
|
||||||
void *user_data; // user data which would be passed to the trip zone callback
|
void *user_data; // user data which would be passed to the trip zone callback
|
||||||
|
@ -591,6 +591,23 @@ static void redfedb_only_set_dead_time(mcpwm_gen_handle_t gena, mcpwm_gen_handle
|
|||||||
TEST_ESP_OK(mcpwm_generator_set_dead_time(genb, genb, &dead_time_config));
|
TEST_ESP_OK(mcpwm_generator_set_dead_time(genb, genb, &dead_time_config));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void invalid_reda_redb_set_dead_time(mcpwm_gen_handle_t gena, mcpwm_gen_handle_t genb)
|
||||||
|
{
|
||||||
|
mcpwm_dead_time_config_t dead_time_config = {
|
||||||
|
.posedge_delay_ticks = 10,
|
||||||
|
};
|
||||||
|
// generator_a adds delay on the posedge
|
||||||
|
TEST_ESP_OK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
|
||||||
|
// generator_b adds delay on the posedge as well, which is not allowed
|
||||||
|
TEST_ESP_ERR(ESP_ERR_INVALID_STATE, mcpwm_generator_set_dead_time(genb, genb, &dead_time_config));
|
||||||
|
// bypass the delay module for generator_a
|
||||||
|
dead_time_config.posedge_delay_ticks = 0;
|
||||||
|
TEST_ESP_OK(mcpwm_generator_set_dead_time(gena, gena, &dead_time_config));
|
||||||
|
// now generator_b can add delay on the posedge
|
||||||
|
dead_time_config.posedge_delay_ticks = 10;
|
||||||
|
TEST_ESP_OK(mcpwm_generator_set_dead_time(genb, genb, &dead_time_config));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_CASE("mcpwm_generator_deadtime_classical_configuration", "[mcpwm]")
|
TEST_CASE("mcpwm_generator_deadtime_classical_configuration", "[mcpwm]")
|
||||||
{
|
{
|
||||||
printf("Active High Complementary\r\n");
|
printf("Active High Complementary\r\n");
|
||||||
@ -613,6 +630,9 @@ TEST_CASE("mcpwm_generator_deadtime_classical_configuration", "[mcpwm]")
|
|||||||
|
|
||||||
printf("Bypass A, RED + FED on B\r\n");
|
printf("Bypass A, RED + FED on B\r\n");
|
||||||
mcpwm_deadtime_test_template(1000000, 500, 350, 350, 0, 2, redfedb_only_set_generator_actions, redfedb_only_set_dead_time);
|
mcpwm_deadtime_test_template(1000000, 500, 350, 350, 0, 2, redfedb_only_set_generator_actions, redfedb_only_set_dead_time);
|
||||||
|
|
||||||
|
printf("Can't apply one delay module to multiple generators\r\n");
|
||||||
|
mcpwm_deadtime_test_template(1000000, 500, 350, 350, 0, 2, redfedb_only_set_generator_actions, invalid_reda_redb_set_dead_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_CASE("mcpwm_duty_empty_full", "[mcpwm]")
|
TEST_CASE("mcpwm_duty_empty_full", "[mcpwm]")
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
{
|
{
|
||||||
"name": "origin",
|
"name": "origin",
|
||||||
"wave": "0...1.....0...",
|
"wave": "0...1.....0...",
|
||||||
"node": "....a.....b..."
|
"node": "....a.e...b..."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pwm_A",
|
"name": "pwm_A",
|
||||||
@ -13,12 +13,13 @@
|
|||||||
{
|
{
|
||||||
"name": "pwm_B",
|
"name": "pwm_B",
|
||||||
"wave": "1...0......1..",
|
"wave": "1...0......1..",
|
||||||
"node": "...........d.."
|
"node": "......f....d.."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"edge": [
|
"edge": [
|
||||||
"a|->c RED",
|
"a|->c RED",
|
||||||
"b|->d FED"
|
"b|->d FED",
|
||||||
|
"e<->f Invert"
|
||||||
],
|
],
|
||||||
"head": {
|
"head": {
|
||||||
"text": "Active High, Complementary"
|
"text": "Active High, Complementary"
|
||||||
|
@ -3,22 +3,24 @@
|
|||||||
{
|
{
|
||||||
"name": "origin",
|
"name": "origin",
|
||||||
"wave": "0...1.....0...",
|
"wave": "0...1.....0...",
|
||||||
"node": "....a.....b..."
|
"node": "....a..ef.b..."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pwm_A",
|
"name": "pwm_A",
|
||||||
"wave": "1....0....1...",
|
"wave": "1....0....1...",
|
||||||
"node": ".....c....."
|
"node": ".....c.g..."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pwm_B",
|
"name": "pwm_B",
|
||||||
"wave": "1...0......1..",
|
"wave": "1...0......1..",
|
||||||
"node": "...........d.."
|
"node": "........h..d.."
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"edge": [
|
"edge": [
|
||||||
"a|->c RED",
|
"a|->c RED",
|
||||||
"b|->d FED"
|
"b|->d FED",
|
||||||
|
"e<->g Invert",
|
||||||
|
"f<->h Invert"
|
||||||
],
|
],
|
||||||
"head": {
|
"head": {
|
||||||
"text": "Active Low"
|
"text": "Active Low"
|
||||||
|
@ -3,12 +3,12 @@
|
|||||||
{
|
{
|
||||||
"name": "origin",
|
"name": "origin",
|
||||||
"wave": "0...1.....0...",
|
"wave": "0...1.....0...",
|
||||||
"node": "....a.....b..."
|
"node": "....a..e..b..."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pwm_A",
|
"name": "pwm_A",
|
||||||
"wave": "1....0....1...",
|
"wave": "1....0....1...",
|
||||||
"node": ".....c....."
|
"node": ".....c.f..."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "pwm_B",
|
"name": "pwm_B",
|
||||||
@ -18,7 +18,8 @@
|
|||||||
],
|
],
|
||||||
"edge": [
|
"edge": [
|
||||||
"a|->c RED",
|
"a|->c RED",
|
||||||
"b|->d FED"
|
"b|->d FED",
|
||||||
|
"e<->f Invert"
|
||||||
],
|
],
|
||||||
"head": {
|
"head": {
|
||||||
"text": "Active Low, Complementary"
|
"text": "Active Low, Complementary"
|
||||||
|
@ -407,13 +407,29 @@ Dead Time
|
|||||||
|
|
||||||
In power electronics, the rectifier and inverter are commonly used. This requires the use of rectifier bridge and 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, but 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 that generated by setting `Generator Actions on Events <#generator-actions-on-events>`__.
|
In power electronics, the rectifier and inverter are commonly used. This requires the use of rectifier bridge and 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, but 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 that generated by setting `Generator Actions on Events <#generator-actions-on-events>`__.
|
||||||
|
|
||||||
The dead-time driver works like a *decorator*, which 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're adding the time delay to the PWM waveform in a "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``.
|
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``.
|
||||||
|
|
||||||
Dead-time specific configuration is listed in the :cpp:type:`mcpwm_dead_time_config_t` structure:
|
Dead-time specific configuration is listed in the :cpp:type:`mcpwm_dead_time_config_t` structure:
|
||||||
|
|
||||||
- :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 to bypass the dead-time module. The resolution of the dead-time tick is the same to the timer that is connected with the operator by :cpp:func:`mcpwm_operator_connect_timer`.
|
- :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 to bypass the dead-time module. The resolution of the dead-time tick is the same to the timer that is connected with the operator by :cpp:func:`mcpwm_operator_connect_timer`.
|
||||||
- :cpp:member:`mcpwm_dead_time_config_t::invert_output`: Whether to invert the signal after applying the dead-time, which can be used to control the delay edge polarity.
|
- :cpp:member:`mcpwm_dead_time_config_t::invert_output`: Whether to invert the signal after applying the dead-time, which can be used to control the delay edge polarity.
|
||||||
|
|
||||||
|
.. warning::
|
||||||
|
|
||||||
|
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**:
|
||||||
|
|
||||||
|
.. code:: c
|
||||||
|
|
||||||
|
mcpwm_dead_time_config_t dt_config = {
|
||||||
|
.posedge_delay_ticks = 10,
|
||||||
|
};
|
||||||
|
// Set posedge delay to generator A
|
||||||
|
mcpwm_generator_set_dead_time(mcpwm_gen_a, mcpwm_gen_a, &dt_config);
|
||||||
|
// NOTE: This is invalid, you can't apply the posedge delay to another generator
|
||||||
|
mcpwm_generator_set_dead_time(mcpwm_gen_b, mcpwm_gen_b, &dt_config);
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
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.
|
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.
|
||||||
|
Loading…
Reference in New Issue
Block a user