diff --git a/components/driver/mcpwm/include/driver/mcpwm_gen.h b/components/driver/mcpwm/include/driver/mcpwm_gen.h index f152ec1129..154e721250 100644 --- a/components/driver/mcpwm/include/driver/mcpwm_gen.h +++ b/components/driver/mcpwm/include/driver/mcpwm_gen.h @@ -59,6 +59,7 @@ esp_err_t mcpwm_del_generator(mcpwm_gen_handle_t gen); * @note The force level will be applied to the generator immediately, regardless any other events that would change the generator's behaviour. * @note If the `hold_on` is true, the force level will retain forever, until user removes the force level by setting the force level to `-1`. * @note If the `hold_on` is false, the force level can be overridden by the next event action. + * @note The force level set by this function can be inverted by GPIO matrix or dead-time module. So the level set here doesn't equal to the final output level. * * @param[in] gen MCPWM generator handle, allocated by `mcpwm_new_generator()` * @param[in] level GPIO level to be applied to MCPWM generator, specially, -1 means to remove the force level diff --git a/components/driver/test_apps/mcpwm/main/test_mcpwm_gen.c b/components/driver/test_apps/mcpwm/main/test_mcpwm_gen.c index 958e41b5cb..668773224a 100644 --- a/components/driver/test_apps/mcpwm/main/test_mcpwm_gen.c +++ b/components/driver/test_apps/mcpwm/main/test_mcpwm_gen.c @@ -77,6 +77,56 @@ TEST_CASE("mcpwm_generator_force_level_hold_on", "[mcpwm]") TEST_ESP_OK(mcpwm_del_operator(oper)); } +// mcpwm_generator_set_force_level acts before the dead time module +// so the value output on the generator is a combined result +TEST_CASE("mcpwm_force_level_and_dead_time", "[mcpwm]") +{ + printf("create operator and generators\r\n"); + mcpwm_oper_handle_t oper = NULL; + mcpwm_operator_config_t operator_config = { + .group_id = 0, + }; + TEST_ESP_OK(mcpwm_new_operator(&operator_config, &oper)); + + mcpwm_gen_handle_t gen_a = NULL; + mcpwm_gen_handle_t gen_b = NULL; + const int gen_a_gpio = 0; + const int gen_b_gpio = 2; + mcpwm_generator_config_t generator_config = { + .gen_gpio_num = gen_a_gpio, + .flags.io_loop_back = true, // loop back for test + }; + TEST_ESP_OK(mcpwm_new_generator(oper, &generator_config, &gen_a)); + generator_config.gen_gpio_num = gen_b_gpio; + generator_config.flags.invert_pwm = true; // Inversion add to the GPIO matrix + TEST_ESP_OK(mcpwm_new_generator(oper, &generator_config, &gen_b)); + + mcpwm_dead_time_config_t dt_config = { + .posedge_delay_ticks = 5, + }; + ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(gen_a, gen_a, &dt_config)); + dt_config = (mcpwm_dead_time_config_t) { + .negedge_delay_ticks = 5, + .flags.invert_output = true, // Inversion applied by the dead time module + }; + ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(gen_b, gen_b, &dt_config)); + + printf("add force level to the generator, hold on"); + for (int i = 0; i < 10; i++) { + TEST_ESP_OK(mcpwm_generator_set_force_level(gen_b, 0, true)); + vTaskDelay(pdMS_TO_TICKS(10)); + TEST_ASSERT_EQUAL(0, gpio_get_level(gen_b_gpio)); + TEST_ESP_OK(mcpwm_generator_set_force_level(gen_b, 1, true)); + vTaskDelay(pdMS_TO_TICKS(10)); + TEST_ASSERT_EQUAL(1, gpio_get_level(gen_b_gpio)); + } + + printf("delete generator and operator\r\n"); + TEST_ESP_OK(mcpwm_del_generator(gen_a)); + TEST_ESP_OK(mcpwm_del_generator(gen_b)); + TEST_ESP_OK(mcpwm_del_operator(oper)); +} + TEST_CASE("mcpwm_generator_force_level_recovery", "[mcpwm]") { printf("create mcpwm timer\r\n"); diff --git a/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/main/mcpwm_bldc_hall_control_example_main.c b/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/main/mcpwm_bldc_hall_control_example_main.c index cb138adb9e..d437c72421 100644 --- a/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/main/mcpwm_bldc_hall_control_example_main.c +++ b/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/main/mcpwm_bldc_hall_control_example_main.c @@ -61,24 +61,26 @@ static void bldc_set_phase_up_vm(mcpwm_gen_handle_t (*gens)[2]) // U+ = PWM, U- = _PWM_ mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_U][BLDC_MCPWM_GEN_INDEX_HIGH], -1, true); mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_U][BLDC_MCPWM_GEN_INDEX_LOW], -1, true); - // V+ = 0, V- = 1 + + // V+ = 0, V- = 1 --[because gen_low is inverted by dead time]--> V+ = 0, V- = 0 mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_HIGH], 0, true); - mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_LOW], 1, true); - // W+ = 0, W- = 0 + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_LOW], 0, true); + + // W+ = 0, W- = 0 --[because gen_low is inverted by dead time]--> W+ = 0, W- = 1 mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_HIGH], 0, true); - mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_LOW], 0, true); + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_LOW], 1, true); } // W+U- static void bldc_set_phase_wp_um(mcpwm_gen_handle_t (*gens)[2]) { - // U+ = 0, U- = 1 + // U+ = 0, U- = 1 --[because gen_low is inverted by dead time]--> U+ = 0, U- = 0 mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_U][BLDC_MCPWM_GEN_INDEX_HIGH], 0, true); - mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_U][BLDC_MCPWM_GEN_INDEX_LOW], 1, true); + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_U][BLDC_MCPWM_GEN_INDEX_LOW], 0, true); - // V+ = 0, V- = 0 + // V+ = 0, V- = 0 --[because gen_low is inverted by dead time]--> V+ = 0, V- = 1 mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_HIGH], 0, true); - mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_LOW], 0, true); + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_LOW], 1, true); // W+ = PWM, W- = _PWM_ mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_HIGH], -1, true); @@ -88,13 +90,13 @@ static void bldc_set_phase_wp_um(mcpwm_gen_handle_t (*gens)[2]) // W+V- static void bldc_set_phase_wp_vm(mcpwm_gen_handle_t (*gens)[2]) { - // U+ = 0, U- = 0 + // U+ = 0, U- = 0 --[because gen_low is inverted by dead time]--> U+ = 0, U- = 1 mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_U][BLDC_MCPWM_GEN_INDEX_HIGH], 0, true); - mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_U][BLDC_MCPWM_GEN_INDEX_LOW], 0, true); + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_U][BLDC_MCPWM_GEN_INDEX_LOW], 1, true); - // V+ = 0, V- = 1 + // V+ = 0, V- = 1 --[because gen_low is inverted by dead time]--> V+ = 0, V- = 0 mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_HIGH], 0, true); - mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_LOW], 1, true); + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_LOW], 0, true); // W+ = PWM, W- = _PWM_ mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_HIGH], -1, true); @@ -104,23 +106,7 @@ static void bldc_set_phase_wp_vm(mcpwm_gen_handle_t (*gens)[2]) // V+U- static void bldc_set_phase_vp_um(mcpwm_gen_handle_t (*gens)[2]) { - // U+ = 0, U- = 1 - mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_U][BLDC_MCPWM_GEN_INDEX_HIGH], 0, true); - mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_U][BLDC_MCPWM_GEN_INDEX_LOW], 1, true); - - // V+ = PWM, V- = _PWM_ - mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_HIGH], -1, true); - mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_LOW], -1, true); - - // W+ = 0, W- = 0 - mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_HIGH], 0, true); - mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_LOW], 0, true); -} - -// V+W- -static void bldc_set_phase_vp_wm(mcpwm_gen_handle_t (*gens)[2]) -{ - // U+ = 0, U- = 0 + // U+ = 0, U- = 1 --[because gen_low is inverted by dead time]--> U+ = 0, U- = 0 mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_U][BLDC_MCPWM_GEN_INDEX_HIGH], 0, true); mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_U][BLDC_MCPWM_GEN_INDEX_LOW], 0, true); @@ -128,11 +114,27 @@ static void bldc_set_phase_vp_wm(mcpwm_gen_handle_t (*gens)[2]) mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_HIGH], -1, true); mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_LOW], -1, true); - // W+ = 0, W- = 1 + // W+ = 0, W- = 0 --[because gen_low is inverted by dead time]--> W+ = 0, W- = 1 mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_HIGH], 0, true); mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_LOW], 1, true); } +// V+W- +static void bldc_set_phase_vp_wm(mcpwm_gen_handle_t (*gens)[2]) +{ + // U+ = 0, U- = 0 --[because gen_low is inverted by dead time]--> U+ = 0, U- = 1 + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_U][BLDC_MCPWM_GEN_INDEX_HIGH], 0, true); + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_U][BLDC_MCPWM_GEN_INDEX_LOW], 1, true); + + // V+ = PWM, V- = _PWM_ + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_HIGH], -1, true); + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_LOW], -1, true); + + // W+ = 0, W- = 1 --[because gen_low is inverted by dead time]--> W+ = 0, W- = 0 + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_HIGH], 0, true); + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_LOW], 0, true); +} + // U+W- / A+C- static void bldc_set_phase_up_wm(mcpwm_gen_handle_t (*gens)[2]) { @@ -140,13 +142,13 @@ static void bldc_set_phase_up_wm(mcpwm_gen_handle_t (*gens)[2]) mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_U][BLDC_MCPWM_GEN_INDEX_HIGH], -1, true); mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_U][BLDC_MCPWM_GEN_INDEX_LOW], -1, true); - // V+ = 0, V- = 0 + // V+ = 0, V- = 0 --[because gen_low is inverted by dead time]--> V+ = 0, V- = 1 mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_HIGH], 0, true); - mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_LOW], 0, true); + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_V][BLDC_MCPWM_GEN_INDEX_LOW], 1, true); - // W+ = 0, W- = 1 + // W+ = 0, W- = 1 --[because gen_low is inverted by dead time]--> W+ = 0, W- = 0 mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_HIGH], 0, true); - mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_LOW], 1, true); + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_LOW], 0, true); } static const bldc_hall_phase_action_t s_hall_actions[] = { @@ -255,6 +257,8 @@ void app_main(void) } ESP_LOGI(TAG, "Set generator actions"); + // gen_high and gen_low output the same waveform after the following configuration + // we will use the dead time module to add edge delay, also make gen_high and gen_low complementary for (int i = 0; i < 3; i++) { ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(generators[i][BLDC_MCPWM_GEN_INDEX_HIGH], MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH))); @@ -264,6 +268,15 @@ void app_main(void) MCPWM_GEN_BRAKE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_OPER_BRAKE_MODE_CBC, MCPWM_GEN_ACTION_LOW))); ESP_ERROR_CHECK(mcpwm_generator_set_action_on_brake_event(generators[i][BLDC_MCPWM_GEN_INDEX_HIGH], MCPWM_GEN_BRAKE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_OPER_BRAKE_MODE_CBC, MCPWM_GEN_ACTION_LOW))); + + ESP_ERROR_CHECK(mcpwm_generator_set_action_on_timer_event(generators[i][BLDC_MCPWM_GEN_INDEX_LOW], + MCPWM_GEN_TIMER_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_TIMER_EVENT_EMPTY, MCPWM_GEN_ACTION_HIGH))); + ESP_ERROR_CHECK(mcpwm_generator_set_action_on_compare_event(generators[i][BLDC_MCPWM_GEN_INDEX_LOW], + MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, comparators[i], MCPWM_GEN_ACTION_LOW))); + ESP_ERROR_CHECK(mcpwm_generator_set_action_on_brake_event(generators[i][BLDC_MCPWM_GEN_INDEX_LOW], + MCPWM_GEN_BRAKE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_OPER_BRAKE_MODE_CBC, MCPWM_GEN_ACTION_LOW))); + ESP_ERROR_CHECK(mcpwm_generator_set_action_on_brake_event(generators[i][BLDC_MCPWM_GEN_INDEX_LOW], + MCPWM_GEN_BRAKE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, MCPWM_OPER_BRAKE_MODE_CBC, MCPWM_GEN_ACTION_LOW))); } ESP_LOGI(TAG, "Setup deadtime"); @@ -278,14 +291,14 @@ void app_main(void) .flags.invert_output = true, }; for (int i = 0; i < 3; i++) { - ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(generators[i][BLDC_MCPWM_GEN_INDEX_HIGH], generators[i][BLDC_MCPWM_GEN_INDEX_LOW], &dt_config)); + ESP_ERROR_CHECK(mcpwm_generator_set_dead_time(generators[i][BLDC_MCPWM_GEN_INDEX_LOW], generators[i][BLDC_MCPWM_GEN_INDEX_LOW], &dt_config)); } ESP_LOGI(TAG, "Turn off all the gates"); for (int i = 0; i < 3; i++) { - for (int j = 0; j < 2; j++) { - ESP_ERROR_CHECK(mcpwm_generator_set_force_level(generators[i][j], 0, true)); - } + ESP_ERROR_CHECK(mcpwm_generator_set_force_level(generators[i][BLDC_MCPWM_GEN_INDEX_HIGH], 0, true)); + // because gen_low is inverted by dead time module, so we need to set force level to 1 + ESP_ERROR_CHECK(mcpwm_generator_set_force_level(generators[i][BLDC_MCPWM_GEN_INDEX_LOW], 1, true)); } ESP_LOGI(TAG, "Create Hall sensor capture channels");