【嵌入式外设精学】Day10|校验与协议帧:和、CRC、帧头长度
今天解决:有校验为什么还会执行错命令?帧该怎么设计?
目录
1. 问题
- 串口收对了字节却解错业务
- 校验过了但动作错乱
2. 原理
2.1 两层“校验”
| 硬件帧 | 8N1 起止位 | 保证一个字节能收 |
| 协议帧 | 帧头、长度、命令、数据、校验 | 保证一段消息完整、正确 |
2.2 校验能力差别(直觉)
| 奇偶 | 只查 1 的个数,很弱 |
| 累加和/异或 | 能挡不少错,交换、抵消类错误可能漏 |
| CRC | 抗突发错误强,推荐 |
2.3 为什么还要帧头和长度
- 帧头:从字节流里认出“一条消息开始”
- 长度:知道这条收到哪结束,并防止乱值导致缓冲区爆掉
- 校验:整条对不对
三件套缺一不可。
3. 代码示例
#include <stdint.h>
#include <stdbool.h>
#define FRAME_HDR 0xAAu
#define FRAME_MAX 16u
typedef struct {
uint8_t hdr;
uint8_t len;
uint8_t cmd;
uint8_t data[8];
uint8_t sum;
} frame_t;
uint8_t sum_range(const uint8_t *p, uint32_t n)
{
uint8_t s = 0;
while (n—) {
s = (uint8_t)(s + *p++);
}
return s;
}
bool frame_valid(const frame_t *f)
{
/* 约定:sum 覆盖 hdr..data */
uint8_t s = sum_range((const uint8_t *)f, (uint32_t)(2u + 1u + 8u));
return (f->hdr == FRAME_HDR) && (s == f->sum);
}
简单 CRC8 示例(可替换为库函数):
uint8_t crc8_calc(const uint8_t *d, uint32_t n)
{
uint8_t crc = 0x00u;
uint32_t i;
int b;
for (i = 0; i < n; i++) {
crc ^= d[i];
for (b = 0; b < 8; b++) {
crc = (crc & 0x80u) ? (uint8_t)((crc << 1) ^ 0x07u)
: (uint8_t)(crc << 1);
}
}
return crc;
}
4. 常见误区
| 只有奇偶就够 | 协议里还要和/CRC |
| 校验过了就一定业务对 | 还要 cmd 合法、长度合法 |
| len 不设上限 | 错帧可能把缓冲写爆 |
| 数据里可以随便出现帧头 | 要靠状态机+校验丢弃假峰 |
5. 自测
6. 答案
作者:shuifanyu 标签:协议 CRC 校验和 串口







