example: update bldc example with new driver API

This commit is contained in:
morris 2022-05-28 17:04:38 +08:00
parent 938b3d717f
commit b77446b5c8
3 changed files with 360 additions and 212 deletions

View File

@ -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.

View File

@ -8,47 +8,46 @@
#include <stdio.h>
#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);
}

View File

@ -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')