前阵子一朋友使用单片机与某外设进行通信时,外设返回的是一堆格式如下的数据:
AA AA 04 80 02 00 02 7B AA AA 04 80 02 00 08 75 AA AA 04 80 02 00 9B E2 AA AA 04 80 02 00 F6 87 AA AA 04 80 02 00 EC 91
其中 AA AA 04 80 02 是数据校验头,后面三位是有效数据,问我怎么从外设不断返回的数据中取出有效的数据。
对于这种问题最容易想到的就是使用一个标志位用于标志当前正解析到一帧数据的第几位,然后判断当前接收的数据是否与校验数据一致,如果一致则将标志位加一,否则将标志位置0重新判断,使用这种方法解析数据的代码如下:
if(flag == 0)
{
if(tempData == 0xAA)
flag++;
else
flag = 0;
}
else if(flag == 1)
{
if(tempData == 0xAA)
flag++;
else
flag = 0;
}
else if(flag == 2)
{
if(tempData == 0x04)
flag++;
else
flag = 0;
}
else if(flag == 3)
{
if(tempData == 0x80)
flag++;
else
flag = 0;
}
else if(flag == 4)
{
if(tempData == 0x02)
flag++;
else
flag = 0;
}
else if(flag == 5 || flag == 6 || flag == 7)
{
data[flag-5] = tempData;
flag = (flag == 7) ? 0 : flag+1;
}
使用上述方法是最容易想到的也是最简单的方法了,百度了一下基本上也都是使用类似的方法进行数据解析,但是使用这种方法有如下几个缺点:
1、 大量使用了判断,容易导致出现逻辑混乱
2、 代码重复率高,抽象程度低。从上述代码可以看到一大堆代码仅仅是判断的数据不同,其他代码都完全一致
3、 代码可复用性差。写好的代码无法用在其他类似的外设上,如果有多个外设就需要编写多份类似的代码
4、 可扩展性低。如果外设还有一个数据校验尾需要校验或者数据校验头发生改变,就需要再次写多个判断重新用于校验,无法在原有的代码上进行扩展
5、 容易出现误判
对此,这里提出了一种新的解决方案,可以通用与所有类似的数据解析,原理如下:
使用一个固定容量的队列用来缓存接收到的数据,队列容量等于一帧数据的大小,每来一个数据就将数据往队列里面加,当完整接收到一帧数据时此时队列中的全部数据也就是一帧完整的数据,因此只需要判断队列是否是数据校验头,队列尾是否是数据校验尾就可以得知当前是否已经接收到了一帧完整的数据,然后在将数据从队列中取出即可。原理图如下:
每来一个数据就往队列里面加:
当接收到一帧完整数据时队列头和数据校验头重合:
如果有数据尾校验,仅仅只需要添加一个校验尾即可,如下图所示: