添加智能灯固件代码

This commit is contained in:
kerwincui
2021-07-13 17:14:51 +08:00
parent 332f74dd17
commit ecc0b91b8b
2568 changed files with 229441 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mcpwm_basic_config)

View File

@@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := mcpwm_basic_config
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,35 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
# MCPWM basic config Example
This example will show you how to use each submodule of MCPWM unit
The example can't be used without modifying the code first
Edit the macros at the top of mcpwm_example_basic_config.c to enable/disable the submodules which are used in the example
## Step 1: Pin assignment
* The gpio init function initializes:
* six MCPWM output pins
* three MCPWM fault input pins
* three MCPWM sync input pins
* three MCPWM capture input pins
## Step 2: Connection
* Six MCPWM output pins to motor driver input signals
* Fault, sync, capture signals can be connected to respective signals
## Step 3: Initialize MCPWM
* You need to set the frequency and duty cycle of each three MCPWM timer along with other parameters mentioned
* You need to set the MCPWM channel you want to use, with these timers
## Step 4: Testing
* The deadtime module, set deadtime type and with value as time*100ns
* The sync module, synchonizes all the timer pulses
* The fault module when enabled takes action on MCPWM signals when fault occurs
* The capture module captures input signal(digital i.e. hall sensor value, etc), timing between two rising/falling edge

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "mcpwm_basic_config_example.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@@ -0,0 +1,293 @@
/* MCPWM basic config example
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.
*/
/*
* This example will show you how to use each submodule of MCPWM unit.
* The example can't be used without modifying the code first.
* Edit the macros at the top of mcpwm_example_basic_config.c to enable/disable the submodules which are used in the example.
*/
#include <stdio.h>
#include "string.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_attr.h"
#include "soc/rtc.h"
#include "driver/mcpwm.h"
#include "soc/mcpwm_periph.h"
#define MCPWM_EN_CARRIER 0 //Make this 1 to test carrier submodule of mcpwm, set high frequency carrier parameters
#define MCPWM_EN_DEADTIME 0 //Make this 1 to test deadtime submodule of mcpwm, set deadtime value and deadtime mode
#define MCPWM_EN_FAULT 0 //Make this 1 to test fault submodule of mcpwm, set action on MCPWM signal on fault occurence like overcurrent, overvoltage, etc
#define MCPWM_EN_SYNC 0 //Make this 1 to test sync submodule of mcpwm, sync timer signals
#define MCPWM_EN_CAPTURE 0 //Make this 1 to test capture submodule of mcpwm, measure time between rising/falling edge of captured signal
#define MCPWM_GPIO_INIT 0 //select which function to use to initialize gpio signals
#define CAP_SIG_NUM 3 //Three capture signals
#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 19 //Set GPIO 19 as PWM0A
#define GPIO_PWM0B_OUT 18 //Set GPIO 18 as PWM0B
#define GPIO_PWM1A_OUT 17 //Set GPIO 17 as PWM1A
#define GPIO_PWM1B_OUT 16 //Set GPIO 16 as PWM1B
#define GPIO_PWM2A_OUT 15 //Set GPIO 15 as PWM2A
#define GPIO_PWM2B_OUT 14 //Set GPIO 14 as PWM2B
#define GPIO_CAP0_IN 23 //Set GPIO 23 as CAP0
#define GPIO_CAP1_IN 25 //Set GPIO 25 as CAP1
#define GPIO_CAP2_IN 26 //Set GPIO 26 as CAP2
#define GPIO_SYNC0_IN 2 //Set GPIO 02 as SYNC0
#define GPIO_SYNC1_IN 4 //Set GPIO 04 as SYNC1
#define GPIO_SYNC2_IN 5 //Set GPIO 05 as SYNC2
#define GPIO_FAULT0_IN 32 //Set GPIO 32 as FAULT0
#define GPIO_FAULT1_IN 34 //Set GPIO 34 as FAULT1
#define GPIO_FAULT2_IN 34 //Set GPIO 34 as FAULT2
typedef struct {
uint32_t capture_signal;
mcpwm_capture_signal_t sel_cap_signal;
} capture;
xQueueHandle cap_queue;
#if MCPWM_EN_CAPTURE
static mcpwm_dev_t *MCPWM[2] = {&MCPWM0, &MCPWM1};
#endif
static void mcpwm_example_gpio_initialize(void)
{
printf("initializing mcpwm 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);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_SYNC_0, GPIO_SYNC0_IN);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_SYNC_1, GPIO_SYNC1_IN);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_SYNC_2, GPIO_SYNC2_IN);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_FAULT_0, GPIO_FAULT0_IN);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_FAULT_1, GPIO_FAULT1_IN);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM_FAULT_2, GPIO_FAULT2_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_sync0_in_num = GPIO_SYNC0_IN,
.mcpwm_sync1_in_num = GPIO_SYNC1_IN,
.mcpwm_sync2_in_num = GPIO_SYNC2_IN,
.mcpwm_fault0_in_num = GPIO_FAULT0_IN,
.mcpwm_fault1_in_num = GPIO_FAULT1_IN,
.mcpwm_fault2_in_num = GPIO_FAULT2_IN,
.mcpwm_cap0_in_num = GPIO_CAP0_IN,
.mcpwm_cap1_in_num = GPIO_CAP1_IN,
.mcpwm_cap2_in_num = GPIO_CAP2_IN
};
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
gpio_pulldown_en(GPIO_SYNC0_IN); //Enable pull down on SYNC0 signal
gpio_pulldown_en(GPIO_SYNC1_IN); //Enable pull down on SYNC1 signal
gpio_pulldown_en(GPIO_SYNC2_IN); //Enable pull down on SYNC2 signal
gpio_pulldown_en(GPIO_FAULT0_IN); //Enable pull down on FAULT0 signal
gpio_pulldown_en(GPIO_FAULT1_IN); //Enable pull down on FAULT1 signal
gpio_pulldown_en(GPIO_FAULT2_IN); //Enable pull down on FAULT2 signal
}
/**
* @brief Set gpio 12 as our test signal that generates high-low waveform continuously, connect this gpio to capture pin.
*/
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_12;
gpio_config(&gp);
while (1) {
//here the period of test signal is 20ms
gpio_set_level(GPIO_NUM_12, 1); //Set high
vTaskDelay(10); //delay of 10ms
gpio_set_level(GPIO_NUM_12, 0); //Set low
vTaskDelay(10); //delay of 10ms
}
}
/**
* @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(CAP_SIG_NUM*sizeof(uint32_t));
uint32_t *previous_cap_value = (uint32_t *)malloc(CAP_SIG_NUM*sizeof(uint32_t));
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]);
}
}
}
#if MCPWM_EN_CAPTURE
/**
* @brief this is ISR handler function, here we check for interrupt that triggers rising edge on CAP0 signal and according take action
*/
static void IRAM_ATTR isr_handler(void)
{
uint32_t mcpwm_intr_status;
capture evt;
mcpwm_intr_status = MCPWM[MCPWM_UNIT_0]->int_st.val; //Read interrupt status
if (mcpwm_intr_status & CAP0_INT_EN) { //Check for interrupt on rising edge on CAP0 signal
evt.capture_signal = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP0); //get capture signal counter value
evt.sel_cap_signal = MCPWM_SELECT_CAP0;
xQueueSendFromISR(cap_queue, &evt, NULL);
}
if (mcpwm_intr_status & CAP1_INT_EN) { //Check for interrupt on rising edge on CAP0 signal
evt.capture_signal = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP1); //get capture signal counter value
evt.sel_cap_signal = MCPWM_SELECT_CAP1;
xQueueSendFromISR(cap_queue, &evt, NULL);
}
if (mcpwm_intr_status & CAP2_INT_EN) { //Check for interrupt on rising edge on CAP0 signal
evt.capture_signal = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP2); //get capture signal counter value
evt.sel_cap_signal = MCPWM_SELECT_CAP2;
xQueueSendFromISR(cap_queue, &evt, NULL);
}
MCPWM[MCPWM_UNIT_0]->int_clr.val = mcpwm_intr_status;
}
#endif
/**
* @brief Configure whole MCPWM module
*/
static void mcpwm_example_config(void *arg)
{
//1. mcpwm gpio initialization
mcpwm_example_gpio_initialize();
//2. initialize mcpwm configuration
printf("Configuring Initial Parameters of mcpwm...\n");
mcpwm_config_t pwm_config;
pwm_config.frequency = 1000; //frequency = 1000Hz
pwm_config.cmpr_a = 60.0; //duty cycle of PWMxA = 60.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_0;
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); //Configure PWM0A & PWM0B with above settings
pwm_config.frequency = 500; //frequency = 500Hz
pwm_config.cmpr_a = 45.9; //duty cycle of PWMxA = 45.9%
pwm_config.cmpr_b = 7.0; //duty cycle of PWMxb = 07.0%
pwm_config.counter_mode = MCPWM_UP_COUNTER;
pwm_config.duty_mode = MCPWM_DUTY_MODE_0;
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_1, &pwm_config); //Configure PWM1A & PWM1B with above settings
pwm_config.frequency = 400; //frequency = 400Hz
pwm_config.cmpr_a = 23.2; //duty cycle of PWMxA = 23.2%
pwm_config.cmpr_b = 97.0; //duty cycle of PWMxb = 97.0%
pwm_config.counter_mode = MCPWM_UP_DOWN_COUNTER; //frequency is half when up down count mode is set i.e. SYMMETRIC PWM
pwm_config.duty_mode = MCPWM_DUTY_MODE_1;
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_2, &pwm_config); //Configure PWM2A & PWM2B with above settings
#if MCPWM_EN_CARRIER
//3. carrier configuration
//comment if you don't want to use carrier mode
//in carrier mode very high frequency carrier signal is generated at mcpwm high level signal
mcpwm_carrier_config_t chop_config;
chop_config.carrier_period = 6; //carrier period = (6 + 1)*800ns
chop_config.carrier_duty = 3; //carrier duty = (3)*12.5%
chop_config.carrier_os_mode = MCPWM_ONESHOT_MODE_EN; //If one shot mode is enabled then set pulse width, if disabled no need to set pulse width
chop_config.pulse_width_in_os = 3; //first pulse width = (3 + 1)*carrier_period
chop_config.carrier_ivt_mode = MCPWM_CARRIER_OUT_IVT_EN; //output signal inversion enable
mcpwm_carrier_init(MCPWM_UNIT_0, MCPWM_TIMER_2, &chop_config); //Enable carrier on PWM2A and PWM2B with above settings
//use mcpwm_carrier_disable function to disable carrier on mcpwm timer on which it was enabled
#endif
#if MCPWM_EN_DEADTIME
//4. deadtime configuration
//comment if you don't want to use deadtime submodule
//add rising edge delay or falling edge delay. There are 8 different types, each explained in mcpwm_deadtime_type_t in mcpwm.h
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_2, MCPWM_BYPASS_FED, 1000, 1000); //Enable deadtime on PWM2A and PWM2B with red = (1000)*100ns on PWM2A
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_BYPASS_RED, 300, 2000); //Enable deadtime on PWM1A and PWM1B with fed = (2000)*100ns on PWM1B
mcpwm_deadtime_enable(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_ACTIVE_RED_FED_FROM_PWMXA, 656, 67); //Enable deadtime on PWM0A and PWM0B with red = (656)*100ns & fed = (67)*100ns on PWM0A and PWM0B generated from PWM0A
//use mcpwm_deadtime_disable function to disable deadtime on mcpwm timer on which it was enabled
#endif
#if MCPWM_EN_FAULT
//5. enable fault condition
//comment if you don't want to use fault submodule, also u can comment the fault gpio signals
//whenever fault occurs you can configure mcpwm signal to either force low, force high or toggle.
//in cycmode, as soon as fault condition is over, the mcpwm signal is resumed, whereas in oneshot mode you need to reset.
mcpwm_fault_init(MCPWM_UNIT_0, MCPWM_HIGH_LEVEL_TGR, MCPWM_SELECT_F0); //Enable FAULT, when high level occurs on FAULT0 signal
mcpwm_fault_init(MCPWM_UNIT_0, MCPWM_HIGH_LEVEL_TGR, MCPWM_SELECT_F1); //Enable FAULT, when high level occurs on FAULT1 signal
mcpwm_fault_init(MCPWM_UNIT_0, MCPWM_HIGH_LEVEL_TGR, MCPWM_SELECT_F2); //Enable FAULT, when high level occurs on FAULT2 signal
mcpwm_fault_set_oneshot_mode(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_SELECT_F0, MCPWM_FORCE_MCPWMXA_HIGH, MCPWM_FORCE_MCPWMXB_LOW); //Action taken on PWM1A and PWM1B, when FAULT0 occurs
mcpwm_fault_set_oneshot_mode(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_SELECT_F1, MCPWM_FORCE_MCPWMXA_LOW, MCPWM_FORCE_MCPWMXB_HIGH); //Action taken on PWM1A and PWM1B, when FAULT1 occurs
mcpwm_fault_set_oneshot_mode(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_SELECT_F2, MCPWM_FORCE_MCPWMXA_HIGH, MCPWM_FORCE_MCPWMXB_LOW); //Action taken on PWM0A and PWM0B, when FAULT2 occurs
mcpwm_fault_set_oneshot_mode(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_SELECT_F1, MCPWM_FORCE_MCPWMXA_LOW, MCPWM_FORCE_MCPWMXB_HIGH); //Action taken on PWM0A and PWM0B, when FAULT1 occurs
#endif
#if MCPWM_EN_SYNC
//6. Syncronization configuration
//comment if you don't want to use sync submodule, also u can comment the sync gpio signals
//here synchronization occurs on PWM1A and PWM1B
mcpwm_sync_enable(MCPWM_UNIT_0, MCPWM_TIMER_1, MCPWM_SELECT_SYNC0, 200); //Load counter value with 20% of period counter of mcpwm timer 1 when sync 0 occurs
#endif
#if MCPWM_EN_CAPTURE
//7. Capture configuration
//comment if you don't want to use capture submodule, also u can comment the capture gpio signals
//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_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, MCPWM_POS_EDGE, 0); //capture signal on rising edge, prescale = 0 i.e. 800,000,000 counts is equal to one second
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP2, MCPWM_POS_EDGE, 0); //capture signal on rising edge, prescale = 0 i.e. 800,000,000 counts is equal to one second
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP1, MCPWM_POS_EDGE, 0); //capture signal on rising edge, prescale = 0 i.e. 800,000,000 counts is equal to one second
//enable interrupt, so each this a rising edge occurs interrupt is triggered
MCPWM[MCPWM_UNIT_0]->int_ena.val = CAP0_INT_EN | CAP1_INT_EN | CAP2_INT_EN; //Enable interrupt on CAP0, CAP1 and CAP2 signal
mcpwm_isr_register(MCPWM_UNIT_0, isr_handler, NULL, ESP_INTR_FLAG_IRAM, NULL); //Set ISR Handler
#endif
vTaskDelete(NULL);
}
void app_main(void)
{
printf("Testing MCPWM...\n");
cap_queue = xQueueCreate(1, sizeof(capture)); //comment if you don't want to use capture module
xTaskCreate(disp_captured_signal, "mcpwm_config", 4096, NULL, 5, NULL); //comment if you don't want to use capture module
xTaskCreate(gpio_test_signal, "gpio_test_signal", 4096, NULL, 5, NULL); //comment if you don't want to use capture module
xTaskCreate(mcpwm_example_config, "mcpwm_example_config", 4096, NULL, 5, NULL);
}

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mcpwm_bldc_control_hall_sensor)

View File

@@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := mcpwm_bldc_control_hall_sensor
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,46 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
# 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

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "mcpwm_bldc_control_hall_sensor_example.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@@ -0,0 +1,323 @@
/* 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 "esp_attr.h"
#include "soc/rtc.h"
#include "driver/mcpwm.h"
#include "soc/mcpwm_periph.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 mcpwm_dev_t *MCPWM[2] = {&MCPWM0, &MCPWM1};
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 void IRAM_ATTR isr_handler(void *arg)
{
uint32_t mcpwm_intr_status;
capture evt;
mcpwm_intr_status = MCPWM[MCPWM_UNIT_0]->int_st.val; //Read interrupt status
if (mcpwm_intr_status & CAP0_INT_EN) { //Check for interrupt on rising edge on CAP0 signal
evt.capture_signal = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP0); //get capture signal counter value
evt.sel_cap_signal = MCPWM_SELECT_CAP0;
xQueueSendFromISR(cap_queue, &evt, NULL);
}
if (mcpwm_intr_status & CAP1_INT_EN) { //Check for interrupt on rising edge on CAP1 signal
evt.capture_signal = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP1); //get capture signal counter value
evt.sel_cap_signal = MCPWM_SELECT_CAP1;
xQueueSendFromISR(cap_queue, &evt, NULL);
}
if (mcpwm_intr_status & CAP2_INT_EN) { //Check for interrupt on rising edge on CAP2 signal
evt.capture_signal = mcpwm_capture_signal_get_value(MCPWM_UNIT_0, MCPWM_SELECT_CAP2); //get capture signal counter value
evt.sel_cap_signal = MCPWM_SELECT_CAP2;
xQueueSendFromISR(cap_queue, &evt, NULL);
}
MCPWM[MCPWM_UNIT_0]->int_clr.val = mcpwm_intr_status;
}
#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 = 1000; //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_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP0, MCPWM_POS_EDGE, 0); //capture signal on rising edge, pulse num = 0 i.e. 800,000,000 counts is equal to one second
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP1, MCPWM_POS_EDGE, 0); //capture signal on rising edge, pulse num = 0 i.e. 800,000,000 counts is equal to one second
mcpwm_capture_enable(MCPWM_UNIT_0, MCPWM_SELECT_CAP2, MCPWM_POS_EDGE, 0); //capture signal on rising edge, pulse num = 0 i.e. 800,000,000 counts is equal to one second
//enable interrupt, so each this a rising edge occurs interrupt is triggered
MCPWM[MCPWM_UNIT_0]->int_ena.val = (CAP0_INT_EN | CAP1_INT_EN | CAP2_INT_EN); //Enable interrupt on CAP0, CAP1 and CAP2 signal
mcpwm_isr_register(MCPWM_UNIT_0, isr_handler, NULL, ESP_INTR_FLAG_IRAM, NULL); //Set ISR Handler
//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);
}

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mcpwm_brushed_dc_control)

View File

@@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := mcpwm_brushed_dc_control
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,25 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
# MCPWM brushed dc motor control Example
This example will show you how to use MCPWM module to control brushed dc motor, you need to make connection between ESP32 and motor driver
This code is tested with L298 motor driver, user needs to make changes according to the driver they use
Motor first moves forward, then backward and then stops for 2 Secs each countinuously
## Step 1: Pin assignment
* GPIO15 is assigned as the enable/input 1 for motor driver
* GPIO16 is assigned as the enable/input 2 for motor driver
## Step 2: Connection
* connect GPIO15 with input 1 of motor driver
* connect GPIO16 with input 2 of motor driver
## Step 3: Initialize MCPWM
* You need to set the frequency and duty cycle of MCPWM timer
* You need to set the MCPWM unit you want to use, and bind the unit with one of the timers

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "mcpwm_brushed_dc_control_example.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@@ -0,0 +1,95 @@
/* brushed dc motor control example
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.
*/
/*
* This example will show you how to use MCPWM module to control brushed dc motor.
* This code is tested with L298 motor driver.
* User may need to make changes according to the motor driver they use.
*/
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_attr.h"
#include "driver/mcpwm.h"
#include "soc/mcpwm_periph.h"
#define GPIO_PWM0A_OUT 15 //Set GPIO 15 as PWM0A
#define GPIO_PWM0B_OUT 16 //Set GPIO 16 as PWM0B
static void mcpwm_example_gpio_initialize(void)
{
printf("initializing mcpwm gpio...\n");
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, GPIO_PWM0A_OUT);
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0B, GPIO_PWM0B_OUT);
}
/**
* @brief motor moves in forward direction, with duty cycle = duty %
*/
static void brushed_motor_forward(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num , float duty_cycle)
{
mcpwm_set_signal_low(mcpwm_num, timer_num, MCPWM_OPR_B);
mcpwm_set_duty(mcpwm_num, timer_num, MCPWM_OPR_A, duty_cycle);
mcpwm_set_duty_type(mcpwm_num, timer_num, MCPWM_OPR_A, MCPWM_DUTY_MODE_0); //call this each time, if operator was previously in low/high state
}
/**
* @brief motor moves in backward direction, with duty cycle = duty %
*/
static void brushed_motor_backward(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num , float duty_cycle)
{
mcpwm_set_signal_low(mcpwm_num, timer_num, MCPWM_OPR_A);
mcpwm_set_duty(mcpwm_num, timer_num, MCPWM_OPR_B, duty_cycle);
mcpwm_set_duty_type(mcpwm_num, timer_num, MCPWM_OPR_B, MCPWM_DUTY_MODE_0); //call this each time, if operator was previously in low/high state
}
/**
* @brief motor stop
*/
static void brushed_motor_stop(mcpwm_unit_t mcpwm_num, mcpwm_timer_t timer_num)
{
mcpwm_set_signal_low(mcpwm_num, timer_num, MCPWM_OPR_A);
mcpwm_set_signal_low(mcpwm_num, timer_num, MCPWM_OPR_B);
}
/**
* @brief Configure MCPWM module for brushed dc motor
*/
static void mcpwm_example_brushed_motor_control(void *arg)
{
//1. mcpwm gpio initialization
mcpwm_example_gpio_initialize();
//2. initial mcpwm configuration
printf("Configuring Initial Parameters of mcpwm...\n");
mcpwm_config_t pwm_config;
pwm_config.frequency = 1000; //frequency = 500Hz,
pwm_config.cmpr_a = 0; //duty cycle of PWMxA = 0
pwm_config.cmpr_b = 0; //duty cycle of PWMxb = 0
pwm_config.counter_mode = MCPWM_UP_COUNTER;
pwm_config.duty_mode = MCPWM_DUTY_MODE_0;
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); //Configure PWM0A & PWM0B with above settings
while (1) {
brushed_motor_forward(MCPWM_UNIT_0, MCPWM_TIMER_0, 50.0);
vTaskDelay(2000 / portTICK_RATE_MS);
brushed_motor_backward(MCPWM_UNIT_0, MCPWM_TIMER_0, 30.0);
vTaskDelay(2000 / portTICK_RATE_MS);
brushed_motor_stop(MCPWM_UNIT_0, MCPWM_TIMER_0);
vTaskDelay(2000 / portTICK_RATE_MS);
}
}
void app_main(void)
{
printf("Testing brushed motor...\n");
xTaskCreate(mcpwm_example_brushed_motor_control, "mcpwm_examlpe_brushed_motor_control", 4096, NULL, 5, NULL);
}

View File

@@ -0,0 +1,6 @@
# The following lines of boilerplate have to be in your project's CMakeLists
# in this exact order for cmake to work correctly
cmake_minimum_required(VERSION 3.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(mcpwm_servo_control)

View File

@@ -0,0 +1,9 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := mcpwm_servo_control
include $(IDF_PATH)/make/project.mk

View File

@@ -0,0 +1,22 @@
| Supported Targets | ESP32 |
| ----------------- | ----- |
# MCPWM servo motor control Example
This example will show you how to use MCPWM module to control servo motor
Assign pulse width range and the maximum degree, accordingly the servo will move from 0 to maximum degree continuously
## Step 1: Pin assignment
* GPIO18 is assigned as the MCPWM signal for servo motor
## Step 2: Connection
* connect GPIO18 with servo pwm signal
* other two wires of servo motor are VCC and GND
## Step 3: Initialize MCPWM
* You need to set the frequency(generally 50 Hz) 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

View File

@@ -0,0 +1,2 @@
idf_component_register(SRCS "mcpwm_servo_control_example.c"
INCLUDE_DIRS ".")

View File

@@ -0,0 +1,3 @@
#
# Main Makefile. This is basically the same as a component makefile.
#

View File

@@ -0,0 +1,77 @@
/* servo motor control example
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 "esp_attr.h"
#include "driver/mcpwm.h"
#include "soc/mcpwm_periph.h"
//You can get these value from the datasheet of servo you use, in general pulse width varies between 1000 to 2000 mocrosecond
#define SERVO_MIN_PULSEWIDTH 1000 //Minimum pulse width in microsecond
#define SERVO_MAX_PULSEWIDTH 2000 //Maximum pulse width in microsecond
#define SERVO_MAX_DEGREE 90 //Maximum angle in degree upto which servo can rotate
static void mcpwm_example_gpio_initialize(void)
{
printf("initializing mcpwm servo control gpio......\n");
mcpwm_gpio_init(MCPWM_UNIT_0, MCPWM0A, 18); //Set GPIO 18 as PWM0A, to which servo is connected
}
/**
* @brief Use this function to calcute pulse width for per degree rotation
*
* @param degree_of_rotation the angle in degree to which servo has to rotate
*
* @return
* - calculated pulse width
*/
static uint32_t servo_per_degree_init(uint32_t degree_of_rotation)
{
uint32_t cal_pulsewidth = 0;
cal_pulsewidth = (SERVO_MIN_PULSEWIDTH + (((SERVO_MAX_PULSEWIDTH - SERVO_MIN_PULSEWIDTH) * (degree_of_rotation)) / (SERVO_MAX_DEGREE)));
return cal_pulsewidth;
}
/**
* @brief Configure MCPWM module
*/
void mcpwm_example_servo_control(void *arg)
{
uint32_t angle, count;
//1. mcpwm gpio initialization
mcpwm_example_gpio_initialize();
//2. initial mcpwm configuration
printf("Configuring Initial Parameters of mcpwm......\n");
mcpwm_config_t pwm_config;
pwm_config.frequency = 50; //frequency = 50Hz, i.e. for every servo motor time period should be 20ms
pwm_config.cmpr_a = 0; //duty cycle of PWMxA = 0
pwm_config.cmpr_b = 0; //duty cycle of PWMxb = 0
pwm_config.counter_mode = MCPWM_UP_COUNTER;
pwm_config.duty_mode = MCPWM_DUTY_MODE_0;
mcpwm_init(MCPWM_UNIT_0, MCPWM_TIMER_0, &pwm_config); //Configure PWM0A & PWM0B with above settings
while (1) {
for (count = 0; count < SERVO_MAX_DEGREE; count++) {
printf("Angle of rotation: %d\n", count);
angle = servo_per_degree_init(count);
printf("pulse width: %dus\n", angle);
mcpwm_set_duty_in_us(MCPWM_UNIT_0, MCPWM_TIMER_0, MCPWM_OPR_A, angle);
vTaskDelay(10); //Add delay, since it takes time for servo to rotate, generally 100ms/60degree rotation at 5V
}
}
}
void app_main(void)
{
printf("Testing servo motor.......\n");
xTaskCreate(mcpwm_example_servo_control, "mcpwm_example_servo_control", 4096, NULL, 5, NULL);
}