此次使用的是6020电机,即选择STM32F105R8T6芯片。
一、实现电机的双向通信
1、发送电机控制命令(见STM32 CubeMX CAN通信配置&开环控制Robomaster 2006电机);
2、接收电机反馈数据(电机速度);
(1)配置过滤器
因为之前的CSDN中有启动can通信和激活can通信的函数,所以可以删除重复的部分。
void can_filter_init(void)//过滤器(can总线上理论可以挂载无数多的设备,为把一些不必要的设备过滤掉)用过滤器
{
CAN_FilterTypeDef can_filter_st;//因为此项目没有多余的设备,所以都配置为0
can_filter_st.FilterActivation = ENABLE;
can_filter_st.FilterMode = CAN_FILTERMODE_IDMASK;
can_filter_st.FilterScale = CAN_FILTERSCALE_32BIT;
can_filter_st.FilterIdHigh = 0x0000;
can_filter_st.FilterIdLow = 0x0000;
can_filter_st.FilterMaskIdHigh = 0x0000;
can_filter_st.FilterMaskIdLow = 0x0000;
can_filter_st.FilterBank = 0;
can_filter_st.FilterFIFOAssignment = CAN_RX_FIFO0;
HAL_CAN_ConfigFilter(&hcan1, &can_filter_st);//配置过滤器
HAL_CAN_Start(&hcan1);//启动can通信
HAL_CAN_ActivateNotification(&hcan1, CAN_IT_RX_FIFO0_MSG_PENDING);//激活CAN中断通知功能
}
(2)调用过滤器相关的函数
can_filter_init();//can通信过滤器, 如果不配置过滤器,会接收不到函数
(3)启动中断接收
只需要打开RX0中断,然后重新运行keil软件。
(4)、 在中断中插入回调函数
uint16_t GetSpeed,SetSpeed;
void HAL_CAN_RxFifo0MsgPendingCallback(CAN_HandleTypeDef *hcan)//回调函数
{
CAN_RxHeaderTypeDef rx_header;
uint8_t rx_data[8];
HAL_CAN_GetRxMessage(hcan, CAN_RX_FIFO0, &rx_header, rx_data);//将数据存放于rx_data数组中
switch (rx_header.StdId)
{
case 0x205://根据电机具体id号设置 0x204+id(6020手册上找)
{
GetSpeed = (uint16_t)((rx_data)[2] << 8 | (rx_data)[3]); // 根据手册 2、3 位分别为电机转速的高八位、低八位
break; // 此处是将两个数据合并为一个数据
}
}
}
二、配置pid
首先对要用到的函数进行定义。
typedef struct
{
float Kp, Ki, Kd,taxget,e,I_Band,i_out,d_out,last_e,totalout,p_out,current;
}pid;
pid Motor_pid;
extern uint16_t GetSpeed,SetSpeed;
因为已经定义了GetSpeed,SetSpeed变量,这里用extern表示在别的地方已经定义过了。然后开始用pid进行计算
void pid_calc(pid* pid)//该函数为PID计算
{
pid->e = pid->taxget - pid->current;//计算偏差值
pid->p_out = (int32_t )(pid->Kp *pid->e);//计算p的输出值(比例)
pid->i_out += (int32_t )(pid->Ki * pid->e);//计算i的输出值(积分)
pid->d_out = (int32_t )(pid->Kd * (pid->e - pid->last_e));//计算d的输出值(微分)
pid->totalout = pid->p_out + pid->i_out + pid->d_out;//总输出
pid->last_e = pid->e;//记录当前偏差值
}
然后就可以在while函数里进行调用了
SetSpeed = 60;//
Motor_pid.Kp=60;//
Motor_pid.Ki=5;//
while (1)
{
Motor_pid.current=(float)GetSpeed;
Motor_pid.taxget=SetSpeed;
pid_calc(&Motor_pid);//调用计算pid的函数
CAN_cmd_motor(Motor_pid.totalout,0, 0, 0);//向id1的电机发送电流值为200
}
这里要注意的是can发送时一定要加delay函数,否则接收不到数据。
HAL_Delay(10);//用delay才能接收到数据
之后编译运行,就可以实现电机的闭环控制了。