背景:
在做霍尔采集电流的时候,CSSV1500N 系列电流传感器通过can数据输出的报文需要做crc校验,嵌入式常用查表的方式,所以就问了下deepseek怎么算这个CRC.
以下是使用 查表法(Lookup Table) 在C语言中高效计算8位SAE J1850 CRC的实现方法。查表法通过预先生成CRC表,避免了逐位计算,显著提升计算速度,特别适合嵌入式系统或实时性要求高的场景。
1. SAE J1850 CRC参数
- 多项式(Polynomial):
0x1D
(对应多项式 ( x^8 + x^4 + x^3 + x^2 + 1 )) - 初始值(Initial Value):
0xFF
- 结果异或(Final XOR):
0xFF
- 输入反转(Input Reflect):无
- 输出反转(Output Reflect):无
2. 查表法实现代码
(1) 预生成CRC表
首先生成256个元素的CRC表(每个字节值对应一个CRC值):
#include <stdint.h>// 生成SAE J1850 CRC表
static void generate_crc_table(uint8_t table[256]) {const uint8_t poly = 0x1D;for (uint16_t i = 0; i < 256; i++) {uint8_t crc = i;for (uint8_t j = 0; j < 8; j++) {if (crc & 0x80) {crc = (crc << 1) ^ poly;} else {crc <<= 1;}}table[i] = crc;}
}// 全局CRC表
static uint8_t crc_table[256];
static int is_table_generated = 0;// 初始化CRC表(只需调用一次)
void init_crc_table() {if (!is_table_generated) {generate_crc_table(crc_table);is_table_generated = 1;}
}
(2) 查表计算CRC
uint8_t crc8_sae_j1850_table(uint8_t *data, uint16_t length) {if (!is_table_generated) {init_crc_table(); // 确保表已生成}uint8_t crc = 0xFF; // 初始值for (uint16_t i = 0; i < length; i++) {crc = crc_table[crc ^ data[i]]; // 查表更新CRC}return crc ^ 0xFF; // 最终异或
}
3. 代码解释
(1) CRC表生成逻辑
- 逐字节生成:遍历所有可能的8位值(0x00~0xFF)。
- 逐位计算:对每个字节模拟8次位移,根据多项式决定是否异或。
- 表格内容:
crc_table[i]
表示字节i
的CRC值。
(2) 查表计算步骤
- 初始化CRC:初始值为
0xFF
。 - 逐字节处理:
- 将当前数据字节与CRC值异或,得到索引。
- 通过索引直接查表获取新的CRC值。
- 最终异或:返回结果前异或
0xFF
。
4. 性能对比
方法 | 计算复杂度 | 内存占用 | 适用场景 |
---|---|---|---|
逐位计算 | O(n×8) | 极小 | 低速或资源受限系统 |
查表法 | O(n) | 256字节 | 高速、实时性要求高的系统 |
5. 示例测试
#include <stdio.h>int main() {uint8_t data[] = {0x12, 0x34};uint16_t length = sizeof(data) / sizeof(data[0]);init_crc_table(); // 初始化表(只需一次)uint8_t crc = crc8_sae_j1850_table(data, length);printf("CRC8 SAE J1850: 0x%02X\n", crc);return 0;
}
输出结果:
CRC8 SAE J1850: 0x4B //自己算了下,结果不一样,可能使用的环境不一样。
6. 查表法的优化原理
查表法的核心思想是 空间换时间:
- 预计算:提前将所有可能的8位输入对应的CRC值存储在表中。
- 直接映射:运行时只需一次异或和查表操作即可处理一个字节,无需逐位计算。
7. 验证表正确性
可以通过对比查表法与逐位计算的结果来验证表的正确性:
// 逐位计算的函数(参考之前的实现)
uint8_t crc8_sae_j1850_bitwise(uint8_t *data, uint16_t length) {uint8_t crc = 0xFF;for (uint16_t i = 0; i < length; i++) {crc ^= data[i];for (uint8_t j = 0; j < 8; j++) {if (crc & 0x80) {crc = (crc << 1) ^ 0x1D;} else {crc <<= 1;}}}return crc ^ 0xFF;
}// 验证函数
void validate_table() {uint8_t test_data[] = {0x00, 0x55, 0xAA, 0xFF};for (int i = 0; i < 4; i++) {uint8_t crc_table = crc8_sae_j1850_table(&test_data[i], 1);uint8_t crc_bitwise = crc8_sae_j1850_bitwise(&test_data[i], 1);printf("Data=0x%02X, Table=0x%02X, Bitwise=0x%02X\n", test_data[i], crc_table, crc_bitwise);}
}
8. 总结
通过查表法实现SAE J1850 CRC的优点是 速度快、代码简洁,缺点是需占用256字节内存。在实际嵌入式开发中,若内存允许,推荐使用查表法提升效率。
9. 代码
#include <stdint.h>
#include <stdio.h>// 全局CRC表
static uint8_t crc_table[256];
static int is_table_generated = 0;// 生成SAE J1850 CRC表
static void generate_crc_table(uint8_t table[256])
{const uint8_t poly = 0x1D;for (uint16_t i = 0; i < 256; i++) {uint8_t crc = i;for (uint8_t j = 0; j < 8; j++) {if (crc & 0x80) {crc = (crc << 1) ^ poly;} else {crc <<= 1;}}table[i] = crc;}
}// 初始化CRC表(只需调用一次)
void init_crc_table()
{if (!is_table_generated) {generate_crc_table(crc_table);is_table_generated = 1;}
}uint8_t crc8_sae_j1850_table(uint8_t *data, uint16_t length)
{if (!is_table_generated){init_crc_table(); // 确保表已生成}uint8_t crc = 0xFF; // 初始值for (uint16_t i = 0; i < length; i++) {crc = crc_table[crc ^ data[i]]; // 查表更新CRC}return crc ^ 0xFF; // 最终异或
}int main()
{uint8_t data[] = {0x12, 0x34};uint16_t length = sizeof(data) / sizeof(data[0]);init_crc_table(); // 初始化表(只需一次)//打印出crc表for(int i=0; i<(sizeof(crc_table)/sizeof(crc_table[0])); i++){printf("0x%02x ", crc_table[i]);if((i/8==0) && (i!=0)){printf("\r\n");} }uint8_t crc = crc8_sae_j1850_table(data, length);printf("CRC8 SAE J1850: 0x%02X\n", crc);return 0;
}
10. 运行结果
0x00 0x1d 0x3a 0x27 0x74 0x69 0x4e 0x53
0xe8 0xf5 0xd2 0xcf 0x9c 0x81 0xa6 0xbb
0xcd 0xd0 0xf7 0xea 0xb9 0xa4 0x83 0x9e
0x25 0x38 0x1f 0x02 0x51 0x4c 0x6b 0x76
0x87 0x9a 0xbd 0xa0 0xf3 0xee 0xc9 0xd4
0x6f 0x72 0x55 0x48 0x1b 0x06 0x21 0x3c
0x4a 0x57 0x70 0x6d 0x3e 0x23 0x04 0x19
0xa2 0xbf 0x98 0x85 0xd6 0xcb 0xec 0xf1
0x13 0x0e 0x29 0x34 0x67 0x7a 0x5d 0x40
0xfb 0xe6 0xc1 0xdc 0x8f 0x92 0xb5 0xa8
0xde 0xc3 0xe4 0xf9 0xaa 0xb7 0x90 0x8d
0x36 0x2b 0x0c 0x11 0x42 0x5f 0x78 0x65
0x94 0x89 0xae 0xb3 0xe0 0xfd 0xda 0xc7
0x7c 0x61 0x46 0x5b 0x08 0x15 0x32 0x2f
0x59 0x44 0x63 0x7e 0x2d 0x30 0x17 0x0a
0xb1 0xac 0x8b 0x96 0xc5 0xd8 0xff 0xe2
0x26 0x3b 0x1c 0x01 0x52 0x4f 0x68 0x75
0xce 0xd3 0xf4 0xe9 0xba 0xa7 0x80 0x9d
0xeb 0xf6 0xd1 0xcc 0x9f 0x82 0xa5 0xb8
0x03 0x1e 0x39 0x24 0x77 0x6a 0x4d 0x50
0xa1 0xbc 0x9b 0x86 0xd5 0xc8 0xef 0xf2
0x49 0x54 0x73 0x6e 0x3d 0x20 0x07 0x1a
0x6c 0x71 0x56 0x4b 0x18 0x05 0x22 0x3f
0x84 0x99 0xbe 0xa3 0xf0 0xed 0xca 0xd7
0x35 0x28 0x0f 0x12 0x41 0x5c 0x7b 0x66
0xdd 0xc0 0xe7 0xfa 0xa9 0xb4 0x93 0x8e
0xf8 0xe5 0xc2 0xdf 0x8c 0x91 0xb6 0xab
0x10 0x0d 0x2a 0x37 0x64 0x79 0x5e 0x43
0xb2 0xaf 0x88 0x95 0xc6 0xdb 0xfc 0xe1
0x5a 0x47 0x60 0x7d 0x2e 0x33 0x14 0x09
0x7f 0x62 0x45 0x58 0x0b 0x16 0x31 0x2c
0x97 0x8a 0xad 0xb0 0xe3 0xfe 0xd9 0xc4 CRC8 SAE J1850: 0xAC