PEC基于CRC-8,一般用在I2C / SMBus这样的地方,它没有纠错能力,只能判断数据是否完整可靠;
主要逻辑为:
发送端在发送数据时,利用一个式子把需要校验的数据进行计算,得到一个PEC校验码;
接收端接收到数据后,也用相同式子计算,对比两者PEC校验码是否相同,相同的话认为数据完整可靠,不同认为这个包错误;
伪代码示例
cpp
#include <stdio.h>
#include <stdint.h>
// CRC-8 PEC计算函数(I2C标准多项式:0x07)
uint8_t i2c_calculate_pec(const uint8_t *data, uint16_t length) {
uint8_t crc = 0x00; // 初始值为0
for (uint16_t i = 0; i < length; i++) {
crc ^= data[i]; // 与当前数据字节异或
// 对每个比特位进行处理
for (uint8_t bit = 0; bit < 8; bit++) {
if (crc & 0x80) { // 最高位为1时,左移后与多项式异或
crc = (crc << 1) ^ 0x07;
} else { // 最高位为0时,仅左移
crc <<= 1;
}
crc &= 0xFF; // 保持8位
}
}
return crc;
}
// 模拟I2C发送端:生成包含PEC的数据帧
void i2c_transmit(uint8_t *tx_data, uint16_t data_len) {
// 计算PEC校验值(包含地址、命令、数据等所有传输内容)
uint8_t pec = i2c_calculate_pec(tx_data, data_len);
// 打印发送信息(实际I2C中会通过硬件发送)
printf("I2C发送数据: ");
for (uint16_t i = 0; i < data_len; i++) {
printf("0x%02X ", tx_data[i]);
}
printf("| PEC: 0x%02X\n", pec);
}
// 模拟I2C接收端:验证PEC是否正确
bool i2c_receive(const uint8_t *rx_data, uint16_t data_len, uint8_t received_pec) {
// 对接收到的数据重新计算PEC
uint8_t calculated_pec = i2c_calculate_pec(rx_data, data_len);
// 验证PEC是否一致
if (calculated_pec == received_pec) {
printf("I2C接收验证成功:PEC匹配\n");
return true;
} else {
printf("I2C接收验证失败:计算PEC=0x%02X,接收PEC=0x%02X\n",
calculated_pec, received_pec);
return false;
}
}
int main() {
// 示例:I2C数据帧(包含从机地址、命令、数据)
uint8_t i2c_frame[] = {0xA0, 0x01, 0x34, 0x56}; // 地址0xA0,命令0x01,数据0x34、0x56
uint16_t frame_len = sizeof(i2c_frame) / sizeof(i2c_frame[0]);
// 发送端生成并发送数据+PEC
uint8_t tx_pec = i2c_calculate_pec(i2c_frame, frame_len);
i2c_transmit(i2c_frame, frame_len);
// 模拟接收过程(正常情况)
printf("\n--- 正常接收场景 ---\n");
i2c_receive(i2c_frame, frame_len, tx_pec); // 验证通过
// 模拟接收过程(数据被干扰的情况)
printf("\n--- 数据错误场景 ---\n");
uint8_t corrupted_frame[] = {0xA0, 0x01, 0x35, 0x56}; // 第3字节被篡改(0x34→0x35)
i2c_receive(corrupted_frame, frame_len, tx_pec); // 验证失败
return 0;
}