wheel/main/mcpwm_bdc_control.c
2024-01-22 03:36:10 +08:00

312 lines
12 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "esp_timer.h"
#include "driver/pulse_cnt.h"
#include "bdc_motor.h"
#include "pid_ctrl.h"
#include "modbus.h"
static const char *TAG = "bdc_control";
// Enable this config, we will print debug formated string, which in return can be captured and parsed by Serial-Studio
#define SERIAL_STUDIO_DEBUG CONFIG_SERIAL_STUDIO_DEBUG
#define BDC_MCPWM_TIMER_RESOLUTION_HZ 10000000 // 10MHz, 1 tick = 0.1us
#define BDC_MCPWM_FREQ_HZ 25000 // 25KHz PWM
#define BDC_MCPWM_DUTY_TICK_MAX (BDC_MCPWM_TIMER_RESOLUTION_HZ / BDC_MCPWM_FREQ_HZ) // maximum value we can set for the duty cycle, in ticks
#define BDC_MCPWM_GPIO_A 12
#define BDC_MCPWM_GPIO_B 13
#define BDC_MCPWM_GPIO_A1 14
#define BDC_MCPWM_GPIO_B1 15
#define BDC_MCPWM_GPIO_A2 16
#define BDC_MCPWM_GPIO_B2 17
//编码器对应IO口
#define BDC_ENCODER_GPIO_A 34
#define BDC_ENCODER_GPIO_B 35
#define BDC_ENCODER_GPIO_A1 33
#define BDC_ENCODER_GPIO_B1 26
#define BDC_CH1_LOW0_GPIO 1
#define BDC_CH1_HI0_GPIO 2
#define BDC_CH1_LOW1_GPIO 3
#define BDC_CH1_HI1_GPIO 4
#define BDC_CH1_AOUT_GPIO 5
#define BDC_CH1_AOUT_GPIO 6
#define BDC_CH0_LOW0_GPIO 7
#define BDC_CH0_HI0_GPIO 8
#define BDC_CH0_LOW1_GPIO 9
#define BDC_CH0_HI1_GPIO 10
//编码器脉冲计数误差范围
#define BDC_ENCODER_PCNT_HIGH_LIMIT 1000
#define BDC_ENCODER_PCNT_LOW_LIMIT -1000
#define BDC_PID_LOOP_PERIOD_MS 1000 // calculate the motor speed every 10ms
#define BDC_PID_EXPECT_SPEED 400 // expected motor speed, in the pulses counted by the rotary encoder
typedef struct
{
bdc_motor_handle_t motor;
pcnt_unit_handle_t pcnt_encoder;
pid_ctrl_block_handle_t pid_ctrl;
esp_timer_handle_t pid_loop_timer;
int accumu_count;
int report_pulses;
int id; /* 标识一个电机 */
} motor_control_context_t;
typedef struct
{
/* 从Mudbus服务器端得到设置参数 */
uint16_t speed; /* 速度大小 */
uint16_t foreward; /* 速度方向现在只考虑前进和后退因此0正1负 但是期望未来该值为一个-1->1之间的数可以指导转弯 */
uint16_t inversion;
/* 发送到Modbus服务器显示的数据 */
uint16_t real_speedl; /* 真实速度 */
uint16_t real_forewardl; /* 真实方向 */
uint16_t real_motor_outputl; /* 真实电机输出 */
uint16_t real_motor_output_dirl; /* 注这里将方向单独拆出只是为了在mudbus调试器上方便观看 */
uint16_t real_speedr; /* 真实速度 */
uint16_t real_forewardr; /* 真实方向 */
uint16_t real_motor_outputr; /* 真实电机输出 */
uint16_t real_motor_output_dirr; /* 注这里将方向单独拆出只是为了在mudbus调试器上方便观看 */
} motor_control_t;
/* gWordVar寄存器组对应到motor_control_t的各个结构 */
motor_control_t *motor_control = (motor_control_t *)&gWordVar[0];
static bool example_pcnt_on_reach(pcnt_unit_handle_t unit, const pcnt_watch_event_data_t *edata, void *user_ctx)
{
int *accumu_count = (int *)user_ctx;
*accumu_count += edata->watch_point_value;
return false;
}
/* 定时器定时触发使用PID算法控速
* 注意:速度以每次采样期间编码器计数个数为衡量标准
* 待修正:对于转弯的逻辑,可能左右轮的速度是期望速度的不同函数关系
*/
static void pid_loop_cb(void *args)
{
static int last_pulse_count[2] = {0, 0};
motor_control_context_t *ctx = (motor_control_context_t *)args;
pcnt_unit_handle_t pcnt_unit = ctx->pcnt_encoder;
pid_ctrl_block_handle_t pid_ctrl = ctx->pid_ctrl;
bdc_motor_handle_t motor = ctx->motor;
/* 从编码器获得计数,得到实际速度 */
int cur_pulse_count = 0;
pcnt_unit_get_count(pcnt_unit, &cur_pulse_count);
cur_pulse_count += ctx->accumu_count;
int real_pulses = cur_pulse_count - last_pulse_count[ctx->id]; //新的脉冲数减去上一次的脉冲数为真实脉冲数
last_pulse_count[ctx->id] = cur_pulse_count;
ctx->report_pulses = real_pulses;
/* 从Modbus服务器端获得期望速度 */
/* 修正:转弯的速度逻辑还需要进行左右轮判断处理 */
int speed = motor_control->foreward == 0 ? motor_control->speed : -motor_control->speed;
/* 计算速度误差->增量式PID->控制电机输出 */
float error = speed - real_pulses; /* 注意:这里使用的隐式类型转换 */
float new_speed = 0;
pid_compute(pid_ctrl, error, &new_speed);
bdc_motor_set_speed(motor, (uint32_t)new_speed);
/* 更新Modbus端的数据 */
if (ctx->id == 0)
{
motor_control->real_speedl = real_pulses > 0 ? real_pulses : -real_pulses;
motor_control->real_forewardl = real_pulses > 0 ? 0 : 1;
motor_control->real_motor_outputl = new_speed > 0 ? new_speed : -new_speed;
motor_control->real_motor_output_dirl = new_speed > 0 ? 0 : 1;
}
else if (ctx->id == 1)
{
motor_control->real_speedr = real_pulses > 0 ? real_pulses : -real_pulses;
motor_control->real_forewardr = real_pulses > 0 ? 0 : 1;
motor_control->real_motor_outputr = new_speed > 0 ? new_speed : -new_speed;
motor_control->real_motor_output_dirr = new_speed > 0 ? 0 : 1;
}
}
void bdc_motor_init(uint32_t bdc_mcpwm_gpio_a, uint32_t bdc_mcpwm_gpio_b, int group_id, bdc_motor_handle_t *motor_ret)
{
ESP_LOGI(TAG, "Create DC motor");
bdc_motor_config_t motor_config = {
.pwm_freq_hz = BDC_MCPWM_FREQ_HZ,
.pwma_gpio_num = bdc_mcpwm_gpio_a,
.pwmb_gpio_num = bdc_mcpwm_gpio_b,
};
bdc_motor_mcpwm_config_t mcpwm_config = {
.group_id = group_id,
.resolution_hz = BDC_MCPWM_TIMER_RESOLUTION_HZ, // 0.1us精密度
};
bdc_motor_handle_t motor = NULL;
ESP_ERROR_CHECK(bdc_motor_new_mcpwm_device(&motor_config, &mcpwm_config, &motor)); //根据当前误差检查
*motor_ret = motor;
}
void bdc_encoder_init(uint32_t bdc_encoder_gpioa, uint32_t bdc_encoder_gpiob, int *on_reach_callbackarg, pcnt_unit_handle_t *pcnt_unit_ret)
{
ESP_LOGI(TAG, "Init pcnt driver to decode rotary signal");
/* 实例化pcntpcnt单元是自动分配无需特指单元 */
pcnt_unit_config_t unit_config = {
.high_limit = BDC_ENCODER_PCNT_HIGH_LIMIT,
.low_limit = BDC_ENCODER_PCNT_LOW_LIMIT,
};
pcnt_unit_handle_t pcnt_unit = NULL;
ESP_ERROR_CHECK(pcnt_new_unit(&unit_config, &pcnt_unit));
/* 设置过滤器宽度ns */
pcnt_glitch_filter_config_t filter_config = {
.max_glitch_ns = 1000,
};
ESP_ERROR_CHECK(pcnt_unit_set_glitch_filter(pcnt_unit, &filter_config));
/* 设置通道a */
pcnt_chan_config_t chan_a_config = {
.edge_gpio_num = bdc_encoder_gpioa,
.level_gpio_num = bdc_encoder_gpiob,
};
pcnt_channel_handle_t pcnt_chan_a = NULL;
ESP_ERROR_CHECK(pcnt_new_channel(pcnt_unit, &chan_a_config, &pcnt_chan_a));
/* 设置通道b */
pcnt_chan_config_t chan_b_config = {
.edge_gpio_num = bdc_encoder_gpiob,
.level_gpio_num = bdc_encoder_gpioa,
};
pcnt_channel_handle_t pcnt_chan_b = NULL;
ESP_ERROR_CHECK(pcnt_new_channel(pcnt_unit, &chan_b_config, &pcnt_chan_b));
/* 配置两通道模式,实现两个通道的边沿都能计数 */
ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan_a, PCNT_CHANNEL_EDGE_ACTION_DECREASE, PCNT_CHANNEL_EDGE_ACTION_INCREASE));
ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan_a, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));
ESP_ERROR_CHECK(pcnt_channel_set_edge_action(pcnt_chan_b, PCNT_CHANNEL_EDGE_ACTION_INCREASE, PCNT_CHANNEL_EDGE_ACTION_DECREASE));
ESP_ERROR_CHECK(pcnt_channel_set_level_action(pcnt_chan_b, PCNT_CHANNEL_LEVEL_ACTION_KEEP, PCNT_CHANNEL_LEVEL_ACTION_INVERSE));
/* 配置该pcnt的上下观察点以及溢出回调函数 */
ESP_ERROR_CHECK(pcnt_unit_add_watch_point(pcnt_unit, BDC_ENCODER_PCNT_HIGH_LIMIT));
ESP_ERROR_CHECK(pcnt_unit_add_watch_point(pcnt_unit, BDC_ENCODER_PCNT_LOW_LIMIT));
pcnt_event_callbacks_t pcnt_cbs = {
.on_reach = example_pcnt_on_reach, // accumulate the overflow in the callback
};
ESP_ERROR_CHECK(pcnt_unit_register_event_callbacks(pcnt_unit, &pcnt_cbs, on_reach_callbackarg));
ESP_ERROR_CHECK(pcnt_unit_enable(pcnt_unit));
ESP_ERROR_CHECK(pcnt_unit_clear_count(pcnt_unit));
ESP_ERROR_CHECK(pcnt_unit_start(pcnt_unit));
*pcnt_unit_ret = pcnt_unit;
}
/* 注:只将不同的电机的不同参数作为接口留出,里面也有需要配置的共同参数 */
void bdc_pid_init(float p, float i, float d, pid_ctrl_block_handle_t *pid_ctrl_ret)
{
ESP_LOGI(TAG, "Create PID control block");
pid_ctrl_parameter_t pid_runtime_param = {
.kp = p,
.ki = i,
.kd = d,
.cal_type = PID_CAL_TYPE_INCREMENTAL, // 增量式pid
.max_output = BDC_MCPWM_DUTY_TICK_MAX - 1, //最大输出(占空比-1、最小输出0
.min_output = 0,
.max_integral = 1000, //最大积分限制1000、最小积分限制-1000
.min_integral = -1000,
};
pid_ctrl_block_handle_t pid_ctrl = NULL;
pid_ctrl_config_t pid_config = {
.init_param = pid_runtime_param,
};
ESP_ERROR_CHECK(pid_new_control_block(&pid_config, &pid_ctrl));
*pid_ctrl_ret = pid_ctrl;
}
void bdc_ctrl_timer_init(motor_control_context_t *motor_ctrl_ctx, esp_timer_handle_t *pid_loop_timer_ret)
{
ESP_LOGI(TAG, "Create a timer to do PID calculation periodically");
const esp_timer_create_args_t periodic_timer_args = {
.callback = pid_loop_cb,
.arg = motor_ctrl_ctx,
.name = "pid_loop"
};
esp_timer_handle_t pid_loop_timer = NULL;
ESP_ERROR_CHECK(esp_timer_create(&periodic_timer_args, &pid_loop_timer));
*pid_loop_timer_ret = pid_loop_timer;
}
void bdc_motor_init_all(void)
{
/* 左轮 */
static motor_control_context_t motor_ctrl_ctxl = {
.id = 0, /* 左轮 */
.accumu_count = 0,
.pcnt_encoder = NULL,
.motor = NULL
};
bdc_motor_init(BDC_MCPWM_GPIO_A, BDC_MCPWM_GPIO_B, 0, &(motor_ctrl_ctxl.motor));
bdc_encoder_init(BDC_ENCODER_GPIO_A, BDC_ENCODER_GPIO_B, &(motor_ctrl_ctxl.accumu_count), &(motor_ctrl_ctxl.pcnt_encoder));
bdc_pid_init(0.6, 0.4, 0.2, &(motor_ctrl_ctxl.pid_ctrl));
bdc_ctrl_timer_init(&motor_ctrl_ctxl, &(motor_ctrl_ctxl.pid_loop_timer));
/* 右轮 */
static motor_control_context_t motor_ctrl_ctxr = {
.id = 0, /* 左轮 */
.accumu_count = 0,
.pcnt_encoder = NULL,
.motor = NULL
};
bdc_motor_init(BDC_MCPWM_GPIO_A1, BDC_MCPWM_GPIO_B1, 1, &(motor_ctrl_ctxr.motor));
bdc_encoder_init(BDC_ENCODER_GPIO_A1, BDC_ENCODER_GPIO_B1, &(motor_ctrl_ctxr.accumu_count), &(motor_ctrl_ctxr.pcnt_encoder));
bdc_pid_init(0.6, 0.4, 0.2, &(motor_ctrl_ctxr.pid_ctrl));
bdc_ctrl_timer_init(&motor_ctrl_ctxr, &(motor_ctrl_ctxr.pid_loop_timer));
ESP_LOGI(TAG, "Enable motor and forward");
ESP_ERROR_CHECK(bdc_motor_enable(motor_ctrl_ctxl.motor)); //启动电机
ESP_ERROR_CHECK(bdc_motor_forward(motor_ctrl_ctxl.motor)); //电机正转
ESP_ERROR_CHECK(bdc_motor_enable(motor_ctrl_ctxr.motor));
ESP_ERROR_CHECK(bdc_motor_forward(motor_ctrl_ctxr.motor));
ESP_LOGI(TAG, "Start motor speed loop");
ESP_ERROR_CHECK(esp_timer_start_periodic(motor_ctrl_ctxl.pid_loop_timer, BDC_PID_LOOP_PERIOD_MS * 1000));
ESP_ERROR_CHECK(esp_timer_start_periodic(motor_ctrl_ctxr.pid_loop_timer, BDC_PID_LOOP_PERIOD_MS * 1000));
}