Arduino解析DSM2总线-Arduino中文社区 - Powered by Discuz!

Arduino中文社区

 找回密码
 立即注册

QQ登录

只需一步,快速开始

查看: 4375|回复: 1

Arduino解析DSM2总线

[复制链接]
发表于 2021-6-28 15:47 | 显示全部楼层 |阅读模式
本帖最后由 gangguo 于 2021-6-28 15:51 编辑

DSM2是另一大航模遥控器品牌:JR PROPO及SPEKTRUM使用的一种遥控器通道串行数据总线,它的作用与FUTABA S.BUS一样。同样的,很多航模电子设备都支持DSM2数据的输入:如V-BAR陀螺仪、PIXHAWK开源飞控。
DSX9.jpg
dx6i.jpg

pix_dsm.jpg
SPEKTRUM的接收机可以和JR遥控器对频使用,因此,在本次试验中,使用了JR DSX9发射机和SPEKTRUM DX6i发射机,接收机使用SPEKTRUM的AR6200携带的“卫星天线”模块,信号解析使用了Arduino mega2560板。下面进入正题。
通过查阅网络资料,DSM2总线是标准的3.3V的TTL(串口)信号,波特率为115200,但对于DSM2的帧协议,小编未找到准确的资料,因此,只能自己破解了。我的思路是:先将DSM2帧尽可能完整地从mega2560板的串口1读取进来,然后从串口0输出到串口监视器进行数据记录,寻找规律。具体的方法就是,设置一个“足够长”的数组来存放这些原始数据。Mega2560串口1的缓冲区大小是64个字节,那就先设定一个长度为“64”的“char”型数组吧。
试验中先接入的是SPEKTRUM的DSM2数据,数据采集过程中,不停的拨动遥控器的摇杆,串口0以“十进制”的形式向电脑发送数据,通过串口监视器观察到,在第一个有变化的字节前面有两个连在一起的字节是固定的“3”,从“3”第一次出现到下一次出现,中间间隔53个字节,由此可以推断:SPEKTRUM DSM2帧的长度应该为54,并且“帧头”为两个连续的“3”,转换为十六进制为“0x03”。
接下来接入JR DSM2,和上面一样,数据采集过程中不停地拨动遥控器摇杆,通过串口监视器观察到:数据开始有变化的字节前面,始终不变的仍为两个字节,但这里变成了“3”和“18”(十进制),“3”两次出现的间隔变为了59,因此可以判断:JR DSM2帧的长度为60字节,帧头为“3”和“18”,转化为十六进制为“0x03”和“0x12”。

接下来,我们回到SPEKTRUM DSM2的原始数据,先进行SPEKTRUM DSM2的通道解析。为了观察并判断数据变化与通道的关系,我们采取的办法是,按顺序拨动通道摇杆,然后观察数据的变化。先从1通道:油门通道开始,其他通道的摇杆保持中立位置,上下拉动油门摇杆。此时可以看到,数据有变化的是帧头后面两个字节的数据,其余字节保持不变。接下来,油门摇杆保持中立,左右拨动2通道“副翼”摇杆,此时可以看到,有变化的两个字节为油门通道字节的后面两个字节。用这种方法进行6个通道的测试,可以得到数据帧的通道对应规则,如下表:
DSM1.jpg
用同样的方法可以测出JR DSM2的帧定义,JR DSM2SPEKTRUM DSM2在帧定义上是有很大区别的,看下表:
DSM2.jpg
注意:这里的通道编号是从0开始的,即0通道实际上就是我们使用的1通道;帧字节的排序也是从“0”开始,因此最后一个字节的编号为“53”和“59”,总共5460个字节。
接下来我们先对SPEKTRUM DSM2进行通道数据解码。这里采取的方法是分别分析通道摇杆在三个特殊位置的数据格式,比如,油门摇杆在最高位置、中间位置、最低位置时,分析SPEKTRUM DSM2帧的第2、3字节数据的变化,为便于观察,我们将通道字节数据转换为二进制分析,下面给出4个通道的分析图表:

1-4通道摇杆处于“中立位置”时
DSM3.jpg
1-4通道摇杆处于“一侧极限位置”时:
DSM4.jpg
1-4通道摇杆处于“另一侧极限位置”时:
DSM5.jpg
现在来分析一下油门通道在三个位置的数据。通过观察我们发现:高字节的前6位(最高位第7位—第2位)始终不变,后两位(第1位及第0位)随油门杆的变化而变化;低字节始终跟随油门杆的变化而变化。我们再看看副翼通道的情况:副翼通道也具备与油门通道一样的特性,不同的是:副翼通道高字节数据的第3位和第4位为“01”,始终保持不变。其余通道也具有相同的性质,但俯仰通道的高字节的第3位和第4位为“10”,方向通道高字节的第3位和第4位为“11”,下一通道则第5位至第3位为“100”。通过进制转换(转换为十进制),我们发现这正好是通道的编号,例如油门通道:“00”—0,副翼通道:“01”—1,俯仰通道:“10”—2,方向通道:“11”—3,第4通道:“100”—4,以此类推。
现在来看看剩下的数据位,以油门通道为例来分析。油门杆在中立位时,我们将高字节的最后两位与低字节数据合并,将高字节的最后两位放在前,将低字节放在后,组成一个十位的二进制数据:0111111111,将它转换为十进制得:512;再将油门杆在上和下极限位置时的数据进行组合换算可得:1101011011—859;0010111010
186。其余通道通过这样的这样的组合换算,也能得到近似的结果,但中立位时都是512。因此,可以得出结论:DSM2的通道输出为:01024,中点位置为512。对于JR DSM2数据,也有相同的特性,它相对于SPEKTRUM只是通道数据的位置不同。至此,DSM2总线协议“破解”完成。对比他们的帧定义,我们可以利用帧头的区别来设计程序,自动识别当前接入的DSM2总线是JR的还是SPEKTRUM的。下面给出DSM2总线解析的关键代码:
  1. uint8_t DSM2_date[60];
  2. uint16_t channels[7];
  3. char DSM2_flag=0;


  4. void serialEvent2(void)
  5. {
  6.   if(Serial2.available()>60)
  7.   {
  8.     while(DSM2_flag==0)
  9.     {
  10.     DSM2_date[0]=Serial2.read();
  11.     if(DSM2_date[0]==0x03)
  12.     {DSM2_flag=1;}
  13.     }
  14.    
  15.     if(DSM2_flag==1)
  16.     {
  17.       DSM2_date[1]=Serial2.read();
  18.       if(DSM2_date[1]==0x03||DSM2_date[1]==0x12)
  19.          {
  20.           DSM2_flag=2;
  21.          
  22.          }
  23.    
  24.       else
  25.          {
  26.           DSM2_flag=0;
  27.          
  28.           }
  29.       }
  30.       if(DSM2_flag==2)
  31.       {
  32.         if(DSM2_date[1]==0x03)
  33.         {
  34.         for(int i=2;i<54;i++)
  35.         {
  36.           DSM2_date[i]=Serial2.read();
  37.           }
  38.         }
  39.         if( DSM2_date[1]==0x12)
  40.          {
  41.         for(int i=2;i<60;i++)
  42.         {
  43.           DSM2_date[i]=Serial2.read();
  44.           }
  45.         }
  46.         
  47.         }
  48.     DSM2_flag=0;   
  49.     }
  50.    
  51. }
  52. void channels_update()
  53. {
  54.   if( DSM2_date[1]==0x03)
  55.   {
  56.   channels[0]=((DSM2_date[2]&0x03)<<8)|DSM2_date[3];
  57.   channels[1]=((DSM2_date[4]&0x03)<<8)|DSM2_date[5];
  58.   channels[2]=((DSM2_date[6]&0x03)<<8)|DSM2_date[7];
  59.   channels[3]=((DSM2_date[8]&0x03)<<8)|DSM2_date[9];
  60.   channels[4]=((DSM2_date[10]&0x03)<<8)|DSM2_date[11];
  61.   channels[5]=((DSM2_date[12]&0x03)<<8)|DSM2_date[13];
  62.   channels[6]=((DSM2_date[14]&0x03)<<8)|DSM2_date[15];
  63.    }
  64.   if( DSM2_date[1]==0x12)
  65.   {
  66.   channels[0]=((DSM2_date[12]&0x03)<<8)|DSM2_date[13];
  67.   channels[1]=((DSM2_date[2]&0x03)<<8)|DSM2_date[3];
  68.   channels[2]=((DSM2_date[8]&0x03)<<8)|DSM2_date[9];
  69.   channels[3]=((DSM2_date[14]&0x03)<<8)|DSM2_date[15];
  70.   channels[4]=((DSM2_date[6]&0x03)<<8)|DSM2_date[7];
  71.   channels[5]=((DSM2_date[4]&0x03)<<8)|DSM2_date[5];
  72.   channels[6]=((DSM2_date[10]&0x03)<<8)|DSM2_date[11];
  73.   }

  74.   }

  75. void channels_display()
  76. {
  77.     Serial.print(channels[0]);
  78.    Serial.print("  ");
  79.    Serial.print(channels[1]);
  80.    Serial.print("  ");
  81.    Serial.print(channels[2]);
  82.    Serial.print("  ");
  83.    Serial.print(channels[3]);
  84.    Serial.print("  ");
  85.    Serial.print(channels[4]);
  86.    Serial.print("  ");
  87.    Serial.print(channels[5]);
  88.    Serial.print("  ");
  89.    Serial.println(channels[6]);

  90.   }
  91.   
  92. void setup() {
  93.    Serial.begin(115200);
  94.   Serial2.begin(115200);

  95. }

  96. void loop() {
  97. channels_update();
  98. channels_display();

  99. }
复制代码

复制代码
发表于 2021-10-29 15:44 | 显示全部楼层















































您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|Archiver|手机版|Arduino中文社区

GMT+8, 2024-12-1 04:49 , Processed in 0.108371 second(s), 18 queries .

Powered by Discuz! X3.4

Copyright © 2001-2021, Tencent Cloud.

快速回复 返回顶部 返回列表