本帖最后由 tovinz 于 2026-1-22 12:04 编辑
如果你的协议是自定义的,推荐你用 cobs编解码算法 进行数据包封装和提取。
cobs.c
(1.58 KB, 下载次数: 2)
cobs.h
(836 Bytes, 下载次数: 2)
高效可靠的数据字节编码算法COBS,可用于串口通信
https://forum.anfulai.cn/forum.p ... 15429&fromuid=41790
(出处: 硬汉嵌入式论坛)
下面提供一个字节流数据包提取器组件(根据分割字符进行提取,仅提取,提取后可自行用于对接 cobs 解码)
组件依赖 pipe,其实就是一个环形缓冲区。此组件不依赖 cobs 组件
pipe.c
(3.75 KB, 下载次数: 5)
pipe.h
(2.68 KB, 下载次数: 3)
pkt_rx.c
(4.06 KB, 下载次数: 5)
pkt_rx.h
(1.79 KB, 下载次数: 3)
原理很简单,
1. 初始化pipe组件;初始化 pkt_rx 组件,关联pipe组件,配置分隔符、操作缓冲区、超时等信息
1. 将接收到的字节流通过 pkt_rx_feed() 或 pkt_rx_feed_byte() 填充到关联的pipe中
2. 通过不断调用 pkt_rx_next() 来从关联的 pipe 中解析数据包
代码片段
1. 初始化
[C] 纯文本查看 复制代码
// 传输接收到的数据的管道
static pipe_t g_u6_rxpipe;
static uint8_t g_u6_rxpipe_buff[ CONFIG_RX_PIPE_BUFF_SIZE ];
// 接收数据包提取器
#define U6_PKT_SEPARATOR 0x00
static pkt_rx_t g_u6_pkt;
// 存放提取出来的接收数据帧
static uint8_t g_u6_pkt_fbuf[ CONFIG_FRAM_MAX_LEN + 20 ];
void init (void)
{
pkt_rx_cfg_t cfg;
// 初始化pipe
pipe_init(&g_u6_rxpipe, g_u6_rxpipe_buff, sizeof(g_u6_rxpipe_buff));
// 初始化数据包提取器
memset(&cfg, 0, sizeof(cfg));
cfg.pipe = &g_u6_rxpipe; // 关联pipe
cfg.frame_buf = g_u6_pkt_fbuf; // 用于提取数据包的操作的缓冲区
cfg.frame_cap = sizeof(g_u6_pkt_fbuf);
cfg.separator = U6_PKT_SEPARATOR; // 字节流中区分数据包的分隔字符
cfg.timeout_ms = 500; // 超时时间。用于长时间没有数据接收时清理pipe中未成功解析的数据
pkt_rx_init(&g_u6_pkt, &cfg);
}
2. 数据写入
[C] 纯文本查看 复制代码
/* 将接收到的数据写入到关联的pipe中 */
pkt_rx_feed(&g_u6_pkt, d, s);
3. 数据包提取
[C] 纯文本查看 复制代码 // 线程. 从管道中拉取数据, 使用cobs解码
static void u6_rx_thread_entry (void *arg)
{
// 解码缓冲区
uint8_t g_u6_rx_decode[ CONFIG_FRAM_MAX_LEN ];
size_t frame_len = 0;
pkt_rx_view_t view;
for (;;)
{
osDelay(5);
/* 从管道中拉取数据, 并通过分隔字符机制提取数据包 */
while (pkt_rx_next(&g_u6_pkt, osKernelGetTickCount(), &view) == PKT_RX_OK)
{
/* 使用cobs解码数据包 */
frame_len = cobsDecode(view.data, view.len, g_u6_rx_decode);
/* 处理数据包 */
rx_hook(g_u6_rx_decode, frame_len);
}
}
}
|