2 Star 8 Fork 2

觉皇嵌入式 / psm

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README
MIT

PSM:协议状态机(Protocol State Machine),一款用于流式传输的数据协议解析组件


介绍

PSM:Protocol State Machine,协议状态机。

一款用于流式传输的数据协议解析组件,可有效解决沾包、断帧问题。

PSM以回调函数的形式将完整的协议数据包抛到应用软件层,确保应用软件层收到的数据是一包完整的、有效的数据帧。

代码以纯C编写,可无缝在windows、linux、嵌入式单片机、嵌入式RTOS下应用。

代码下载:https://gitee.com/jhembedded/psm


使用场景

  • windows、linux下使用tcp、udp等流式传输协议
  • 嵌入式linux、单片机开发时,使用到了串口、SPI、IIC、USB、网络等进行收发不定长数据

协议的制定也是比较有规律的,一包完整的协议数据包的结构包含:包头、数据字段的长度、包尾等信息,或者只有协议包头后跟数据的形式,基于此特性,PSM由此而生。


使用介绍

PSM由psm_t这个结构体进行管理,该结构如下:

typedef struct psm psm_t;
struct psm
{
	uint8_t* buf;                     // 指向数据缓存
	uint32_t bufsize;                 // 数据缓存大小

	uint32_t wpos;                    // 写指针
	uint32_t rpos;                    // 读指针

	uint32_t match_start;             // 数据匹配索引

	psm_state_t state;                // 当前协议状态机的状态

	int (*recv_callback)(psm_t* psm, const void* buf, uint32_t len);  // 数据回调函数

	uint8_t  delimiter[4];             // 协议尾分隔符
	uint32_t delimiter_bytes;          // 协议尾分隔符长度
	uint32_t delimiter_index;          // 协议尾分隔符匹配索引

	uint8_t  identifier[4];            // 协议头标识符
	uint32_t identifier_bytes;         // 协议头标识符长度
	uint32_t identifier_index;         // 协议头标识符匹配索引

	uint32_t body_offset;              // 主体数据偏移地址
	uint32_t body_length;              // 主体数据长度

	uint32_t length_field_offset;                 // 长度字段偏移地址
	uint32_t length_field_bytes;                  // 长度字段所占字节数
	psm_unpack_coding_t length_field_coding;      // 长度字节编码方式

	void* arg;                                    // 可用于携带附加参数
};

PSM的移植也很简单,API如下:

int psm_recv(psm_t* psm, const void* buf, uint32_t len);
int psm_recv_byte(psm_t* psm, uint8_t c);
void psm_reset(psm_t* psm);
int psm_unpack(psm_t* psm);
  • psm_recv:数据接收函数,将接收到的数据存储于PSM中
  • psm_recv_byte:数据单字节接收函数,将接收到的数据存储于PSM中
  • psm_reset:重置PSM
  • psm_unpack:解析接收到的数据

移植时将psm_recv或psm_recv_byte放置于实际的数据接收位置进行数据接收,使用psm_unpack对收到的数据进行解析,psm_unpack解析成功后自动调用回调函数将完整的协议包抛到上层。

以单片机串口为例,将psm_recv_byte或psm_recv放置到串口接收中断中进行数据接收,在main函数中的死循环中轮训psm_unpack函数即可。

示例:


psm_t psm_usart =
{
	...
	...
	...
	...
};

void UART1_IRQHandler(void)
{
	static uint8_t RxData;
	...
	psm_recv_byte(&psm_usart, RxData);  // 接收一个字节
	...
}

int main()
{
	...
	...
	...

	while (1)
	{
		psm_unpack(&psm_usart);  // 解析
	}
}

同时,也可将psm_unpack直接放置在串口接收中断中,由于此时数据是在串口中断中进行的数据解析,解析成功后会调用回调函数,所以在在回调函数中不可存在耗时操作。

示例:

void UART1_IRQHandler(void)
{
	static uint8_t RxData;
	...
	psm_recv_byte(&psm_usart, RxData);  // 接收一个字节
	psm_unpack(&psm_usart);  // 解析
	...
}

使用案例

示例1:以回车换行结尾的字符串形式


int recv_callback(psm_t * psm, const void* buf, uint32_t len)
{
	// 在此函数中对完整的数据包进行使用
	return 0;
}

uint8_t buffer[512];

psm_t psm =
{
	.buf                = buffer,
	.bufsize            = sizeof(buffer),

	.recv_callback      = recv_callback,

	.delimiter[0]       = '\r',  // 结束符第一个字节
	.delimiter[1]       = '\n',  // 结束符第二个字节
	.delimiter_bytes    = 2,     // 结束符大小
};

示例2:协议头形式

该协议结构一般为:

包头 命令码 数据长度 CRC N字节数据

其中:包头 +命令码 +数据长度 +CRC组合成协议头,后跟实际的数据。

以包头、命令码、数据长度、CRC都为unsigned short类型,且包头为0XA1B0,PSM设置如下:

psm_t psm =
{
	.buf              = buffer,
	.bufsize          = sizeof(buffer),

	.recv_callback    = recv_callback,

	.identifier[0]    = 0XB0,  // 包头低字节
	.identifier[1]    = 0XA1,  // 包头高字节
	.identifier_bytes = 2,     // 包头的大小

	.length_field_bytes  = 2,  // 协议头中数据长度字段的大小
	.length_field_offset = 4,  // 协议头中数据长度字段的位置偏移
	.length_field_coding = PSM_UNPACK_ENCODE_BY_LITTEL_ENDIAN,   // 编码方式:小端

	.body_offset = 8,  // 主体数据的位置偏移
};

示例3:包头包尾形式

注:不支持相同字节的包头包尾,例如包头为0XAA、0XAA,包尾为0x55、0x55。

该协议结构一般为:

包头 数据 包尾

假设包头、包尾为unsigned short类型,且包头为0XAA、0X55、包尾为0X55、0XAA,PSM设置如下:

psm_t psm =
{
	.buf              = buffer,
	.bufsize          = sizeof(buffer),

	.recv_callback    = recv_callback,

	.identifier[0]    = 0XAA,  // 包头低字节
	.identifier[1]    = 0X55,  // 包头高字节
	.identifier_bytes = 2,     // 包头的大小

	.delimiter[0]     = 0X55,  // 包尾低字节
	.delimiter[1]     = 0XAA,  // 包尾高字节
	.delimiter_bytes  = 2,     // 包尾的大小
};

MIT License Copyright (c) 2023 觉皇嵌入式 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

简介

PSM:Protocol State Machine,协议状态机,用于流式传输的数据协议解析,有效解决沾包、断帧问题。 展开 收起
C
MIT
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
1
https://gitee.com/jhembedded/psm.git
git@gitee.com:jhembedded/psm.git
jhembedded
psm
psm
main

搜索帮助