2. CRC-16/CCITT计算
任务目标
实现CRC-16/CCITT(多项式0x1021,初值0xFFFF)算法,对给定数据块计算校验值,并与标准参考结果比对,验证实现正确性。
实现方案
采用查表法提升计算效率,表项覆盖0~255所有字节的CRC中间值。核心逻辑:对每个字节,将当前CRC高8位与该字节异或后作为查表索引,查表结果与CRC左移8位再异或,迭代至数据结束。最终结果按CCITT规范输出16位校验值。
完整代码
#include <stdio.h>
#include <stdint.h>
#include <string.h>
/* CRC-16/CCITT 参数:poly=0x1021, init=0xFFFF, refin=false, refout=false, xorout=0x0000 */
static uint16_t crc_table[256];
/* 生成查表法所需的CRC表 */
static void crc16_ccitt_init_table(void) {
for (uint16_t i = 0; i < 256; i++) {
uint16_t crc = i << 8;
for (uint8_t bit = 0; bit < 8; bit++) {
crc = (crc & 0x8000) ? (crc << 1) ^ 0x1021 : (crc << 1);
}
crc_table[i] = crc;
}
}
/* 计算CRC-16/CCITT校验值 */
uint16_t crc16_ccitt(const uint8_t *data, size_t len) {
uint16_t crc = 0xFFFF;
for (size_t i = 0; i < len; i++) {
uint8_t idx = (crc >> 8) ^ data[i];
crc = (crc << 8) ^ crc_table[idx];
}
return crc;
}
int main(void) {
crc16_ccitt_init_table();
/* 测试用例1:空数据 */
uint16_t crc1 = crc16_ccitt((const uint8_t*)"", 0);
printf("空数据 CRC = 0x%04X\n", crc1);
/* 测试用例2:ASCII字符串 "123456789"(标准验证数据) */
const char *test = "123456789";
uint16_t crc2 = crc16_ccitt((const uint8_t*)test, strlen(test));
printf("\"123456789\" CRC = 0x%04X\n", crc2);
/* 测试用例3:二进制数据 {0x01, 0x02, 0x03, 0x04} */
uint8_t bin_data[] = {0x01, 0x02, 0x03, 0x04};
uint16_t crc3 = crc16_ccitt(bin_data, sizeof(bin_data));
printf("二进制数据 CRC = 0x%04X\n", crc3);
return 0;
}
编译与运行
gcc -o crc16_ccitt crc16_ccitt.c
./crc16_ccitt
验证方法与判定标准
CRC-16/CCITT标准验证数据为ASCII字符串"123456789",其标准校验值为0x29B1。程序输出中第二行结果必须等于0x29B1,否则实现有误。空数据校验值应为0xFFFF(初值直接输出),二进制数据{0x01,0x02,0x03,0x04}的预期校验值为0x8D6A,可作为附加参考。
判定通过条件:"123456789" CRC = 0x29B1输出与标准值完全一致,且空数据输出为0xFFFF。
常见问题
- 结果与在线计算器不一致:确认使用的CRC参数是否为CCITT(poly=0x1021, init=0xFFFF, refin=false, refout=false, xorout=0x0000)。部分工具默认使用"CRC-16/CCITT-FALSE"或"XModem"等变体,参数不同结果不同。
- 查表法表项生成错误:表生成时需对每个字节值左移8位后逐位处理,若直接使用poly进行8次异或会得到错误表项。