我有以下问题:我正在用 C 语言编写微控制器(ATmega 8bit、8051 等),并通过UART
接口接收自定义总线协议。我将接收到的字节放入缓冲区并稍后处理它们。
现在的问题是:是否有任何设计模式或策略来解码接收到的帧?最好的方法是如何做到这一点?有书籍/教程吗?
这是我在这里的第一个问题,所以如果问题格式不正确,请不要打我:)
由于所有此类协议都是自定义的,因此没有标准的方法来处理它们。唯一类似于 ADT(或“设计模式”,如果你愿意的话)是数据的实际接收,这通常是通过ring buffer完成的。
您通常用来解析实际协议的操作并不花哨,但总是以相同的方式完成。你最终会得到这样的东西:
(我在下面的代码中使用前缀 XYZ 表示该代码旨在解码虚构的“XYZ”协议。将其替换为自定义协议的名称。)
// xyz.h
#ifndef XYZ_H
#define XYZ_H
typedef enum
{
XYZ_OK,
XYZ_ERR_SYNC, // various error codes for things that can go wrong
XYZ_ERR_LENGTH,
XYZ_ERR_CHECKSUM,
...
} xyz_result_t;
xyz_result_t xyz_decode (const uint8_t* buf, size_t n);
#endif /* XYZ_H */
// xyz.c
#include "xyz.h"
xyz_result_t xyz_decode (const uint8_t* buf, size_t n)
{
// various protocol-specific checks:
if(buf[0] != SOME_SYNC_CHARACTER)
{
return XYZ_ERR_SYNC;
}
if(buf[something] < expected_min_length ||
buf[something] > expected_max_length)
{
return XYZ_ERR_LENGTH;
}
...
return XYZ_OK;
}
我发现设计协议以使其易于解码很重要。我当前的有特定的开始和结束字节。因此,当 UART 接收到东西时,它知道数据包从哪里开始(将缓冲区索引设置为零)和在哪里结束(以便可以处理它)。
如果这样做,则需要“转义”数据中的开始/结束字节,在其中添加另一个字节。即 0x7F -> 0x7D,0x5F 其中 0x7D 表示“下一个字节被转义”,0x5F 表示转义的 0x7F。
这让我的事情变得更容易了。IRQ 很简单,取消转义/检查数据包很简单,然后很容易从数据包字节中获取数据。
中断请求
If (received byte == start flag)
set receive buffer index to zero
else
if (space left in buffer)
store character in buffer
if (received byte == end flag)
process buffer
进程缓冲区
Unescape the packet data
Check length
Check checksum
Analyse data