第4章 蓝牙CRC参数规范:多项式0x1021、初始值0xFFFF、异或值0x0000、输入输出反转规则
蓝牙基带层采用CRC-CCITT的定制变体,其参数并非标准CRC-CCITT的默认值,而是针对蓝牙无线传输特性做了专门适配。本章逐项拆解多项式0x1021、初始值0xFFFF、异或值0x0000以及输入/输出反转规则,说明每个参数在蓝牙协议栈中的物理含义与配置方法。
4.1 四参数总览
蓝牙CRC参数由四个核心字段唯一确定。下表列出蓝牙协议栈采用的参数及其与标准CRC-CCITT的对比:
| 参数 | 蓝牙值 | 标准CRC-CCITT值 | 差异说明 |
|---|---|---|---|
| 多项式 (Poly) | 0x1021 | 0x1021 | 相同,均为生成多项式 x¹⁶ + x¹² + x⁵ + 1 |
| 初始值 (Init) | 0xFFFF | 0xFFFF | 相同,全1初始值 |
| 异或值 (XorOut) | 0x0000 | 0x0000 | 相同,不对结果做额外掩码 |
| 输入反转 (RefIn) | TRUE | TRUE | 相同,字节内LSB先入 |
| 输出反转 (RefOut) | TRUE | TRUE | 相同,结果比特顺序反转 |
4.2 多项式0x1021的位级含义
多项式0x1021对应二进制 1 0001 0000 0010 0001,去掉最高位后为 0001 0000 0010 0001。该多项式是16位CRC中经过理论验证的汉明距离最大化选择之一,在蓝牙2.4GHz ISM频段下对≤3比特的突发错误具有100%检测能力。
// 多项式除法中的移位掩码定义(C语言风格)
#define CRC_POLY 0x1021 // x^16 + x^12 + x^5 + 1
// 硬件实现中,多项式通过移位寄存器抽头位置体现
// 抽头位置: bit12, bit5, bit0 (bit16隐式)
0x1021 << 0 或直接作为16位掩码。部分低级驱动会将多项式按反射形式存储为0x8408,用于LSB优先的移位算法,两者等价但字节序不同。
4.3 初始值0xFFFF与异或值0x0000的作用
初始值0xFFFF将CRC移位寄存器预置为全1状态,确保数据前面的连续0不会被漏检。如果初始值为0x0000,则数据流前导的0字节不会改变寄存器状态,导致错误隐藏。
异或值0x0000意味着CRC结果在输出前不做任何掩码或求反操作。这一选择简化了蓝牙基带控制器的硬件设计——最终CRC结果直接附加在数据包尾部,接收端比较时无需额外修正。
// 初始化寄存器(伪代码)
uint16_t crc = 0xFFFF; // 初始值
// 逐字节处理完成后,不进行额外异或
// 最终结果直接使用:
uint16_t final_crc = crc; // XorOut = 0x0000
4.4 输入/输出反转规则详解
输入反转 (RefIn = TRUE) 表示每个数据字节在进入CRC计算器之前,其比特顺序被反转——最低有效位(LSB)被当作最高有效位(MSB)处理。这符合蓝牙空中接口的小端字节序传输惯例:数据字节在无线帧中总是LSB优先发送。
输出反转 (RefOut = TRUE) 表示CRC计算完成后,寄存器结果的比特顺序同样被反转,然后才附加到数据包尾部。反转后的CRC值与接收端预期的小端序校验值对齐。
// 输入反转示例:字节 0xB1 (1011 0001)
// 反转前:b7 b6 b5 b4 b3 b2 b1 b0 = 1 0 1 1 0 0 0 1
// 反转后:b0 b1 b2 b3 b4 b5 b6 b7 = 1 0 0 0 1 1 0 1 = 0x8D
// 标准反转宏(C语言)
#define REFLECT_BYTE(b) (uint8_t)(((b) * 0x0202020202ULL & 0x010884422010ULL) % 1023)
ref_out 参数设置为 true。
4.5 参数配置实战:crcmod与BlueZ API
在Python原型验证或测试工具中,使用 crcmod 库配置蓝牙CRC参数:
import crcmod
# 蓝牙CRC-CCITT参数:poly=0x1021, init=0xFFFF, xor=0x0000, rev_in=True, rev_out=True
bluetooth_crc = crcmod.mkCrcFun(
0x11021, # 多项式(含隐式最高位)
initCrc=0xFFFF,
xorOut=0x0000,
rev=True # 同时启用输入反转和输出反转
)
# 验证:对空数据计算CRC
test_crc = bluetooth_crc(b'')
print(f"Empty CRC: 0x{test_crc:04X}") # 应输出 0x1D0F
在BlueZ蓝牙协议栈的HCI层,CRC参数由固件直接管理,主机仅通过HCI命令使能或禁用CRC。常见配置命令如下:
# 启用蓝牙基带CRC(通常默认开启)
hciconfig hci0 encap 3 0x05 0x01
# 读取当前CRC状态(通过HCI Read Packet Boundary Flag)
hcitool cmd 0x03 0x05
# 注意:蓝牙规范中CRC参数为固定值,主机不可修改多项式或初始值
4.6 蓝牙CRC计算完整流程
下图展示了蓝牙CRC从数据输入到最终校验值的完整计算链路,清晰标出每个参数的作用位置:
4.7 常见参数配置错误与验证
- 多项式字节序选用错误:软件实现时误将0x1021按反射形式写为0x8408,但算法未配套使用LSB优先移位,导致结果完全不匹配。统一使用标准CRC-CCITT库可避免此问题。
- 输出反转遗漏:接收端计算CRC后直接与原值比较,未做RefOut,导致校验失败。确认库参数
rev_out=True。 - 初始值误用为0x0000:部分裁剪版实现为“简化”将Init设为0,导致前导零字节被漏检。严格按照蓝牙规范使用0xFFFF。
- 异或值误配为0xFFFF:如果使用了其他协议的CRC配置(如Modbus的XorOut=0xFFFF),结果会按位取反,与蓝牙不兼容。
b'' 计算CRC,预期输出应为 0x1D0F(RefIn=true, RefOut=true, Init=0xFFFF, XorOut=0x0000)。如果结果不同,则参数配置存在偏差。