字节序和 bit 位到底怎么读:从 0x0B 到蓝牙 HCI 日志解析

📅 2026/7/8 6:08:17
字节序和 bit 位到底怎么读:从 0x0B 到蓝牙 HCI 日志解析
在解析蓝牙 HCI、BLE 广播数据、二进制协议、网络报文、MCU 寄存器时经常会遇到几个容易混淆的问题多字节数据到底按小端还是大端解析 一个字节里的 bit0 是最左边还是最右边 0x0B 写成二进制 0000 1011那么 bit0 是 0 还是 1 为什么有些工具显示 bit 顺序看起来和二进制写法不一样这些问题如果没搞清楚后面解析 HCI Opcode、事件参数、Supported Commands、Feature Mask 等字段时很容易把值看反。这篇文章专门把“字节”和“bit 位”的读取方式讲清楚。一、先区分两个概念字节序和 bit 编号解析二进制数据时最容易混在一起的是这两个概念字节序多个字节组成一个数值时字节之间怎么排列 bit 编号一个字节内部bit0、bit1、bit7 分别是哪一位它们不是一回事。例如01 10这是两个字节。如果按小端序解析它是0x1001如果按大端序解析它是0x0110这是“字节序”的问题。再比如0x0B 0000 1011这里讨论的是一个字节内部的 bit0、bit1、bit2、bit3 分别是什么这是“bit 编号”的问题。所以要先建立一个基本原则多字节字段先看字节序 单字节内部再看 bit 位编号二、什么是字节序字节序也叫 Endianness解决的是当一个数值由多个字节组成时低位字节和高位字节谁放前面常见有两种Little Endian小端序低位字节在前 Big Endian大端序高位字节在前三、小端序 Little Endian小端序的特点是低位字节放在前面 高位字节放在后面例如原始数据01 10如果按小端序解析低位字节 0x01 高位字节 0x10所以实际数值是0x1001也就是01 10 --小端解析-- 0x1001再举一个例子FA 4F按小端序解析低位字节 0xFA 高位字节 0x4F所以实际值是0x4FFA四、大端序 Big Endian大端序的特点是高位字节放在前面 低位字节放在后面例如原始数据01 10如果按大端序解析高位字节 0x01 低位字节 0x10所以实际数值是0x0110也就是01 10 --大端解析-- 0x0110五、解析多字节字段时第一步一定是查规范一个非常重要的原则是不能凭感觉判断字节序必须看协议规范。比如在蓝牙 HCI 中很多多字节字段都是小端序。例如 HCI Command Opcode 字段占 2 字节01 10按照 HCI 的小端序解析Opcode 0x1001然后再把 Opcode 拆成 OGF 和 OCFOGF Opcode 10 OCF Opcode 0x03FF计算结果Opcode 0x1001 OGF 0x04 OCF 0x0001这条命令就是Read Local Version Information如果误按大端序解析成0x0110后面的 OGF、OCF 就全部错了。所以解析二进制数据时的第一步是先看字段长度 如果字段超过 1 字节就先确认字节序 再把原始字节转换成实际数值。六、一个字节里面bit0 是最左边还是最右边现在来看单字节内部的 bit 编号。一个字节有 8 个 bitbit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0通常写二进制时是从高位到低位写0000 1011最左边是最高位 bit7最右边是最低位 bit0。所以bit0 是最右边那一位 bit7 是最左边那一位这是二进制数值表达中的常规理解。七、0x0B 的 bit0 到底是 0 还是 1现在看这个例子0x0B先转换成二进制0x0B 0000 1011按照常规写法左边是高位右边是低位bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 0 0 0 0 1 0 1 1所以bit0 1 bit1 1 bit2 0 bit3 1 bit4 0 bit5 0 bit6 0 bit7 0因此0x0B的 bit0 不是 0而是 1。可以这样记bit0 永远是这个字节的最低有效位也就是最右边那一位。八、为什么 0x0B 0000 1011十六进制和二进制之间可以按 4 bit 一组转换。0x0B拆成两个十六进制字符0 0000 B 1011所以0x0B 0000 1011这里的1011表示8 0 2 1 11也就是十进制的 11。对应 bit 展开0000 1011 ││││ ││││ ││││ │││└── bit0 1 ││││ ││└─── bit1 1 ││││ │└──── bit2 0 ││││ └───── bit3 1 │││└─────── bit4 0 ││└──────── bit5 0 │└───────── bit6 0 └────────── bit7 0九、不要把“二进制书写顺序”和“bit 编号顺序”混淆正常写二进制时一般写成0000 1011这个写法是bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0也就是从高位到低位。但是有些协议文档、调试工具、抓包工具为了方便看 bit mask可能会按 bit 编号显示bit0 bit1 bit2 bit3 bit4 bit5 bit6 bit7这样显示0x0B时可能会看到1 1 0 1 0 0 0 0这不是说0x0B的二进制变成了1101 0000而是工具按 bit0 到 bit7 的顺序把每一位列出来了。所以0x0B 的二进制数值写法0000 1011 按 bit0 到 bit7 展开1 1 0 1 0 0 0 0这两个是同一个值只是展示顺序不同。十、以 0x0B 为例完整说明原始值0x0B二进制数值写法0000 1011按 bit7 到 bit0 看bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 0 0 0 0 1 0 1 1按 bit0 到 bit7 展示bit0 bit1 bit2 bit3 bit4 bit5 bit6 bit7 1 1 0 1 0 0 0 0所以最终结论bit0 1 bit1 1 bit2 0 bit3 1 bit4 0 bit5 0 bit6 0 bit7 0十一、如何判断某个 bit 是否为 1判断某个 bit 是否为 1常用方法是位与运算。例如判断0x0B的 bit00x0B 0x01因为0x0B 0000 1011 0x01 0000 0001 ----------------- 0000 0001结果不为 0所以bit0 1判断 bit10x0B 0x02因为0x0B 0000 1011 0x02 0000 0010 ----------------- 0000 0010结果不为 0所以bit1 1判断 bit20x0B 0x04因为0x0B 0000 1011 0x04 0000 0100 ----------------- 0000 0000结果为 0所以bit2 0判断 bit30x0B 0x08因为0x0B 0000 1011 0x08 0000 1000 ----------------- 0000 1000结果不为 0所以bit3 1十二、bit 掩码的通用判断公式如果要判断某个字节value的第n位是否为 1可以使用value (1 n)如果结果不为 0表示 bit n 为 1。如果结果为 0表示 bit n 为 0。例如value 0x0B判断 bit30x0B (1 3)因为1 3 0x08所以0x0B 0x08 0x08结果不为 0说明 bit3 为 1。十三、蓝牙 HCI Supported Commands 里的 bit 怎么看在蓝牙 HCI 中Read Local Supported Commands命令会返回Supported_Commands[64]这是一个 64 字节的 bit mask。总共有64 × 8 512 bits每一个 bit 表示一个 HCI Command 是否被 Controller 支持。规则是bit 1支持对应的 HCI Command bit 0不支持对应的 HCI Command例如返回值中某个字节是Supported_Commands[3] 0x030x03的二进制写法是0000 0011按 bit0 到 bit7 展开bit0 bit1 bit2 bit3 bit4 bit5 bit6 bit7 1 1 0 0 0 0 0 0所以它表示Supported_Commands[3] 的 bit0 1 Supported_Commands[3] 的 bit1 1 Supported_Commands[3] 的 bit2 0 Supported_Commands[3] 的 bit3 0 Supported_Commands[3] 的 bit4 0 Supported_Commands[3] 的 bit5 0 Supported_Commands[3] 的 bit6 0 Supported_Commands[3] 的 bit7 0然后再去规范中的Supported Commands表查Byte 3, bit 0 对应哪个 HCI Command Byte 3, bit 1 对应哪个 HCI Command Byte 3, bit 2 对应哪个 HCI Command ...如果某一位是 1说明当前 Controller 支持该命令。十四、蓝牙 HCI 解析时的推荐步骤解析 HCI 原始数据时可以按照下面这个顺序第一步确认当前数据是否包含 HCI Packet Type 第二步确认当前包类型是 HCI Command、HCI Event 还是 ACL Data 第三步按照对应 Packet 格式切字段 第四步遇到多字节字段先根据规范确认字节序 第五步按正确字节序转换成数值 第六步如果字段是 bit mask再按 bit0 到 bit7 判断每一位 第七步根据规范表格查每个 bit 或每个字段对应的含义例如 HCI Command02 10 00如果这条原始数据不包含 HCI Packet Type那么直接按 HCI Command Packet 解析02 10 Opcode2 字节小端序 0x1002 00 Parameter_Total_Length 0Opcode 再拆OGF 0x1002 10 0x04 OCF 0x1002 0x03FF 0x0002所以Opcode 0x1002 Read Local Supported Commands后续它的 Command Complete Event 会返回 64 字节的 Supported_Commands bit mask。十五、字节序不会改变一个字节内部的 bit0 位置这里有一个非常容易误解的地方小端序 / 大端序影响的是多个字节之间的排列顺序。 bit0 / bit7 是一个字节内部的位编号。例如01 10这是两个字节组成的字段才需要讨论小端还是大端。但是0x0B这只是一个字节不存在字节序问题。对于单个字节0x0B 0000 1011bit0 永远是最低位也就是最右边的 1。所以字节序不影响单个字节内部 bit0 的位置。十六、最容易犯错的地方第一个错误把小端序和 bit 顺序混为一谈。错误理解小端序是不是表示 bit0 在左边这是错误的。正确理解小端序只影响多个字节之间的顺序 bit0 是一个字节内的最低有效位。第二个错误看到工具显示1 1 0 1 0 0 0 0就以为二进制是1101 0000。正确理解工具可能是按 bit0 到 bit7 展示。 0x0B 的二进制仍然是 0000 1011。第三个错误解析多字节字段时直接按肉眼顺序拼成数值。例如01 10在 HCI 中通常应该按小端解析成0x1001而不是直接看成0x0110第四个错误把 bit mask 当成普通数字理解。在 bit mask 里重点不是这个字节整体代表十进制多少而是每一位是否为 1。例如0x0B 11在普通数值场景下它就是十进制 11。但在 bit mask 场景下更重要的是bit0 1 bit1 1 bit2 0 bit3 1表示 bit0、bit1、bit3 对应的能力或选项被设置。十七、总结解析二进制数据时要先分清楚“字节序”和“bit 编号”。字节序解决的是多个字节组成一个字段时字节之间如何排列。小端序是低位字节在前大端序是高位字节在前。蓝牙 HCI 中很多多字节字段使用小端序因此01 10通常要解析为0x1001而不是0x0110。bit 编号解决的是一个字节内部每一位怎么编号。通常情况下bit0 是最低有效位也就是二进制写法中最右边那一位bit7 是最高有效位也就是最左边那一位。以0x0B为例0x0B 0000 1011按 bit7 到 bit0 看bit7 bit6 bit5 bit4 bit3 bit2 bit1 bit0 0 0 0 0 1 0 1 1所以bit0 1 bit1 1 bit2 0 bit3 1如果工具显示为1 1 0 1 0 0 0 0它很可能是按 bit0 到 bit7 的顺序展示而不是普通二进制书写顺序。最终可以记住三句话多字节字段先看字节序。 单字节内部 bit0 是最低位也就是最右边那一位。 bit mask 看的不是整体数值大小而是每一个 bit 是否为 1。掌握这三个原则后再去解析 HCI Opcode、HCI Event、Supported Commands、Feature Mask、Advertising Data 等二进制数据就会清晰很多。