From b77446b5c834a2447a3a5012aa900a50d3a15685 Mon Sep 17 00:00:00 2001 From: morris Date: Sat, 28 May 2022 17:04:38 +0800 Subject: [PATCH] example: update bldc example with new driver API --- .../mcpwm/mcpwm_bldc_hall_control/README.md | 111 +++-- .../mcpwm_bldc_hall_control_example_main.c | 436 +++++++++++------- .../pytest_bldc_hall_control.py | 25 + 3 files changed, 360 insertions(+), 212 deletions(-) create mode 100644 examples/peripherals/mcpwm/mcpwm_bldc_hall_control/pytest_bldc_hall_control.py diff --git a/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/README.md b/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/README.md index 5fa70fe40d..9e53e1c2ea 100644 --- a/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/README.md +++ b/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/README.md @@ -1,51 +1,54 @@ | Supported Targets | ESP32 | ESP32-S3 | | ----------------- | ----- | -------- | -# MCPWM BLDC Hall motor control Example +# MCPWM BLDC Motor Control with HALL Sensor Example (See the README.md file in the upper level 'examples' directory for more information about examples.) -This example will illustrate how to use MCPWM driver to control BLDC motor with hall sensor feedback. In the example, a timer is running at the background to update the motor speed periodically. - -With the hardware fault detection feature of MCPWM, the example will shut down the MOSFETs when over current happens. +The MCPWM peripheral can generate three pairs of complementary PWMs by the internal dead time submodule, which is suitable for a BLDC motor application. This example demonstrates how to use the MCPWM peripheral to control a BLDC motor in a six-step commutation scheme. +We will change the on/off state of the six MOSFETs in a predefined order when the Hall sensor detects a change of the motor phase, so that the motor can spin in a predefined direction. ## How to Use Example ### Hardware Required -1. The BLDC motor used in this example has a hall sensor capture sequence of `6-->4-->5-->1-->3-->2-->6-->4-->` and so on. -2. A three-phase gate driver, this example uses [IR2136](https://www.infineon.com/cms/en/product/power/gate-driver-ics/ir2136s/). -3. Six N-MOSFETs, this example uses [IRF540NS](https://www.infineon.com/cms/en/product/power/mosfet/12v-300v-n-channel-power-mosfet/irf540ns/). -4. A development board with any Espressif SoC which features MCPWM peripheral (e.g., ESP32-DevKitC, ESP-WROVER-KIT, etc.) -5. A USB cable for Power supply and programming. +1. A ESP board with MCPWM peripheral supported (e.g. ESP32-S3-Motor-DevKit) +2. A BLDC motor whose commutation table is `6-->4-->5-->1-->3-->2-->6` +3. A three-phase gate driver, for example, the [DRV8302](https://www.ti.com.cn/product/zh-cn/DRV8302) +4. Six N-MOSFETs, for example, the [IRF540NS](https://www.infineon.com/cms/en/product/power/mosfet/12v-300v-n-channel-power-mosfet/irf540ns/) +5. A USB cable for Power supply and programming Connection : ``` - ┌─────────────────────────────────────────────┐ - │ │ - │ ┌───────────────────────────┐ │ - │ │ │ │ -┌─────────┴─────────┴───────────┐ ┌────────┴───────┴──────────┐ -│ GPIO19 GPIO18 │ │ EN FAULT │ -│ GPIO21├──────┤PWM_UH │ ┌────────────┐ -│ GPIO22├──────┤PWM_UL U├────────┤ │ -│ │ │ │ │ │ -│ GPIO23├──────┤PWM_VH V├────────┤ BLDC │ -│ ESP Board GPIO25├──────┤PWM_VL 3-Phase Bridge │ │ │ -│ │ │ + W├────────┤ │ -│ GPIO26├──────┤PWM_WH MOSFET │ └─┬───┬───┬──┘ -│ GPIO27├──────┤PWM_WL │ │ │ │ -│ GPIO5 GPIO4 GPIO2 │ │ │ │ │ │ -└─────┬──────┬──────┬───────────┘ └───────────────────────────┘ │ │ │ - │ │ │ Hall U │ │ │ - │ │ └─────────────────────────────────────────────────────────┘ │ │ - │ │ Hall V │ │ - │ └────────────────────────────────────────────────────────────────────┘ │ - │ Hall W │ - └───────────────────────────────────────────────────────────────────────────────┘ + +---------------------------------------------------------------------------------+ + | | + | +---------------------------------------------+ | VM + | | | | ^ + | | +---------------------------+ | | | + | | | | | | | ++-------------+-----------------------------+---------+-----------+ +--------+-------+-----+---++ +| GND BLDC_DRV_FAULT_GPIO BLDC_DRV_EN_GPIO | | EN FAULT GND | +| BLDC_PWM_UH_GPIO +------+PWM_UH | +------------+ +| BLDC_PWM_UL_GPIO +------+PWM_UL U+--------+ | +| | | | | | +| ESP Board BLDC_PWM_VH_GPIO +------+PWM_VH V+--------+ BLDC | +| BLDC_PWM_VL_GPIO +------+PWM_VL 3-Phase Bridge | | | +| | | + W+--------+ | +| BLDC_PWM_WH_GPIO +------+PWM_WH MOSFET | +-+---+---+--+ +| BLDC_PWM_WL_GPIO +------+PWM_WL | | | | +| HALL_CAP_W_GPIO HALL_CAP_V_GPIO HALL_CAP_U_GPIO | | | | | | ++-----------+------------------+------------------+---------------+ +---------------------------+ | | | + | | | Hall U | | | + | | +-------------------------------------------------------------+ | | + | | Hall V | | + | +------------------------------------------------------------------------------------+ | + | Hall W | + +-----------------------------------------------------------------------------------------------------------+ ``` +You can change the GPIO number in the [example code](main/mcpwm_bldc_hall_control_example_main.c) according to your board. You can define the spin direction in the code as well by the `BLDC_SPIN_DIRECTION_CCW` macro. + ### Build and Flash Run `idf.py -p PORT flash monitor` to build, flash and monitor the project. @@ -62,20 +65,42 @@ Run the example, you will see the following output log: ``` ... I (0) cpu_start: Starting scheduler on APP CPU. -I (327) example: Disable gate driver -I (327) gpio: GPIO[18]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 -I (337) example: Setup PWM and Hall GPIO (pull up internally) -I (347) example: Initialize PWM (default to turn off all MOSFET) -I (357) example: Initialize over current fault action -I (357) example: Initialize Hall sensor capture -I (367) example: Please turn on the motor power -I (5367) example: Enable gate driver -I (5367) example: Changing speed at background +I (307) example: Disable MOSFET gate +I (307) gpio: GPIO[46]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0 +I (317) example: Create MCPWM timer +I (317) example: Create MCPWM operator +I (327) example: Connect operators to the same timer +I (327) example: Create comparators +I (337) example: Create over current fault detector +I (337) gpio: GPIO[10]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (347) example: Set brake mode on the fault event +I (357) example: Create PWM generators +I (357) gpio: GPIO[47]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (367) gpio: GPIO[21]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (377) gpio: GPIO[14]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (387) gpio: GPIO[13]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (397) gpio: GPIO[12]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (407) gpio: GPIO[11]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (417) example: Set generator actions +I (417) example: Setup deadtime +I (427) example: Turn off all the gates by default +I (427) example: Create Hall sensor capture channels +I (437) gpio: GPIO[4]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (447) gpio: GPIO[5]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (457) gpio: GPIO[6]| InputEn: 1| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0 +I (457) example: Register event callback for capture channels +I (467) example: Start a timer to adjust motor speed periodically +I (477) example: Enable MOSFET gate +I (477) example: Start the MCPWM timer ... ``` -## Dive into the example +The BLDC motor will update the speed periodically. -1. How to change the rotation direction? +## Troubleshooting - The rotation direction is controlled by how the hall sensor value is parsed. If you pass `false` to `bldc_get_hall_sensor_value`, the BLDC should rotate in clock wise. Likewise, passing `true` to that function will make tha BLDC rotate in counter clock wise. +* Make sure your ESP board and H-bridge module have been connected to the same GND panel. +* Check the fault signal polarity, otherwise the motor will not spin if the MCPWM detector treats the normal level as a fault one. +* Don't use the PC USB as the power source of the BLDC motor (see the `VM` in the above connection diagram), it might damage your UAB port. + +For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you soon. 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 f4a0f62bbb..3fcb12d21e 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 @@ -8,47 +8,46 @@ #include #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include "driver/mcpwm.h" -#include "driver/gpio.h" -#include "esp_timer.h" #include "esp_attr.h" #include "esp_log.h" +#include "esp_timer.h" +#include "driver/mcpwm_prelude.h" +#include "driver/gpio.h" -#define PWM_DEFAULT_FREQ 14400 -#define PWM_MIN_DUTY 40.0 -#define PWM_MAX_DUTY 80.0 -#define PWM_DUTY_STEP 5.0 -#define BLDC_MCPWM_GROUP 0 -#define BLDC_MCPWM_TIMER_U 0 -#define BLDC_MCPWM_TIMER_V 1 -#define BLDC_MCPWM_TIMER_W 2 -#define BLDC_MCPWM_GEN_HIGH MCPWM_GEN_A -#define BLDC_MCPWM_GEN_LOW MCPWM_GEN_B +#define BLDC_MCPWM_TIMER_RESOLUTION_HZ 10000000 // 10MHz, 1 tick = 0.1us +#define BLDC_MCPWM_PERIOD 500 // 50us, 20KHz +#define BLDC_SPIN_DIRECTION_CCW false // define the spin direction +#define BLDC_SPEED_UPDATE_PERIOD_US 200000 // 200ms +#define BLDC_DRV_EN_GPIO 46 +#define BLDC_DRV_FAULT_GPIO 10 +#define BLDC_PWM_UH_GPIO 47 +#define BLDC_PWM_UL_GPIO 21 +#define BLDC_PWM_VH_GPIO 14 +#define BLDC_PWM_VL_GPIO 13 +#define BLDC_PWM_WH_GPIO 12 +#define BLDC_PWM_WL_GPIO 11 +#define HALL_CAP_U_GPIO 4 +#define HALL_CAP_V_GPIO 5 +#define HALL_CAP_W_GPIO 6 -#define BLDC_DRV_EN_GPIO 18 -#define BLDC_DRV_FAULT_GPIO 19 -#define BLDC_DRV_OVER_CURRENT_FAULT MCPWM_SELECT_F0 - -#define BLDC_PWM_UH_GPIO 21 -#define BLDC_PWM_UL_GPIO 22 -#define BLDC_PWM_VH_GPIO 23 -#define BLDC_PWM_VL_GPIO 25 -#define BLDC_PWM_WH_GPIO 26 -#define BLDC_PWM_WL_GPIO 27 -#define HALL_CAP_U_GPIO 2 -#define HALL_CAP_V_GPIO 4 -#define HALL_CAP_W_GPIO 5 +#define BLDC_MCPWM_OP_INDEX_U 0 +#define BLDC_MCPWM_OP_INDEX_V 1 +#define BLDC_MCPWM_OP_INDEX_W 2 +#define BLDC_MCPWM_GEN_INDEX_HIGH 0 +#define BLDC_MCPWM_GEN_INDEX_LOW 1 static const char *TAG = "example"; +typedef void (*bldc_hall_phase_action_t)(mcpwm_gen_handle_t (*gens)[2]); + static inline uint32_t bldc_get_hall_sensor_value(bool ccw) { uint32_t hall_val = gpio_get_level(HALL_CAP_U_GPIO) * 4 + gpio_get_level(HALL_CAP_V_GPIO) * 2 + gpio_get_level(HALL_CAP_W_GPIO) * 1; return ccw ? hall_val ^ (0x07) : hall_val; } -static bool IRAM_ATTR bldc_hall_updated(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_channel, const cap_event_data_t *edata, void *user_data) +static bool IRAM_ATTR bldc_hall_updated(mcpwm_cap_channel_handle_t cap_channel, const mcpwm_capture_event_data_t *edata, void *user_data) { TaskHandle_t task_to_notify = (TaskHandle_t)user_data; BaseType_t high_task_wakeup = pdFALSE; @@ -56,101 +55,99 @@ static bool IRAM_ATTR bldc_hall_updated(mcpwm_unit_t mcpwm, mcpwm_capture_channe return high_task_wakeup == pdTRUE; } -static void update_bldc_speed(void *arg) +// U+V- +static void bldc_set_phase_up_vm(mcpwm_gen_handle_t (*gens)[2]) { - static float duty = PWM_MIN_DUTY; - static float duty_step = PWM_DUTY_STEP; - duty += duty_step; - if (duty > PWM_MAX_DUTY || duty < PWM_MIN_DUTY) { - duty_step *= -1; - } - mcpwm_set_duty(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U, BLDC_MCPWM_GEN_HIGH, duty); - mcpwm_set_duty(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U, BLDC_MCPWM_GEN_LOW, duty); - mcpwm_set_duty(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V, BLDC_MCPWM_GEN_HIGH, duty); - mcpwm_set_duty(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V, BLDC_MCPWM_GEN_LOW, duty); - mcpwm_set_duty(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W, BLDC_MCPWM_GEN_HIGH, duty); - mcpwm_set_duty(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W, BLDC_MCPWM_GEN_LOW, duty); + // 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 + 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_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+V- / A+B- -static void bldc_set_phase_up_vm(void) +// W+U- +static void bldc_set_phase_wp_um(mcpwm_gen_handle_t (*gens)[2]) { - mcpwm_set_duty_type(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U, BLDC_MCPWM_GEN_HIGH, MCPWM_DUTY_MODE_0); // U+ = PWM - mcpwm_deadtime_enable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U, MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, 3, 3); // U- = _PWM_ - mcpwm_deadtime_disable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V); - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V, BLDC_MCPWM_GEN_HIGH); // V+ = 0 - mcpwm_set_signal_high(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V, BLDC_MCPWM_GEN_LOW); // V- = 1 - mcpwm_deadtime_disable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W); - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W, BLDC_MCPWM_GEN_HIGH); // W+ = 0 - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W, BLDC_MCPWM_GEN_LOW); // W- = 0 + // 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+ = 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], 0, true); + + // W+ = PWM, W- = _PWM_ + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_HIGH], -1, true); + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_LOW], -1, true); } -// W+U- / C+A- -static void bldc_set_phase_wp_um(void) +// W+V- +static void bldc_set_phase_wp_vm(mcpwm_gen_handle_t (*gens)[2]) { - mcpwm_deadtime_disable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U); - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U, BLDC_MCPWM_GEN_HIGH); // U+ = 0 - mcpwm_set_signal_high(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U, BLDC_MCPWM_GEN_LOW); // U- = 1 - mcpwm_deadtime_disable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V); - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V, BLDC_MCPWM_GEN_HIGH); // V+ = 0 - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V, BLDC_MCPWM_GEN_LOW); // V- = 0 - mcpwm_set_duty_type(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W, BLDC_MCPWM_GEN_HIGH, MCPWM_DUTY_MODE_0); // W+ = PWM - mcpwm_deadtime_enable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W, MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, 3, 3); // W- = _PWM_ + // 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); + + // 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], 1, true); + + // W+ = PWM, W- = _PWM_ + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_HIGH], -1, true); + mcpwm_generator_set_force_level(gens[BLDC_MCPWM_OP_INDEX_W][BLDC_MCPWM_GEN_INDEX_LOW], -1, true); } -// W+V- / C+B- -static void bldc_set_phase_wp_vm(void) +// V+U- +static void bldc_set_phase_vp_um(mcpwm_gen_handle_t (*gens)[2]) { - mcpwm_deadtime_disable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U); - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U, BLDC_MCPWM_GEN_HIGH); // U+ = 0 - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U, BLDC_MCPWM_GEN_LOW); // U- = 0 - mcpwm_deadtime_disable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V); - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V, BLDC_MCPWM_GEN_HIGH); // V+ = 0 - mcpwm_set_signal_high(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V, BLDC_MCPWM_GEN_LOW); // V- = 1 - mcpwm_set_duty_type(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W, BLDC_MCPWM_GEN_HIGH, MCPWM_DUTY_MODE_0); // W+ = PWM - mcpwm_deadtime_enable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W, MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, 3, 3); // W- = _PWM_ + // 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+U- / B+A- -static void bldc_set_phase_vp_um(void) +// V+W- +static void bldc_set_phase_vp_wm(mcpwm_gen_handle_t (*gens)[2]) { - mcpwm_deadtime_disable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U); - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U, BLDC_MCPWM_GEN_HIGH); // U+ = 0 - mcpwm_set_signal_high(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U, BLDC_MCPWM_GEN_LOW); // U- = 1 - mcpwm_set_duty_type(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V, BLDC_MCPWM_GEN_HIGH, MCPWM_DUTY_MODE_0); // V+ = PWM - mcpwm_deadtime_enable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V, MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, 3, 3); // V- = _PWM_ - mcpwm_deadtime_disable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W); - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W, BLDC_MCPWM_GEN_HIGH); // W+ = 0 - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W, BLDC_MCPWM_GEN_LOW); // W- = 0 -} + // 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); -// V+W- / B+C- -static void bldc_set_phase_vp_wm(void) -{ - mcpwm_deadtime_disable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U); - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U, BLDC_MCPWM_GEN_HIGH); // U+ = 0 - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U, BLDC_MCPWM_GEN_LOW); // U- = 0 - mcpwm_set_duty_type(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V, BLDC_MCPWM_GEN_HIGH, MCPWM_DUTY_MODE_0); // V+ = PWM - mcpwm_deadtime_enable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V, MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, 3, 3); // V- = _PWM_ - mcpwm_deadtime_disable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W); - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W, BLDC_MCPWM_GEN_HIGH); // W+ = 0 - mcpwm_set_signal_high(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W, BLDC_MCPWM_GEN_LOW); // W- = 1 + // 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 + 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); } // U+W- / A+C- -static void bldc_set_phase_up_wm(void) +static void bldc_set_phase_up_wm(mcpwm_gen_handle_t (*gens)[2]) { - mcpwm_set_duty_type(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U, BLDC_MCPWM_GEN_HIGH, MCPWM_DUTY_MODE_0); // U+ = PWM - mcpwm_deadtime_enable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U, MCPWM_ACTIVE_HIGH_COMPLIMENT_MODE, 3, 3); // U- = _PWM_ - mcpwm_deadtime_disable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V); - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V, BLDC_MCPWM_GEN_HIGH); // V+ = 0 - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V, BLDC_MCPWM_GEN_LOW); // V- = 0 - mcpwm_deadtime_disable(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W); - mcpwm_set_signal_low(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W, BLDC_MCPWM_GEN_HIGH); // W+ = 0 - mcpwm_set_signal_high(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W, BLDC_MCPWM_GEN_LOW); // W- = 1 -} + // 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); -typedef void (*bldc_hall_phase_action_t)(void); + // 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], 0, true); + + // 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); +} static const bldc_hall_phase_action_t s_hall_actions[] = { [2] = bldc_set_phase_up_vm, @@ -161,90 +158,191 @@ static const bldc_hall_phase_action_t s_hall_actions[] = { [3] = bldc_set_phase_up_wm, }; +static void update_motor_speed_callback(void *arg) +{ + static int step = 20; + static int cur_speed = 0; + if ((cur_speed + step) > 300 || (cur_speed + step) < 0) { + step *= -1; + } + cur_speed += step; + + mcpwm_cmpr_handle_t *cmprs = (mcpwm_cmpr_handle_t *)arg; + for (int i = 0; i < 3; i++) { + ESP_ERROR_CHECK(mcpwm_comparator_set_compare_value(cmprs[i], cur_speed)); + } +} + void app_main(void) { - uint32_t hall_sensor_value = 0; - TaskHandle_t cur_task = xTaskGetCurrentTaskHandle(); - - ESP_LOGI(TAG, "Disable gate driver"); + ESP_LOGI(TAG, "Disable MOSFET gate"); gpio_config_t drv_en_config = { .mode = GPIO_MODE_OUTPUT, - .pin_bit_mask = 1 << BLDC_DRV_EN_GPIO, + .pin_bit_mask = 1ULL << BLDC_DRV_EN_GPIO, }; ESP_ERROR_CHECK(gpio_config(&drv_en_config)); gpio_set_level(BLDC_DRV_EN_GPIO, 0); - ESP_LOGI(TAG, "Setup PWM and Hall GPIO (pull up internally)"); - mcpwm_pin_config_t mcpwm_gpio_config = { - .mcpwm0a_out_num = BLDC_PWM_UH_GPIO, - .mcpwm0b_out_num = BLDC_PWM_UL_GPIO, - .mcpwm1a_out_num = BLDC_PWM_VH_GPIO, - .mcpwm1b_out_num = BLDC_PWM_VL_GPIO, - .mcpwm2a_out_num = BLDC_PWM_WH_GPIO, - .mcpwm2b_out_num = BLDC_PWM_WL_GPIO, - .mcpwm_cap0_in_num = HALL_CAP_U_GPIO, - .mcpwm_cap1_in_num = HALL_CAP_V_GPIO, - .mcpwm_cap2_in_num = HALL_CAP_W_GPIO, - .mcpwm_sync0_in_num = -1, //Not used - .mcpwm_sync1_in_num = -1, //Not used - .mcpwm_sync2_in_num = -1, //Not used - .mcpwm_fault0_in_num = BLDC_DRV_FAULT_GPIO, - .mcpwm_fault1_in_num = -1, //Not used - .mcpwm_fault2_in_num = -1 //Not used + ESP_LOGI(TAG, "Create MCPWM timer"); + mcpwm_timer_handle_t timer = NULL; + mcpwm_timer_config_t timer_config = { + .group_id = 0, + .clk_src = MCPWM_TIMER_CLK_SRC_DEFAULT, + .resolution_hz = BLDC_MCPWM_TIMER_RESOLUTION_HZ, + .count_mode = MCPWM_TIMER_COUNT_MODE_UP, + .period_ticks = BLDC_MCPWM_PERIOD, }; - ESP_ERROR_CHECK(mcpwm_set_pin(BLDC_MCPWM_GROUP, &mcpwm_gpio_config)); - // In case there's no pull-up resister for hall sensor on board - gpio_pullup_en(HALL_CAP_U_GPIO); - gpio_pullup_en(HALL_CAP_V_GPIO); - gpio_pullup_en(HALL_CAP_W_GPIO); - gpio_pullup_en(BLDC_DRV_FAULT_GPIO); + ESP_ERROR_CHECK(mcpwm_new_timer(&timer_config, &timer)); - ESP_LOGI(TAG, "Initialize PWM (default to turn off all MOSFET)"); - mcpwm_config_t pwm_config = { - .frequency = PWM_DEFAULT_FREQ, - .cmpr_a = PWM_MIN_DUTY, - .cmpr_b = PWM_MIN_DUTY, - .counter_mode = MCPWM_UP_COUNTER, - .duty_mode = MCPWM_HAL_GENERATOR_MODE_FORCE_LOW, + ESP_LOGI(TAG, "Create MCPWM operator"); + mcpwm_oper_handle_t operators[3]; + mcpwm_operator_config_t operator_config = { + .group_id = 0, }; - ESP_ERROR_CHECK(mcpwm_init(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U, &pwm_config)); - ESP_ERROR_CHECK(mcpwm_init(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V, &pwm_config)); - ESP_ERROR_CHECK(mcpwm_init(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W, &pwm_config)); + for (int i = 0; i < 3; i++) { + ESP_ERROR_CHECK(mcpwm_new_operator(&operator_config, &operators[i])); + } - ESP_LOGI(TAG, "Initialize over current fault action"); - ESP_ERROR_CHECK(mcpwm_fault_init(BLDC_MCPWM_GROUP, MCPWM_LOW_LEVEL_TGR, BLDC_DRV_OVER_CURRENT_FAULT)); - ESP_ERROR_CHECK(mcpwm_fault_set_cyc_mode(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_U, BLDC_DRV_OVER_CURRENT_FAULT, MCPWM_ACTION_FORCE_LOW, MCPWM_ACTION_FORCE_LOW)); - ESP_ERROR_CHECK(mcpwm_fault_set_cyc_mode(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_V, BLDC_DRV_OVER_CURRENT_FAULT, MCPWM_ACTION_FORCE_LOW, MCPWM_ACTION_FORCE_LOW)); - ESP_ERROR_CHECK(mcpwm_fault_set_cyc_mode(BLDC_MCPWM_GROUP, BLDC_MCPWM_TIMER_W, BLDC_DRV_OVER_CURRENT_FAULT, MCPWM_ACTION_FORCE_LOW, MCPWM_ACTION_FORCE_LOW)); + ESP_LOGI(TAG, "Connect operators to the same timer"); + for (int i = 0; i < 3; i++) { + ESP_ERROR_CHECK(mcpwm_operator_connect_timer(operators[i], timer)); + } - ESP_LOGI(TAG, "Initialize Hall sensor capture"); - mcpwm_capture_config_t cap_config = { - .cap_edge = MCPWM_BOTH_EDGE, - .cap_prescale = 1, - .capture_cb = bldc_hall_updated, - .user_data = cur_task, + ESP_LOGI(TAG, "Create comparators"); + mcpwm_cmpr_handle_t comparators[3]; + mcpwm_comparator_config_t compare_config = { + .flags.update_cmp_on_tez = true, }; - ESP_ERROR_CHECK(mcpwm_capture_enable_channel(BLDC_MCPWM_GROUP, 0, &cap_config)); - ESP_ERROR_CHECK(mcpwm_capture_enable_channel(BLDC_MCPWM_GROUP, 1, &cap_config)); - ESP_ERROR_CHECK(mcpwm_capture_enable_channel(BLDC_MCPWM_GROUP, 2, &cap_config)); - ESP_LOGI(TAG, "Please turn on the motor power"); - vTaskDelay(pdMS_TO_TICKS(5000)); - ESP_LOGI(TAG, "Enable gate driver"); + for (int i = 0; i < 3; i++) { + ESP_ERROR_CHECK(mcpwm_new_comparator(operators[i], &compare_config, &comparators[i])); + // set compare value to 0, we will adjust the speed in a period timer callback + ESP_ERROR_CHECK(mcpwm_comparator_set_compare_value(comparators[i], 0)); + } + + ESP_LOGI(TAG, "Create over current fault detector"); + mcpwm_fault_handle_t over_cur_fault = NULL; + mcpwm_gpio_fault_config_t gpio_fault_config = { + .gpio_num = BLDC_DRV_FAULT_GPIO, + .group_id = 0, + .flags.active_level = 0, // low level means fault, refer to DRV8302 datasheet + .flags.pull_up = true, // internally pull up + }; + ESP_ERROR_CHECK(mcpwm_new_gpio_fault(&gpio_fault_config, &over_cur_fault)); + + ESP_LOGI(TAG, "Set brake mode on the fault event"); + mcpwm_brake_config_t brake_config = { + .brake_mode = MCPWM_OPER_BRAKE_MODE_CBC, + .fault = over_cur_fault, + .flags.cbc_recover_on_tez = true, + }; + for (int i = 0; i < 3; i++) { + ESP_ERROR_CHECK(mcpwm_operator_set_brake_on_fault(operators[i], &brake_config)); + } + + ESP_LOGI(TAG, "Create PWM generators"); + mcpwm_gen_handle_t generators[3][2] = {}; + mcpwm_generator_config_t gen_config = {}; + const int gen_gpios[3][2] = { + {BLDC_PWM_UH_GPIO, BLDC_PWM_UL_GPIO}, + {BLDC_PWM_VH_GPIO, BLDC_PWM_VL_GPIO}, + {BLDC_PWM_WH_GPIO, BLDC_PWM_WL_GPIO}, + }; + for (int i = 0; i < 3; i++) { + for (int j = 0; j < 2; j++) { + gen_config.gen_gpio_num = gen_gpios[i][j]; + ESP_ERROR_CHECK(mcpwm_new_generator(operators[i], &gen_config, &generators[i][j])); + } + } + + ESP_LOGI(TAG, "Set generator actions"); + for (int i = 0; i < 3; i++) { + ESP_ERROR_CHECK(mcpwm_generator_set_actions_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), + MCPWM_GEN_TIMER_EVENT_ACTION_END())); + ESP_ERROR_CHECK(mcpwm_generator_set_actions_on_compare_event(generators[i][BLDC_MCPWM_GEN_INDEX_HIGH], + MCPWM_GEN_COMPARE_EVENT_ACTION(MCPWM_TIMER_DIRECTION_UP, comparators[i], MCPWM_GEN_ACTION_LOW), + MCPWM_GEN_COMPARE_EVENT_ACTION_END())); + ESP_ERROR_CHECK(mcpwm_generator_set_actions_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), + MCPWM_GEN_BRAKE_EVENT_ACTION_END())); + ESP_ERROR_CHECK(mcpwm_generator_set_actions_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), + MCPWM_GEN_BRAKE_EVENT_ACTION_END())); + } + + ESP_LOGI(TAG, "Setup deadtime"); + mcpwm_dead_time_config_t dt_config = { + .posedge_delay_ticks = 5, + }; + 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_HIGH], &dt_config)); + } + dt_config = (mcpwm_dead_time_config_t) { + .negedge_delay_ticks = 5, + .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_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_LOGI(TAG, "Create Hall sensor capture channels"); + mcpwm_cap_timer_handle_t cap_timer = NULL; + mcpwm_capture_timer_config_t cap_timer_config = { + .group_id = 0, + .clk_src = MCPWM_CAPTURE_CLK_SRC_DEFAULT, + }; + ESP_ERROR_CHECK(mcpwm_new_capture_timer(&cap_timer_config, &cap_timer)); + mcpwm_cap_channel_handle_t cap_channels[3]; + mcpwm_capture_channel_config_t cap_channel_config = { + .prescale = 1, + .flags.pull_up = true, + .flags.neg_edge = true, + .flags.pos_edge = true, + }; + const int cap_chan_gpios[3] = {HALL_CAP_U_GPIO, HALL_CAP_V_GPIO, HALL_CAP_W_GPIO}; + for (int i = 0; i < 3; i++) { + cap_channel_config.gpio_num = cap_chan_gpios[i]; + ESP_ERROR_CHECK(mcpwm_new_capture_channel(cap_timer, &cap_channel_config, &cap_channels[i])); + } + + ESP_LOGI(TAG, "Register event callback for capture channels"); + TaskHandle_t task_to_notify = xTaskGetCurrentTaskHandle(); + for (int i = 0; i < 3; i++) { + mcpwm_capture_event_callbacks_t cbs = { + .on_cap = bldc_hall_updated, + }; + ESP_ERROR_CHECK(mcpwm_capture_channel_register_event_callbacks(cap_channels[i], &cbs, task_to_notify)); + } + + ESP_LOGI(TAG, "Start a timer to adjust motor speed periodically"); + esp_timer_handle_t periodic_timer = NULL; + const esp_timer_create_args_t periodic_timer_args = { + .callback = update_motor_speed_callback, + .arg = comparators, + }; + ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &periodic_timer)); + ESP_ERROR_CHECK(esp_timer_start_periodic(periodic_timer, BLDC_SPEED_UPDATE_PERIOD_US)); + + ESP_LOGI(TAG, "Enable MOSFET gate"); gpio_set_level(BLDC_DRV_EN_GPIO, 1); - ESP_LOGI(TAG, "Changing speed at background"); - const esp_timer_create_args_t bldc_timer_args = { - .callback = update_bldc_speed, - .name = "bldc_speed" - }; - esp_timer_handle_t bldc_speed_timer; - ESP_ERROR_CHECK(esp_timer_create(&bldc_timer_args, &bldc_speed_timer)); - ESP_ERROR_CHECK(esp_timer_start_periodic(bldc_speed_timer, 2000000)); + ESP_LOGI(TAG, "Start the MCPWM timer"); + ESP_ERROR_CHECK(mcpwm_timer_enable(timer)); + ESP_ERROR_CHECK(mcpwm_timer_start_stop(timer, MCPWM_TIMER_START_NO_STOP)); + + uint32_t hall_sensor_value = 0; while (1) { // The rotation direction is controlled by inverting the hall sensor value - hall_sensor_value = bldc_get_hall_sensor_value(false); - if (hall_sensor_value >= 1 && hall_sensor_value < sizeof(s_hall_actions) / sizeof(s_hall_actions[0])) { - s_hall_actions[hall_sensor_value](); + hall_sensor_value = bldc_get_hall_sensor_value(BLDC_SPIN_DIRECTION_CCW); + if (hall_sensor_value >= 1 && hall_sensor_value <= 6) { + s_hall_actions[hall_sensor_value](generators); } else { ESP_LOGE(TAG, "invalid bldc phase, wrong hall sensor value:%d", hall_sensor_value); } diff --git a/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/pytest_bldc_hall_control.py b/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/pytest_bldc_hall_control.py new file mode 100644 index 0000000000..ccc6a4e34e --- /dev/null +++ b/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/pytest_bldc_hall_control.py @@ -0,0 +1,25 @@ +# SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32s3 +@pytest.mark.generic +def test_bldc_hall_control_example(dut: Dut) -> None: + dut.expect_exact('example: Disable MOSFET gate') + dut.expect_exact('example: Create MCPWM timer') + dut.expect_exact('example: Create MCPWM operator') + dut.expect_exact('example: Connect operators to the same timer') + dut.expect_exact('example: Create comparators') + dut.expect_exact('example: Create over current fault detector') + dut.expect_exact('example: Set brake mode on the fault event') + dut.expect_exact('example: Create PWM generators') + dut.expect_exact('example: Set generator actions') + dut.expect_exact('example: Setup deadtime') + dut.expect_exact('example: Turn off all the gates') + dut.expect_exact('example: Create Hall sensor capture channels') + dut.expect_exact('example: Start a timer to adjust motor speed periodically') + dut.expect_exact('example: Enable MOSFET gate') + dut.expect_exact('example: Start the MCPWM timer')