mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
mcpwm: bldc hall example
This commit is contained in:
parent
3bfd8f5d5f
commit
0dbe872542
@ -139,7 +139,7 @@ typedef enum {
|
||||
* @brief MCPWM select triggering level of fault signal
|
||||
*/
|
||||
typedef enum {
|
||||
MCPWM_LOW_LEVEL_TGR, /*!<Fault condition occurs when fault input signal goes from high to low, currently not supported*/
|
||||
MCPWM_LOW_LEVEL_TGR, /*!<Fault condition occurs when fault input signal goes from high to low*/
|
||||
MCPWM_HIGH_LEVEL_TGR, /*!<Fault condition occurs when fault input signal goes low to high*/
|
||||
} mcpwm_fault_input_level_t;
|
||||
|
||||
|
@ -179,7 +179,7 @@ Application Example
|
||||
MCPWM example are located under: :example:`peripherals/mcpwm`:
|
||||
|
||||
* Demonstration how to use each submodule of the MCPWM - :example:`peripherals/mcpwm/mcpwm_basic_config`
|
||||
* Control of BLDC (brushless DC) motor with hall sensor feedback - :example:`peripherals/mcpwm/mcpwm_bldc_control`
|
||||
* Control of BLDC (brushless DC) motor with hall sensor feedback - :example:`peripherals/mcpwm/mcpwm_bldc_hall_control`
|
||||
* Brushed DC motor control - :example:`peripherals/mcpwm/mcpwm_brushed_dc_control`
|
||||
* Servo motor control - :example:`peripherals/mcpwm/mcpwm_servo_control`
|
||||
* HC-SR04 sensor with capture - :example:`peripherals/mcpwm/mcpwm_capture_hc_sr04`
|
||||
|
@ -1,46 +0,0 @@
|
||||
| Supported Targets | ESP32 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- |
|
||||
|
||||
# MCPWM BLDC motor control (hall sensor feedback) Example
|
||||
|
||||
This example will show you how to use MCPWM module to control BLDC motor with hall sensor feedback.
|
||||
|
||||
The following examples uses MCPWM module to control bldc motor and vary its speed continuously
|
||||
|
||||
The bldc motor used for testing this code had hall sensor capture sequence of 6-->4-->5-->1-->3-->2-->6-->4--> and so on
|
||||
|
||||
IR2136 3-ph bridge driver is used for testing this example code
|
||||
|
||||
User needs to make changes according to the motor and gate driver ic used
|
||||
|
||||
|
||||
## Step 1: Pin assignment
|
||||
* The gpio init function initializes:
|
||||
* GPIO15 is assigned as the MCPWM signal for 1H(UH)
|
||||
* GPIO02 is assigned as the MCPWM signal for 1L(UL)
|
||||
* GPIO00 is assigned as the MCPWM signal for 2H(VH)
|
||||
* GPIO04 is assigned as the MCPWM signal for 2L(VL)
|
||||
* GPIO16 is assigned as the MCPWM signal for 3H(WH)
|
||||
* GPIO17 is assigned as the MCPWM signal for 3L(WL)
|
||||
* GPIO25 is assigned as the MCPWM capture signal for Hall A
|
||||
* GPIO26 is assigned as the MCPWM capture signal for HALL B
|
||||
* GPIO27 is assigned as the MCPWM capture signal for HALL C
|
||||
|
||||
|
||||
## Step 2: Connection
|
||||
* Connect GPIO15 with 1H/UH of BLDC motor driver
|
||||
* Connect GPIO02 with 1L/UL of BLDC motor driver
|
||||
* Connect GPIO00 with 2H/VH of BLDC motor driver
|
||||
* Connect GPIO04 with 2L/VL of BLDC motor driver
|
||||
* Connect GPIO16 with 3H/WH of BLDC motor driver
|
||||
* Connect GPIO17 with 3L/WL of BLDC motor driver
|
||||
* Connect GPIO25 to hall sensor A output
|
||||
* Connect GPIO26 to hall sensor B output
|
||||
* Connect GPIO27 to hall sensor C output
|
||||
|
||||
|
||||
## Step 3: Initialize MCPWM
|
||||
* You need to set the frequency and duty cycle of MCPWM timer
|
||||
* You need to set the MCPWM channel you want to use, and bind the channel with one of the timers
|
||||
* You need to set the capture unit, for POS/NEG edge capture
|
||||
* Also reversing the hall sensor BIT weight, will make bldc motor rotate CW or CCW
|
@ -1,2 +0,0 @@
|
||||
idf_component_register(SRCS "mcpwm_bldc_control_hall_sensor_example.c"
|
||||
INCLUDE_DIRS ".")
|
@ -1,319 +0,0 @@
|
||||
|
||||
/* MCPWM BLDC control Test code
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
/*
|
||||
* The following examples uses mcpwm module to control bldc motor vary its speed continiously
|
||||
* The BLDC motor used for testing this code had sequence of 6-->4-->5-->1-->3-->2-->6-->4--> and so on
|
||||
* IR2136 3-ph bridge driver is used for testing this example code
|
||||
* User needs to make changes according to the motor and gate driver ic used
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "soc/rtc.h"
|
||||
#include "driver/mcpwm.h"
|
||||
|
||||
#define INITIAL_DUTY 10.0 //initial duty cycle is 10.0%
|
||||
#define MCPWM_GPIO_INIT 0 //select which function to use to initialize gpio signals
|
||||
|
||||
#define GPIO_HALL_TEST_SIGNAL 0 //Make this 1 to enable generation of hall sensors test signal on GPIO13, 12, 14
|
||||
#define CHANGE_DUTY_CONTINUOUSLY 0 //Make this 1 to change duty continuously
|
||||
|
||||
#define CAP_SIG_NUM 3 //three capture signals from HALL-A, HALL-B, HALL-C
|
||||
#define CAP0_INT_EN BIT(27) //Capture 0 interrupt bit
|
||||
#define CAP1_INT_EN BIT(28) //Capture 1 interrupt bit
|
||||
#define CAP2_INT_EN BIT(29) //Capture 2 interrupt bit
|
||||
|
||||
#define GPIO_PWM0A_OUT 15 //Set GPIO 15 as PWM0A
|
||||
#define GPIO_PWM0B_OUT 02 //Set GPIO 02 as PWM0B
|
||||
#define GPIO_PWM1A_OUT 00 //Set GPIO 00 as PWM1A
|
||||
#define GPIO_PWM1B_OUT 04 //Set GPIO 04 as PWM1B
|
||||
#define GPIO_PWM2A_OUT 16 //Set GPIO 16 as PWM2A
|
||||
#define GPIO_PWM2B_OUT 17 //Set GPIO 17 as PWM2B
|
||||
#define GPIO_CAP0_IN 25 //Set GPIO 25 as CAP0
|
||||
#define GPIO_CAP1_IN 26 //Set GPIO 26 as CAP1
|
||||
#define GPIO_CAP2_IN 27 //Set GPIO 27 as CAP2
|
||||
|
||||
typedef struct {
|
||||
uint32_t capture_signal;
|
||||
mcpwm_capture_signal_t sel_cap_signal;
|
||||
} capture;
|
||||
|
||||
static uint32_t hall_sensor_value = 0;
|
||||
static uint32_t hall_sensor_previous = 0;
|
||||
|
||||
xQueueHandle cap_queue;
|
||||
|
||||
static void mcpwm_example_gpio_initialize(void)
|
||||
{
|
||||
printf("initializing mcpwm bldc control gpio...\n");
|
||||
#if MCPWM_GPIO_INIT
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, GPIO_PWM0A_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0B, GPIO_PWM0B_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1A, GPIO_PWM1A_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM1B, GPIO_PWM1B_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM2A, GPIO_PWM2A_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM2B, GPIO_PWM2B_OUT);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_0, GPIO_CAP0_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_1, GPIO_CAP1_IN);
|
||||
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_CAP_2, GPIO_CAP2_IN);
|
||||
#else
|
||||
mcpwm_pin_config_t pin_config = {
|
||||
.mcpwm0a_out_num = GPIO_PWM0A_OUT,
|
||||
.mcpwm0b_out_num = GPIO_PWM0B_OUT,
|
||||
.mcpwm1a_out_num = GPIO_PWM1A_OUT,
|
||||
.mcpwm1b_out_num = GPIO_PWM1B_OUT,
|
||||
.mcpwm2a_out_num = GPIO_PWM2A_OUT,
|
||||
.mcpwm2b_out_num = GPIO_PWM2B_OUT,
|
||||
.mcpwm_cap0_in_num = GPIO_CAP0_IN,
|
||||
.mcpwm_cap1_in_num = GPIO_CAP1_IN,
|
||||
.mcpwm_cap2_in_num = GPIO_CAP2_IN,
|
||||
.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 = -1, //Not used
|
||||
.mcpwm_fault1_in_num = -1, //Not used
|
||||
.mcpwm_fault2_in_num = -1 //Not used
|
||||
};
|
||||
mcpwm_set_pin(MCPWM_UNIT_0, &pin_config);
|
||||
#endif
|
||||
gpio_pulldown_en(GPIO_CAP0_IN); //Enable pull down on CAP0 signal
|
||||
gpio_pulldown_en(GPIO_CAP1_IN); //Enable pull down on CAP1 signal
|
||||
gpio_pulldown_en(GPIO_CAP2_IN); //Enable pull down on CAP2 signal
|
||||
}
|
||||
|
||||
#if GPIO_HALL_TEST_SIGNAL
|
||||
/**
|
||||
* @brief Set gpio 13, 12, 14 as our test signal of hall sensors, that generates high-low waveform continuously
|
||||
* Attach this pins to GPIO 27, 26, 25 respectively for capture unit
|
||||
*/
|
||||
static void gpio_test_signal(void *arg)
|
||||
{
|
||||
printf("intializing test signal...\n");
|
||||
gpio_config_t gp;
|
||||
gp.intr_type = GPIO_INTR_DISABLE;
|
||||
gp.mode = GPIO_MODE_OUTPUT;
|
||||
gp.pin_bit_mask = GPIO_SEL_13 | GPIO_SEL_12 | GPIO_SEL_14;
|
||||
gpio_config(&gp);
|
||||
while (1) {
|
||||
gpio_set_level(GPIO_NUM_13, 1); //Set H1 high
|
||||
gpio_set_level(GPIO_NUM_12, 0); //Set H2 low
|
||||
gpio_set_level(GPIO_NUM_14, 1); //Set H3 high
|
||||
vTaskDelay(1);
|
||||
gpio_set_level(GPIO_NUM_14, 0); //Set H3 low
|
||||
vTaskDelay(1);
|
||||
gpio_set_level(GPIO_NUM_12, 1); //Set H2 high
|
||||
vTaskDelay(1);
|
||||
gpio_set_level(GPIO_NUM_13, 0); //Set H1 low
|
||||
vTaskDelay(1);
|
||||
gpio_set_level(GPIO_NUM_14, 1); //Set H3 high
|
||||
vTaskDelay(1);
|
||||
gpio_set_level(GPIO_NUM_12, 0); //Set H2 high
|
||||
vTaskDelay(1);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief When interrupt occurs, we receive the counter value and display the time between two rising edge
|
||||
*/
|
||||
static void disp_captured_signal(void *arg)
|
||||
{
|
||||
uint32_t *current_cap_value = (uint32_t *)malloc(sizeof(CAP_SIG_NUM));
|
||||
uint32_t *previous_cap_value = (uint32_t *)malloc(sizeof(CAP_SIG_NUM));
|
||||
capture evt;
|
||||
while (1) {
|
||||
xQueueReceive(cap_queue, &evt, portMAX_DELAY);
|
||||
if (evt.sel_cap_signal == MCPWM_SELECT_CAP0) {
|
||||
current_cap_value[0] = evt.capture_signal - previous_cap_value[0];
|
||||
previous_cap_value[0] = evt.capture_signal;
|
||||
current_cap_value[0] = (current_cap_value[0] / 10000) * (10000000000 / rtc_clk_apb_freq_get());
|
||||
//printf("CAP0 : %d us\n", current_cap_value[0]);
|
||||
}
|
||||
if (evt.sel_cap_signal == MCPWM_SELECT_CAP1) {
|
||||
current_cap_value[1] = evt.capture_signal - previous_cap_value[1];
|
||||
previous_cap_value[1] = evt.capture_signal;
|
||||
current_cap_value[1] = (current_cap_value[1] / 10000) * (10000000000 / rtc_clk_apb_freq_get());
|
||||
//printf("CAP1 : %d us\n", current_cap_value[1]);
|
||||
}
|
||||
if (evt.sel_cap_signal == MCPWM_SELECT_CAP2) {
|
||||
current_cap_value[2] = evt.capture_signal - previous_cap_value[2];
|
||||
previous_cap_value[2] = evt.capture_signal;
|
||||
current_cap_value[2] = (current_cap_value[2] / 10000) * (10000000000 / rtc_clk_apb_freq_get());
|
||||
//printf("CAP2 : %d us\n", current_cap_value[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief this is ISR handler function, here we check for interrupt that triggers rising edge on CAP0 signal and according take action
|
||||
*/
|
||||
static bool isr_handler(mcpwm_unit_t mcpwm, mcpwm_capture_channel_id_t cap_sig, const cap_event_data_t *edata, void *arg)
|
||||
{
|
||||
capture evt;
|
||||
if (cap_sig == MCPWM_SELECT_CAP0) { //Check for interrupt on rising edge on CAP0 signal
|
||||
evt.capture_signal = edata->cap_value; //get capture signal counter value
|
||||
evt.sel_cap_signal = MCPWM_SELECT_CAP0;
|
||||
xQueueSendFromISR(cap_queue, &evt, NULL);
|
||||
}
|
||||
if (cap_sig == MCPWM_SELECT_CAP1) { //Check for interrupt on rising edge on CAP1 signal
|
||||
evt.capture_signal = edata->cap_value; //get capture signal counter value
|
||||
evt.sel_cap_signal = MCPWM_SELECT_CAP1;
|
||||
xQueueSendFromISR(cap_queue, &evt, NULL);
|
||||
}
|
||||
if (cap_sig == MCPWM_SELECT_CAP2) { //Check for interrupt on rising edge on CAP2 signal
|
||||
evt.capture_signal = edata->cap_value; //get capture signal counter value
|
||||
evt.sel_cap_signal = MCPWM_SELECT_CAP2;
|
||||
xQueueSendFromISR(cap_queue, &evt, NULL);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
#if CHANGE_DUTY_CONTINUOUSLY
|
||||
static void change_duty(void *arg)
|
||||
{
|
||||
int j;
|
||||
while (1) {
|
||||
for (j = 0; j < 18; j++) {
|
||||
//printf("duty cycle: %d\n", (0 +j*50));
|
||||
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, (INITIAL_DUTY + j * 5.0));
|
||||
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B, (INITIAL_DUTY + j * 5.0));
|
||||
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_A, (INITIAL_DUTY + j * 5.0));
|
||||
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_B, (INITIAL_DUTY + j * 5.0));
|
||||
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_A, (INITIAL_DUTY + j * 5.0));
|
||||
mcpwm_set_duty(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_B, (INITIAL_DUTY + j * 5.0));
|
||||
vTaskDelay(500 / portTICK_RATE_MS);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Configure whole MCPWM module for bldc motor control
|
||||
*/
|
||||
static void mcpwm_example_bldc_control(void *arg)
|
||||
{
|
||||
//1. mcpwm gpio initialization
|
||||
mcpwm_example_gpio_initialize();
|
||||
|
||||
//2. initial mcpwm configuration
|
||||
printf("Configuring Initial Parameters of mcpwm bldc control...\n");
|
||||
mcpwm_config_t pwm_config;
|
||||
pwm_config.frequency = 14400; //frequency = 1000Hz
|
||||
pwm_config.cmpr_a = 50.0; //duty cycle of PWMxA = 50.0%
|
||||
pwm_config.cmpr_b = 50.0; //duty cycle of PWMxb = 50.0%
|
||||
pwm_config.counter_mode = MCPWM_UP_COUNTER;
|
||||
pwm_config.duty_mode = MCPWM_DUTY_MODE_1;
|
||||
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); //Configure PWM0A & PWM0B with above settings
|
||||
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_1, &pwm_config); //Configure PWM1A & PWM1B with above settings
|
||||
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_2, &pwm_config); //Configure PWM2A & PWM2B with above settings
|
||||
|
||||
//3. Capture configuration
|
||||
//configure CAP0, CAP1 and CAP2 signal to start capture counter on rising edge
|
||||
//we generate a gpio_test_signal of 20ms on GPIO 12 and connect it to one of the capture signal, the disp_captured_function displays the time between rising edge
|
||||
//In general practice you can connect Capture to external signal, measure time between rising edge or falling edge and take action accordingly
|
||||
mcpwm_capture_config_t conf = {
|
||||
.cap_edge = MCPWM_POS_EDGE,
|
||||
.cap_prescale = 1,
|
||||
.capture_cb = isr_handler,
|
||||
.user_data = NULL,
|
||||
};
|
||||
mcpwm_capture_enable_channel(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, &conf); //capture signal on rising edge, pulse num = 0 i.e. 800,000,000 counts is equal to one second
|
||||
mcpwm_capture_enable_channel(MCPWM_UNIT_0, MCPWM_SELECT_CAP1, &conf); //capture signal on rising edge, pulse num = 0 i.e. 800,000,000 counts is equal to one second
|
||||
mcpwm_capture_enable_channel(MCPWM_UNIT_0, MCPWM_SELECT_CAP2, &conf); //capture signal on rising edge, pulse num = 0 i.e. 800,000,000 counts is equal to one second
|
||||
//According to the hall sensor input value take action on PWM0A/0B/1A/1B/2A/2B
|
||||
while (1) {
|
||||
hall_sensor_value = (gpio_get_level(GPIO_NUM_27) * 1) + (gpio_get_level(GPIO_NUM_26) * 2) + (gpio_get_level(GPIO_NUM_25) * 4);
|
||||
if (hall_sensor_value != hall_sensor_previous) {
|
||||
//printf("hall_sen val: %d\n", hall_sensor_value);
|
||||
if (hall_sensor_value == 2) {
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_B);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_high(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_B);
|
||||
//MCPWMXA to duty mode 1 and MCPWMXB to duty mode 0 or vice versa will generate MCPWM compliment signal of each other, there are also other ways to do it
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, MCPWM_DUTY_MODE_1); //Set PWM0A to duty mode one
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B, MCPWM_DUTY_MODE_0); //Set PWM0B back to duty mode zero
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_BYPASS_FED, 100, 100); //Deadtime of 10us
|
||||
}
|
||||
if (hall_sensor_value == 6) {
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B);
|
||||
mcpwm_deadtime_disable(MCPWM_UNIT_0, MCPWM_TIMER_0);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_high(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_B);
|
||||
//MCPWMXA to duty mode 1 and MCPWMXB to duty mode 0 or vice versa will generate MCPWM compliment signal of each other, there are also other ways to do it
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_A, MCPWM_DUTY_MODE_1); //Set PWM2A to duty mode one
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_B, MCPWM_DUTY_MODE_0); //Set PWM2B back to duty mode zero
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_BYPASS_FED, 100, 100); //Deadtime of 10us
|
||||
}
|
||||
if (hall_sensor_value == 4) {
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_B);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_high(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B);
|
||||
//MCPWMXA to duty mode 1 and MCPWMXB to duty mode 0 or vice versa will generate MCPWM compliment signal of each other, there are also other ways to do it
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_A, MCPWM_DUTY_MODE_1); //Set PWM2A to duty mode one
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_B, MCPWM_DUTY_MODE_0); //Set PWM2B back to duty mode zero
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_BYPASS_FED, 100, 100); //Deadtime of 10us
|
||||
}
|
||||
if (hall_sensor_value == 5) {
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_B);
|
||||
mcpwm_deadtime_disable(MCPWM_UNIT_0, MCPWM_TIMER_2);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_high(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B);
|
||||
//MCPWMXA to duty mode 1 and MCPWMXB to duty mode 0 or vice versa will generate MCPWM compliment signal of each other, there are also other ways to do it
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_A, MCPWM_DUTY_MODE_1); //Set PWM1A to duty mode one
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_B, MCPWM_DUTY_MODE_0); //Set PWM1B back to duty mode zero
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_BYPASS_FED, 100, 100); //Deadtime of 10uss
|
||||
}
|
||||
if (hall_sensor_value == 1) {
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_high(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_B);
|
||||
//MCPWMXA to duty mode 1 and MCPWMXB to duty mode 0 or vice versa will generate MCPWM compliment signal of each other, there are also other ways to do it
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_A, MCPWM_DUTY_MODE_1); //Set PWM1A to duty mode one
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_B, MCPWM_DUTY_MODE_0); //Set PWM1B back to duty mode zero
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_BYPASS_FED, 100, 100); //Deadtime of 10uss
|
||||
}
|
||||
if (hall_sensor_value == 3) {
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_OPR_B);
|
||||
mcpwm_deadtime_disable(MCPWM_UNIT_0, MCPWM_TIMER_1);
|
||||
mcpwm_set_signal_low(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_A);
|
||||
mcpwm_set_signal_high(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_OPR_B);
|
||||
//MCPWMXA to duty mode 1 and MCPWMXB to duty mode 0 or vice versa will generate MCPWM compliment signal of each other, there are also other ways to do it
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, MCPWM_DUTY_MODE_1); //Set PWM0A to duty mode one
|
||||
mcpwm_set_duty_type(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_B, MCPWM_DUTY_MODE_0); //Set PWM0B back to duty mode zero
|
||||
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_BYPASS_FED, 100, 100); //Deadtime of 10us
|
||||
}
|
||||
hall_sensor_previous = hall_sensor_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
printf("Testing MCPWM BLDC Control...\n");
|
||||
#if CHANGE_DUTY_CONTINUOUSLY
|
||||
xTaskCreate(change_duty, "change_duty", 2048, NULL, 2, NULL);
|
||||
#endif
|
||||
cap_queue = xQueueCreate(1, sizeof(capture)); //comment if you don't want to use capture module
|
||||
#if GPIO_HALL_TEST_SIGNAL
|
||||
xTaskCreate(gpio_test_signal, "gpio_test_signal", 2048, NULL, 2, NULL);
|
||||
#endif
|
||||
xTaskCreate(disp_captured_signal, "mcpwm_config", 4096, NULL, 2, NULL); //comment if you don't want to use capture module
|
||||
xTaskCreate(mcpwm_example_bldc_control, "mcpwm_example_bldc_control", 4096, NULL, 2, NULL);
|
||||
}
|
@ -3,4 +3,4 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(mcpwm_bldc_control_hall_sensor)
|
||||
project(mcpwm_bldc_hall_control)
|
@ -3,6 +3,6 @@
|
||||
# project subdirectory.
|
||||
#
|
||||
|
||||
PROJECT_NAME := mcpwm_bldc_control_hall_sensor
|
||||
PROJECT_NAME := mcpwm_bldc_hall_control
|
||||
|
||||
include $(IDF_PATH)/make/project.mk
|
81
examples/peripherals/mcpwm/mcpwm_bldc_hall_control/README.md
Normal file
81
examples/peripherals/mcpwm/mcpwm_bldc_hall_control/README.md
Normal file
@ -0,0 +1,81 @@
|
||||
| Supported Targets | ESP32 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- |
|
||||
|
||||
# MCPWM BLDC Hall motor control 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.
|
||||
|
||||
## 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.
|
||||
|
||||
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 │
|
||||
└───────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Build and Flash
|
||||
|
||||
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
|
||||
## Example Output
|
||||
|
||||
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
|
||||
...
|
||||
```
|
||||
|
||||
## Dive into the example
|
||||
|
||||
1. How to change the rotation direction?
|
||||
|
||||
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.
|
@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "mcpwm_bldc_hall_control_example_main.c"
|
||||
INCLUDE_DIRS ".")
|
@ -0,0 +1,255 @@
|
||||
|
||||
/* MCPWM BLDC control with Hall sensor
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "driver/mcpwm.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_attr.h"
|
||||
#include "esp_log.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_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
|
||||
|
||||
static const char *TAG = "example";
|
||||
|
||||
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)
|
||||
{
|
||||
TaskHandle_t task_to_notify = (TaskHandle_t)user_data;
|
||||
BaseType_t high_task_wakeup = pdFALSE;
|
||||
vTaskNotifyGiveFromISR(task_to_notify, &high_task_wakeup);
|
||||
return high_task_wakeup == pdTRUE;
|
||||
}
|
||||
|
||||
static void update_bldc_speed(void *arg)
|
||||
{
|
||||
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+V- / A+B-
|
||||
static void bldc_set_phase_up_vm(void)
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
// W+U- / C+A-
|
||||
static void bldc_set_phase_wp_um(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_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_
|
||||
}
|
||||
|
||||
// W+V- / C+B-
|
||||
static void bldc_set_phase_wp_vm(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_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_
|
||||
}
|
||||
|
||||
// V+U- / B+A-
|
||||
static void bldc_set_phase_vp_um(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_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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// U+W- / A+C-
|
||||
static void bldc_set_phase_up_wm(void)
|
||||
{
|
||||
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
|
||||
}
|
||||
|
||||
typedef void (*bldc_hall_phase_action_t)(void);
|
||||
|
||||
static const bldc_hall_phase_action_t s_hall_actions[] = {
|
||||
[2] = bldc_set_phase_up_vm,
|
||||
[6] = bldc_set_phase_wp_vm,
|
||||
[4] = bldc_set_phase_wp_um,
|
||||
[5] = bldc_set_phase_vp_um,
|
||||
[1] = bldc_set_phase_vp_wm,
|
||||
[3] = bldc_set_phase_up_wm,
|
||||
};
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
uint32_t hall_sensor_value = 0;
|
||||
TaskHandle_t cur_task = xTaskGetCurrentTaskHandle();
|
||||
|
||||
ESP_LOGI(TAG, "Disable gate driver");
|
||||
gpio_config_t drv_en_config = {
|
||||
.mode = GPIO_MODE_OUTPUT,
|
||||
.pin_bit_mask = 1 << 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_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_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_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));
|
||||
|
||||
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, "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_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");
|
||||
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));
|
||||
|
||||
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]();
|
||||
} else {
|
||||
ESP_LOGE(TAG, "invalid bldc phase, wrong hall sensor value:%d", hall_sensor_value);
|
||||
}
|
||||
ulTaskNotifyTake(pdTRUE, portMAX_DELAY);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user