之前介绍的电机PID控制的系列文章:
电机控制与PID实践
得到不少电子爱好者的关注,不过也收到一些关于串口通信的疑问反馈。之前的一系列文章,确实没有着重介绍串口软件的使用细节以及通信协议的具体格式。本篇就来补充一下野火PID调试助手的串口协议。
下野火PID调试助手的使用界面如下,与串口通信协议相关的,主要分为三个部分:
在介绍串口协议之前,推荐安装一个虚拟串口驱动软件来在自己的电脑上虚拟出两个串口,方便分析串口数据。
比如我们在电脑上产生两个虚拟串口,这两个串口可以看作是通过线连接了起来,比如我们使用野火PID助手和另一个串口助手软件分别连接这两个虚拟串口,当野火PID助手发送数据时(比如按下启动按钮),另一端的串口助手软件就可以收到并显示野火PID助手发来的数据,这样我们就能分析野火PID助手下发数据的数据格式了。
先来看一下野火PID调试助手的串口协议数据格式。
串口协议的定义参考野火论坛的介绍:https://www.firebbs.cn/forum.php?mod=viewthread&tid=29923&extra=page%3D1
串口数据是通过一包一包的数据发送的,每一包的数据格式如下:
字节数 | 名称 | 内容 |
---|---|---|
4bytes | 包头 | 0x59485A53 |
1bytes | 数据通道 | 0xXX |
4bytes | 包长度 | 0xXXXXXXXX |
1bytes | 指令 | 0xXX |
1bytes | 参数1 | 0xXX |
… | … | … |
1bytes | 参数n | 0xXX |
1bytes | 校验和 | 0xXX |
说明:
上述数据格式中,需要重点关注的是“指令”这一字段,它表明了这一包数据的具体含义。
另外,串口数据包括下发数据和上传数据,下发数据就是野火PID助手按照协议包格式向板子发送串口数据,上传数据就是板子按照协议包格式向野火PID助手送串口数据。
野火PID助手下发设定的数据或指令到板子中
功能 | 指令 | 参数 |
---|---|---|
PID | 0x10 | 3个float |
目标值 | 0x11 | 1个int |
启动 | 0x12 | 无 |
停止 | 0x13 | 无 |
复位 | 0x14 | 无 |
周期 | 0x15 | 1个uint |
板子上传数据或指令到野火PID助手
功能 | 指令 | 参数 |
---|---|---|
目标值 | 0x01 | 1个int |
实际值 | 0x02 | 1个int |
PID | 0x03 | 3个float |
启动 | 0x04 | 无 |
停止 | 0x05 | 无 |
周期 | 0x06 | 1个uint |
测试数据的下发,可以只使用一个电脑进行测试,通过虚拟串口,分别连接野火PID助手和另一个串口助手软件,通过野火PID助手向另一个串口助手发送数据,以Hex方式显示接收的数据,并观察数据的格式。
这3个是指令,没有数据参数,数据长度为0x0B,也就是11个byte
包头 | 通道 | 包长度 | 指令 | 校验 | |
---|---|---|---|---|---|
启动 | 53 5A 48 59 | 01 | 0B 00 00 00 | 12 | 6C |
停止 | 53 5A 48 59 | 01 | 0B 00 00 00 | 13 | 6D |
复位 | 53 5A 48 59 | 01 | 0B 00 00 00 | 14 | 6E |
这2个指令,带有1byte数据,数据长度为0x0F,也就是15个byte
包头 | 通道 | 包长度 | 指令 | 参数(数据) | 校验 | |
---|---|---|---|---|---|---|
目标值 | 53 5A 48 59 | 01 | 0F 00 00 00 | 11 | C8 00 00 00 | 37 |
周期 | 53 5A 48 59 | 01 | 0F 00 00 00 | 15 | 64 00 00 00 | D7 |
这个指令,带有12byte数据(3个float),数据长度为0x17,也就是23个byte
包头 | 通道 | 包长度 | 指令 | 参数(数据) | 校验 |
---|---|---|---|---|---|
53 5A 48 59 | 01 | 17 00 00 00 | 10 | 9A 99 99 3F 00 00 20 40 9A 99 99 3E | EB |
注意:这里PID的数据是float型的,在发送时是需要拆分成4个字节的Hex格式发送的,关于float类型数据转为Hex格式的介绍,可参考:这里
测试数据的上传,需要将程序下载到板子中,板子通过软件连接电脑上的任意串口调试软件,以Hex方式显示接收的数据,分析数据的格式。
包头 | 通道 | 包长度 | 指令 | 校验 | |
---|---|---|---|---|---|
启动 | 53 5A 48 59 | 01 | 0B 00 00 00 | 04 | 5E |
停止 | 53 5A 48 59 | 01 | 0B 00 00 00 | 05 | 5F |
包头 | 通道 | 包长度 | 指令 | 参数(数据) | 校验 | |
---|---|---|---|---|---|---|
目标值 | 53 5A 48 59 | 01 | 0F 00 00 00 | 01 | 70 3A 00 00 | 09 |
周期 | 53 5A 48 59 | 01 | 0F 00 00 00 | 06 | xx xx xx xx | xx |
包头 | 通道 | 包长度 | 指令 | 参数(数据) | 校验 |
---|---|---|---|---|---|
53 5A 48 59 | 01 | 17 00 00 00 | 03 | CD CC 4C 3D 00 00 00 00 00 00 00 00 | 8B |
这里就是将电机的转速和位置值上传到野火PID助手中,用于显示位置曲线或速度曲线。
包头 | 通道 | 包长度 | 指令 | 参数(数据) | 校验 |
---|---|---|---|---|---|
53 5A 48 59 | 01 | 0F 00 00 00 | 02 | 2D 00 00 00 | 8D |
再来看一下与串口数据发送与接收相关的主要代码:
按格式组包数据:
/**
* @brief 设置上位机的值
* @param cmd:命令
* @param ch: 曲线通道
* @param data:参数指针
* @param num:参数个数
* @retval 无
*/
void set_computer_value(uint8_t cmd, uint8_t ch, void *data, uint8_t num)
{
static packet_head_t set_packet;
uint8_t sum = 0; // 校验和
num *= 4; // 一个参数 4 个字节
set_packet.head = FRAME_HEADER; // 包头 0x59485A53
set_packet.ch = ch; // 设置通道
set_packet.len = 0x0B + num; // 包长
set_packet.cmd = cmd; // 设置命令
sum = check_sum(0, (uint8_t *)&set_packet, sizeof(set_packet)); // 计算包头校验和
sum = check_sum(sum, (uint8_t *)data, num); // 计算参数校验和
usart1_send((uint8_t *)&set_packet, sizeof(set_packet)); // 发送数据头
usart1_send((uint8_t *)data, num); // 发送参数
usart1_send((uint8_t *)&sum, sizeof(sum)); // 发送校验和
}
将一包数据一个个发送给上位机
void usart1_send(u8*data, u8 len)
{
u8 i;
for(i=0;i<len;i++)
{
while(USART_GetFlagStatus(USART1,USART_FLAG_TC)==RESET);
USART_SendData(USART1,data[i]);
}
}
使用中断的方式进行串口数据的接收:
//=======================================
//串口1中断服务程序
//=======================================
uint8_t Recv1[128]={0};//串口接收缓存
u8 rx_cnt=0;//接收数据个数计数变量
int sizecopy=128;
void USART1_IRQHandler(void)
{
uint8_t data;//接收数据暂存变量
uint8_t bufcopy[128];//最多只取前64个数据
if(USART_GetITStatus(USART1, USART_IT_RXNE) != RESET) //接收中断
{
data = USART_ReceiveData(USART1);
Recv1[rx_cnt++]=data;//接收的数据存入接收数组
USART_ClearITPendingBit(USART1,USART_IT_RXNE);
}
if(USART_GetITStatus(USART1, USART_IT_IDLE) != RESET)//空闲中断
{
data = USART1->SR;//串口空闲中断的中断标志只能通过先读SR寄存器,再读DR寄存器清除!
data = USART1->DR;
//清空本地接收数组
memset(bufcopy,0,sizecopy);
memcpy(bufcopy,Recv1,rx_cnt);//有几个复制几个
protocol_data_recv(bufcopy, rx_cnt);
memset(Recv1,0,sizecopy);
rx_cnt=0;
}
}
接收之后的数据保存在串口接收缓存Recv1数组中,然后再根据协议格式解析数据即可。
本篇介绍的野火PID助手的串口协议格式,包括下发的数据格式和上传的数据格式,并通过实际获取串口数据的Hex格式数据,与协议的定义进行对比分析,使得能够更加的理解串口数据的格式。
如果遇到野火PID助手下发指令板子没反应,或板子上传数据PID数据无法显示曲线,这时就要先排查一下串口数据的格式是否正确,若不正确,就要看下自己程序中的串口收发函数编写的是否正确,只有串口数据符合了规定的协议格式,才能正确的进行数据通信。
如果串口数据格式正常,电机还不转,就要排查硬件接线是否正常以及是否要根据自己的电机编码器参数来修改程序中的参数。