C 嵌入式系统的串行通信数据包组织
我有一个嵌入式项目,我正在实现一个库来处理两个微处理器之间的串行通信。我心目中有一种结构,可以为可能随着时间推移而添加的数据包提供一种可伸缩的方法 需要有携带可变长度信息的不同数据包。接收数据包的处理器对每种类型的数据包都有不同的解释 每次输入一个字节时都要执行读取。当一个完整的数据包到达时,调用consumer函数来尽快处理它 我想解释一下我的方法,并就它的缺点以及如何改进得到一些反馈 目前的做法:C 嵌入式系统的串行通信数据包组织,c,serialization,embedded,uart,C,Serialization,Embedded,Uart,我有一个嵌入式项目,我正在实现一个库来处理两个微处理器之间的串行通信。我心目中有一种结构,可以为可能随着时间推移而添加的数据包提供一种可伸缩的方法 需要有携带可变长度信息的不同数据包。接收数据包的处理器对每种类型的数据包都有不同的解释 每次输入一个字节时都要执行读取。当一个完整的数据包到达时,调用consumer函数来尽快处理它 我想解释一下我的方法,并就它的缺点以及如何改进得到一些反馈 目前的做法: 创建一个显示数据成员和长度的结构 创建一个包含所有类型数据包和uint_8缓冲区的联合
- 创建一个显示数据成员和长度的结构
- 创建一个包含所有类型数据包和uint_8缓冲区的联合
- 创建一个结构,其中包含读取的字节、当前正在读取的数据包类型(联合的标记)、转义状态
- 创建一个枚举,显示不同类型数据包的值(还包含转义字符的值)
void DispRead1B(void)
{
static uint8_t ReadByte;
ReadByte = (uint8_t)UARTCharGet(DISP_BASE);
if(DisplayPacket.EscapeNext)
{
DisplayPacketPushByte(ReadByte);
DisplayPacket.EscapeNext = false;
}
else
{
if(ReadByte == RECEIVE_ESCAPE)
{
DisplayPacket.EscapeNext = true;
}
else if((ReadByte < RECEIVE_ESCAPE) && (ReadByte > RECEIVE_NO_OPERATION))
{
DisplayPacketChangeOperation(ReadByte);
}
else
{
DisplayPacketPushByte(ReadByte);
}
}
}
void DisplayPacketChangeOperation(uint8_t Operation)
{
DisplayPacket.CurrentOperation = Operation;
DisplayPacketResetBytesRead();
DisplayPacket.EscapeNext = false;
DisplayPacketCheckOperationFinished();
}
void DisplayPacketPushByte(uint8_t Value)
{
DisplayPacket.SerialBuffer[DisplayPacket.BytesRead] = Value;
DisplayPacketIncrementBytesRead();
DisplayPacketCheckOperationFinished();
}
void DisplayPacketIncrementBytesRead(void)
{
DisplayPacket.BytesRead += 1;
if(DisplayPacket.CurrentOperation == RECEIVE_NO_OPERATION)
{
DisplayPacket.BytesRead = 0;
}
}
bool DisplayPacketCheckOperationFinished(void)
{
bool PacketFinished = false;
switch (DisplayPacket.CurrentOperation)
{
case ACK:
PacketFinished = DisplayPacketCheckAckFinished(); break;
case VERSION_REQUEST:
PacketFinished = DisplayPacketCheckVersionRequestFinished(); break;
case PASSWORD:
PacketFinished = DisplayPacketCheckPasswordFinished(); break;
default:
return false;
}
if(PacketFinished)
{
DisplayPacketChangeOperation(RECEIVE_NO_OPERATION);
}
return PacketFinished;
}
bool DisplayPacketCheckAckFinished(void)
{
if(DisplayPacket.BytesRead == ACK_PACKET_BYTES)
{
if(CheckSysFlagNotSet(KeepAliveAckReceived))
{
UpdateSysFlag(KeepAliveAckReceived,true);
StartAliveTimer();
}
else
{
UpdateSysFlag(ConnectionError,false);
ReloadAliveTimer();
}
return true;
}
return false;
}
bool DisplayPacketCheckVersionRequestFinished(void)
{
if(DisplayPacket.BytesRead == VER_REQ_PACKET_BYTES)
{
UpdateSysFlag(FirmwareVerRequested,true);
return true;
}
return false;
}
bool DisplayPacketCheckPasswordFinished(void)
{
if(DisplayPacket.BytesRead == PASSWORD_PACKET_BYTES)
{
PasswordPacket = DisplayPacket.PasswordPacket;
UpdateSysFlag(PasswordReceived,true);
return true;
}
return false;
}
void DispRead1B(void)
{
静态uint8_t ReadByte;
ReadByte=(uint8_t)UARTCharGet(DISP_BASE);
if(displaypack.EscapeNext)
{
显示packetpushbyte(ReadByte);
displaypack.EscapeNext=false;
}
其他的
{
if(ReadByte==接收\u转义)
{
displaypack.EscapeNext=true;
}
否则如果((ReadByteRECEIVE\u NO\u操作))
{
显示PacketChangeOperation(读取字节);
}
其他的
{
显示packetpushbyte(ReadByte);
}
}
}
无效显示packetchange操作(uint8\t操作)
{
DisplayPacket.CurrentOperation=操作;
DisplayPacketResetBytesRead();
displaypack.EscapeNext=false;
DisplayPacketCheckOperationFinished();
}
无效显示packetpushbyte(uint8_t值)
{
displaypack.SerialBuffer[displaypack.BytesRead]=值;
DisplayPacketIncrementBytesRead();
DisplayPacketCheckOperationFinished();
}
void display packetincrementbytesread(void)
{
displaypack.BytesRead+=1;
if(DisplayPacket.CurrentOperation==接收\u否\u操作)
{
displaypack.BytesRead=0;
}
}
bool DisplayPacketCheckOperationFinished(无效)
{
bool PacketFinished=false;
交换机(DisplayPacket.CurrentOperation)
{
案例确认:
PacketFinished=显示PacketCheckAckFinished();中断;
案例版本请求:
PacketFinished=DisplayPacketCheckVersionRequestFinished();中断;
案例密码:
PacketFinished=DisplayPacketCheckPasswordFinished();中断;
违约:
返回false;
}
如果(包装完成)
{
显示PacketChangeOperation(接收\u否\u操作);
}
返回包装完成;
}
bool DisplayPacketCheckAckFinished(无效)
{
if(displaypack.BytesRead==ACK\u PACKET\u字节)
{
if(CheckSysFlagNotSet(KeepAliveAckReceived))
{
UpdateSysFlag(KeepAliveAckReceived,true);
StartAliveTimer();
}
其他的
{
UpdateSysFlag(ConnectionError,false);
重新加载AliveTimer();
}
返回true;
}
返回false;
}
bool DisplayPacketCheckVersionRequestFinished(无效)
{
if(displaypack.BytesRead==VER\u REQ\u PACKET\u字节)
{
UpdateSysFlag(firmWareRequested,true);
返回true;
}
返回false;
}
bool DisplayPacketCheckPasswordFinished(无效)
{
if(displaypack.BytesRead==密码\数据包\字节)
{
PasswordPacket=DisplayPacket.PasswordPacket;
UpdateSysFlag(PasswordReceived,true);
返回true;
}
返回false;
}
使用这种方法,不需要序列化,因为联合会处理它。此外,如果需要数据的备份副本,复制数据包的语法也很简单。即:PacketType=CommUnion.PacketType
添加新数据包时。创建一个结构并将其添加到union中,添加新的enum字段,并向check FINATED switch语句添加一个案例,该语句调用此数据包的函数
这需要库的源代码在我不喜欢的多行上进行更改
有没有一种方法可以以一种更简单的方式实现类似的功能,即在编译的comm结构保持不变的情况下,需要进行外部更改
第二种方法(使用函数指针):
- 摆脱工会。控制结构仍然保持不变,因为我们需要跟踪当前的数据包类型和字节读取、转义等
- 摆脱enum方法以创建数据包值。创建函数,为不同的数据包注册检查完成的函数,并像enum方法一样为它们赋值。寄存器函数是用于检查函数的函数指针数组
- 启动时,初始化所有数据包结构。(主程序或项目特定模块中的更改不在库中。)
- 然后使用register函数注册所有数据包。这样,数据包的数量和转义字符的值可以在运行时计算(如果编译器是智能的,则在编译时计算)
- 当您按下一个数据字节时,Check finished函数指针用于调用该函数。此函数序列化(或调用此函数)并在数据包完全接收时处理数据包
#define DISPLAY_PACKET_BYTES 16
typedef struct
{
union
{
uint8_t SerialBuffer[DISPLAY_PACKET_BYTES];
password_packet_t PasswordPacket;
};
struct
{
uint8_t CurrentOperation;
uint8_t BytesRead;
bool EscapeNext;
};
}disp_receive_packet_t;
enum
{
RECEIVE_NO_OPERATION = 0xA0,
ACK,
PASSWORD,
VERSION_REQUEST,
RECEIVE_ESCAPE,
}DISP_RECEIVE_OPERATIONS;
void DispRead1B(void)
{
static uint8_t ReadByte;
ReadByte = (uint8_t)UARTCharGet(DISP_BASE);
if(DisplayPacket.EscapeNext)
{
DisplayPacketPushByte(ReadByte);
DisplayPacket.EscapeNext = false;
}
else
{
if(ReadByte == RECEIVE_ESCAPE)
{
DisplayPacket.EscapeNext = true;
}
else if((ReadByte < RECEIVE_ESCAPE) && (ReadByte > RECEIVE_NO_OPERATION))
{
DisplayPacketChangeOperation(ReadByte);
}
else
{
DisplayPacketPushByte(ReadByte);
}
}
}
void DisplayPacketChangeOperation(uint8_t Operation)
{
DisplayPacket.CurrentOperation = Operation;
DisplayPacketResetBytesRead();
DisplayPacket.EscapeNext = false;
DisplayPacketCheckOperationFinished();
}
void DisplayPacketPushByte(uint8_t Value)
{
DisplayPacket.SerialBuffer[DisplayPacket.BytesRead] = Value;
DisplayPacketIncrementBytesRead();
DisplayPacketCheckOperationFinished();
}
void DisplayPacketIncrementBytesRead(void)
{
DisplayPacket.BytesRead += 1;
if(DisplayPacket.CurrentOperation == RECEIVE_NO_OPERATION)
{
DisplayPacket.BytesRead = 0;
}
}
bool DisplayPacketCheckOperationFinished(void)
{
bool PacketFinished = false;
switch (DisplayPacket.CurrentOperation)
{
case ACK:
PacketFinished = DisplayPacketCheckAckFinished(); break;
case VERSION_REQUEST:
PacketFinished = DisplayPacketCheckVersionRequestFinished(); break;
case PASSWORD:
PacketFinished = DisplayPacketCheckPasswordFinished(); break;
default:
return false;
}
if(PacketFinished)
{
DisplayPacketChangeOperation(RECEIVE_NO_OPERATION);
}
return PacketFinished;
}
bool DisplayPacketCheckAckFinished(void)
{
if(DisplayPacket.BytesRead == ACK_PACKET_BYTES)
{
if(CheckSysFlagNotSet(KeepAliveAckReceived))
{
UpdateSysFlag(KeepAliveAckReceived,true);
StartAliveTimer();
}
else
{
UpdateSysFlag(ConnectionError,false);
ReloadAliveTimer();
}
return true;
}
return false;
}
bool DisplayPacketCheckVersionRequestFinished(void)
{
if(DisplayPacket.BytesRead == VER_REQ_PACKET_BYTES)
{
UpdateSysFlag(FirmwareVerRequested,true);
return true;
}
return false;
}
bool DisplayPacketCheckPasswordFinished(void)
{
if(DisplayPacket.BytesRead == PASSWORD_PACKET_BYTES)
{
PasswordPacket = DisplayPacket.PasswordPacket;
UpdateSysFlag(PasswordReceived,true);
return true;
}
return false;
}