From 0dbe8725425095f99ed2f1a27812c53f7a66eab6 Mon Sep 17 00:00:00 2001 From: morris Date: Thu, 29 Jul 2021 21:34:24 +0800 Subject: [PATCH] mcpwm: bldc hall example --- components/driver/include/driver/mcpwm.h | 2 +- docs/en/api-reference/peripherals/mcpwm.rst | 2 +- .../mcpwm/mcpwm_bldc_control/README.md | 46 --- .../mcpwm_bldc_control/main/CMakeLists.txt | 2 - .../mcpwm_bldc_control_hall_sensor_example.c | 319 ------------------ .../CMakeLists.txt | 2 +- .../Makefile | 2 +- .../mcpwm/mcpwm_bldc_hall_control/README.md | 81 +++++ .../main/CMakeLists.txt | 2 + .../main/component.mk | 0 .../mcpwm_bldc_hall_control_example_main.c | 255 ++++++++++++++ 11 files changed, 342 insertions(+), 371 deletions(-) delete mode 100644 examples/peripherals/mcpwm/mcpwm_bldc_control/README.md delete mode 100644 examples/peripherals/mcpwm/mcpwm_bldc_control/main/CMakeLists.txt delete mode 100644 examples/peripherals/mcpwm/mcpwm_bldc_control/main/mcpwm_bldc_control_hall_sensor_example.c rename examples/peripherals/mcpwm/{mcpwm_bldc_control => mcpwm_bldc_hall_control}/CMakeLists.txt (84%) rename examples/peripherals/mcpwm/{mcpwm_bldc_control => mcpwm_bldc_hall_control}/Makefile (76%) create mode 100644 examples/peripherals/mcpwm/mcpwm_bldc_hall_control/README.md create mode 100644 examples/peripherals/mcpwm/mcpwm_bldc_hall_control/main/CMakeLists.txt rename examples/peripherals/mcpwm/{mcpwm_bldc_control => mcpwm_bldc_hall_control}/main/component.mk (100%) create mode 100644 examples/peripherals/mcpwm/mcpwm_bldc_hall_control/main/mcpwm_bldc_hall_control_example_main.c diff --git a/components/driver/include/driver/mcpwm.h b/components/driver/include/driver/mcpwm.h index db8816069d..20b41ffd32 100644 --- a/components/driver/include/driver/mcpwm.h +++ b/components/driver/include/driver/mcpwm.h @@ -139,7 +139,7 @@ typedef enum { * @brief MCPWM select triggering level of fault signal */ typedef enum { - MCPWM_LOW_LEVEL_TGR, /*!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 diff --git a/examples/peripherals/mcpwm/mcpwm_bldc_control/main/CMakeLists.txt b/examples/peripherals/mcpwm/mcpwm_bldc_control/main/CMakeLists.txt deleted file mode 100644 index 61d8388b1b..0000000000 --- a/examples/peripherals/mcpwm/mcpwm_bldc_control/main/CMakeLists.txt +++ /dev/null @@ -1,2 +0,0 @@ -idf_component_register(SRCS "mcpwm_bldc_control_hall_sensor_example.c" - INCLUDE_DIRS ".") diff --git a/examples/peripherals/mcpwm/mcpwm_bldc_control/main/mcpwm_bldc_control_hall_sensor_example.c b/examples/peripherals/mcpwm/mcpwm_bldc_control/main/mcpwm_bldc_control_hall_sensor_example.c deleted file mode 100644 index 290b1fb2fa..0000000000 --- a/examples/peripherals/mcpwm/mcpwm_bldc_control/main/mcpwm_bldc_control_hall_sensor_example.c +++ /dev/null @@ -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 - -#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); -} diff --git a/examples/peripherals/mcpwm/mcpwm_bldc_control/CMakeLists.txt b/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/CMakeLists.txt similarity index 84% rename from examples/peripherals/mcpwm/mcpwm_bldc_control/CMakeLists.txt rename to examples/peripherals/mcpwm/mcpwm_bldc_hall_control/CMakeLists.txt index 10ed869e1b..6fbfed3cee 100644 --- a/examples/peripherals/mcpwm/mcpwm_bldc_control/CMakeLists.txt +++ b/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/CMakeLists.txt @@ -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) diff --git a/examples/peripherals/mcpwm/mcpwm_bldc_control/Makefile b/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/Makefile similarity index 76% rename from examples/peripherals/mcpwm/mcpwm_bldc_control/Makefile rename to examples/peripherals/mcpwm/mcpwm_bldc_hall_control/Makefile index eb1842cc7b..5e6eca0206 100644 --- a/examples/peripherals/mcpwm/mcpwm_bldc_control/Makefile +++ b/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/Makefile @@ -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 diff --git a/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/README.md b/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/README.md new file mode 100644 index 0000000000..5fa70fe40d --- /dev/null +++ b/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/README.md @@ -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. diff --git a/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/main/CMakeLists.txt b/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/main/CMakeLists.txt new file mode 100644 index 0000000000..1d97d04dc0 --- /dev/null +++ b/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/main/CMakeLists.txt @@ -0,0 +1,2 @@ +idf_component_register(SRCS "mcpwm_bldc_hall_control_example_main.c" + INCLUDE_DIRS ".") diff --git a/examples/peripherals/mcpwm/mcpwm_bldc_control/main/component.mk b/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/main/component.mk similarity index 100% rename from examples/peripherals/mcpwm/mcpwm_bldc_control/main/component.mk rename to examples/peripherals/mcpwm/mcpwm_bldc_hall_control/main/component.mk 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 new file mode 100644 index 0000000000..6acbbdbd17 --- /dev/null +++ b/examples/peripherals/mcpwm/mcpwm_bldc_hall_control/main/mcpwm_bldc_hall_control_example_main.c @@ -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 +#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); + } +}