You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

QByteArray处理异常致类HDLC协议CRC校验失败问题排查

类HDLC协议解码CRC校验失败问题分析与解决

问题背景

基于开源项目Arduhdlc实现类HDLC协议解码,使用Qt的QByteArray处理串口接收数据,流程为:移除帧边界字节→处理转义序列→CRC校验。编写test1至test5共5个测试用例后,发现test2、test3、test4的CRC序列与预期不符(如test2中本该传入\xf6\x41,实际传入\xff\xf6),导致CRC校验失败。

相关代码

#include <QCoreApplication>
#include <QDebug>
#include "hdlc_qt.h"

#define FRAME_BOUNDARY_OCTET 0x7E
#define CONTROL_ESCAPE_OCTET 0x7D
#define INVERT_OCTET 0x20
QByteArray receive_frame_buffer;
quint16 frame_position;
quint16 frame_checksum;
bool escape_character;


bool hdlcFrameCRC_check(int frame_index);

void charReceiver(QByteArray dataArray)
{

    quint8 data = 0;
    qDebug() << "Received:\t"<<dataArray.toHex();
    for(QByteArray::iterator it = dataArray.begin(); it != dataArray.end(); it++) {
        data = (*it);
        /* Start flag or end flag */
        if(data == FRAME_BOUNDARY_OCTET) {

            qDebug("FRAME_BOUNDARY_OCTET: %x",data);
            qDebug() << "frame_position:"<<frame_position;
            if(escape_character == true) {
                escape_character = false;
            }
            /* Do CRC check if frame is valid */

            else if(  (frame_position >= 2) && (hdlcFrameCRC_check(frame_position)) )  {
                /* Call user defined function to handle HDLC frame */
                qDebug() << "ValidFrame"<<receive_frame_buffer.toHex();

            }
            qDebug() << "frame_buffer:\t"<<receive_frame_buffer.toHex();
            /* Reset all for next frame */
            frame_position = 0;
            frame_checksum = 0;
            receive_frame_buffer.clear();
            continue;

        }

        if(escape_character) {
            escape_character = false;
            data ^= INVERT_OCTET;
        } else if(data == CONTROL_ESCAPE_OCTET) {
            escape_character = true;
            continue;
        }
        receive_frame_buffer.append(data);

        frame_position++;

        /* If we don't ever receive valid frame,
         * buffer will keep growing bigger and bigger.
         * Hard coded max size limit and then reset
         */
        if(frame_position >= 2048)
        {
            receive_frame_buffer.clear();
            frame_position = 0;
            frame_checksum = 0;
        }
    }
}

bool hdlcFrameCRC_check(int frame_index)
{
    /* frame = ...[CRC-LO] [CRC-HI] */
    quint16 crc_received = 0;
    crc_received = receive_frame_buffer[frame_index-1]; // msb
    crc_received = crc_received << 8;
    crc_received |= receive_frame_buffer[frame_index-2]; // lsb
    quint16 crc_calculated = qChecksum((const char*)receive_frame_buffer.constData(), frame_index-2);
    //crc_calculated = crc_calculated^0xFFFF;

    qDebug("CRC received:%x",crc_received);
    qDebug("CRC calculated:%x",crc_calculated);

    if(crc_received == crc_calculated) {
        return true;
    } else {
        return false;
    }
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);


    QByteArray test1 = QByteArray("\x7E\x08\x1A\x00\x00\x07\x2B\x08\x00\x08\x16\x00\x00\x08\x8E\x07\xF7\x22\x20\x7E",20);
    QByteArray test2 = QByteArray("\x7e\x08\x22\x00\x00\x07\x28\x08\x08\x08\x10\x00\x05\x08\x95\x07\xff\xf6\x41\x7e",20);
    QByteArray test3 = QByteArray("\x7e\x08\x22\x00\x00\x07\x21\x08\x09\x08\x10\x00\x07\x08\x8f\x08\x00\xe7\x89\x7e",20);
    QByteArray test4 = QByteArray("\x7e\x08\x0e\x00\x00\x07\x2f\x08\x07\x08\x1b\x00\x00\x08\x93\x07\xf8\x9a\xb1\x7e",20);
    QByteArray test5 = QByteArray("\x7e\x08\x27\x00\x00\x07\x27\x08\x0a\x08\x10\x00\x05\x08\x93\x07\xfd\x18\x53\x7e",20);

    QVector<QByteArray> test;
    test.push_back(test1);
    test.push_back(test2);
    test.push_back(test3);
    test.push_back(test4);
    test.push_back(test5);

    for(int i = 0; i < test.size();++i) {
        qDebug()<<"-------------------------------------------------------";
        qDebug()<<"Test #" << i+1<< ":\t "<< test[i].toHex();
        charReceiver(test[i]);

    }
    return a.exec();
}

问题原因

  1. CRC算法不匹配:Qt的qChecksum采用IBM CRC-16(初始值0x0000,多项式0x8005),而Arduhdlc使用的是CRC-16/IBM反转算法(初始值0xFFFF,多项式0xA001),两者计算逻辑差异导致校验结果不匹配。你注释掉了crc_calculated = crc_calculated^0xFFFF;,这一步是将qChecksum结果转换为Arduhdlc兼容格式的关键。
  2. CRC字节顺序可能错误:代码注释标注帧格式为...[CRC-LO] [CRC-HI],但当前逻辑将最后一个字节作为MSB、倒数第二个作为LSB,若Arduhdlc实际发送的是CRC-HI在前、CRC-LO在后,会导致顺序颠倒,校验失败。

解决办法

1. 修正CRC计算逻辑

方案一:恢复异或操作

取消crc_calculated = crc_calculated^0xFFFF;的注释,或替换为更直观的取反操作:

quint16 crc_calculated = qChecksum((const char*)receive_frame_buffer.constData(), frame_index-2);
crc_calculated = ~crc_calculated; // 等价于^0xFFFF,匹配Arduhdlc的CRC输出格式

方案二:实现Arduhdlc原生CRC函数

直接使用与Arduhdlc一致的CRC计算逻辑,避免依赖Qt的qChecksum:

quint16 crc16(const uint8_t *data, uint16_t length) {
    quint16 crc = 0xFFFF;
    for (uint16_t i = 0; i < length; i++) {
        crc ^= data[i];
        for (uint8_t j = 0; j < 8; j++) {
            if (crc & 0x0001) {
                crc = (crc >> 1) ^ 0xA001;
            } else {
                crc >>= 1;
            }
        }
    }
    return crc;
}

然后在hdlcFrameCRC_check中替换为:

quint16 crc_calculated = crc16((const uint8_t*)receive_frame_buffer.constData(), frame_index-2);

2. 修正CRC字节顺序

根据Arduhdlc的实际帧格式调整字节顺序:

  • 若Arduhdlc发送的是CRC低字节在前、高字节在后,当前逻辑无需修改;
  • 若发送的是高字节在前、低字节在后,调整CRC接收逻辑:
    crc_received = receive_frame_buffer[frame_index-2]; // MSB
    crc_received = crc_received << 8;
    crc_received |= receive_frame_buffer[frame_index-1]; // LSB
    

3. 验证测试用例的转义正确性

确保测试用例中的HDLC帧符合转义规则:帧边界0x7E和转义符0x7D必须被转义为0x7D+原字节^0x20(如0x7E转义为0x7D 0x5E),避免未转义的特殊字节被误判为帧边界或转义符,导致数据错误。

内容的提问来源于stack exchange,提问作者Marco81

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 18:37:33