SBUS全称serial-bus,是一种串口通信协议,广泛应用于航模遥控器(接收机)中。只用一根信号线就能传输多达16通道的数据,比多路PWM捕获高效且省资源。
串口配置:
100k波特率,8位数据位,2位停止位,偶校验(EVEN),无控流,25个字节。
协议格式:(8字节)
[startbyte] [data1][data2]…[data22][flags][endbyte]
startbyte=0x0f;
endbyte=0x00;
data1…data22: LSB(低位在前),对应16个通道(ch1-ch16),每个通道11bit(22 × 8=16 × 11);
flag位标志遥控器的通讯状态,我使用的乐迪AT9S在遥控器通上的时候是0x00,断开的时候是0xC0,可以通过查询flag位来采取失控保护。
数据范围
航模遥控器输出的PWM值是1000~2000,中值为1500,sbus输出的会不一样,例如乐迪AT9S的范围为300 ~ 1700,中值1000,这个我估计跟遥控器厂家有关。
sbus的负逻辑
这个地方一定要万分注意,必须加硬件反相器,因为SBUS的信号是采用的负逻辑,也就是电平相反,不要试图在软件里面取反,因为软件里面只能操作数据位(记得串口配置里面的数据位8么),你是操作不了停止位、校验位啥的!!
// 串口中断函数,在中断函数里面接收数据,进行SBUS信号解析。
void HAL_UART_RxCpltCallback(UART_HandleTypeDef *huart)
{int i;while (huart->Instance == USART1) //如果是串口1{USART1_RX_BUF[USART1_RX_STA] = aRxBuffer1[0];if (USART1_RX_STA == 0 && USART1_RX_BUF[USART1_RX_STA] != 0x0F) break; //帧头不对,丢掉USART1_RX_STA++;if (USART1_RX_STA > USART_REC_LEN) USART1_RX_STA = 0; ///接收数据错误,重新开始接收if (USART1_RX_BUF[0] == 0x0F && USART1_RX_BUF[24] == 0x00 && USART1_RX_STA == 25) //接受完一帧数据{update_sbus(USART1_RX_BUF);for (i = 0; i<25; i++) //清空缓存区USART1_RX_BUF[i] = 0;USART1_RX_STA = 0;}break;}
}
信号解析
上面中断函数里面有一个update_sbus函数,原型为u8 update_sbus(u8 *buf),解析subs信号全靠它了!!新建一个sbus.c文件,输入如下代码
#include "sbus.h"SBUS_CH_Struct SBUS_CH;//将sbus信号转化为通道值
u8 update_sbus(u8 *buf)
{int i;if (buf[23] == 0){SBUS_CH.ConnectState = 1;SBUS_CH.CH1 = ((int16_t)buf[ 1] >> 0 | ((int16_t)buf[ 2] << 8 )) & 0x07FF;SBUS_CH.CH2 = ((int16_t)buf[ 2] >> 3 | ((int16_t)buf[ 3] << 5 )) & 0x07FF;SBUS_CH.CH3 = ((int16_t)buf[ 3] >> 6 | ((int16_t)buf[ 4] << 2 ) | (int16_t)buf[ 5] << 10 ) & 0x07FF;SBUS_CH.CH4 = ((int16_t)buf[ 5] >> 1 | ((int16_t)buf[ 6] << 7 )) & 0x07FF;SBUS_CH.CH5 = ((int16_t)buf[ 6] >> 4 | ((int16_t)buf[ 7] << 4 )) & 0x07FF;SBUS_CH.CH6 = ((int16_t)buf[ 7] >> 7 | ((int16_t)buf[ 8] << 1 ) | (int16_t)buf[9] << 9 ) & 0x07FF;SBUS_CH.CH7 = ((int16_t)buf[ 9] >> 2 | ((int16_t)buf[10] << 6 )) & 0x07FF;SBUS_CH.CH8 = ((int16_t)buf[10] >> 5 | ((int16_t)buf[11] << 3 )) & 0x07FF;SBUS_CH.CH9 = ((int16_t)buf[12] << 0 | ((int16_t)buf[13] << 8 )) & 0x07FF;SBUS_CH.CH10 = ((int16_t)buf[13] >> 3 | ((int16_t)buf[14] << 5 )) & 0x07FF;SBUS_CH.CH11 = ((int16_t)buf[14] >> 6 | ((int16_t)buf[15] << 2 ) | (int16_t)buf[16] << 10 ) & 0x07FF;SBUS_CH.CH12 = ((int16_t)buf[16] >> 1 | ((int16_t)buf[17] << 7 )) & 0x07FF;SBUS_CH.CH13 = ((int16_t)buf[17] >> 4 | ((int16_t)buf[18] << 4 )) & 0x07FF;SBUS_CH.CH14 = ((int16_t)buf[18] >> 7 | ((int16_t)buf[19] << 1 ) | (int16_t)buf[20] << 9 ) & 0x07FF;SBUS_CH.CH15 = ((int16_t)buf[20] >> 2 | ((int16_t)buf[21] << 6 )) & 0x07FF;SBUS_CH.CH16 = ((int16_t)buf[21] >> 5 | ((int16_t)buf[22] << 3 )) & 0x07FF;return 1;}else {SBUS_CH.ConnectState = 0;return 0;}
}u16 sbus_to_pwm(u16 sbus_value)
{float pwm;pwm = (float)SBUS_TARGET_MIN + (float)(sbus_value - SBUS_RANGE_MIN) * SBUS_SCALE_FACTOR;// 1000 300 1000/1400if (pwm > 2000) pwm = 2000;if (pwm < 1000) pwm = 1000;return (u16)pwm;
}
上面定义了一个SBUS_CH_Struct 结构体类型的变量SBUS_CH,该结构体在sbus.h中定义
typedef struct
{uint16_t CH1;//通道1数值uint16_t CH2;//通道2数值uint16_t CH3;//通道3数值uint16_t CH4;//通道4数值uint16_t CH5;//通道5数值uint16_t CH6;//通道6数值uint16_t CH7;//通道7数值uint16_t CH8;//通道8数值uint16_t CH9;//通道9数值uint16_t CH10;//通道10数值uint16_t CH11;//通道11数值uint16_t CH12;//通道12数值uint16_t CH13;//通道13数值uint16_t CH14;//通道14数值uint16_t CH15;//通道15数值uint16_t CH16;//通道16数值uint8_t ConnectState;//遥控器与接收器连接状态 0=未连接,1=正常连接
}SBUS_CH_Struct;
u16 sbus_to_pwm(u16 sbus_value)很好理解了,就是把sbus的值转化为标准的1000-2000的pwm值,因为我用的遥控器sbus值是300-1700,大家用的时候具体数值到时候可以通过串口直接读出来看看。
这样就读出了16个通道的数据啦。同时通过读取ConnectState位判断遥控器的状态,在主函数中采取失控保护。