gangguo 发表于 2021-6-30 11:02:43

Arduino解析FUTABA-S.BUS总线

本帖最后由 gangguo 于 2021-6-30 11:05 编辑

S.BUS是FUTABA(模型遥控器知名品牌)特有的一个接收机串行总线输出,通过这根总线,可以获得遥控器上所有通道的数据。目前很多模型及无人机电子设备都支持S.BUS总线的接入,如:V-BAR陀螺仪、Pixhawk飞控等。使用S.BUS总线获取通道数据,效率是比较高的,而且非常节省硬件资源-只需要“一根线”即可获取所有通道的数据。在本次试验中,使用的遥控器为FUTABA T10CHG,接收机使用的穿越机专用神器:FM800PRO微型总线输出接收机,Arduino板使用的MEGA2560板。

在解析数据前,先说说S.BUS总线的一些特性。经过资料查阅得到:S.BUS总线使用的是TTL电平的反向电平,即标准TTL中的1取反为0,而0则取反为1,串口波特率为100000,数据位为8位,2个停止位,偶校验。因此,这里需要制作一个电平反向电路,电平反向电路在百度上可以找到大神们的无私共享,这里直接给出原理图,图中使用的三极管型号为:SS8050。
下图为制作好的电平反向电路模块,这个模块在上面原理图的基础上做了一点小小的改进:增加了一个5V的VCC贯通输入和输出端,图中的3.3V上拉则从5V的VCC上经过电阻分压获得,这样的方便之处在于方便用Arduino板对模块接收机供电,也可以接收机对模块及Arduino板供电。
获得正确的电平后,接下来看看S.BUS的数据帧结构。S.BUS一帧数据的长度为25个字节,其中第“0”个字节为帧头:0x0f;第24个字节为帧尾:0x00;从第1个字节到第22个字节为1-16号比例通道的数据字节;第23字节中,第7位为数字开关通道17通道,第6位为数字开关通道18通道,第5位为帧状态标志为(判断是否丢帧),用于控制接收机上的LED的状态,第4位为失控保护激活标志位,此位为1时,表示接收机进入失控保护状态,本次试验所用的接收机对于第23字节为做出处理,但使用福睿斯接收机时可以正确判断失控保护。(注:数字字节帧的排位从0开始,第0个实质上是指第一个字节)
接下来看1-16比例通道的数据解算,每个通道用11位(bit)二进制数表示,通道数据的值为:0-2048:1通道(AILE):第2字节的低3位(11位数据的高3位)+第1字节(11位数据的低8位);2通道(ELEV):第3字节的低6位(11位数据的高6位)+第2字节的高5位(11位数据的低5位);3通道(THRO):第5字节的最低1位(11位数据的最高1位)+第4字节(11位数据的中间8位)+第3字节的最高2位(11位数据的最低2位);4通道(RUDD):第6字节的低4位(11位数据的高4位)+第5字节的高7位(11位数据的低7位);这里的文字叙述比较繁琐,下面直接给出通道解算的代码:
简要说明:sbusData[]用于存放从串口读取的原始数据,channels[]用于存放解算完成的通道数据,其中channels代表1通道AILE。
下面来看看如何进行帧同步,即寻找帧头。本次试验用Arduino mega2560的串口1来接收SBUS数据,再由串口0将通道数据发送到串口监视器。接收串口数据代码:
简要说明:sbus_flag用于判断是否读取到了帧头,开始时sbus_flag为0,此时还未同步到帧头,程序会一直停留在寻找帧头的位置,不断地从串口缓冲区中读取字节数据,读取一次判断一次,当寻找到帧头0x0f时,sbus_flag置1,跳出帧头寻找,进入后续的帧读取,当sbusData更新完毕后,sbus_flag重新置0,准备进入下一轮的读取。下图为串口监视器显示1-12比例通道+原始数据第23字节(本次试验用的遥控器只有10个通道)的状态:
从上图可以看到,摇杆在中位时,解算出来的通道数据为1024左右,以一通道AILE为例:中杆时为1024,左右满行程的值分别为1686和366,此时的遥控器AILE行程设置(END POINT)为左右100%。至此,SBUS总线数据成功解析,通过试验验证了FUTABA T10CHG确实有10个通道,而且10个通道均为标准PWM(舵机)输出;其中1-8通道可设置为比例通道,即可跟随摇杆、旋钮或3端开关;而9,10通道只能作为开关通道,即只能用两段开关控制PWM的最大值和最小值,但行程可调。下面是完整测试代码:#include <Timer.h>
Timer t;

int16_t channels;
uint8_t sbusData;
char sbus_flag=0;

void setup()
{
Serial.begin(115200);
Serial1.begin(100000);
t.every(10,channels_updata);
t.every(20,dete_print);

}


void serialEvent1(void)
{
if(Serial1.available()>=25)
{
    while(sbus_flag==0)
    {
      sbusData=Serial1.read();
      if(sbusData==0x0f)
      {sbus_flag=1;}
    }
    if(sbus_flag==1)
    {
       for(int i=1;i<25;i++)
       {sbusData=Serial1.read();}
       sbus_flag=0;
      }
    }
}



void channels_updata(void)
{
   if(sbusData==0x0f&&sbusData==0x00)
{
channels= ((sbusData|sbusData<< 8) & 0x07FF);
channels= ((sbusData>>3|sbusData<<5) & 0x07FF);
channels= ((sbusData>>6|sbusData<<2|sbusData<<10) & 0x07FF);
channels= ((sbusData>>1|sbusData<<7) & 0x07FF);
channels= ((sbusData>>4|sbusData<<4) & 0x07FF);
channels= ((sbusData>>7|sbusData<<1|sbusData<<9) & 0x07FF);
channels= ((sbusData>>2|sbusData<<6) & 0x07FF);
channels= ((sbusData>>5|sbusData<<3) & 0x07FF);
channels= ((sbusData|sbusData<< 8) & 0x07FF);
channels= ((sbusData>>3|sbusData<<5) & 0x07FF);
channels = ((sbusData>>6|sbusData<<2|sbusData<<10) & 0x07FF);
channels = ((sbusData>>1|sbusData<<7) & 0x07FF);
channels = ((sbusData>>4|sbusData<<4) & 0x07FF);
channels = ((sbusData>>7|sbusData<<1|sbusData<<9) & 0x07FF);
channels = ((sbusData>>2|sbusData<<6) & 0x07FF);
channels = ((sbusData>>5|sbusData<<3) & 0x07FF);
}
}





void dete_print()
{
   Serial.print(channels);
   Serial.print("");
   Serial.print(channels);
   Serial.print("");
   Serial.print(channels);
   Serial.print("");
   Serial.print(channels);
   Serial.print("");
   Serial.print(channels);
   Serial.print("");
   Serial.print(channels);
   Serial.print("");
   Serial.print(channels);
   Serial.print("");
   Serial.print(channels);
    Serial.print("");
   Serial.print(channels);
    Serial.print("");
   Serial.print(channels);
    Serial.print("");
   Serial.print(channels);
    Serial.print("");
   Serial.print(channels);
    Serial.print("");
   Serial.println(sbusData,BIN);
   

}


void loop()
{

t.update();

}


页: [1]
查看完整版本: Arduino解析FUTABA-S.BUS总线