Arduino,stm32的crc16校验计算源码,支持crc16/modbus,自定义等功能。

撸代码的兄弟肯定都遇到过数据校验的问题,尤其是玩嵌入式开发的时候。今天咱们来聊聊CRC16校验在Arduino和STM32上的花式操作。别被那些复杂的数学公式吓到,直接上能跑的真代码。

先扔个查表法的实现镇场子:

const uint16_t crc16_table[] = {
    0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
    //...完整表太长,自己生成或找现成的
};

uint16_t crc16_calculate(uint8_t *data, uint16_t len) {
    uint16_t crc = 0xFFFF;
    while(len--) {
        crc = (crc >> 8) ^ crc16_table[(crc ^ *data++) & 0xFF];
    }
    return crc;
}

这段代码的精髓在查表加速。注意那个异或操作——初始值0xFFFF是Modbus的特色,要是玩别的协议记得改初始值。每次处理一个字节直接查表,比硬算快十倍不止。

想要支持Modbus协议?加点料:

uint16_t crc16_modbus(uint8_t *data, uint16_t len) {
    uint16_t crc = 0xFFFF;
    for(uint16_t i=0; i<len; i++){
        crc ^= data[i];
        for(int j=0; j<8; j++){
            if(crc & 0x0001) {
                crc = (crc >> 1) ^ 0xA001; // 0x8005反转后的多项式
            } else {
                crc >>= 1;
            }
        }
    }
    return crc;
}

这里用了逐位计算的方式,注意多项式被反转了(Modbus标准操作)。那个0xA001其实是0x8005按位反转后的结果,别傻乎乎直接用标准多项式,会翻车。

Arduino,stm32的crc16校验计算源码,支持crc16/modbus,自定义等功能。

玩STM32的兄弟可以试试硬件CRC外设:

uint16_t stm32_crc16(uint8_t *data, uint32_t len) {
    CRC->CR |= CRC_CR_RESET; // 复位寄存器
    for(uint32_t i=0; i<len; i++){
        *((__IO uint8_t *)&CRC->DR) = data[i]; // 字节写入
    }
    return (CRC->DR & 0xFFFF) ^ 0xFFFF; // Modbus需要异或
}

F4系列实测能用,但注意硬件CRC的多项式可能和标准不同。这里最后那个异或0xFFFF就是用来适配Modbus的特殊处理,不用硬件加速的话可以删掉。

自定义参数才是真需求:

typedef struct {
    uint16_t poly;
    uint16_t init;
    bool refin;
    bool refout;
    uint16_t xorout;
} CRC16_Config;

uint16_t custom_crc16(uint8_t *data, uint16_t len, CRC16_Config cfg) {
    uint16_t crc = cfg.init;
    while(len--) {
        uint8_t c = *data++;
        if(cfg.refin) c = __RBIT(c) >> 24; // 位反转
        crc ^= (c << 8);
        for(int i=0; i<8; i++){
            crc = (crc & 0x8000) ? (crc << 1) ^ cfg.poly : (crc << 1);
        }
    }
    if(cfg.refout) crc = (crc << 8) | (crc >> 8); // 字节反转
    return crc ^ cfg.xorout;
}

这个模板够你玩转各种魔改CRC了。RBIT是STM32的指令级位反转,其他平台可以自己实现。注意多项式方向——左移还是右移决定了计算方向。

实测数据验证很重要:

void test() {
    uint8_t test_data[] = {0x01, 0x03, 0x00, 0x00, 0x00, 0x02};
    // Modbus CRC16应该返回0xC40B
    uint16_t crc = crc16_modbus(test_data, sizeof(test_data));
    Serial.print("CRC16: 0x");
    Serial.println(crc, HEX);
}

跑这个测试用例,如果串口打印出C40B,恭喜你算法写对了。要是出问题,先检查字节顺序——Modbus是低位在前高位在后,别搞反了。

最后提醒:查表法在STM32上能起飞,但Arduino空间吃紧的话还是用逐位计算。遇到校验失败别慌,八成是多项式方向或初始值没设对。收藏这几个代码片段,够应付大多数嵌入式项目的校验需求了。

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐