在嵌入式软件开发中,我们几乎一定会涉及到协议和数据相关的解析,这些协议往往有两种解析方式:流式解析和块式解析。
块式解析是指将整个协议接收下来,然后对整个接收缓冲区内的所有数据进行解析处理。这种解析方式的典型使用场景是:使用DMA进行定长协议的接受。在解析的过程中可以使用标准库函数进行字符串的解析,代码的编写上一般不会十分复杂,但是对于协议的设计有一定的要求。
流式解析是指在接收的过程中,每接收到一个字符就进行一部分处理,进而从字符串流中完成数据解析的方式。典型的使用场景是:使用接收中断进行接收和解析。解析过程相对而言比较复杂,没有现成的函数可用,不过这种方式对于协议设计的要求比较宽松,并且不限制协议的长度。
本模块旨在简化流式数据解析的流程,开发者仅需提供协议头、数据分隔标志符以及协议尾即可完成整个协议的解析。
PS:该模块的开发思路源自GPS中NMEA协议的解析。
该模块的功能是依据格式字符串将字符流中的字符串数据解析为代码中可用的数据。比如:
-
"d":将字符串
“123”
解析为整型123; -
"f":将字符串
“123.456”
解析为浮点型(双精度型)123.456; -
"s":将数据流中的字符串转化为存在于用户缓冲区中的字符串;
-
" ":忽略部分开发者不关心的数据;
一个StreamParser可以处理的典型的协议如下:
$GPGGA,092204.999,4250.5589,S,14718.5084,E,1,04,24.4,19.7,M,,,,0000*1F
其中,我们将$GP
称为头字符串(也可以使用$
),它标志着一个协议包解析的开始;将,
称为分隔字符串,它标志着一个解析部分的结束。在整个协议的解析过程中,格式字符串中的每个字符都对应着一个解析部分;可以将*1F
作为一个尾字符串,它标志着一个协议包解析的结束。
使用*1F进行解析的方式在GPS解析中是错误的操作,因为最后两位是校验位,并不是固定的,此处只是简单举个例子。
要在你的工程中使用StreamParser,仅需将一对头文件与源文件添加进入你的工程路径,解决包含与连接问题后做出如下处理:
-
为你的解析器定义一个
sParser_t
句柄(不同解析器之间相互独立); -
使用
metaData_t
类型为你的协议数据定义一个缓冲区; -
为每个解析器调用
SParser_Init
函数进行初始化;-
初始化参数如下:
parser
:解析器句柄headStr
:协议头字符串tailStr
:协议尾字符串divStr
:协议分隔字符串typeList
:格式字符串
-
-
将你接收到的数据流逐个字符传入
SParser_Parse
的ch
参数;-
解析器参数如下:
parser
:解析器句柄dataArray
:协议数据缓冲区指针ch
:数据流中的字符
-
-
监视
SParser_Parse
函数的返回值,一旦该函数返回1,则说明一个协议包的解析已经完成。 -
从缓冲区中取出数据使用。
关于
dataArray
的使用的注意事项:当你使用字符串解析时,你可以将
dataArray
中对应格式字符串中s
的位置设置为预先创建的字符串缓冲区地址,解析器会自动使用这个地址。否则,请务必确保对应位置为NULL
,解析器会为解析出的字符串自动创建长度为STRING_BUFFER_SIZE
的缓冲区。此外,如果你使用预先创建缓冲区的方式,请确保缓冲区的大小大于等于
STRING_BUFFER_SIZE
(如果你能确保你的缓冲区可以装下协议字符串的话也可以忽略),因为解析器内部只对STRING_BUFFER_SIZE
的长度进行了边界检查。
你还有一些配置宏可以用于配置解析器:
-
考虑到float可能精度不足,所以还可以通过将
USE_DOUBLE
置1来配置解析器使用双精度; -
如果你用不到字符串解析的功能,可以通过将
USE_STRING_PARSE
置0的方式来节省部分代码空间(省,但不多); -
你可以使用
STRING_BUFFER_SIZE
来配置字符串解析默认的缓冲区边界检查范围和默认创建的缓冲区大小。
函数的使用示例位于./test中,也是一个简单的测试文件。
解析器在一些头、分隔、尾字符串较复杂的情况下可能会匹配失败,请自行简化协议设计,当然也欢迎提出PR完善Bug。