串口通讯设计
通讯数据包
在通讯中可以使用特殊字符结尾来判定通讯结束,当接收的数据最后一个字符恰好是结束符时,可以计算一下校验码确定结束,或者确定在没有连续输出指令应答的时可以再等待一定时间来确定是否真正的结束。实际中我使用读取固定长度的方法来防止过早接收。
在通讯协议设计时大多都设计有数据长度字段,但是看过大多数都类似 |帧头|...|长度|数据|校验|帧尾|
这样的,也许有更好的方法解决过早结束的的方法。如果需要通过长度来读数据,那么就不能确定这个长度值是对的。
为此,在传输数据时先传一个简短的固定长度的独立带校验帧用于告诉接收者本次传输数据的长度,再接着发送实际数据。这样接收时先读取固定长度帧,从中获取实际数据的长度,这样不同大小的数据也是一个已知定长,后续可以循环接收直至达到实际长度。
帧头 | 地址 | 功能字 | 返回值 | 数据长度 | 校验 | 帧尾 |
---|---|---|---|---|---|---|
1 | 1 | 1 | 1 | 4 | 1 | 1 |
帧头 | 数据 | 校验 | 帧尾 |
---|---|---|---|
1 | N | 1 | 1 |
如果收发缓冲区LEN小于N,则每次发送以LEN-3为最大数据发送单位分包发送,即大数据发收有分帧和重组步骤。如果通讯请求只有功能字无附带数据或响应只有返回值没有其他数据,那么可以省略掉数据帧。
数据接收
int msg_recv(unsigned char *buff, int ms)
{
//获取头帧
myread(fd, rbuf, hlen, ms);
//校验头帧
check_msg(...);
//根据长度计算分包数和最后一个包长度
npack = ...;
elen = ...;
//循环读分包
for(i =0; i< npack; i++)
{
myread(fd, rbuf, maxlen, 50);
}
//读最后一个包
if(elen > 0)
{
myread(fd, rbuf, elen+3, 50);
}
}
数据发送
int msg_send(uchar cmd, uchar ret, uchar *data, size_t len)
{
//发送头帧
write(fd, ph, hlen);
//没有其他数据
if(len == 0)
{
return 0;
}
//根据长度计算分包数和最后一个包长度
npack = ...;
elen = ...;
//循环发分包
for (i = 0; i < npack; i++)
{
write(fd, pd, maxlen);
}
//发最后一个包
if(elen == 0)
{
write(fd, pd, elen+3);
}
}