状态机串口接收

#以下代码的功能是解析数据包,按照一定的协议格式寻找帧头(0xFF)、标识符(0x01)、控制字节(0x7D)、数据部分(最多 129 字节),以及帧尾(0xFE)。当成功接收到完整数据帧后,会将 frame_done 置为 1(但 frame_done 没有进一步处理)。

VOID  CsiParsePacket (INT8U *pData, DWORD dwLen)
{
  INT16  i;
  INT8U  curr_char;
  BOOL   result;
  static UINT8 buffer[131];
  static UINT8 buf_index = 0;
  static UINT8 step = 0;
  UINT8  frame_done = 0;
  for (i = 0; i < dwLen; i++) {
      switch (step) {
        case 0: { // find 0xff
            if (pData[i] == 0xff) {
                buf_index = 0;
                memset(buffer, 0, 131);
                buffer[buf_index] = pData[i];
                buf_index++;
                step = 1;
            } else {
                step = 0;
                buf_index = 0;
            }
        } break;
        case 1: { // find 0x01
            if (pData[i] == 0x01) {
                buffer[buf_index] = pData[i];
                buf_index++;
                step = 2;
            } else {
                step = 0;
                buf_index = 0;
            }
        } break;
        case 2: { // find 0x7d
            if (pData[i] == 0x7d) {
                buffer[buf_index] = pData[i];
                buf_index++;
                step = 3;
            } else {
                step = 0;
                buf_index = 0;
            }
        } break;
        case 3: { // load data
            if (buf_index < 129) {
                buffer[buf_index] = pData[i];
                buf_index++;
            } else {
                step = 4;
            }
        } break;
        case 4: { // find 0xfe
            if (pData[i] == 0xfe) {
                buffer[buf_index] = pData[i];
                buf_index++;
                frame_done = 1;
            }
            buf_index = 0;
            step = 0;
        } break;
    }
  }

代码解析

1. 函数参数

VOID  CsiParsePacket (INT8U *pData, DWORD dwLen)
  • pData:指向待解析的数据流(字节数组)。
  • dwLen:数据流的长度。

2. 变量定义

INT16  i;                        // 遍历数据流的索引
INT8U  curr_char;                // 当前处理的字节(但未使用)
BOOL   result;                    // 可能用于后续处理(未使用)
static UINT8 buffer[131];         // 存储解析出的数据包(最大 131 字节)
static UINT8 buf_index = 0;       // 当前数据存储索引
static UINT8 step = 0;            // 解析状态机的状态
UINT8  frame_done = 0;            // 标记数据帧是否解析完成(未进一步处理)
  • buffer 用于存储解析出的数据包。
  • step 作为状态机的步骤控制变量,确保按照 0xFF 0x01 0x7D ... 0xFE 格式解析数据。

3. 数据包解析状态机

Step 0: 查找帧头 0xFF
case 0: { // find 0xff
    if (pData[i] == 0xff) {
        buf_index = 0;
        memset(buffer, 0, 131);
        buffer[buf_index] = pData[i];
        buf_index++;
        step = 1; // 进入下一步查找 0x01
    } else {
        step = 0;
        buf_index = 0;
    }
} break;
  • 如果当前字节是 0xFF(帧头)
    • 重置 buf_indexbuffer,清除旧数据。
    • 记录 0xFF 并进入 step = 1(寻找 0x01)。
  • 否则,继续等待 0xFF

Step 1: 查找 0x01
case 1: { // find 0x01
    if (pData[i] == 0x01) {
        buffer[buf_index] = pData[i];
        buf_index++;
        step = 2; // 进入下一步查找 0x7D
    } else {
        step = 0;
        buf_index = 0;
    }
} break;
  • 期待下一个字节是 0x01
  • 如果找到 0x01,记录并进入 step = 2(寻找 0x7D)。
  • 否则,解析失败,回到 step = 0 重新查找 0xFF

Step 2: 查找 0x7D
case 2: { // find 0x7d
    if (pData[i] == 0x7d) {
        buffer[buf_index] = pData[i];
        buf_index++;
        step = 3; // 进入数据部分
    } else {
        step = 0;
        buf_index = 0;
    }
} break;
  • 期待 0x7D 作为控制字节。
  • 如果找到 0x7D,记录并进入 step = 3(开始接收数据)。
  • 否则,解析失败,回到 step = 0 重新查找 0xFF

Step 3: 接收数据
case 3: { // load data
    if (buf_index < 129) {
        buffer[buf_index] = pData[i];
        buf_index++;
    } else {
        step = 4; // 数据超过 129 字节,进入查找帧尾
    }
} break;
  • 继续接收数据,最多 129 字节
  • 如果 buf_index 超过 129,自动进入 step = 4(查找帧尾 0xFE

Step 4: 查找帧尾 0xFE
case 4: { // find 0xfe
    if (pData[i] == 0xfe) {
        buffer[buf_index] = pData[i];
        buf_index++;
        frame_done = 1;  // 数据包完整
    }
    buf_index = 0;
    step = 0;
} break;
  • 期待 0xFE 作为数据包的结束标志。
  • 如果 pData[i] == 0xFE,说明数据包完整,标记 frame_done = 1
  • 无论是否成功解析,重置 buf_indexstep,准备解析下一个数据包

总结

  1. 解析数据格式
    0xFF 0x01 0x7D [最大 129 字节数据] 0xFE
    
  2. 状态机解析流程
    • Step 0:寻找 0xFF(帧头)。
    • Step 1:寻找 0x01(标识符)。
    • Step 2:寻找 0x7D(控制字节)。
    • Step 3:接收数据(最多 129 字节)。
    • Step 4:寻找 0xFE(帧尾)。
    • 成功解析后 frame_done = 1(但代码中未使用)

可能问题

  1. 没有处理 frame_done

    • 解析完整数据帧后,frame_done = 1,但代码没有利用 frame_done 进行后续处理,如回调函数或数据传输。
  2. 数据丢失风险

    • 如果数据包中有 0xFF,但后续字节不符合格式,代码会丢弃整个包,而不是继续查找新的 0xFF
    • 可能导致部分有效数据丢失。
  3. 固定 129 字节数据限制

    • 如果协议允许不同长度的数据帧,当前代码会错误地忽略小于 129 字节的数据。

优化建议

处理 frame_done

if (frame_done) {
    frame_done = 0;  // 复位
    ProcessFrame(buffer, buf_index); // 处理完整帧
}

优化数据丢失问题

如果检测到 0xFF 但没有完整数据包,可以不清空 buffer,而是继续寻找 0xFF

if (step == 0 && pData[i] != 0xFF) {
    continue;  // 继续查找帧头
}

总结

这段代码实现了一个简单的状态机来解析固定格式的数据包(0xFF 0x01 0x7D ... 0xFE)。但它有一些潜在问题,例如 frame_done 未使用、可能丢失数据、以及无法处理变长数据帧。优化后,它可以更可靠地解析数据并触发相应的回调处理完整帧。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值