项目需求从串口中读取数据,刚开始方案是一次从串口中read 512字节的数据然后解析,代码写完后发现有时候接收的数据不是完整的一包数据,导致数据无法解析,为解决这个问题,修改方案为循环读取,一直读到需要的数据为止,最后实现了功能,但算法太复杂,影响效率。
最后采用SLIP协议封装数据
SLIP(Serial Line Internet Protocol)是一个简单的面向字符的协议,其规则如下:
1. 在每个用户信息帧的首尾各加-个特殊的标志字END ,封装成为 SLIP 帧。标识字节 END 的编码为($C0)。
2. 若用户信息帧中的某-个字节与标识字节END的编码($C0)-样,那么就要将这一个字节更换成($DB, $DC)这样的2字节序列。这里的特殊字符($DB)称为SLIP转义字符。
3. 若用户信息帧中的某个字节与SLIP转义字符($DB)-样,那么就要将这一个字节更换成($DB, $DD)这样的2字节序列。
读取数据时每次从串口读取一个字节,进行解析,直到读取完一帧完整的数据,代码如下:
int read_local_panel_fd(int fd, unsigned char *cmd_buf, int *write_fifo)
{
int ret;
int frame = 0;
int rcv = 1;
int quit = FALSE;
unsigned char ch;
int2char(fd, cmd_buf, 1); /*把端口写入cmd_buf中*/
while(1)
{
ret = read(fd, &ch, 1);
if(ret < 0)
{
D3100_PRINT("");
perror("read failure");
}
printf("***cmd buf %2x\n", ch);
switch(ch)
{
case SLIP_FLG: //0xC0
frame++;
D3100_PRINT("\n");
if(2 == frame)
{
quit = TRUE;
frame =0;
if(check_checksum(cmd_buf + 1, rcv - 1)) /*去掉端口号*/
{
D3100_PRINT("\n");
*write_fifo = TRUE;
}
else
{
D3100_PRINT("");
printf("error cmd\n");
reply_cmd(fd, change_to_reply_cmd(cmd_buf[0]), REPLY_ERROR_CODE);
}
}
break;
case SLIP_DB: //0xDB
read(fd, &ch, 1);
switch(ch)
{
case SLIP_DC:
cmd_buf[rcv++] = SLIP_FLG; //0x0C
break;
case SLIP_DD:
cmd_buf[rcv++] = SLIP_DB; //0x0C
break;
}
break;
default: //内容
if(1 == frame)
{
cmd_buf[rcv++] = ch;
}
break;
}
if(quit)
{
break;
}
}
return rcv;
}
在调试过程中遇到的问题主要有下面两个:
1、多次接受数据时,接受数目未初始化,导致数组越界
2、数据中有0时解析数据时会从0的地方断开,经过调试跟踪代码最后发现在copy数据时,copy的长度使用strlen计算,这样会出现问题。
strlen不能用于有数据0 的字符串,但是memcpy可以。