3、SBUS数据帧结构详解:帧长度25字节、起始字节0x0F、结束字节0x00、通道数据位分布
好,咱们直接进入正题。SBUS的数据帧结构,说白了就是遥控器和飞控之间约定好的一种“暗号”。你发一串数据过来,我得知道哪个字节代表哪个通道,哪个位是开关,哪个位是校验。搞懂了这25个字节,你就掌握了SBUS通信的核心。
我个人习惯把SBUS帧比作一个“快递包裹”。它有固定的包装箱尺寸,有封条,有填充物,还有最重要的——货物本身。咱们一层层拆开看。
3.1 帧长度:为什么是25字节?
SBUS帧长度固定为25字节。这不是拍脑袋定的,而是由它的数据量决定的。
你想想看,一个标准的RC遥控器有16个通道。每个通道需要11位分辨率(也就是0-2047的范围)。16个通道 × 11位 = 176位。176位 ÷ 8 = 22字节。嗯,刚好22字节用来存通道数据。
那剩下的3个字节呢?
- 1个起始字节:0x0F,告诉接收机“我要开始发数据了”
- 1个结束字节:0x00,告诉接收机“数据发完了”
- 1个标志字节:包含数字通道(第17-18通道)和信号丢失标志
22 + 1 + 1 + 1 = 25。完美。
重要提示:SBUS帧长度是固定的。如果你收到的数据不是25字节,那肯定有问题。我在项目中遇到过有人把SBUS和别的协议搞混,结果解析出来的通道值全是乱的。
3.2 起始字节0x0F与结束字节0x00
这两个字节就像包裹的封条。没有它们,你没法确定一帧数据从哪里开始、到哪里结束。
起始字节0x0F:二进制是00001111。为什么是0x0F?我记得最早接触SBUS时也好奇过。其实没什么特别原因,就是Futaba公司定的标准。你只要记住,每次解析SBUS数据,第一个字节必须是0x0F,否则就是帧同步丢了。
结束字节0x00:二进制是00000000。这个更简单,就是告诉解析器“到此为止”。
避坑指南:我曾经在调试一个四旋翼时,发现遥控器偶尔会丢控。查了半天,原来是SBUS接收机在信号弱的时候,结束字节会变成0x01(表示信号丢失)。我的解析代码只判断了0x00,没处理0x01的情况。结果飞控一直认为数据有效,但实际上已经失控了。所以,一定要检查结束字节的值!
3.3 通道数据位分布:11位是怎么塞进8位字节的?
这是SBUS最精妙的地方,也是最容易搞晕的地方。16个通道,每个11位,总共176位,要塞进22个字节里。
怎么塞?位对齐。说白了就是跨字节打包。
咱们看一个具体的例子。假设通道0的值是0x7FF(也就是2047,最大值),通道1的值是0x000(最小值)。它们在数据帧中的分布是这样的:
字节0: [ch0 7:0] // 通道0的低8位
字节1: [ch1 2:0][ch0 10:8] // 通道0的高3位 + 通道1的低3位
字节2: [ch1 10:3] // 通道1的高8位
...
看到了吗?通道0的11位数据,跨越了字节0和字节1。通道1的数据,跨越了字节1和字节2。这就是所谓的“位对齐”。
为了让你更直观地理解,我画了一张图:
看到没?每个通道的11位数据,都跨越了两个字节。这就是为什么解析SBUS时,你不能简单地按字节读取,必须做位运算。
3.4 标志字节:数字通道与信号状态
第24个字节(也就是倒数第二个字节)是标志字节。它包含了两个数字通道(第17和18通道)以及信号丢失标志。
| 位 | 含义 | 说明 |
|---|---|---|
| bit 7 | 第17通道 | 0=关闭, 1=开启 |
| bit 6 | 第18通道 | 0=关闭, 1=开启 |
| bit 5 | 保留 | 通常为0 |
| bit 4 | 保留 | 通常为0 |
| bit 3 | 保留 | 通常为0 |
| bit 2 | 信号丢失 | 0=正常, 1=丢失 |
| bit 1 | 故障保护 | 0=正常, 1=故障 |
| bit 0 | 保留 | 通常为0 |
实用技巧:在ArduPilot的SBUS解析代码中,我建议你单独提取bit 2和bit 1。这两个位直接决定了飞控是否应该进入失控保护模式。我曾经见过有人把整个标志字节都忽略了,结果信号丢失了飞控还在傻傻地飞。
3.5 实际解析代码示例
说了这么多理论,咱们来点实际的。下面是我在ArduPilot中常用的SBUS解析代码片段:
// SBUS数据帧解析示例
// 假设 rx_buffer 已经接收到了25字节的SBUS帧
void parse_sbus_frame(uint8_t *rx_buffer, uint16_t *channels) {
// 检查起始字节
if (rx_buffer[0] != 0x0F) {
// 帧同步失败,丢弃
return;
}
// 解析16个通道 (每个通道11位)
channels[0] = ((rx_buffer[1] & 0x07) << 8) | rx_buffer[0];
channels[1] = ((rx_buffer[2] & 0x3F) << 5) | ((rx_buffer[1] >> 3) & 0x1F);
channels[2] = ((rx_buffer[3] & 0x7F) << 4) | ((rx_buffer[2] >> 6) & 0x0F);
channels[3] = ((rx_buffer[4] & 0x0F) << 7) | ((rx_buffer[3] >> 7) & 0x7F);
// ... 以此类推,直到 channels[15]
// 检查结束字节
if (rx_buffer[24] == 0x01) {
// 信号丢失标志置位
// 触发失控保护
failsafe_trigger();
}
// 提取数字通道 (第17、18通道)
uint8_t flags = rx_buffer[23];
channels[16] = (flags & 0x80) ? 1 : 0; // 第17通道
channels[17] = (flags & 0x40) ? 1 : 0; // 第18通道
}
注意:上面的代码只展示了前4个通道的解析。实际项目中,你需要把16个通道全部解析出来。我建议你写一个循环,或者用查表法,别像我第一次那样手写16遍——太容易出错了。
3.6 常见问题与避坑
- 位序问题:SBUS使用的是小端序(LSB first)。也就是说,每个字节的最低位最先发送。解析时要注意位运算的方向。
- 通道值范围:11位分辨率,范围是0-2047。但实际遥控器通常输出172-1811(对应-100%到+100%)。别直接用0-2047去控制电机,会出事的。
- 帧间隔:SBUS帧的发送间隔是7ms(约144Hz)。如果你收到的帧间隔不稳定,可能是信号干扰或者接收机有问题。
嗯,关于SBUS数据帧结构,核心内容就这些了。搞懂了这25个字节的分布,你就能自己写解析代码了。记住,实践出真知——拿个逻辑分析仪抓一下实际波形,比看十遍文档都管用。
公众号:蓝海资料掘金营,微信deep3321