单片机 :STM32F407
开发板:DMF407电机开发板
平台:keil V5.31
HSE 为8MHZ
HSI为16MHZ
PID算法:






一、速度环PID实验:

主函数
int main(void)
{
uint8_t key;
uint16_t t;
uint8_t debug_cmd = 0;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
usart_init(115200); /* 串口1初始化,用于上位机调试 */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
key_init(); /* 初始化按键 */
pid_init(); /* 初始化PID参数 */
atim_timx_cplm_pwm_init(8400 - 1 , 0); /* 168 000 000 / 1 = 168 000 000 168Mhz的计数频率,计数8400次为50us */
dcmotor_init(); /* 初始化电机 */
gtim_timx_encoder_chy_init(0XFFFF, 0); /* 编码器定时器初始化,不分频直接84M的计数频率 */
btim_timx_int_init(1000 - 1 , 84 - 1); /* 基本定时器初始化,1ms计数周期 */
#if DEBUG_ENABLE /* 开启调试 */
debug_init(); /* 初始化调试 */
debug_send_motorcode(DC_MOTOR); /* 上传电机类型(直流有刷电机) */
debug_send_motorstate(IDLE_STATE); /* 上传电机状态(空闲) */
/* 同步数据(选择第1组PID,目标速度地址,P,I,D参数)到上位机 */
debug_send_initdata(TYPE_PID1, (float *)(&g_speed_pid.SetPoint), KP, KI, KD);
#endif
g_point_color = WHITE;
g_back_color = BLACK;
lcd_show_string(10, 10, 200, 16, 16, "DcMotor Test", g_point_color);
lcd_show_string(10, 30, 200, 16, 16, "KEY0:Start forward", g_point_color);
lcd_show_string(10, 50, 200, 16, 16, "KEY1:Start backward", g_point_color);
lcd_show_string(10, 70, 200, 16, 16, "KEY2:Stop", g_point_color);
while (1)
{
key = key_scan(0); /* 按键扫描 */
if(key == KEY0_PRES) /* 当key0按下 */
{
g_run_flag = 1; /* 标记电机启动 */
g_speed_pid.SetPoint += 30;
if (g_speed_pid.SetPoint == 0)
{
dcmotor_stop(); /* 停止则立刻响应 */
g_motor_data.motor_pwm = 0;
motor_pwm_set(g_motor_data.motor_pwm); /* 设置电机方向、转速 */
}
else
{
dcmotor_start(); /* 开启电机 */
if (g_speed_pid.SetPoint >= 300) /* 限速 */
{
g_speed_pid.SetPoint = 300;
}
#if DEBUG_ENABLE
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
#endif
}
}
else if(key == KEY1_PRES) /* 当key1按下 */
{
g_run_flag = 1; /* 标记电机启动 */
g_speed_pid.SetPoint -= 30;
if (g_speed_pid.SetPoint == 0)
{
dcmotor_stop(); /* 停止则立刻响应 */
g_motor_data.motor_pwm = 0;
motor_pwm_set(g_motor_data.motor_pwm); /* 设置电机方向、转速 */
}
else
{
dcmotor_start(); /* 开启电机 */
if (g_speed_pid.SetPoint <= -300) /* 限速 */
{
g_speed_pid.SetPoint = -300;
}
#if DEBUG_ENABLE
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
#endif
}
}
else if(key == KEY2_PRES) /* 当key2按下 */
{
dcmotor_stop(); /* 停止电机 */
pid_init(); /* 重置pid参数 */
g_run_flag = 0; /* 标记电机停止 */
g_motor_data.motor_pwm = 0;
motor_pwm_set(g_motor_data.motor_pwm); /* 设置电机转向、速度 */
#if DEBUG_ENABLE
debug_send_motorstate(BREAKED_STATE); /* 上传电机状态(刹车) */
debug_send_initdata(TYPE_PID1, (float *)(&g_speed_pid.SetPoint), KP, KI, KD); /* 同步数据到上位机 */
#endif
}
#if DEBUG_ENABLE
/* 查询接收PID助手的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) /* 电机刹车 */
{
dcmotor_stop(); /* 停止电机 */
pid_init(); /* 重置pid参数 */
g_run_flag = 0; /* 标记电机停止 */
g_motor_data.motor_pwm = 0;
motor_pwm_set(g_motor_data.motor_pwm); /* 设置电机转向、速度 */
debug_send_motorstate(BREAKED_STATE); /* 上传电机状态(刹车) */
debug_send_initdata(TYPE_PID1, (float *)(&g_speed_pid.SetPoint), KP, KI, KD);
}
else if (debug_cmd == RUN_CODE) /* 电机运行 */
{
dcmotor_start(); /* 开启电机 */
g_speed_pid.SetPoint = 30; /* 设置目标速度:30 RPM */
g_run_flag = 1; /* 标记电机启动 */
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
}
#endif
t++;
if(t % 20 == 0)
{
lcd_dis(); /* 显示数据 */
LED0_TOGGLE(); /*LED0(红灯) 翻转*/
#if DEBUG_ENABLE
debug_send_speed(g_motor_data.speed); /* 发送速度 */
#endif
}
delay_ms(10);
}
}
pid:
/* 位置式PID参数相关宏 */
#define KP 10.0f /* P参数*/
#define KI 6.00f /* I参数*/
#define KD 0.5f /* D参数*/
#define SMAPLSE_PID_SPEED 50 /* 采样周期 单位ms*/
/* PID参数结构体 */
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] */
__IO float LastError; /* Error[-1] */
__IO float PrevError; /* Error[-2] */
} PID_TypeDef;
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 */
}
int32_t increment_pid_ctrl(PID_TypeDef *PID,float Feedback_value)
{
PID->Error = (float)(PID->SetPoint - Feedback_value); /* 计算偏差 */
#if INCR_LOCT_SELECT /* 增量式PID */
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;
#else /* 位置式PID */
PID->SumError += PID->Error;
PID->ActualValue = (PID->Proportion * PID->Error) /* 比例环节 */
+ (PID->Integral * PID->SumError) /* 积分环节 */
+ (PID->Derivative * (PID->Error - PID->LastError)); /* 微分环节 */
PID->LastError = PID->Error;
#endif
return ((int32_t)(PID->ActualValue)); /* 返回计算后输出的数值 */
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint8_t val = 0;
if (htim->Instance == TIM3)
{
if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle)) /* 判断CR1的DIR位 */
{
g_timx_encode_count--; /* DIR位为1,也就是递减计数 */
}
else
{
g_timx_encode_count++; /* DIR位为0,也就是递增计数 */
}
}
else if (htim->Instance == TIM6)
{
int Encode_now = gtim_get_encode(); /* 获取编码器值,用于计算速度 */
speed_computer(Encode_now, 5); /* 中位平均值滤除编码器抖动数据,5ms计算一次速度*/
if (val % SMAPLSE_PID_SPEED == 0) /* 50ms进行一次pid计算 */
{
if (g_run_flag) /* 判断电机是否启动了*/
{
/* PID计算,输出比较值(占空比) */
g_motor_data.motor_pwm = increment_pid_ctrl(&g_speed_pid, g_motor_data.speed);
if (g_motor_data.motor_pwm >= 8200) /* 限速 */
{
g_motor_data.motor_pwm = 8200;
}
else if (g_motor_data.motor_pwm <= -8200)
{
g_motor_data.motor_pwm = -8200;
}
#if DEBUG_ENABLE /* 发送基本参数*/
debug_send_wave_data( 1 ,g_motor_data.speed); /* 选择通道1,发送实际速度(波形显示)*/
debug_send_wave_data( 2 ,g_speed_pid.SetPoint); /* 选择通道2,发送目标速度(波形显示)*/
debug_send_wave_data( 3 ,g_motor_data.motor_pwm * 100 / 8400); /* 选择通道3,发送占空比(波形显示)*/
#endif
motor_pwm_set(g_motor_data.motor_pwm); /* 设置电机转速 */
}
val = 0;
}
val ++;
}
}
debug:
/*********************************** 开发板 ------> 上位机 *******************************************/
/* 数据类别 */
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;
void debug_obj_init(debug_data *data)
{
size_t obj_size = sizeof(debug_data);
memset(data, 0, (size_t)obj_size); /* 把指定范围内存清零 */
}
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 ++;
}
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); /* 发送数据到上位机 */
}
}
void debug_init(void)
{
debug_obj_init(&g_debug); /* 初始化所需内存 */
}
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; /* 配置为最大允许速度 */
}
}
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参数 */
}
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); /* 发送电流数据 */
}
void debug_send_valtage(float valtage)
{
g_debug.bus_vol = valtage; /* 传入电压值 */
debug_upload_data(&g_debug, TYPE_VBUS); /* 发送电压数据 */
}
void debug_send_power(float power)
{
g_debug.power = power; /* 传入功率值 */
debug_upload_data(&g_debug, TYPE_POWER); /* 发送功率数据 */
}
void debug_send_speed(float speed)
{
g_debug.speed = (int16_t)(speed); /* 传入速度值 */
debug_upload_data(&g_debug, TYPE_SPEED); /* 发送速度数据 */
}
void debug_send_distance(uint64_t len)
{
g_debug.sum_len = len; /* 传入总里程值 */
debug_upload_data(&g_debug, TYPE_SUM_LEN); /* 发送总里程数据 */
}
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); /* 发送温度数据 */
}
void debug_send_motorstate(motor_state motor_codestae)
{
g_debug.status = motor_codestae; /* 传入电机状态 */
debug_upload_data(&g_debug, TYPE_STATUS); /* 发送电机状态 */
}
void debug_send_motorcode(motor_code motorcode)
{
g_debug.motor_code = motorcode; /* 传入电机类型 */
debug_upload_data(&g_debug, TYPE_MOTOR_CODE); /* 发送电机类型 */
}
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); /* 发送波形数据 */
}
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参数 */
}
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;
}
测试结果:

二、电流环PID控制实验

主函数
int main(void)
{
uint8_t key;
uint16_t t;
uint8_t debug_cmd = 0;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
usart_init(115200); /* 串口1初始化,用于上位机调试 */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
key_init(); /* 初始化按键 */
pid_init(); /* 初始化PID参数 */
atim_timx_cplm_pwm_init(8400 - 1 , 0); /* 168 000 000 / 1 = 168 000 000 168Mhz的计数频率,计数8400次为50us */
dcmotor_init(); /* 初始化电机 */
gtim_timx_encoder_chy_init(0XFFFF, 0); /* 编码器定时器初始化,不分频直接84M的计数频率 */
btim_timx_int_init(1000 - 1 , 84 - 1); /* 基本定时器初始化,1ms计数周期 */
adc_nch_dma_init(); /* 初始化ADC、DMA */
#if DEBUG_ENABLE /* 开启调试 */
debug_init(); /* 初始化调试 */
debug_send_motorcode(DC_MOTOR); /* 上传电机类型(直流有刷电机) */
debug_send_motorstate(IDLE_STATE); /* 上传电机状态(空闲) */
/* 同步数据(选择第1组PID,目标电流地址,P,I,D参数)到上位机 */
debug_send_initdata(TYPE_PID1, (float *)(&g_current_pid.SetPoint), KP, KI, KD);
#endif
g_point_color = WHITE;
g_back_color = BLACK;
lcd_show_string(10, 10, 200, 16, 16, "DcMotor Test", g_point_color);
lcd_show_string(10, 30, 200, 16, 16, "KEY0:Start forward", g_point_color);
lcd_show_string(10, 50, 200, 16, 16, "KEY1:Start backward", g_point_color);
lcd_show_string(10, 70, 200, 16, 16, "KEY2:Stop", g_point_color);
delay_ms(500);
while (1)
{
key = key_scan(0); /* 按键扫描 */
if(key == KEY0_PRES) /* 当key0按下 */
{
g_run_flag = 1; /* 标记电机启动 */
dcmotor_start(); /* 启动电机 */
g_current_pid.SetPoint += 20;
if (g_current_pid.SetPoint >= 120) /* 限制电流:120mA */
{
g_current_pid.SetPoint = 120;
}
#if DEBUG_ENABLE
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
#endif
}
else if(key == KEY1_PRES) /* 当key1按下 */
{
g_current_pid.SetPoint -= 20;
if (g_current_pid.SetPoint < 60) /* 目标电流小于60mA */
{
dcmotor_stop(); /* 停止电机 */
pid_init(); /* 重置pid参数 */
g_run_flag = 0; /* 标记电机停止 */
g_motor_data.motor_pwm = 0;
motor_pwm_set(g_motor_data.motor_pwm); /* 设置电机转向、速度 */
#if DEBUG_ENABLE
debug_send_motorstate(BREAKED_STATE); /* 上传电机状态(刹车) */
debug_send_initdata(TYPE_PID1, (float *)(&g_current_pid.SetPoint), KP, KI, KD);
#endif
}
}
else if(key == KEY2_PRES) /* 当key2按下 */
{
dcmotor_stop(); /* 停止电机 */
pid_init(); /* 重置pid参数 */
g_run_flag = 0; /* 标记电机停止 */
g_motor_data.motor_pwm = 0;
motor_pwm_set(g_motor_data.motor_pwm); /* 设置电机转向、速度 */
#if DEBUG_ENABLE
debug_send_motorstate(BREAKED_STATE); /* 上传电机状态(刹车)*/
debug_send_initdata(TYPE_PID1, (float *)(&g_current_pid.SetPoint), KP, KI, KD);
#endif
}
#if DEBUG_ENABLE
/* 查询接收PID助手的PID参数 */
debug_receive_pid(TYPE_PID1, (float *)&g_current_pid.Proportion,(float *)&g_current_pid.Integral, (float *)&g_current_pid.Derivative);
debug_set_point_range(120, 0, 120); /* 设置目标调节范围 */
debug_cmd = debug_receive_ctrl_code(); /* 读取上位机指令 */
if (debug_cmd == BREAKED) /* 电机刹车 */
{
dcmotor_stop(); /* 停止电机 */
pid_init(); /* 重置pid参数 */
g_run_flag = 0; /* 标记电机停止 */
g_motor_data.motor_pwm = 0;
motor_pwm_set(g_motor_data.motor_pwm); /* 设置电机转向、速度 */
debug_send_motorstate(BREAKED_STATE); /* 上传电机状态(刹车) */
debug_send_initdata(TYPE_PID1, (float *)(&g_current_pid.SetPoint), KP, KI, KD);
}
else if (debug_cmd == RUN_CODE) /* 电机运行 */
{
dcmotor_start(); /* 启动电机 */
g_current_pid.SetPoint = 60; /* 目标电流:60mA */
g_run_flag = 1; /* 标记电机启动 */
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
}
#endif
t++;
if(t % 20 == 0)
{
lcd_dis(); /* 显示数据 */
LED0_TOGGLE(); /* LED0(红灯) 翻转 */
#if DEBUG_ENABLE
debug_send_current(g_motor_data.current * 0.001f, 0, 0);/* 发送电流 */
debug_send_speed(g_motor_data.speed); /* 发送速度 */
#endif
}
delay_ms(10);
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
int32_t motor_pwm_temp = 0;
static uint8_t val = 0;
if (htim->Instance == TIM3)
{
if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle)) /* 判断CR1的DIR位 */
{
g_timx_encode_count--; /* DIR位为1,也就是递减计数 */
}
else
{
g_timx_encode_count++; /* DIR位为0,也就是递增计数 */
}
}
else if (htim->Instance == TIM6)
{
int Encode_now = gtim_get_encode(); /* 获取编码器值,用于计算速度 */
speed_computer(Encode_now, 5); /* 中位平均值滤除编码器抖动数据,5ms计算一次速度 */
if (val % SMAPLSE_PID_SPEED == 0) /* 进行一次pid计算 */
{
if (g_run_flag) /* 判断电机是否启动了 */
{
/* PID计算,输出比较值(占空比),再进行一阶低通滤波 */
motor_pwm_temp = increment_pid_ctrl(&g_current_pid, g_motor_data.current);
g_motor_data.motor_pwm = (int32_t)((g_motor_data.motor_pwm * 0.5) + (motor_pwm_temp * 0.5));
if (g_motor_data.motor_pwm >= 8200) /* 限制占空比 */
{
g_motor_data.motor_pwm = 8200;
}
else if (g_motor_data.motor_pwm <= 0)
{
g_motor_data.motor_pwm = 0;
}
#if DEBUG_ENABLE /* 发送基本参数*/
debug_send_wave_data( 1 ,g_motor_data.current); /* 选择通道1,发送实际电流(波形显示)*/
debug_send_wave_data( 2 ,g_current_pid.SetPoint); /* 选择通道2,发送目标电流(波形显示)*/
debug_send_wave_data( 3 ,g_motor_data.motor_pwm * 100 / 8400 ); /* 选择通道3,发送占空比(波形显示)*/
#endif
motor_pwm_set(g_motor_data.motor_pwm); /* 设置占空比(电机转速) */
}
val = 0;
}
val ++;
}
}
测试结果:

三、位置环PID控制

用编码器计数总值代表电机的位置。
主函数:
int main(void)
{
uint8_t key;
uint16_t t;
uint8_t debug_cmd = 0;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
usart_init(115200); /* 串口1初始化,用于上位机调试 */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
key_init(); /* 初始化按键 */
pid_init(); /* 初始化PID参数 */
atim_timx_cplm_pwm_init(8400 - 1 , 0); /* 168 000 000 / 1 = 168 000 000 168Mhz的计数频率,计数8400次为50us */
dcmotor_init(); /* 初始化电机 */
gtim_timx_encoder_chy_init(0XFFFF, 0); /* 编码器定时器初始化,不分频直接84M的计数频率 */
btim_timx_int_init(1000 - 1 , 84 - 1); /* 基本定时器初始化,1ms计数周期 */
adc_nch_dma_init(); /* 初始化ADC、DMA */
#if DEBUG_ENABLE /* 开启调试 */
debug_init(); /* 初始化调试 */
debug_send_motorcode(DC_MOTOR); /* 上传电机类型(直流有刷电机) */
debug_send_motorstate(IDLE_STATE); /* 上传电机状态(空闲) */
/* 同步数据(选择第1组PID,目标位置地址,P,I,D参数)到上位机 */
debug_send_initdata(TYPE_PID1, (float *)(&g_location_pid.SetPoint), KP, KI, KD);
#endif
g_point_color = WHITE;
g_back_color = BLACK;
lcd_show_string(10, 10, 200, 16, 16, "DcMotor Test", g_point_color);
lcd_show_string(10, 30, 200, 16, 16, "KEY0:Start forward", g_point_color);
lcd_show_string(10, 50, 200, 16, 16, "KEY1:Start backward", g_point_color);
lcd_show_string(10, 70, 200, 16, 16, "KEY2:Stop", g_point_color);
while (1)
{
key = key_scan(0); /* 按键扫描 */
if(key == KEY0_PRES) /* 当key0按下 */
{
g_run_flag = 1; /* 标记电机启动 */
dcmotor_start(); /* 开启电机 */
g_location_pid.SetPoint += 1320; /* 正转一圈,电机旋转圈数 = 计数值变化量 / 44 / 30 */
if (g_location_pid.SetPoint >= 6600) /* 限制电机位置(正转最大5圈) */
{
g_location_pid.SetPoint = 6600;
}
#if DEBUG_ENABLE
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
#endif
}
else if(key == KEY1_PRES) /* 当key1按下 */
{
g_run_flag = 1; /* 标记电机启动 */
dcmotor_start(); /* 开启电机 */
g_location_pid.SetPoint -= 1320; /* 反转一圈 */
if (g_location_pid.SetPoint <= -6600) /* 限制电机位置(反转最大5圈) */
{
g_location_pid.SetPoint = -6600;
}
#if DEBUG_ENABLE
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
#endif
}
else if(key == KEY2_PRES) /* 当key2按下 */
{
g_location_pid.SetPoint = 0; /* 恢复初始位置 */
}
#if DEBUG_ENABLE
/* 查询接收PID助手的PID参数 */
debug_receive_pid(TYPE_PID1, (float *)&g_location_pid.Proportion,(float *)&g_location_pid.Integral, (float *)&g_location_pid.Derivative);
debug_set_point_range(6600, -6600, 6600); /* 设置目标调节范围 */
debug_cmd = debug_receive_ctrl_code(); /* 读取上位机指令 */
if (debug_cmd == HALT_CODE) /* 电机停机 */
{
g_location_pid.SetPoint = 0; /* 恢复初始位置 */
}
else if (debug_cmd == RUN_CODE) /* 电机运行 */
{
g_run_flag = 1; /* 标记电机启动 */
dcmotor_start(); /* 开启电机 */
g_location_pid.SetPoint = 1320; /* 设置目标位置 */
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
}
#endif
t++;
if(t % 20 == 0)
{
lcd_dis(); /* 显示数据 */
LED0_TOGGLE(); /* LED0(红灯)翻转 */
g_debug.encode_p = g_motor_data.location; /* 传入编码器当前总计数值 */
debug_upload_data(&g_debug, TYPE_HAL_ENC); /* 发送编码器当前总计数值 */
}
delay_ms(10);
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
int32_t motor_pwm_temp = 0;
static uint8_t val = 0;
if (htim->Instance == TIM3)
{
if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle)) /* 判断CR1的DIR位 */
{
g_timx_encode_count--; /* DIR位为1,也就是递减计数 */
}
else
{
g_timx_encode_count++; /* DIR位为0,也就是递增计数 */
}
}
else if (htim->Instance == TIM6)
{
int Encode_now = gtim_get_encode(); /* 获取编码器值,用于计算速度 */
speed_computer(Encode_now, 5); /* 中位平均值滤除编码器抖动数据,5ms计算一次速度 */
g_motor_data.location = Encode_now; /* 获取当前计数总值,用于位置闭环控制 */
if (val % SMAPLSE_PID_SPEED == 0) /* 进行一次pid计算 */
{
if (g_run_flag) /* 判断电机是否启动了 */
{
/* PID计算,输出比较值(占空比),再进行一阶低通滤波 */
motor_pwm_temp = increment_pid_ctrl(&g_location_pid, g_motor_data.location);
g_motor_data.motor_pwm = (int32_t)((g_motor_data.motor_pwm * 0.5) + (motor_pwm_temp * 0.5));
if (g_motor_data.motor_pwm >= 4200) /* 限制占空比 */
{
g_motor_data.motor_pwm = 4200;
}
else if (g_motor_data.motor_pwm <= -4200)
{
g_motor_data.motor_pwm = -4200;
}
#if DEBUG_ENABLE /* 发送基本参数*/
debug_send_wave_data( 1 ,g_motor_data.location); /* 选择通道1,发送实际位置(波形显示)*/
debug_send_wave_data( 2 ,g_location_pid.SetPoint); /* 选择通道2,发送目标位置(波形显示)*/
#endif
motor_pwm_set(g_motor_data.motor_pwm); /* 设置占空比(电机转速)*/
}
val = 0;
}
val ++;
}
}
测试结果:

四、速度环+位置环控制
主函数
int main(void)
{
uint8_t key;
uint16_t t;
uint8_t debug_cmd = 0;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
usart_init(115200); /* 串口1初始化,用于上位机调试 */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
key_init(); /* 初始化按键 */
pid_init(); /* 初始化PID参数 */
atim_timx_cplm_pwm_init(8400 - 1 , 0); /* 168 000 000 / 1 = 168 000 000 168Mhz的计数频率,计数8400次为50us */
dcmotor_init(); /* 初始化电机 */
gtim_timx_encoder_chy_init(0XFFFF, 0); /* 编码器定时器初始化,不分频直接84M的计数频率 */
btim_timx_int_init(1000 - 1 , 84 - 1); /* 基本定时器初始化,1ms计数周期 */
#if DEBUG_ENABLE /* 开启调试 */
debug_init(); /* 初始化调试 */
debug_send_motorcode(DC_MOTOR); /* 上传电机类型(直流有刷电机) */
debug_send_motorstate(IDLE_STATE); /* 上传电机状态(空闲) */
/* 同步数据PID参数到上位机 ,无论同步哪一组数据,目标值地址只能是外环PID的 */
debug_send_initdata(TYPE_PID1, (float *)(&g_location_pid.SetPoint), L_KP, L_KI, L_KD); /* 位置环PID参数(PID1)*/
debug_send_initdata(TYPE_PID2, (float *)(&g_location_pid.SetPoint), S_KP, S_KI, S_KD); /* 速度环PID参数(PID2)*/
#endif
g_point_color = WHITE;
g_back_color = BLACK;
lcd_show_string(10, 10, 200, 16, 16, "DcMotor Test", g_point_color);
lcd_show_string(10, 30, 200, 16, 16, "KEY0:Start forward", g_point_color);
lcd_show_string(10, 50, 200, 16, 16, "KEY1:Start backward", g_point_color);
lcd_show_string(10, 70, 200, 16, 16, "KEY2:Stop", g_point_color);
while (1)
{
key = key_scan(0); /* 按键扫描 */
if(key == KEY0_PRES) /* 当key0按下 */
{
g_run_flag = 1; /* 标记电机启动 */
dcmotor_start(); /* 开启电机 */
g_location_pid.SetPoint += 1320; /* 正转一圈,电机旋转圈数 = 编码器总计数值 / 44 / 30 */
if (g_location_pid.SetPoint >= 6600) /* 限制电机位置(正转最大5圈) */
{
g_location_pid.SetPoint = 6600;
}
#if DEBUG_ENABLE
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
#endif
}
else if(key == KEY1_PRES) /* 当key1按下 */
{
g_run_flag = 1; /* 标记电机启动 */
dcmotor_start(); /* 开启电机 */
g_location_pid.SetPoint -= 1320; /* 反转一圈 */
if (g_location_pid.SetPoint <= -6600) /* 限制电机位置(反转最大5圈) */
{
g_location_pid.SetPoint = -6600;
}
#if DEBUG_ENABLE
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
#endif
}
else if(key == KEY2_PRES) /* 当key2按下 */
{
g_location_pid.SetPoint = 0; /* 恢复初始位置 */
}
#if DEBUG_ENABLE
/* 接收PID助手设置的位置环PID参数 */
debug_receive_pid(TYPE_PID1, (float *)&g_location_pid.Proportion,(float *)&g_location_pid.Integral, (float *)&g_location_pid.Derivative);
/* 接收PID助手设置的速度环PID参数 */
debug_receive_pid(TYPE_PID2, (float *)&g_speed_pid.Proportion, (float *)&g_speed_pid.Integral, (float *)&g_speed_pid.Derivative);
debug_set_point_range(6600, -6600, 6600); /* 设置目标调节范围 */
debug_cmd = debug_receive_ctrl_code(); /* 读取上位机指令 */
if (debug_cmd == HALT_CODE) /* 电机停机 */
{
g_location_pid.SetPoint = 0; /* 恢复初始位置 */
}
else if (debug_cmd == RUN_CODE) /* 电机运行 */
{
g_run_flag = 1; /* 标记电机启动 */
dcmotor_start(); /* 开启电机 */
g_location_pid.SetPoint = 1320; /* 设置目标位置 */
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
}
#endif
t++;
if(t % 20 == 0)
{
lcd_dis();
LED0_TOGGLE(); /* LED0(红灯) 翻转 */
#if DEBUG_ENABLE
debug_send_speed(g_motor_data.speed); /* 发送速度 */
g_debug.encode_p = g_motor_data.location; /* 传入编码器当前总计数值 */
debug_upload_data(&g_debug, TYPE_HAL_ENC); /* 发送编码器当前总计数值 */
#endif
}
delay_ms(10);
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint8_t val = 0;
if (htim->Instance == TIM3)
{
if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle)) /* 判断CR1的DIR位 */
{
g_timx_encode_count--; /* DIR位为1,也就是递减计数 */
}
else
{
g_timx_encode_count++; /* DIR位为0,也就是递增计数 */
}
}
else if (htim->Instance == TIM6)
{
int Encode_now = gtim_get_encode(); /* 获取编码器值,用于计算速度 */
speed_computer(Encode_now, 5); /* 中位平均值滤除编码器抖动数据,5ms计算一次速度 */
if (val % SMAPLSE_PID_SPEED == 0) /* 进行一次pid计算 */
{
if (g_run_flag) /* 判断电机是否启动了 */
{
g_motor_data.location = (float)Encode_now; /* 获取当前编码器总计数值,用于位置闭环控制 */
g_motor_data.motor_pwm = increment_pid_ctrl(&g_location_pid, g_motor_data.location); /* 位置环PID控制(外环) */
if (g_motor_data.motor_pwm >= 150) /* 限制外环输出(目标速度) */
{
g_motor_data.motor_pwm = 150;
}
else if (g_motor_data.motor_pwm <= -150)
{
g_motor_data.motor_pwm = -150;
}
g_speed_pid.SetPoint = g_motor_data.motor_pwm; /* 设置目标速度,外环输出作为内环输入 */
g_motor_data.motor_pwm = increment_pid_ctrl(&g_speed_pid, g_motor_data.speed); /* 速度环PID控制(内环) */
if (g_motor_data.motor_pwm >= 8200) /* 限制占空比 */
{
g_motor_data.motor_pwm = 8200;
}
else if (g_motor_data.motor_pwm <= -8200)
{
g_motor_data.motor_pwm = -8200;
}
#if DEBUG_ENABLE /* 发送基本参数*/
debug_send_wave_data( 1 ,g_motor_data.location); /* 选择通道1,发送实际位置(波形显示)*/
debug_send_wave_data( 2 ,g_location_pid.SetPoint); /* 选择通道2,发送目标位置(波形显示)*/
#endif
motor_pwm_set(g_motor_data.motor_pwm); /* 设置占空比(电机转速) */
}
val = 0;
}
val ++;
}
}
测试结果:

五、电流环+位置环控制
主函数
int main(void)
{
uint8_t key;
uint16_t t;
uint8_t debug_cmd = 0;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
usart_init(115200); /* 串口1初始化,用于上位机调试 */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
key_init(); /* 初始化按键 */
pid_init(); /* 初始化PID参数 */
atim_timx_cplm_pwm_init(8400 - 1 , 0); /* 168 000 000 / 1 = 168 000 000 168Mhz的计数频率,计数8400次为50us */
dcmotor_init(); /* 初始化电机 */
gtim_timx_encoder_chy_init(0XFFFF, 0); /* 编码器定时器初始化,不分频直接84M的计数频率 */
btim_timx_int_init(1000 - 1 , 84 - 1); /* 基本定时器初始化,1ms计数周期 */
adc_nch_dma_init(); /* 初始化ADC、DMA */
#if DEBUG_ENABLE /* 开启调试 */
debug_init(); /* 初始化调试 */
debug_send_motorcode(DC_MOTOR); /* 上传电机类型(直流有刷电机) */
debug_send_motorstate(IDLE_STATE); /* 上传电机状态(空闲) */
/* 同步数据PID参数到上位机 ,无论同步哪一组数据,目标值地址只能是外环PID的 */
debug_send_initdata(TYPE_PID1, (float *)(&g_location_pid.SetPoint), L_KP, L_KI, L_KD); /* 位置环PID参数(PID1)*/
debug_send_initdata(TYPE_PID2, (float *)(&g_location_pid.SetPoint), C_KP, C_KI, C_KD); /* 电流环PID参数(PID2)*/
#endif
g_point_color = WHITE;
g_back_color = BLACK;
lcd_show_string(10, 10, 200, 16, 16, "DcMotor Test", g_point_color);
lcd_show_string(10, 30, 200, 16, 16, "KEY0:Start forward", g_point_color);
lcd_show_string(10, 50, 200, 16, 16, "KEY1:Start backward", g_point_color);
lcd_show_string(10, 70, 200, 16, 16, "KEY2:Stop", g_point_color);
while (1)
{
key = key_scan(0); /* 按键扫描 */
if(key == KEY0_PRES) /* 当key0按下 */
{
g_run_flag = 1; /* 标记电机启动 */
dcmotor_start(); /* 开启电机 */
g_location_pid.SetPoint += 1320; /* 正转一圈,电机旋转圈数 = 编码器总计数值 / 44 / 30 */
if (g_location_pid.SetPoint >= 6600) /* 限制电机位置(正转最大5圈) */
{
g_location_pid.SetPoint = 6600;
}
#if DEBUG_ENABLE
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
#endif
}
else if(key == KEY1_PRES) /* 当key1按下 */
{
g_run_flag = 1; /* 标记电机启动 */
dcmotor_start(); /* 开启电机 */
g_location_pid.SetPoint -= 1320; /* 反转一圈 */
if (g_location_pid.SetPoint <= -6600) /* 限制电机位置(反转最大5圈) */
{
g_location_pid.SetPoint = -6600;
}
#if DEBUG_ENABLE
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
#endif
}
else if(key == KEY2_PRES) /* 当key2按下 */
{
g_location_pid.SetPoint = 0; /* 恢复初始位置 */
}
#if DEBUG_ENABLE
/* 接收PID助手设置的位置环PID参数 */
debug_receive_pid(TYPE_PID1, (float *)&g_location_pid.Proportion,(float *)&g_location_pid.Integral, (float *)&g_location_pid.Derivative);
/* 接收PID助手设置的速度环PID参数 */
debug_receive_pid(TYPE_PID2, (float *)&g_current_pid.Proportion, (float *)&g_current_pid.Integral, (float *)&g_current_pid.Derivative);
debug_set_point_range(6600, -6600, 6600); /* 设置目标调节范围 */
debug_cmd = debug_receive_ctrl_code(); /* 读取上位机指令 */
if (debug_cmd == HALT_CODE) /* 电机停机 */
{
g_location_pid.SetPoint = 0; /* 恢复初始位置 */
}
else if (debug_cmd == RUN_CODE) /* 电机运行 */
{
g_run_flag = 1; /* 标记电机启动 */
dcmotor_start(); /* 开启电机 */
g_location_pid.SetPoint = 1320; /* 设置目标位置 */
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
}
#endif
t++;
if(t % 20 == 0)
{
lcd_dis();
LED0_TOGGLE(); /* LED0(红灯) 翻转 */
#if DEBUG_ENABLE
debug_send_current(g_motor_data.current,0,0); /* 发送电流到U相 */
g_debug.encode_p = g_motor_data.location; /* 传入编码器当前总计数值 */
debug_upload_data(&g_debug, TYPE_HAL_ENC); /* 发送编码器当前总计数值 */
#endif
}
delay_ms(10);
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint8_t val = 0;
if (htim->Instance == TIM3)
{
if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle)) /* 判断CR1的DIR位 */
{
g_timx_encode_count--; /* DIR位为1,也就是递减计数 */
}
else
{
g_timx_encode_count++; /* DIR位为0,也就是递增计数 */
}
}
else if (htim->Instance == TIM6)
{
int Encode_now = gtim_get_encode(); /* 获取编码器值,用于计算速度 */
speed_computer(Encode_now, 5); /* 中位平均值滤除编码器抖动数据,5ms计算一次速度 */
if (val % SMAPLSE_PID_SPEED == 0) /* 进行一次pid计算 */
{
if (g_run_flag) /* 判断电机是否启动了 */
{
g_motor_data.location = (float)Encode_now; /* 获取当前编码器总计数值,用于位置闭环控制 */
g_motor_data.motor_pwm = increment_pid_ctrl(&g_location_pid, g_motor_data.location); /* 位置环PID控制(外环) */
if ( g_motor_data.motor_pwm > 0) /* 判断位置环输出值是否为正数 */
{
dcmotor_dir(0); /* 输出为正数,设置电机正转 */
}
else
{
g_motor_data.motor_pwm = -g_motor_data.motor_pwm; /* 输出为负数,取反 */
dcmotor_dir(1); /* 设置电机反转 */
}
if (g_motor_data.motor_pwm >= 120) /* 限制外环输出(目标电流) */
{
g_motor_data.motor_pwm = 120;
}
g_current_pid.SetPoint = g_motor_data.motor_pwm; /* 设置目标电流,外环输出作为内环输入 */
g_motor_data.motor_pwm = increment_pid_ctrl(&g_current_pid, g_motor_data.current); /* 电流环PID控制(内环) */
if (g_motor_data.motor_pwm >= 8200) /* 限制占空比 */
{
g_motor_data.motor_pwm = 8200;
}
else if (g_motor_data.motor_pwm <= 0)
{
g_motor_data.motor_pwm = 0;
}
#if DEBUG_ENABLE /* 发送基本参数*/
debug_send_wave_data( 1 ,g_motor_data.location); /* 选择通道1,发送实际位置(波形显示)*/
debug_send_wave_data( 2 ,g_location_pid.SetPoint); /* 选择通道2,发送目标位置(波形显示)*/
#endif
dcmotor_speed(g_motor_data.motor_pwm); /* 设置占空比 */
}
val = 0;
}
val ++;
}
}
测试结果:

六、电流环+速度环控制
主函数
int main(void)
{
uint8_t key;
uint16_t t;
uint8_t debug_cmd = 0;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
usart_init(115200); /* 串口1初始化,用于上位机调试 */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
key_init(); /* 初始化按键 */
pid_init(); /* 初始化PID参数 */
atim_timx_cplm_pwm_init(8400 - 1 , 0); /* 168 000 000 / 1 = 168 000 000 168Mhz的计数频率,计数8400次为50us */
dcmotor_init(); /* 初始化电机 */
gtim_timx_encoder_chy_init(0XFFFF, 0); /* 初始化编码器定时器,不分频直接84M的计数频率 */
btim_timx_int_init(1000 - 1 , 84 - 1); /* 初始化基本定时器,1ms计数周期 */
adc_nch_dma_init(); /* 初始化ADC、DMA */
#if DEBUG_ENABLE /* 开启调试 */
debug_init(); /* 初始化调试 */
debug_send_motorcode(DC_MOTOR); /* 上传电机类型(直流有刷电机)*/
debug_send_motorstate(IDLE_STATE); /* 上传电机状态(空闲)*/
/* 同步数据PID参数到上位机 ,无论同步哪一组PID,目标值地址只能是外环PID的 */
debug_send_initdata(TYPE_PID1, (float *)(&g_speed_pid.SetPoint), S_KP, S_KI, S_KD); /* 速度环PID参数(PID1)*/
debug_send_initdata(TYPE_PID2, (float *)(&g_speed_pid.SetPoint), C_KP, C_KI, C_KD); /* 电流环PID参数(PID2)*/
#endif
g_point_color = WHITE;
g_back_color = BLACK;
lcd_show_string(10, 10, 200, 16, 16, "DcMotor Test", g_point_color);
lcd_show_string(10, 30, 200, 16, 16, "KEY0:Start forward", g_point_color);
lcd_show_string(10, 50, 200, 16, 16, "KEY1:Start backward", g_point_color);
lcd_show_string(10, 70, 200, 16, 16, "KEY2:Stop", g_point_color);
while (1)
{
key = key_scan(0); /* 按键扫描 */
if(key == KEY0_PRES) /* 当key0按下 */
{
g_run_flag = 1; /* 标记电机启动 */
g_speed_pid.SetPoint += 50;
dcmotor_start(); /* 开启电机 */
if (g_speed_pid.SetPoint >= 300) /* 限速 */
{
g_speed_pid.SetPoint = 300;
}
#if DEBUG_ENABLE
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
#endif
}
else if(key == KEY1_PRES) /* 当key1按下 */
{
g_run_flag = 1; /* 标记电机启动 */
g_speed_pid.SetPoint -= 50;
dcmotor_start(); /* 开启电机 */
if (g_speed_pid.SetPoint <= -300) /* 限速 */
{
g_speed_pid.SetPoint = -300;
}
#if DEBUG_ENABLE
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
#endif
}
else if(key == KEY2_PRES) /* 当key2按下 */
{
dcmotor_stop(); /* 停止电机 */
pid_init(); /* 重置pid参数 */
g_run_flag = 0; /* 标记电机停止 */
g_motor_data.motor_pwm = 0;
motor_pwm_set(g_motor_data.motor_pwm); /* 设置电机转向、速度 */
#if DEBUG_ENABLE
debug_send_motorstate(BREAKED_STATE); /* 上传电机状态(刹车) */
debug_send_initdata(TYPE_PID1, (float *)(&g_speed_pid.SetPoint), S_KP, S_KI, S_KD); /* 同步PID参数到上位机 */
debug_send_initdata(TYPE_PID2, (float *)(&g_speed_pid.SetPoint), C_KP, C_KI, C_KD);
#endif
}
#if DEBUG_ENABLE
/* 接收PID助手设置的速度环PID参数 */
debug_receive_pid(TYPE_PID1, (float *)&g_speed_pid.Proportion,(float *)&g_speed_pid.Integral,(float *)&g_speed_pid.Derivative);
/* 接收PID助手设置的电流环PID参数 */
debug_receive_pid(TYPE_PID2, (float *)&g_current_pid.Proportion,(float *)&g_current_pid.Integral, (float *)&g_current_pid.Derivative);
debug_set_point_range(300, -300, 300); /* 设置目标调节范围,有电流环的系统目标值突变不要太大 */
debug_cmd = debug_receive_ctrl_code(); /* 读取上位机指令 */
if (debug_cmd == BREAKED) /* 电机刹车 */
{
dcmotor_stop(); /* 停止电机 */
pid_init(); /* 重置pid参数 */
g_run_flag = 0; /* 标记电机停止 */
g_motor_data.motor_pwm = 0;
motor_pwm_set(g_motor_data.motor_pwm); /* 设置电机转向、速度 */
debug_send_motorstate(BREAKED_STATE); /* 上传电机状态(刹车) */
debug_send_initdata(TYPE_PID1, (float *)(&g_speed_pid.SetPoint), S_KP, S_KI, S_KD); /* 同步PID参数到上位机 */
debug_send_initdata(TYPE_PID2, (float *)(&g_speed_pid.SetPoint), C_KP, C_KI, C_KD);
}
else if (debug_cmd == RUN_CODE) /* 电机运行 */
{
dcmotor_start(); /* 开启电机 */
g_speed_pid.SetPoint = 50; /* 设置目标速度 */
g_run_flag = 1; /* 标记电机启动 */
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
}
#endif
t++;
if(t % 20 == 0)
{
c_sum_error = (int)g_current_pid.SumError; /* 此变量用于调试时查看电流环累计积分 */
s_sum_error = (int)g_speed_pid.SumError; /* 此变量用于调试时查看速度环累计积分 */
lcd_dis(); /* 显示数据 */
LED0_TOGGLE(); /* LED0(红灯) 翻转 */
#if DEBUG_ENABLE
debug_send_speed(g_motor_data.speed); /* 发送速度 */
#endif
}
delay_ms(10);
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint8_t val = 0;
if (htim->Instance == TIM3)
{
if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle)) /* 判断CR1的DIR位 */
{
g_timx_encode_count--; /* DIR位为1,也就是递减计数 */
}
else
{
g_timx_encode_count++; /* DIR位为0,也就是递增计数 */
}
}
else if (htim->Instance == TIM6)
{
int Encode_now = gtim_get_encode(); /* 获取编码器值,用于计算速度 */
speed_computer(Encode_now, 5); /* 中位平均值滤除编码器抖动数据,5ms计算一次速度 */
if (val % SMAPLSE_PID_SPEED == 0) /* 进行一次pid计算 */
{
if (g_run_flag) /* 判断电机是否启动了 */
{
integral_limit( &g_speed_pid , 7000 , -7000 ); /* 速度环积分限幅 */
integral_limit( &g_current_pid , 2500 , -2500 ); /* 电流环积分限幅 */
g_motor_data.motor_pwm = increment_pid_ctrl(&g_speed_pid, g_motor_data.speed); /* 速度环PID控制(外环) */
if ( g_motor_data.motor_pwm > 0) /* 判断速度环输出值是否为正数 */
{
dcmotor_dir(0); /* 输出为正数,设置电机正转 */
}
else
{
g_motor_data.motor_pwm = -g_motor_data.motor_pwm; /* 目标电流不能为负数,速度环输出要取反 */
dcmotor_dir(1); /* 设置电机反转 */
}
if (g_motor_data.motor_pwm >= 200) /* 限制外环输出(目标电流) */
{
g_motor_data.motor_pwm = 200;
}
g_current_pid.SetPoint = g_motor_data.motor_pwm; /* 设置目标电流,外环输出作为内环输入 */
g_motor_data.motor_pwm = increment_pid_ctrl(&g_current_pid, g_motor_data.current); /* 电流环PID控制(内环) */
if (g_motor_data.motor_pwm >= 8200) /* 限制占空比 */
{
g_motor_data.motor_pwm = 8200;
}
else if (g_motor_data.motor_pwm <= 0) /* 滤掉无效输出 */
{
g_motor_data.motor_pwm = 0;
}
#if DEBUG_ENABLE /* 发送基本参数*/
debug_send_wave_data( 1 ,g_motor_data.speed); /* 选择通道1,发送实际速度(波形显示)*/
debug_send_wave_data( 2 ,g_speed_pid.SetPoint); /* 选择通道2,发送目标速度(波形显示)*/
#endif
dcmotor_speed(g_motor_data.motor_pwm); /* 设置占空比 */
}
val = 0;
}
val ++;
}
}
测试结果:

七、电流环+速度环+位置环控制

主函数
int main(void)
{
uint8_t key;
uint16_t t;
uint8_t debug_cmd = 0;
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(336, 8, 2, 7); /* 设置时钟,168Mhz */
delay_init(168); /* 延时初始化 */
usart_init(115200); /* 串口1初始化,用于上位机调试 */
led_init(); /* 初始化LED */
lcd_init(); /* 初始化LCD */
key_init(); /* 初始化按键 */
pid_init(); /* 初始化PID参数 */
atim_timx_cplm_pwm_init(8400 - 1 , 0); /* 168 000 000 / 1 = 168 000 000 168Mhz的计数频率,计数8400次为50us */
dcmotor_init(); /* 初始化电机 */
gtim_timx_encoder_chy_init(0XFFFF, 0); /* 编码器定时器初始化,不分频直接84M的计数频率 */
btim_timx_int_init(1000 - 1 , 84 - 1); /* 基本定时器初始化,1ms计数周期 */
adc_nch_dma_init();
#if DEBUG_ENABLE /* 开启调试 */
debug_init(); /* 初始化调试 */
debug_send_motorcode(DC_MOTOR); /* 上传电机类型(直流有刷电机) */
debug_send_motorstate(IDLE_STATE); /* 上传电机状态(空闲) */
/* 同步数据PID参数到上位机 ,无论同步哪一组数据,目标值地址只能是外环PID的 */
debug_send_initdata(TYPE_PID1, (float *)(&g_location_pid.SetPoint), L_KP, L_KI, L_KD); /* 位置环PID参数(PID1)*/
debug_send_initdata(TYPE_PID2, (float *)(&g_location_pid.SetPoint), S_KP, S_KI, S_KD); /* 速度环PID参数(PID2)*/
debug_send_initdata(TYPE_PID3, (float *)(&g_location_pid.SetPoint), C_KP, C_KI, C_KD); /* 电流环PID参数(PID3)*/
#endif
g_point_color = WHITE;
g_back_color = BLACK;
lcd_show_string(10, 10, 200, 16, 16, "DcMotor Test", g_point_color);
lcd_show_string(10, 30, 200, 16, 16, "KEY0:Start forward", g_point_color);
lcd_show_string(10, 50, 200, 16, 16, "KEY1:Start backward", g_point_color);
lcd_show_string(10, 70, 200, 16, 16, "KEY2:Stop", g_point_color);
while (1)
{
key = key_scan(0); /* 按键扫描 */
if(key == KEY0_PRES) /* 当key0按下 */
{
g_run_flag = 1; /* 标记电机启动 */
dcmotor_start(); /* 开启电机 */
g_location_pid.SetPoint += 1320; /* 正转一圈,电机旋转圈数 = 编码器总计数值 / 44 / 30 */
if (g_location_pid.SetPoint >= 6600) /* 限制电机位置(正转最大5圈) */
{
g_location_pid.SetPoint = 6600;
}
#if DEBUG_ENABLE
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
#endif
}
else if(key == KEY1_PRES) /* 当key1按下 */
{
g_run_flag = 1; /* 标记电机启动 */
dcmotor_start(); /* 开启电机 */
g_location_pid.SetPoint -= 1320; /* 反转一圈 */
if (g_location_pid.SetPoint <= -6600) /* 限制电机位置(反转最大5圈) */
{
g_location_pid.SetPoint = -6600;
}
#if DEBUG_ENABLE
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
#endif
}
else if(key == KEY2_PRES) /* 当key2按下 */
{
g_location_pid.SetPoint = 0; /* 恢复初始位置 */
}
#if DEBUG_ENABLE
/* 接收PID助手设置的PID参数 */
debug_receive_pid(TYPE_PID1,(float *)&g_location_pid.Proportion,(float *)&g_location_pid.Integral,(float *)&g_location_pid.Derivative);
debug_receive_pid(TYPE_PID2,(float *)&g_speed_pid.Proportion,(float *)&g_speed_pid.Integral,(float *)&g_speed_pid.Derivative);
debug_receive_pid(TYPE_PID3,(float *)&g_current_pid.Proportion,(float *)&g_current_pid.Integral,(float *)&g_current_pid.Derivative);
debug_set_point_range(6600, -6600, 6600); /* 设置目标调节范围 */
debug_cmd = debug_receive_ctrl_code(); /* 读取上位机指令 */
if (debug_cmd == HALT_CODE) /* 电机停机 */
{
g_location_pid.SetPoint = 0; /* 恢复初始位置 */
}
else if (debug_cmd == RUN_CODE) /* 电机运行 */
{
g_run_flag = 1; /* 标记电机启动 */
dcmotor_start(); /* 开启电机 */
g_location_pid.SetPoint = 1320; /* 设置目标位置 */
debug_send_motorstate(RUN_STATE); /* 上传电机状态(运行) */
}
#endif
t++;
if(t % 20 == 0)
{
lcd_dis();
LED0_TOGGLE(); /* LED0(红灯) 翻转 */
#if DEBUG_ENABLE
debug_send_speed(g_motor_data.speed); /* 发送速度 */
g_debug.encode_p = g_motor_data.location; /* 传入编码器当前总计数值 */
debug_upload_data(&g_debug, TYPE_HAL_ENC); /* 发送编码器当前总计数值 */
#endif
}
delay_ms(10);
}
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
static uint8_t val = 0;
if (htim->Instance == TIM3)
{
if(__HAL_TIM_IS_TIM_COUNTING_DOWN(&g_timx_encode_chy_handle)) /* 判断CR1的DIR位 */
{
g_timx_encode_count--; /* DIR位为1,也就是递减计数 */
}
else
{
g_timx_encode_count++; /* DIR位为0,也就是递增计数 */
}
}
else if (htim->Instance == TIM6)
{
int Encode_now = gtim_get_encode(); /* 获取编码器值,用于计算速度 */
speed_computer(Encode_now, 5); /* 中位平均值滤除编码器抖动数据,5ms计算一次速度 */
if (val % SMAPLSE_PID_SPEED == 0) /* 进行一次pid计算 */
{
if (g_run_flag) /* 判断电机是否启动了 */
{
g_motor_data.location = (float)Encode_now; /* 获取当前编码器总计数值,用于位置闭环控制 */
integral_limit(&g_location_pid , 1000 ,-1000); /* 位置环积分限幅 */
integral_limit(&g_speed_pid , 200 ,-200); /* 速度环积分限幅 */
integral_limit(&g_current_pid , 150 ,-150); /* 电流环积分限幅 */
if( (g_location_pid.Error <= 20) && (g_location_pid.Error >= -20) ) /* 设置闭环死区 */
{
g_location_pid.Error = 0; /* 偏差太小了,直接清零 */
g_location_pid.SumError = 0; /* 清除积分 */
}
g_motor_data.motor_pwm = increment_pid_ctrl(&g_location_pid, g_motor_data.location); /* 位置环PID控制(最外环) */
if (g_motor_data.motor_pwm >= 120) /* 限制外环输出(目标速度) */
{
g_motor_data.motor_pwm = 120;
}
else if (g_motor_data.motor_pwm <= -120)
{
g_motor_data.motor_pwm = -120;
}
g_speed_pid.SetPoint = g_motor_data.motor_pwm; /* 设置目标速度,外环输出作为内环输入 */
g_motor_data.motor_pwm = increment_pid_ctrl(&g_speed_pid, g_motor_data.speed); /* 速度环PID控制(次外环) */
if ( g_motor_data.motor_pwm > 0) /* 判断速度环输出值是否为正数 */
{
dcmotor_dir(0); /* 输出为正数,设置电机正转 */
}
else
{
g_motor_data.motor_pwm = -g_motor_data.motor_pwm; /* 输出取反 */
dcmotor_dir(1); /* 设置电机反转 */
}
if (g_motor_data.motor_pwm >= 100) /* 限制外环输出(目标电流) */
{
g_motor_data.motor_pwm = 100;
}
g_current_pid.SetPoint = g_motor_data.motor_pwm; /* 设置目标电流,外环输出作为内环输入 */
g_motor_data.motor_pwm = increment_pid_ctrl(&g_current_pid, g_motor_data.current); /* 电流环PID控制(内环) */
if (g_motor_data.motor_pwm >= 8200) /* 限制占空比 */
{
g_motor_data.motor_pwm = 8200;
}
else if (g_motor_data.motor_pwm <= 0) /* 滤掉无效输出 */
{
g_motor_data.motor_pwm = 0;
}
#if DEBUG_ENABLE /* 发送基本参数*/
debug_send_wave_data( 1 ,g_motor_data.location); /* 选择通道1,发送实际位置(波形显示)*/
debug_send_wave_data( 2 ,g_location_pid.SetPoint); /* 选择通道2,发送目标位置(波形显示)*/
#endif
dcmotor_speed(g_motor_data.motor_pwm); /* 设置占空比 */
}
val = 0;
}
val ++;
}
}
测试结果:
