问题描述:
项目中有定时查询传感器并将查询到的数据记录下来的需要,在做的时候发现通过whlie循环配合select监测串口上的数据,然后将数据保存在数组的过程中,read函数的第三个参数即选择读取的字节数,在大于8的情况下返回的个数仍然为8,而传感器返回的数据包一般都是十几位,无法一次性返回,就需要记录好几次。更为严重的是,如果数据包的字节数大于16的情况下,第二次返回8个字节的同时,会将第一次的返回的8个字节覆盖掉,从而导致记录的数据不完整。
解决思路:
串口每次查询只能返回8位受制于缓冲的大小,一般情况下无法改变,那么应该着手通过对返回的数据包进行处理,不要每次返回都进行记录,而是在一次数据包返回完整之后,再将它传递给用于存储的数组进行记录。
程序修改前如下:
int receive(char *str , unsigned int length)
{
fd_set fs_read;
struct timeval time;
FD_ZERO(&fs_read);
FD_SET(fd,&fs_read);
time.tv_sec=0;
time.tv_usec=0;
unsigned int i_counter=0;
int len=0;
while(select(fd+1,&fs_read,NULL,NULL,&time) > 0 )
{
if((len=read(fd,str,128))<0)
{
perror("read");
return -1;
}
else{
i_counter+= len;
}
}
return i_counter;
}
上述代码中的str会传递到另外一个记录数据包的函数里面,本来以为将read的第三个参数设为128就可以完全满足需要,返回全部的数据长度,但是实际上一次最多只能返回8位,如果总的字节数大于16位,还会将前面的数据刷新掉,而只能够记录到最后小于16个字节的包。
程序修改后如下:
unsigned int i_counter=0;
int receive (char *str,unsigned int length)
{
int len=0;
unsigned char str_house[2056]={0};
fd_set fs_read;
struct timeval time;
FD_ZERO(&fs_read);
FD_SET(fd,&fs_read);
time.tv_sec=0;
time.tv_usec=0;
int j_count=0;
while(select(fd+1,&fs_read,NULL,NULL,&time)>0){
if((len=read(fd,str_house,8))<0)
{
perror("read");
return -1;
}
else{
i_counter+=len;
if(i_counter>length)
{
i_counter=8;
}
printf("len is %d counter is %d\n",len,i_counter);
if(len==8)
{
for(j_count=0;j_count<8;j_count++)
{
str[i_counter-8+j_count]=str_house[j_count];
}
}
else if(len<8)
{
for(j_count=0;j_count<len;j_count++)
{
str[i_counter-len+j_count]=str_house[j_count];
}
}
}
}
return i_counter;
}
上述程序中,i_counter用来记录当前已经收到的数据包中元素的个数,如果放在函数内部的话,每次调用该函数来查询,该值都会被初始化为零,起不到计数的作用,所以应该作为全局变量,但是当它计数超过我们本次查询返回字节的最大值之后,会被清零,否则会一直无限增长下去,然后就可以创建一个暂时存放数据包的仓库,即数组str_house,每次读八位并记录,直到它读到的字节大于零且小于八,说明它返回的是最后几个字节,这几个字节存完之后,这一条查询指令所返回的数据包就全部存放到str_house这个数组里了,该数组通过被其他写函数调用,就可以存放到SD卡或者SATA硬盘等存储设备,从而解决Linux下串口查询一次只能返回8位的情况。