mirror of
https://gitee.com/beecue/fastbee.git
synced 2025-12-20 01:45:55 +08:00
添加智能灯固件代码
This commit is contained in:
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "mcpwm_basic_config_example.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "mcpwm_bldc_control_hall_sensor_example.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "mcpwm_brushed_dc_control_example.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "mcpwm_servo_control_example.c"
|
||||
INCLUDE_DIRS ".")
|
||||
@@ -0,0 +1,3 @@
|
||||
#
|
||||
# Main Makefile. This is basically the same as a component makefile.
|
||||
#
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user