概述

  1. 电机pwm实现–定时器pwm输出
  2. 电机速度采集–定时器编码器
  3. pid核心算法实现
  4. 电机控制函数实现
  5. 循环处理速度和pid计算的定时器
  6. 正点原子的调试pid助手使用

详细说明

1.电机pwm实现–定时器pwm输出

定时器器pwm输出主要涉:
在 PWM 输 出 模 式 下 ,通道根据TIMERx_CAR寄存器和TIMERx_CHxCV寄存器的值,输出
PWM波形。
根据计数模式,我们可以分为两种PWM波:

  • EAPWM(边沿对齐PWM)
  • CAPWM(中央对齐PWM)。

EAPWM 的周期由 TIMERx_CAR 寄存器值决定,占空比由 TIMERx_CHxCV 寄存器值决定。
图 15-42. EAPWM 时序图显示了 EAPWM 的输出波形和中断。
CAPWM 的周期由(2*TIMERx_CAR 寄存器值)决定,占空比由(2*TIMERx_CHxCV 寄存器
值)决定。 图 15-43. CAPWM 时序图显示了 CAPWM 的输出波形和中断。

在 PWM0 模 式 下 如 果 TIMERx_CHxCV 寄 存 器 的 值 大 于TIMERx_CAR寄存器的值,通道输出一直为有效电平。
在PWM0模式下(CHxCOMCTL==3’b110),如果TIMERx_CHxCV寄存器的值等于0,通道输出
一直为无效电平。

C文件

#include "bsp_time_pwm.h"/*定时器的基本初始化和打开更新中断enable: 是否使能定时器
*/
void pwm_time_base_init(int enable)
{/*定时器结构*/timer_parameter_struct timer_initpara;//开启定时器时钟rcu_periph_clock_enable(RCU_TIMER0);//结构体复位初始化timer_deinit(TIMER0);/* 初始化定时器结构体 */timer_struct_para_init(&timer_initpara);/* TIMER1 configuration */timer_initpara.prescaler         = 0; //定时器的时钟频率是108MHztimer_initpara.alignedmode       = TIMER_COUNTER_EDGE;//timer_initpara.counterdirection  = TIMER_COUNTER_UP;//向上计数timer_initpara.period            = TIM_PERIOD - 1; //重载值timer_initpara.clockdivision     = TIMER_CKDIV_DIV1; //不分频timer_initpara.repetitioncounter = 0;//重复计数timer_init(TIMER0, &timer_initpara);if (enable)timer_enable(TIMER0);//使能定时器elsetimer_disable(TIMER0);//失能定时器
}/*pwm 输出配置*/
void pwm_output_config(int enable)
{//生成pwm:timer_oc_parameter_struct timer_ocintpara;/* CH1,CH2 and CH3 configuration in PWM mode1 */timer_ocintpara.ocpolarity   = TIMER_OC_POLARITY_LOW;//pwm 输出极性timer_ocintpara.outputstate  = TIMER_CCX_ENABLE;//输出通道使能timer_ocintpara.ocnpolarity  = TIMER_OCN_POLARITY_HIGH;//pwm互补输出极性timer_ocintpara.outputnstate = TIMER_CCXN_ENABLE;//输出通道失能timer_ocintpara.ocidlestate  = TIMER_OC_IDLE_STATE_HIGH;//pwm输出通道空闲电平timer_ocintpara.ocnidlestate = TIMER_OCN_IDLE_STATE_LOW;//pwm输出互补通道空闲电平//配置指定通带timer_channel_output_config(TIMER0, TIMER_CH_0, &timer_ocintpara);timer_channel_output_config(TIMER0, TIMER_CH_1, &timer_ocintpara);timer_channel_output_config(TIMER0, TIMER_CH_2, &timer_ocintpara);/* CH0  50% */timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_0, 500);timer_channel_output_mode_config(TIMER0, TIMER_CH_0, TIMER_OC_MODE_PWM1);timer_channel_output_shadow_config(TIMER0, TIMER_CH_0, TIMER_OC_SHADOW_DISABLE);/* CH1  50% */timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_1, 500);timer_channel_output_mode_config(TIMER0, TIMER_CH_1, TIMER_OC_MODE_PWM1);timer_channel_output_shadow_config(TIMER0, TIMER_CH_1, TIMER_OC_SHADOW_DISABLE);/* CH1  50% */timer_channel_output_pulse_value_config(TIMER0, TIMER_CH_2, 500);timer_channel_output_mode_config(TIMER0, TIMER_CH_2, TIMER_OC_MODE_PWM1);timer_channel_output_shadow_config(TIMER0, TIMER_CH_2, TIMER_OC_SHADOW_DISABLE);//TIM0这句很重要,没有这句不输出pwm 如果不是高级定时器就不需要timer_primary_output_config(TIMER0, ENABLE);/* 自动重载使能 */timer_auto_reload_shadow_enable(TIMER0);/* 使能定时器 */if (enable)timer_enable(TIMER0);}
/*使能定时器的更新中断
*/
void pwm_enable_it(void)
{timer_interrupt_flag_clear(TIMER0, TIMER_INT_FLAG_UP);//清除更新中断标记timer_interrupt_enable(TIMER0, TIMER_INT_UP);//中断更新中断使能nvic_irq_enable(TIMER0_UP_IRQn, 1, 1);//使能中断并设置优先级}
/*中断服务函数
*/
//void TIMER0_UP_IRQHandler(void)
//{//    if (SET == timer_interrupt_flag_get(TIMER0, TIMER_INT_FLAG_UP))
//    {//        /* 清除更新中断标志 */
//        timer_interrupt_flag_clear(TIMER0, TIMER_INT_FLAG_UP);
//
//    }
//}/*通道涉及gpio配置
*/
void pwm_gpio_config(void)
{//使能gpio时钟rcu_periph_clock_enable(RCU_GPIOA);rcu_periph_clock_enable(RCU_GPIOB);//使能复用时钟rcu_periph_clock_enable(RCU_AF);/*配置gpio为复用上拉状态*/gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_8);gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_9);gpio_init(GPIOA, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_10);gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_13);gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_14);gpio_init(GPIOB, GPIO_MODE_AF_PP, GPIO_OSPEED_50MHZ, GPIO_PIN_15);}/*pwm 输出初始化
*/
void pwm_output_init(void)
{pwm_gpio_config();pwm_time_base_init(0);pwm_output_config(1);
}

头文件

#ifndef _BSP_TIME_PWM_H_
#define _BSP_TIME_PWM_H_#include "gd32f10x.h"#define WORK_FREQ 10000
#define TIM_PERIOD  108000000/WORK_FREQ //1MHz/100
/*使能定时器的更新中断
*/
void pwm_enable_it(void);
/*pwm 输出初始化
*/
void pwm_output_init(void);
#endif

2. 电机速度采集–定时器编码器

编码器博客:
https://blog.csdn.net/u010261063/article/details/125802765

C源文件


#include "bsp_quadrature_encoder.h"
int encoder_overflow = -1;//定时器溢出变量统计,因为定时器一起动就会发生一次溢出,故设置为-1,方便计算
void init_quadrature_encoder(void)
{timer_parameter_struct timer_initpara;timer_ic_parameter_struct timer_icinitpara;/* TIM2 时钟使能 */rcu_periph_clock_enable(RCU_TIMER2);rcu_periph_clock_enable(RCU_GPIOA);//使能复用时钟rcu_periph_clock_enable(RCU_AF);/**TIM2 GPIO ConfigurationPA6     ------> TIM2_CH1PA7     ------> TIM2_CH2*/gpio_init(GPIOA, GPIO_MODE_IPU, GPIO_OSPEED_50MHZ, GPIO_PIN_6 | GPIO_PIN_7);timer_deinit(TIMER2);timer_initpara.period = 65535;timer_initpara.prescaler = 0;timer_initpara.alignedmode = TIMER_COUNTER_EDGE;timer_initpara.counterdirection  = TIMER_COUNTER_UP;timer_initpara.clockdivision = TIMER_CKDIV_DIV1;timer_initpara.repetitioncounter = 0;timer_init(TIMER2, &timer_initpara);/* TIMER2 输入通道的配置 */timer_icinitpara.icpolarity  = TIMER_IC_POLARITY_RISING;//上升沿timer_icinitpara.icselection = TIMER_IC_SELECTION_DIRECTTI;//timer_icinitpara.icprescaler = TIMER_IC_PSC_DIV8;//改参数设置对于编码器不起作用timer_icinitpara.icfilter    = 0x05;timer_input_capture_config(TIMER2, TIMER_CH_0, &timer_icinitpara);timer_input_capture_config(TIMER2, TIMER_CH_1, &timer_icinitpara);/*设置定时器工作为编码器模式*/timer_quadrature_decoder_mode_config(TIMER2, TIMER_ENCODER_MODE2, TIMER_IC_POLARITY_RISING, TIMER_IC_POLARITY_RISING);//使能更新中断timer_interrupt_enable(TIMER2, TIMER_INT_UP);nvic_irq_enable(TIMER2_IRQn, 1, 1);//使能中断并设置优先级/* 开启自动重载-使能重载影子寄存器,使能后CAR寄存器的值设置后不会立即生效,只有在更新事件时才生效*/timer_auto_reload_shadow_enable(TIMER2);//使能定时器timer_enable(TIMER2);
}
/*定时器2的中断服务函数*/
void TIMER2_IRQHandler(void)
{if (SET == timer_interrupt_flag_get(TIMER2, TIMER_INT_FLAG_UP)){/* 清除更新中断标志 */timer_interrupt_flag_clear(TIMER2, TIMER_INT_FLAG_UP);/*根据定时器的计数方向判定电机正转和反转,正转计数器+1 ,反转计数器-1*/if((TIMER_CTL0(TIMER2)&TIMER_CTL0_DIR) == TIMER_CTL0_DIR)encoder_overflow--;else encoder_overflow++;}
}

头文件

#ifndef _BSP_ENCODER_H_
#define _BSP_ENCODER_H_#include "gd32f10x.h"
extern int encoder_overflow;
void init_quadrature_encoder(void);#endif

3. pid核心算法实现

C文件

#include "bsp_pid_kernel.h"
#include "stdio.h"
#include "bsp_time_pwm.h"PID_TypeDef g_speed_pid; /* 速度环 PID 参数结构体 */
/**
* @brief pid 初始化
* @param 无
* @retval 无
*/
void pid_init(void)
{g_speed_pid.SetPoint = 0; /* 设定目标值 */g_speed_pid.ActualValue = 0.0; /* 期望输出值 */g_speed_pid.SumError = 0.0; /* 积分值 */g_speed_pid.Error = 0.0; /* Error[1] */g_speed_pid.LastError = 0.0; /* Error[-1] */g_speed_pid.PrevError = 0.0; /* Error[-2] */g_speed_pid.Proportion = KP; /* 比例常数 Proportional Const */g_speed_pid.Integral = KI; /* 积分常数 Integral Const */g_speed_pid.Derivative = KD; /* 微分常数 Derivative Const */
}void set_pid_target(float dst_value )
{g_speed_pid.SetPoint = dst_value; /* 设定目标值 */
}void set_p_i_d(float kp,float  ki, float  kd)
{g_speed_pid.Proportion = kp; /* 比例常数 Proportional Const */g_speed_pid.Integral = ki; /* 积分常数 Integral Const */g_speed_pid.Derivative = kd; /* 微分常数 Derivative Const */
}#ifdef INCR_LOCT_SELECT
/*
* @brief pid 闭环控制----位置式PID
* @param *PID: PID 结构体变量地址
* @param Feedback_value:当前实际值
* @retval 期望输出值
*/
int32_t increment_pid_ctrl(PID_TypeDef *PID, float Feedback_value)
{PID->Error = (float)(PID->SetPoint - Feedback_value); /* 计算偏差 */// if(PID->SumError < PID->Integral * TIM_PERIOD)PID->SumError += PID->Error; /* 累计偏差 */PID->ActualValue = (PID->Proportion * PID->Error) /* 比例环节 */+ (PID->Integral * PID->SumError) /* 积分环节 */+ (PID->Derivative * (PID->Error - PID->LastError)); /* 微分环节 */PID->LastError = PID->Error; /* 存储偏差,用于下次计算 */return ((int32_t)(PID->ActualValue)); /* 返回计算后输出的数值 */
}
#else/*
* @brief pid 闭环控制--增量式PID
* @param *PID: PID 结构体变量地址
* @param Feedback_value:当前实际值
* @retval 期望输出值
*/
int32_t increment_pid_ctrl(PID_TypeDef *PID, float Feedback_value)
{PID->Error = (float)(PID->SetPoint - Feedback_value); /* 计算偏差 */PID->ActualValue +=/* 比例环节 */(PID->Proportion * (PID->Error - PID->LastError))/* 积分环节 */+ (PID->Integral * PID->Error)/* 微分环节 */+ (PID->Derivative * (PID->Error - 2 * PID->LastError + PID->PrevError));PID->PrevError = PID->LastError; /* 存储偏差,用于下次计算 */PID->LastError = PID->Error;return ((int32_t)(PID->ActualValue)); /* 返回计算后输出的数值 */
}
#endif

头文件

#ifndef _BSP_PID_KENEL_H_
#define _BSP_PID_KENEL_H_#include "stdint.h"#define INCR_LOCT_SELECT 0 /* 0:位置式 , 1:增量式 */#if INCR_LOCT_SELECT/* 增量式 PID 参数相关宏 */#define KP 0.00f /* P 参数*/#define KI 0.00f /* I 参数*/#define KD 0.10f /* D 参数*/#define SMAPLSE_PID_SPEED 50 /* 采样周期 单位 ms*/#else/* 位置式 PID 参数相关宏 */#define KP 3.1f /* P 参数*/#define KI 0.8f /* I 参数*/#define KD 0.005f /* D 参数*/#define SMAPLSE_PID_SPEED 50 /* 采样周期 单位 ms*/#endif#define  __IO volatile/*** @brief pid 初始化* @param 无* @retval 无*/void pid_init(void);void set_pid_target(float dst_value );void set_p_i_d(float kp,float  ki, float  kd);typedef struct{__IO float SetPoint; /* 目标值 */__IO float ActualValue; /* 期望输出值 */__IO float SumError; /* 偏差累计 */__IO float Proportion; /* 比例系数 P */__IO float Integral; /* 积分系数 I */__IO float Derivative; /* 微分系数 D */__IO float Error; /* Error[1],第 k 次偏差 */__IO float LastError; /* Error[-1],第 k-1 次偏差 */__IO float PrevError; /* Error[-2],第 k-2 次偏差 */} PID_TypeDef;extern PID_TypeDef g_speed_pid; /* 速度环 PID 参数结构体 */#ifdef INCR_LOCT_SELECT/** @brief pid 闭环控制----位置式PID* @param *PID: PID 结构体变量地址* @param Feedback_value:当前实际值* @retval 期望输出值*/int32_t increment_pid_ctrl(PID_TypeDef *PID,float Feedback_value);#else/** @brief pid 闭环控制 ----增量式PID* @param *PID: PID 结构体变量地址* @param Feedback_value:当前实际值* @retval 期望输出值*/int32_t increment_pid_ctrl(PID_TypeDef *PID,float Feedback_value);#endif
#endif

4. 电机控制函数实现

源文件

#ifndef _BSP_PID_KENEL_H_
#define _BSP_PID_KENEL_H_
#include "stdint.h"#define INCR_LOCT_SELECT 0 /* 0:位置式 , 1:增量式 */
#if INCR_LOCT_SELECT
/* 增量式 PID 参数相关宏 */
#define KP 0.00f /* P 参数*/
#define KI 0.00f /* I 参数*/
#define KD 0.10f /* D 参数*/
#define SMAPLSE_PID_SPEED 50 /* 采样周期 单位 ms*/
#else
/* 位置式 PID 参数相关宏 */
#define KP 3.1f /* P 参数*/
#define KI 0.8f /* I 参数*/
#define KD 0.005f /* D 参数*/
#define SMAPLSE_PID_SPEED 50 /* 采样周期 单位 ms*/
#endif#define  __IO volatile/**
* @brief pid 初始化
* @param 无
* @retval 无
*/
void pid_init(void);void set_pid_target(float dst_value );
void set_p_i_d(float kp,float  ki, float  kd);
typedef struct
{__IO float SetPoint; /* 目标值 */
__IO float ActualValue; /* 期望输出值 */
__IO float SumError; /* 偏差累计 */
__IO float Proportion; /* 比例系数 P */
__IO float Integral; /* 积分系数 I */
__IO float Derivative; /* 微分系数 D */
__IO float Error; /* Error[1],第 k 次偏差 */
__IO float LastError; /* Error[-1],第 k-1 次偏差 */
__IO float PrevError; /* Error[-2],第 k-2 次偏差 */
} PID_TypeDef;extern PID_TypeDef g_speed_pid; /* 速度环 PID 参数结构体 */#ifdef INCR_LOCT_SELECT
/*
* @brief pid 闭环控制----位置式PID
* @param *PID: PID 结构体变量地址
* @param Feedback_value:当前实际值
* @retval 期望输出值
*/
int32_t increment_pid_ctrl(PID_TypeDef *PID,float Feedback_value);
#else/*
* @brief pid 闭环控制 ----增量式PID
* @param *PID: PID 结构体变量地址
* @param Feedback_value:当前实际值
* @retval 期望输出值
*/
int32_t increment_pid_ctrl(PID_TypeDef *PID,float Feedback_value);
#endif
#endif

头文件

#ifndef _BSP_MOTOR_CTRL_H_
#define _BSP_MOTOR_CTRL_H_
/*启动电机*/
void start_motor(void);
/*停止电机*/
void stop_motor(void);
/*电机速度控制*/
int set_motor_speed(int speed);#endif

5. 基本定时器TIM3实现循环计算速度和调用pid计算函数

源文件

#include "bsp_base_time_it.h"
#include "gd32f10x.h"
#include "bsp_base_time_it.h"
#include "bsp_pid_kernel.h"
#include "bsp_motor_ctrl.h"
#include "stdio.h"
#include "bsp_time_pwm.h"
#include "debug.h"
#include "protocol.h"
#include "bsp_quadrature_encoder.h"/*使用定时器3循环计算速度和pid*/
#define USE_TIME TIMER3extern int encoder_overflow;#if SPEED_PID
float motor_speed;
#else
int motor_speed;
#endif/*使能定时器的更新中断
*/
void bsp_timer_enable_it()
{timer_interrupt_flag_clear(USE_TIME, TIMER_INT_FLAG_UP);//清除更新中断标记timer_interrupt_enable(USE_TIME, TIMER_INT_UP);//中断更新中断使能nvic_irq_enable(TIMER3_IRQn, 1, 1);//使能中断并设置优先级}void bsp_time_base_init(int enable)
{/*定时器结构*/timer_parameter_struct timer_initpara;//开启定时器时钟rcu_periph_clock_enable(RCU_TIMER3);//结构体复位初始化timer_deinit(USE_TIME);/* 初始化定时器结构体 */timer_struct_para_init(&timer_initpara);/* TIMER1 configuration */timer_initpara.prescaler         = 108 - 1; //定时器的时钟频率是108MHztimer_initpara.alignedmode       = TIMER_COUNTER_EDGE;//timer_initpara.counterdirection  = TIMER_COUNTER_UP;//向上计数timer_initpara.period            = 1000 - 1; //重载值timer_initpara.clockdivision     = TIMER_CKDIV_DIV1; //不分频timer_initpara.repetitioncounter = 0;//重复计数timer_init(USE_TIME, &timer_initpara);bsp_timer_enable_it();if (enable)timer_enable(USE_TIME);//使能定时器elsetimer_disable(USE_TIME);//失能定时器
}void caculate_motor_speed(void)
{#if SPEED_PIDstatic int last_cnt=0xffffffff;static float speed_arry[5];static int index=0;int cur_cnt = TIMER_CNT(TIMER2) +65536*encoder_overflow;if( last_cnt==0xffffffff){motor_speed=0;}else{speed_arry[index%5]=(cur_cnt-last_cnt)*50*60/16;}if(index%5==4){motor_speed = (speed_arry[0]+speed_arry[1]+speed_arry[2]+speed_arry[3]+speed_arry[4])*0.2;}index++;last_cnt=cur_cnt;
#elsestatic uint8_t flag=1;if(flag){flag=0;motor_speed=0;}else{motor_speed = TIMER_CNT(TIMER2) +65536*encoder_overflow;    }printf("speed %d \r\n",motor_speed);#endif}void motor_pid_ctrl(void)
{int speed;//pid计算speed = increment_pid_ctrl(&g_speed_pid,motor_speed);//控制电机pwmset_motor_speed(speed);int temp = motor_speed;int temp2=g_speed_pid.SetPoint;int duty = speed*1000/(TIM_PERIOD);//波形显示debug_send_wave_data(1,temp);debug_send_wave_data(2,temp2);debug_send_wave_data(3,duty);}
void time_out_1ms(void)
{static uint32_t ms=0;ms++;if(ms%20==19)//每20ms计算一次速度{caculate_motor_speed();}if(ms%100==99)//每100ms计算一次pid{motor_pid_ctrl();}
}/*中断服务函数
*/
void TIMER3_IRQHandler(void)
{if (SET == timer_interrupt_flag_get(USE_TIME, TIMER_INT_FLAG_UP)){/* 清除更新中断标志 */timer_interrupt_flag_clear(USE_TIME, TIMER_INT_FLAG_UP);//定时处理速度和pid计算time_out_1ms();}
}

头文件

#ifndef _BSP_BASE_TIMER_IT_H_
#define _BSP_BASE_TIMER_IT_H_//速度pid和位置pid的切换宏
#if SPEED_PID
extern float motor_speed;
#else
extern int motor_speed;
#endif
extern float motor_speed;
void bsp_time_base_init(int enable);#endif

6. 正点原子的调试pid助手使用

源文件

/******************************************************************************************************* @file        debug.c* @author      正点原子团队(ALIENTEK)* @version     V1.0* @date        2021-10-14* @brief       pid上位机调试 代码* @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司***************************************************************************************************** @attention** 实验平台:正点原子 F407电机开发板* 在线视频:www.yuanzige.com* 技术论坛:www.openedv.com/forum.php* 公司网址:www.alientek.com* 购买地址:openedv.taobao.com** 修改说明* V1.0 20211014* 第一次发布******************************************************************************************************/#include "debug.h"
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "gd32f10x.h"
#include "bsp_uart.h"#define __IO volatile
/*************************************** 第一部分 CRC校验 ********************************************//* CRC 高位字节值表 */
static const uint8_t s_crchi[] = {0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
};/* CRC 低位字节值表 */
const uint8_t s_crclo[] = {0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,0x43, 0x83, 0x41, 0x81, 0x80, 0x40
};/*** @brief       CRC16校验* @param       *_pBuf:数据地址* @param       _usLen:数据长度* @note        采用crc16-modbus,多项式hex:8005* @retval      校验结果*/
uint16_t crc16_calc(uint8_t *_pBuf, uint16_t _usLen)
{uint8_t ucCRCHi = 0xFF;                         /* 高CRC字节初始化 */uint8_t ucCRCLo = 0xFF;                         /* 低CRC 字节初始化 */uint16_t usIndex;                               /* CRC循环中的索引 */while (_usLen--){usIndex = ucCRCLo ^ *_pBuf++;               /* 计算CRC */ucCRCLo = ucCRCHi ^ s_crchi[usIndex];ucCRCHi = s_crclo[usIndex];}return ((uint16_t)ucCRCHi << 8 | ucCRCLo);      /* 返回校验结果,高字节在高位 */
}/*************************************** 第二部分 底层函数 ********************************************/__IO uint8_t debug_rev_data[DEBUG_REV_MAX_LEN];     /* 存放接收数据的数组(环形缓冲区) */
__IO uint8_t debug_rev_p = 0;                       /* 地址偏移量 */debug_data g_debug;
debug_data_rev debug_rev;/*** @brief       内存初始化* @param       *data:内存起始地址* @retval      无*/
void debug_obj_init(debug_data *data)
{size_t obj_size = sizeof(debug_data);memset(data, 0, (size_t)obj_size);             /* 把指定范围内存清零 */
}/*** @brief       上位机数据解析* @param       *data:接收的数据(地址)* @note        利用环形缓冲区来接收数据,再存进相应的结构体成员中* @retval      无*/
void debug_handle(uint8_t *data)
{uint8_t temp[DEBUG_REV_MAX_LEN];uint8_t i;if (debug_rev_p >= DEBUG_REV_MAX_LEN)          /* 超过缓冲区(数组)最大长度 */{debug_rev_p = 0;                           /* 地址偏移量清零 */}debug_rev_data[debug_rev_p] = *(data);         /* 取出数据,存进数组 */if (*data == DEBUG_DATA_END)                   /* 判断是否收到帧尾 */{if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4) % DEBUG_REV_MAX_LEN] == DEBUG_DATA_HEAD)                        /* 数据包长度为5个字节,判断第一个字节是否为帧头 */{for (i = 0; i < 2; i++){temp[i] = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4 + i) % DEBUG_REV_MAX_LEN];                         /* 取出帧头、数据类别,5个字节的数据包没有数据域 */}#if EN_CRC                                         /* 进行CRC校验 */if (crc16_calc(temp, 2) == ((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4 + 2) % DEBUG_REV_MAX_LEN] << 8) | \debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4 + 3) % DEBUG_REV_MAX_LEN]))
#endif{if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 4 + 1) % DEBUG_REV_MAX_LEN] == CMD_GET_ALL_DATA)           /* 判断数据类别是否为:获取全部参数 */{debug_upload_data(&g_debug, TYPE_STATUS);                                                                    /* 发送电机状态 */debug_upload_data(&g_debug, TYPE_SPEED);                                                                     /* 发送速度值 */debug_upload_data(&g_debug, TYPE_HAL_ENC);                                                                   /* 发送霍尔、编码器位置 */debug_upload_data(&g_debug, TYPE_VBUS);                                                                      /* 发送电压 */debug_upload_data(&g_debug, TYPE_AMP);                                                                       /* 发送电流 */debug_upload_data(&g_debug, TYPE_TEMP);                                                                      /* 发送温度 */debug_upload_data(&g_debug, TYPE_SUM_LEN);                                                                   /* 发送总里程 */debug_upload_data(&g_debug, TYPE_BEM);                                                                       /* 发送反电动势 */debug_upload_data(&g_debug, TYPE_MOTOR_CODE);                                                                /* 发送电机类型 */for (i = TYPE_PID1; i < TYPE_PID10; i++){debug_upload_data(&g_debug, i);                                                                          /* 发送PID参数 */}}}}if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5) % DEBUG_REV_MAX_LEN] == DEBUG_DATA_HEAD)                        /* 数据包长度为6个字节,判断第一个字节是否为帧头 */{for (i = 0; i < 3; i++){temp[i] = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + i) % DEBUG_REV_MAX_LEN];                         /* 取出帧头、数据类别、数据域 */}
#if EN_CRC                                         /* 进行CRC校验 */if (crc16_calc(temp, 3) == ((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 3) % DEBUG_REV_MAX_LEN] << 8) | \debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 4) % DEBUG_REV_MAX_LEN]))
#endif{switch (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 1) % DEBUG_REV_MAX_LEN])                           /* 判断数据类别 */{case CMD_SET_CTR_CODE:                                                                                       /* 下发控制指令 */debug_rev.Ctrl_code = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 2) % DEBUG_REV_MAX_LEN];break;case CMD_SET_CTR_MODE:                                                                                       /* 下发控制模式 */debug_rev.Ctrl_mode = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 5 + 2) % DEBUG_REV_MAX_LEN];break;}}}if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6) % DEBUG_REV_MAX_LEN] == DEBUG_DATA_HEAD)                        /* 数据包长度为7个字节,判断第一个字节是否为帧头 */{for (i = 0; i < 4; i++){temp[i] = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + i) % DEBUG_REV_MAX_LEN];                         /* 取出帧头、数据类别、数据域 */}
#if EN_CRC                                         /* 进行CRC校验 */if (crc16_calc(temp, 4) == ((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 4) % DEBUG_REV_MAX_LEN] << 8) | \debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 5) % DEBUG_REV_MAX_LEN]))
#endif{switch (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 1) % DEBUG_REV_MAX_LEN])                           /* 判断数据类别 */{case CMD_SET_SPEED:                                                                                          /* 设定电机速度 */*(debug_rev.speed) = (int16_t)((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 2) % DEBUG_REV_MAX_LEN] << 8) | \debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 3) % DEBUG_REV_MAX_LEN]);break;case CMD_SET_TORQUE:                                                                                         /* 设定转矩 */*(debug_rev.torque) = (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 2) % DEBUG_REV_MAX_LEN] << 8) | \debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 6 + 3) % DEBUG_REV_MAX_LEN];break;}}}if (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16) % DEBUG_REV_MAX_LEN] == DEBUG_DATA_HEAD)                       /* 数据包长度为17个字节,判断第一个字节是否为帧头 */{for (i = 0; i < 14; i++){temp[i] = debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + i) % DEBUG_REV_MAX_LEN];                        /* 取出帧头、数据类别、数据域 */}
#if EN_CRC                                          /* 进行CRC校验 */if (crc16_calc(temp, 14) == ((debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 14) % DEBUG_REV_MAX_LEN] << 8) | \debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 15) % DEBUG_REV_MAX_LEN]))
#endif{switch (debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 1) % DEBUG_REV_MAX_LEN])                          /* 判断数据类别 */{case CMD_SET_PID1:case CMD_SET_PID2:case CMD_SET_PID3:case CMD_SET_PID4:case CMD_SET_PID5:case CMD_SET_PID6:case CMD_SET_PID7:case CMD_SET_PID8:case CMD_SET_PID9:case CMD_SET_PID10:for (i = 0; i < 12; i++)                                                                                     /* 接收设定的PID参数 */{g_debug.pid[debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 1) % DEBUG_REV_MAX_LEN] - CMD_SET_PID1].pid.pidi8[i] = \debug_rev_data[(debug_rev_p + DEBUG_REV_MAX_LEN - 16 + 2 + i) % DEBUG_REV_MAX_LEN];}break;}}}}debug_rev_p ++;
}/*** @brief       数据上传* @param       *data:上传的数据(地址)* @param       upload_type:上传的数据类别* @retval      无*/
void debug_upload_data(debug_data *data, uint8_t upload_type)
{uint8_t cur_data, i;uint8_t upload_data[37];                                            /* 数据上传数组 */upload_data[0] = DEBUG_DATA_HEAD;                                   /* 数据包第1个字节(数组第0个元素),固定为帧头 */cur_data = 2;                                                       /* 数据域从第3个字节(数组第2个元素)开始 */switch (upload_type)                                                /* 判断数据类别 */{case TYPE_STATUS:                                               /* 设备状态 */upload_data[1] = upload_type;                               /* 数据包第2个字节(数组第1个元素),固定为数据类别 */upload_data[cur_data++] = data->status;                     /* 存入要发送的数据域 */break;case TYPE_SPEED:                                                /* 电机速度 */upload_data[1] = upload_type;upload_data[cur_data++] = (data->speed >> 8) & 0xFF;        /* 先存入速度值高8位(小端模式中u16赋给u8类型,只取低8位) */upload_data[cur_data++] = data->speed & 0xFF;               /* 再存入速度值低8位 */break;case TYPE_HAL_ENC:                                              /* 霍尔、编码器位置值 */upload_data[1] = upload_type;upload_data[cur_data++] = (data->hall_p) & 0x08;            /* 存入霍尔位置值 */upload_data[cur_data++] = (data->encode_p >> 8) & 0xFF;     /* 存入编码器位置值高8位 */upload_data[cur_data++] = (data->encode_p) & 0xFF;          /* 存入编码器位置值低8位 */break;case TYPE_VBUS:                                                                 /* 电压,范围 0~100.99 V */upload_data[1] = upload_type;upload_data[cur_data++] = ((uint8_t)data->bus_vol) % 101;                   /* 存入电压值整数部分,整数部分不允许超过100 */upload_data[cur_data++] = ((uint16_t)(data->bus_vol * 100)) % 100;          /* 存入电压值小数部分,小数部分不允许超过99 */break;case TYPE_AMP:                                                                  /* 电流 */upload_data[1] = upload_type;upload_data[cur_data++] = (((int16_t)(data->amp[0] * 1000)) >> 8) & 0xFF;   /* 存入U相电流高8位 */upload_data[cur_data++] = ((int16_t)(data->amp[0] * 1000)) & 0xFF;          /* 存入U相电流低8位 */upload_data[cur_data++] = (((int16_t)(data->amp[1] * 1000)) >> 8) & 0xFF;   /* 存入V相电流高8位 */upload_data[cur_data++] = ((int16_t)(data->amp[1] * 1000)) & 0xFF;          /* 存入V相电流低8位 */upload_data[cur_data++] = (((int16_t)(data->amp[2] * 1000)) >> 8) & 0xFF;   /* 存入W相电流高8位 */upload_data[cur_data++] = ((int16_t)(data->amp[2] * 1000)) & 0xFF;          /* 存入W相电流低8位 */break;case TYPE_TEMP:                                                                 /* 温度 */upload_data[1] = upload_type;upload_data[cur_data++] = (uint8_t)(data->temp[0] + 50);                    /* 存入驱动板温度 */upload_data[cur_data++] = (uint8_t)(data->temp[1] + 50);                    /* 存入电机温度 */break;case TYPE_SUM_LEN:                                                              /* 总里程 */upload_data[1] = upload_type;upload_data[cur_data++] = (data->sum_len >> 56) & 0xFF;                     /* 存入总里程 56~63 位 */upload_data[cur_data++] = (data->sum_len >> 48) & 0xFF;                     /* 存入总里程 48~55 位 */upload_data[cur_data++] = (data->sum_len >> 40) & 0xFF;                     /* 存入总里程 40~47 位 */upload_data[cur_data++] = (data->sum_len >> 32) & 0xFF;                     /* 存入总里程 32~39 位 */upload_data[cur_data++] = (data->sum_len >> 24) & 0xFF;                     /* 存入总里程 24~31 位 */upload_data[cur_data++] = (data->sum_len >> 16) & 0xFF;                     /* 存入总里程 16~23 位 */upload_data[cur_data++] = (data->sum_len >> 8) & 0xFF;                      /* 存入总里程 8~15 位 */upload_data[cur_data++] = (data->sum_len >> 0) & 0xFF;                      /* 存入总里程 0~7 位 */break;case TYPE_BEM:                                                                  /* 反电动势 */upload_data[1] = upload_type;upload_data[cur_data++] = (int8_t)data->bem[0];                             /* 存入U相反电动势电压整数部分 */upload_data[cur_data++] = ((int16_t)(data->bem[0] * 100)) % 100;            /* 存入U相反电动势电压小数部分 */upload_data[cur_data++] = (int8_t)data->bem[1];                             /* 存入V相反电动势电压整数部分 */upload_data[cur_data++] = ((int16_t)(data->bem[1] * 100)) % 100;            /* 存入V相反电动势电压小数部分 */upload_data[cur_data++] = (int8_t)data->bem[2];                             /* 存入W相反电动势电压整数部分 */upload_data[cur_data++] = ((int16_t)(data->bem[2] * 100)) % 100;            /* 存入W相反电动势电压小数部分 */break;case TYPE_MOTOR_CODE:                                                           /* 电机类型 */upload_data[1] = upload_type;upload_data[cur_data++] = data->motor_code;                                 /* 存入电机类型 */break;case TYPE_TORQUE:                                                               /* 扭矩 */upload_data[1] = upload_type;upload_data[cur_data++] = (((int16_t)(data->torque * 1000)) >> 8) & 0xFF;   /* 存入扭矩值整数部分 */upload_data[cur_data++] = ((int16_t)(data->torque * 1000)) & 0xFF;          /* 存入扭矩值小数部分 */break;case TYPE_POWER:                                                                /* 功率 */upload_data[1] = upload_type;upload_data[cur_data++] = (((int16_t)(data->power * 100)) >> 8) & 0xFF;     /* 存入功率值高8位 */upload_data[cur_data++] = ((int16_t)(data->power * 100)) & 0xFF;            /* 存入功率值低8位 */break;case TYPE_PID1:                                                                 /* PID参数组别 */case TYPE_PID2:case TYPE_PID3:case TYPE_PID4:case TYPE_PID5:case TYPE_PID6:case TYPE_PID7:case TYPE_PID8:case TYPE_PID9:case TYPE_PID10:upload_data[1] = upload_type;for (i = 0; i < 3; i++)                                            /* 循环存入P、I、D系数值,每个系数占4个字节 */{upload_data[cur_data++] = data->pid[upload_type - TYPE_PID1].pid.pidi8[i * 4 + 0];upload_data[cur_data++] = data->pid[upload_type - TYPE_PID1].pid.pidi8[i * 4 + 1];upload_data[cur_data++] = data->pid[upload_type - TYPE_PID1].pid.pidi8[i * 4 + 2];upload_data[cur_data++] = data->pid[upload_type - TYPE_PID1].pid.pidi8[i * 4 + 3];}break;case TYPE_USER_DATA:                                                   /* 波形数据 */upload_data[1] = upload_type;for (i = 0; i < 16; i++)                                           /* 循环存入1~16个通道波形数据 */{upload_data[cur_data++] = (data->user_data[i] >> 8) & 0xFF;    /* 存入波形数据高8位 */upload_data[cur_data++] =  data->user_data[i] & 0xFF;          /* 存入波形数据低8位 */}break;default :upload_data[1] = 0xFE;                                             /* 数据类别错误,存入错误码0xFE */break;}if (upload_data[1] == 0xFE)                                                /* 数据类别错误,直接跳出 */{return;}else                                                                       /* 数据类别正确 */{uint16_t crc_res = crc16_calc(&(upload_data[0]), cur_data);            /* 进行CRC校验 */upload_data[cur_data++] = (crc_res >> 8) & 0xFF;                       /* 存入校验结果高8位 */upload_data[cur_data++] = (crc_res) & 0xFF;                            /* 存入校验结果低8位 */upload_data[cur_data++] = DEBUG_DATA_END;                              /* 存入帧尾 *///HAL_UART_Transmit(&g_uart1_handle, upload_data, cur_data, 0xFFFF);     /* 发送数据到上位机 */bsp_transmit(USART0,upload_data, cur_data, 0xFFFFFFFF); }
}/*************************************** 第三部分 应用层函数 ********************************************//*** @brief       初始化调试* @param       无* @retval      无*/
void debug_init(void)
{debug_obj_init(&g_debug);            /* 初始化所需内存 */
}/*** @brief       设置目标速度范围* @param       max_limit:最大值* @param       min_limit:最小值(反转时最大速度)* @param       step_max : 最大突变值* @retval      无*/
void debug_set_point_range(float max_limit, float min_limit, float step_max)
{static float step_temp = 0.0;if (abs((int)(*debug_rev.speed - step_temp)) > step_max)     /* 判断速度突变是否超过允许范围 */{*debug_rev.speed = step_temp;                            /* 超过最大突变值,保持原来速度 */}step_temp = *debug_rev.speed;                                /* 保存本次速度 */if (*debug_rev.speed >= max_limit)                           /* 超过限速 */{*debug_rev.speed = max_limit;                            /* 配置为最大允许速度 */}if (*debug_rev.speed <= min_limit)                           /* 超过限速 */{*debug_rev.speed = min_limit;                            /* 配置为最大允许速度 */}
}/*************************************** 开发板 ——> 上位机 ********************************************//*** @brief       PID数据上传* @param       PIDx      :PID组(1~10)* @param       *SetPoint :目标速度地址* @param       P、I、D   :PID参数* @retval      无*/
void debug_send_initdata(upload_type PIDx, float *SetPoint, float P, float I, float D)
{debug_rev.speed = (float *)(SetPoint);          /* 开发板和上位机共用一个PID目标值的内存地址,数据同步更方便 */g_debug.pid[PIDx - TYPE_PID1].pid.pidf[0] = P;  /* 传入P值 */g_debug.pid[PIDx - TYPE_PID1].pid.pidf[1] = I;  /* 传入I值 */g_debug.pid[PIDx - TYPE_PID1].pid.pidf[2] = D;  /* 传入D值 */debug_upload_data(&g_debug, PIDx);              /* 发送PID参数 */
}/*** @brief       电流数据上传* @param       U_I、V_I、W_I :三相电流数据* @note        如果只有单相,习惯用U_I上传* @retval      无*/
void debug_send_current(float U_I, float V_I, float W_I)
{g_debug.amp[0] = U_I;                           /* 传入U相电流值 */g_debug.amp[1] = V_I;                           /* 传入V相电流值 */g_debug.amp[2] = W_I;                           /* 传入W相电流值 */debug_upload_data(&g_debug, TYPE_AMP);          /* 发送电流数据 */
}/*** @brief       电压数据上传* @param       valtage :电压数据* @retval      无*/
void debug_send_valtage(float valtage)
{g_debug.bus_vol = valtage;                      /* 传入电压值 */debug_upload_data(&g_debug, TYPE_VBUS);         /* 发送电压数据 */
}/*** @brief       功率数据上传* @param       power :功率数据* @retval      无*/
void debug_send_power(float power)
{g_debug.power = power;                          /* 传入功率值 */debug_upload_data(&g_debug, TYPE_POWER);        /* 发送功率数据 */
}/*** @brief       速度数据上传* @param       speed :速度数据* @retval      无*/
void debug_send_speed(float speed)
{g_debug.speed = (int16_t)(speed);               /* 传入速度值 */debug_upload_data(&g_debug, TYPE_SPEED);        /* 发送速度数据 */
}/*** @brief       总里程数据上传* @param       len :总里程数据* @retval      无*/
void debug_send_distance(uint64_t len)
{g_debug.sum_len = len;                          /* 传入总里程值 */debug_upload_data(&g_debug, TYPE_SUM_LEN);      /* 发送总里程数据 */
}/*** @brief       温度数据上传* @param       motor_temp :电机温度* @param       board_temp :驱动板温度* @retval      无*/
void debug_send_temp(float motor_temp, float board_temp)
{g_debug.temp[0] = board_temp;                   /* 传入驱动板温度值 */g_debug.temp[1] = motor_temp;                   /* 传入电机温度值 */debug_upload_data(&g_debug, TYPE_TEMP);         /* 发送温度数据 */
}/*** @brief       电机状态上传* @param       motor_codestae :电机状态* @retval      无*/
void debug_send_motorstate(motor_state motor_codestae)
{g_debug.status = motor_codestae;                /* 传入电机状态 */debug_upload_data(&g_debug, TYPE_STATUS);       /* 发送电机状态 */
}/*** @brief       电机类型上传* @param       motorcode :电机类型* @retval      无*/
void debug_send_motorcode(motor_code motorcode)
{g_debug.motor_code = motorcode;                 /* 传入电机类型 */debug_upload_data(&g_debug, TYPE_MOTOR_CODE);   /* 发送电机类型 */
}/*** @brief       波形数据上传* @param       chx :通道,取值1~16* @param       wave:数据* @retval      无*/
void debug_send_wave_data(uint8_t chx, int16_t wave)
{g_debug.user_data[chx - 1] = wave;              /* 选择通道,传入数据 */debug_upload_data(&g_debug, TYPE_USER_DATA);    /* 发送波形数据 */
}/*************************************** 上位机 ——> 开发板 ********************************************//*** @brief       上位机PID参数接收* @param       PIDx    :PID组(1~10)* @param       P、I、D :PID参数* @retval      无*/
void debug_receive_pid(upload_type PIDx, float *P, float *I, float *D)
{*P = g_debug.pid[PIDx - TYPE_PID1].pid.pidf[0]; /* 接收P参数 */*I = g_debug.pid[PIDx - TYPE_PID1].pid.pidf[1]; /* 接收I参数 */*D = g_debug.pid[PIDx - TYPE_PID1].pid.pidf[2]; /* 接收D参数 */
}/*** @brief       上位机命令接收* @param       无* @retval      0:无效,1:停机,2:运行,3:刹车*/
uint8_t debug_receive_ctrl_code(void)
{static uint8_t rec_r = 0;if (debug_rev.Ctrl_code >= 0x01 && debug_rev.Ctrl_code <= 0x03) /* 判断命令范围是否正确 */{rec_r++;if (rec_r >= 2){rec_r = 0;debug_rev.Ctrl_code = 0;}return debug_rev.Ctrl_code;                 /* 返回命令 */}return 0;
}

头文件

/******************************************************************************************************* @file        debug.h* @author      正点原子团队(ALIENTEK)* @version     V1.0* @date        2021-10-14* @brief       pid上位机调试 代码* @license     Copyright (c) 2020-2032, 广州市星翼电子科技有限公司***************************************************************************************************** @attention** 实验平台:正点原子 F407电机开发板* 在线视频:www.yuanzige.com* 技术论坛:www.openedv.com/forum.php* 公司网址:www.alientek.com* 购买地址:openedv.taobao.com** 修改说明* V1.0 20211014* 第一次发布******************************************************************************************************/#ifndef __DEBUG_H
#define __DEBUG_H#include "stdint.h"#define DEBUG_ENABLE       0         /* PID调试使能 */
#define EN_CRC             1         /* CRC校验,0:关闭,1:开启 */#define DEBUG_DATA_HEAD   0xC5       /* 帧头 */
#define DEBUG_DATA_END    0x5C       /* 帧尾 *//*********************************** 开发板 ——> 上位机 *******************************************//* 数据类别 */
typedef enum
{TYPE_ERROR          = 0x0F,      /* 故障类型 */TYPE_STATUS         = 0x10,      /* 设备状态 */TYPE_SPEED          = 0x11,      /* 速度 */TYPE_HAL_ENC        = 0x12,      /* 霍尔、编码器位置值 */TYPE_VBUS           = 0x13,      /* 电压 */TYPE_AMP            = 0x14,      /* 电流 */TYPE_TEMP           = 0x15,      /* 驱动板温度 */TYPE_SUM_LEN        = 0x16,      /* 总里程 */TYPE_BEM            = 0x17,      /* 反电动势 */TYPE_MOTOR_CODE     = 0x18,      /* 电机类型 */TYPE_TORQUE         = 0x19,      /* 扭矩 */TYPE_POWER          = 0x1A,      /* 功率 */TYPE_PID1           = 0x20,      /* 用户PID1参数上报 */TYPE_PID2           = 0x21,      /* PID2 */TYPE_PID3           = 0x22,      /* PID3 */TYPE_PID4           = 0x23,      /* PID4 */TYPE_PID5           = 0x24,      /* PID5 */TYPE_PID6           = 0x25,      /* PID6 */TYPE_PID7           = 0x26,      /* PID7 */TYPE_PID8           = 0x27,      /* PID8 */TYPE_PID9           = 0x28,      /* PID9 */TYPE_PID10          = 0x29,      /* PID10 */TYPE_USER_DATA      = 0x30,      /* 波形数据上报 */
}upload_type;/* 故障类型 */
typedef enum
{HALL_ENC_ERROR      = 0x01,      /* 编码器、霍尔错误 */OVERSPEED_ERROR     = 0x02,      /* 电机过速 */DB_OVERTEMP_ERROR   = 0x04,      /* 驱动板过温 */M_OVERTEMP_ERROR    = 0x08,      /* 电机过温 */DB_OVERVOL_ERROR    = 0x10,      /* 驱动板过压 */DB_UNDERVOL_ERROR   = 0x10,      /* 驱动板欠压 */DB_OVERCUR_ERROR    = 0x10,      /* 驱动板过流 */UNKNOWN_ERROR       = 0x10,      /* 未知错误 */
}motor_error;/* 电机状态 */
typedef enum
{IDLE_STATE          = 0x00,      /* 空闲状态 */RUN_STATE           = 0x01,      /* 运行状态 */ERROR_STATE         = 0x02,      /* 错误状态 */LRTOUT_STATE        = 0x03,      /* 堵转超时 */BREAKED_STATE       = 0x04,      /* 刹车 */
}motor_state;/* 电机类型 */
typedef enum
{DC_MOTOR            = 0x10,      /* 直流有刷电机 */BLDC_MOTOR          = 0x11,      /* 直流无刷电机 */PMSM_MOTOR          = 0x12,      /* 永磁同步电机 */STEP_MOTOR          = 0x13,      /* 步进电机 */SERVO_MOTOR         = 0x14,      /* 伺服电机 */EXCHANG_MOTOR       = 0x15,      /* 变频器(三相交流异步电机) */HELM_MOTOR          = 0x16,      /* 舵机 */
}motor_code;/* PID参数存放结构体 */
typedef struct
{union{float pidf[3];               /* PID参数发送存放 */int8_t pidi8[12];            /* PID参数接收存放 */}pid;
}pid_struct;/* 参数存放结构体(开发板——>上位机) */
typedef struct
{uint8_t status;                  /* 电机状态 */int16_t speed;                   /* 电机速度 */uint8_t hall_p;                  /* 霍尔位置值 */uint16_t encode_p;               /* 编码器位置值 */float bus_vol;                   /* 电压 */float amp[3];                    /* 电流 */float temp[2];                   /* 温度 */uint64_t sum_len;                /* 总里程 */float bem[3];                    /* 反电动势 */uint8_t motor_code;              /* 电机类型 */float torque;                    /* 扭矩 */float power;                     /* 功率 */pid_struct pid[10];              /* 10组PID参数(收发共用) */int16_t user_data[16];           /* 波形数据 */
}debug_data;extern debug_data g_debug;           /* 发送变量 *//*********************************** 上位机  ——>  开发板 *******************************************//* 数据类别 */
typedef enum
{CMD_GET_ALL_DATA    = 0x19,      /* 获取全部参数 */CMD_SET_CTR_CODE    = 0x21,      /* 下发控制指令 */CMD_SET_CTR_MODE    = 0x22,      /* 下发控制模式 */CMD_SET_SPEED       = 0x23,      /* 设定速度 */CMD_SET_TORQUE      = 0x24,      /* 设定转矩 */CMD_SET_VF_VOL      = 0x25,      /* 设定VF电压 */CMD_SET_VF_IF_FRE   = 0x26,      /* 设定V/F、IF频率 */CMD_SET_IF_CUR      = 0x27,      /* 设定IF电流 */CMD_SET_DQ_CUR_D    = 0x28,      /* 设定DQ电流D */CMD_SET_DQ_CUR_Q    = 0x29,      /* 设定DQ电流Q */CMD_SET_PID1        = 0x31,      /* 设定PID1参数 */CMD_SET_PID2        = 0x32,      /* PID2 */CMD_SET_PID3        = 0x33,      /* PID3 */CMD_SET_PID4        = 0x34,      /* PID4 */CMD_SET_PID5        = 0x35,      /* PID5 */CMD_SET_PID6        = 0x36,      /* PID6 */CMD_SET_PID7        = 0x37,      /* PID7 */CMD_SET_PID8        = 0x38,      /* PID8 */CMD_SET_PID9        = 0x39,      /* PID9 */CMD_SET_PID10       = 0x3A,      /* PID10 */
}cmd_type;/* 下发控制指令 */
typedef enum
{HALT_CODE           = 0x01,      /* 停机 */RUN_CODE            = 0x02,      /* 运行 */BREAKED             = 0x03,      /* 刹车 */
}cmd_code;/* 下发控制模式 */
typedef enum
{SPEED_MODE          = 0x01,      /* 转速模式 */TORQUE_MODE         = 0x02,      /* 转矩模式 */IF_MODE             = 0x03,      /* IF模式 */VF_MODE             = 0x04,      /* VF模式 */DQ_MODE             = 0x05,      /* DQ模式 */
}cmd_mode;/* 参数存放结构体(上位机——>开发板) */
typedef struct
{uint8_t Ctrl_code;uint8_t Ctrl_mode;float *speed;float *torque;float pid[3];
}debug_data_rev;extern debug_data_rev debug_rev;     /* 接收变量 */#define DEBUG_REV_MAX_LEN   17       /* 接收数据最大长度 *//*****************************************************************************************************//* 底层驱动函数 */
void debug_obj_init(debug_data *data);                                              /* 内存初始化 */
void debug_handle(uint8_t *data);                                                   /* 上位机数据解析 */
void debug_upload_data(debug_data * data, uint8_t upload_type);                     /* 数据上传 *//* 应用层函数 */
void debug_init(void);                                                              /* 初始化调试 */
void debug_set_point_range(float max_limit,float min_limit,float step_max);         /* 设置目标速度范围 */void debug_send_initdata(upload_type PIDx,float *SetPoint,float P,float I,float D); /* PID初始化数据上传 */
void debug_send_current(float U_I,float V_I,float W_I);                             /* 电流数据上传 */
void debug_send_valtage(float valtage);                                             /* 电压数据上传 */
void debug_send_power(float power);                                                 /* 功率数据上传 */
void debug_send_speed(float speed);                                                 /* 速度数据上传 */
void debug_send_distance(uint64_t len);                                             /* 总里程数据上传 */
void debug_send_temp(float motor_temp,float board_temp);                            /* 温度数据上传 */
void debug_send_motorstate(motor_state motor_codestae);                             /* 电机状态上传 */
void debug_send_motorcode(motor_code motorcode);                                    /* 电机类型上传 */
void debug_send_wave_data(uint8_t chx,int16_t wave);                                /* 波形数据上传 */void debug_receive_pid(upload_type PIDx,float *P,float *I,float *D);                /* 上位机PID参数接收 */
uint8_t debug_receive_ctrl_code(void);                                              /* 上位机命令接收 */#endif

原子个pid调试工具移植

  1. 串口1的数据发送移植
  2. 接收到的数据调用,没接收到一个数据就调用一次
debug_handle(rxbuffer+i);
  1. 串口发送函数实现
uint32_t bsp_transmit(const uint32_t usart_periph,const uint8_t *data,const uint32_t size,const uint32_t timeout)
{for(int i=0;i<size;i++){cnt=10000;while(RESET == usart_flag_get(USART0, USART_FLAG_TBE) &&cnt--);usart_data_transmit(USART0, (uint8_t)data[i]);}
}
  1. 数据处理
void atk_debug()
{uint8_t debug_cmd = 0;//更新pid参数debug_receive_pid(TYPE_PID1, (float *)&g_speed_pid.Proportion,(float *)&g_speed_pid.Integral, (float *)&g_speed_pid.Derivative);//debug_set_point_range(300, -300, 300);              /* 设置目标速度范围 */debug_cmd = debug_receive_ctrl_code();              /* 读取上位机指令 *///电机的刹车控制if (debug_cmd == BREAKED)                           /* 电机刹车 */{stop_motor();                                 /* 停止电机 */pid_init();                                     /* 重置pid参数 */debug_send_motorstate(BREAKED_STATE);           /* 上传电机状态(刹车) */debug_send_initdata(TYPE_PID1, (float *)(&g_speed_pid.SetPoint), KP, KI, KD);} //电机启动控制else if (debug_cmd == RUN_CODE)                     /* 电机运行 */{  start_motor();                                /* 开启电机 */g_speed_pid.SetPoint = 30;                      /* 设置目标速度:30 RPM *//* 标记电机启动 */debug_send_motorstate(RUN_STATE);               /* 上传电机状态(运行) */}
}
  1. 波形数据发送函数:注意是16位 short
void debug_send_wave_data(uint8_t chx,int16_t wave);                                /* 波形数据上传 */

整个工程代码,查看本次提交

https://gitee.com/chejia12/gd32-f103-c8-t6_-template

commit d83b648239fe33684442d3a32301a499da1b2581 (HEAD -> master)
Author: chejia <1085582540@qq.com>
Date:   Tue Jul 26 16:35:33 2022 +0800位置pid和速度pid

有刷电机的速度pid-位置pid算法相关推荐

  1. 【电机应用控制】——直流有刷电机驱动板/编码器介绍PID算法实操代码思路

    目录 前言 一.电机简介 二.直流有刷电机 1.基本知识 2.直流有刷驱动板 3.编码器介绍 三.PID算法 四.实操思路 1.单环控制 2.双环控制 3.三环控制 拓:闭环死区 总结 前言 声明:学 ...

  2. 串级PID 位置环+速度环

    1位置环和速度环的串级pid,首先要记住,位置环的输出是速度环的输入,最后控制输出为速度环的输出. 速度环的PID控制器 代码如下 float Velocity_KP_A=400,Velocity_K ...

  3. 用纯C语言分别实现增量式与位置式的PID自整定算法

    对于增量式PID自整定算法,C语言代码如下: #include <stdio.h>#define SAMPLE_TIME 0.1 // 采样时间 #define KP_DEFAULT 0. ...

  4. 红外循迹传感器PID循迹算法

    红外循迹传感器PID循迹算法 前一段时间参加了一个小车循迹竞速的比赛,获得了一个还行的成绩,所以在这里想把里面的核心部分PID寻线算法给大家分享一下. 关于做好的实物视频我上传到了B站,大家可以点击查 ...

  5. 我的PID学习历程---PID位置式和增量式

    文章目录 起因 一.什么是开环系统? 二.什么是PID? KP,KI,KD三个参数的作用 三.PID算法的离散化 1.什么是位置式PID? 2.位置式PID实现 3.什么是增量式PID? 4.增量式P ...

  6. 【PID控制原理及其算法】

    前言 本文以自己的学习过程总结而来,将自己的经验写出来以供大家一起学习,如有错误请多指教 一.PID是什么? PID就是比例.积分.微分,PID算法可以说是在自动控制原理中比较经典的一套算法,在现实生 ...

  7. 学习制作平衡小车:(四)PID学习、位置PID参数整定以及匿名上位机显示

    一.PID学习 PID的知识网上有很多教学讲解的非常清楚,可以再参考学习[平衡小车之家]的视频教程. 二.位置PID学习 位置PID控制公式为:Pwm=Kp*e(K)+Ki*∑e(K)+Kd[e(K) ...

  8. 对串级PID控温算法的解析

    ​​​​​​​目录 前言 单级PID 串级PID 系统分析 算法分析 总结 前言 笔者在做项目的过程中,需要对一个目标物体做精确控温,精度要求±1℃,需要在两分钟内使用电阻发热贴将温度由20控制到41 ...

  9. DSPIC30F BLDC三相无刷电机驱动资料(速度开环/闭环PID控制)

    DSPIC30F BLDC三相无刷电机驱动资料(速度开环/闭环PID控制) DSPIC30F2010 BLDC开发板资料: 华愉电子工作室 提供原理图和代码及相关资料,源码均有中文注释,是学习无刷电机 ...

最新文章

  1. 一名合格的运维工程师的历练之路
  2. Spring Boot 2.x整合Websocket(基于Spring Boot 2.x 前后端分离 iview admin vue 集成activiti工作流...
  3. POS主密钥与工作密钥关联详解
  4. c#params应用
  5. GIS数据的查找,插入,删除,更新(ArcEngine)
  6. 通俗易懂:贪心算法(三):习题练习 (力扣605种花问题、122买卖股票的最佳时机)
  7. YunYang1994/tensorflow-yolov3 Readme 翻译
  8. php重载,PHP重载基础知识回顾
  9. Edittext焦点处理
  10. cad快捷键文件路径_办公格式转太难不会看这里!CAD、PDF、Word、Excel、TXT教你玩转...
  11. php写语音朗读,详解在网页上通过JS实现文本的语音朗读
  12. SQLServer中Case when的一个意外问题
  13. adb命令启动某个action_Android adb shell启动应用程序的方法
  14. cpu高 rust腐蚀_木器漆如何选购,Rust-Oleum户外防水防腐木器漆怎么样?
  15. Cocos2d-X开发中国象棋《八》走棋
  16. 读书笔记:陈希孺:概率论与数理统计:2014.01.02
  17. codeup3692 星期英文单词
  18. 量子力学第十一弹——变分法
  19. go、JS AES(CBC模式)加密解密兼容
  20. axios处理特大数据碰到的问题

热门文章

  1. CDA校区恭祝各位朋友元宵节快乐
  2. 如何设计一款好玩的网络游戏
  3. WEB端唤起 百度|腾讯|高德 地图一键导航功能
  4. kubectl get pods no resourse found...
  5. 猪的一生应当这样度过
  6. 女性电子商务服装评论数据集
  7. python编程-图形打印汇总:心型图案 九九乘法表 三角形 金字塔 圣诞树 倒三角形 菱形
  8. 中文分词工具 java_java读取中文分词工具(一)
  9. NLP算法-中文分词工具-Jieba
  10. [NET]什么是公网、私网、内网、外网?