/* * SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ #include #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 "driver/gpio.h" #include "bdc_motor.h" #include "pid_ctrl.h" #include "modbus.h" static const char *TAG = "bdc_control"; /* PWM输出的定时器配置 */ #define BDC_MCPWM_TIMER_RESOLUTION_HZ 10000000 /* 精度,即计数器的频率 10MHz, 1 tick = 0.1us */ #define BDC_MCPWM_FREQ_HZ 25000 /* PWM周期频率 25KHz PWM */ #define BDC_MCPWM_DUTY_TICK_MAX (BDC_MCPWM_TIMER_RESOLUTION_HZ / BDC_MCPWM_FREQ_HZ) /* PWM占空比最大值,由定时器计数频率和PWM周期频率计算得来 */ /* 编码器对应IO口 */ #define BDC_ENCODER_GPIO_A 35 #define BDC_ENCODER_GPIO_B 34 #define BDC_ENCODER_GPIO_A1 26 #define BDC_ENCODER_GPIO_B1 33 /* 有刷直流电机H桥控制,IR203s驱动芯片的LOW是低有效导通 */ #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_CH0_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 /* pid控制时间周期ms */ #define BDC_PID_LOOP_PERIOD_MS 50 typedef struct { int id; /* 标识一个电机 */ uint32_t bdc_low0_gpio; uint32_t bdc_low1_gpio; 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; } 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}; static int cur_pwm_compare[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; // ESP_LOGI(TAG, "cur_pulse_count %d : %d", ctx->id, cur_pulse_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 output = 0; pid_compute(pid_ctrl, error, &output); // printf("error: %f, output: %f\n", error, output); output += cur_pwm_compare[ctx->id]; cur_pwm_compare[ctx->id] = output; // ESP_LOGI(TAG, "err: %f, output: %d", error, (int)output); if (output > 0) ESP_ERROR_CHECK(bdc_motor_forward(ctx->motor, ctx->id)); else if (output < 0) { ESP_ERROR_CHECK(bdc_motor_reverse(ctx->motor, ctx->id)); output = -output; } else ESP_ERROR_CHECK(bdc_motor_brake(ctx->motor, ctx->id)); bdc_motor_set_speed(ctx->motor, (uint32_t)output, ctx->id); /* 更新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 = output > 0 ? output : -output; motor_control->real_motor_output_dirl = output > 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 = output > 0 ? output : -output; motor_control->real_motor_output_dirr = output > 0 ? 0 : 1; } } extern uint32_t low_gpio[2][2]; void bdc_motor_dir_gpio_init(motor_control_context_t motor_ctrl_ctx) { ESP_LOGI(TAG, "DC motor dir gpio init"); /* low mos管状态初始化,使用GPIO驱动,初始化高电平断开 */ gpio_config_t io_conf = {}; io_conf.intr_type = GPIO_INTR_DISABLE; io_conf.mode = GPIO_MODE_OUTPUT; io_conf.pin_bit_mask = ((1ULL<