Building an SBus decoder

Several years ago I was going to write an SBus converter to use with one of my parallax projects but found I didn't have all the tools I needed to get the job done.

While I had a ok start with seeing some example on the net that just used a serial interface and decoded the data from that I didn't like the approach. So I abandoned the project only to recently pick it up again.

This time I have a Prop Scope so I could actually see the data as it came out of the SBus receiver. This help to understand some of the timing and to be able to decode the data looking at the Prop scope screen even before I wrote the code.

After running into a number of block aids while writing the code in Propeller C, I finally got the code to work.

One of the issues was timing. It was way to slow and could not figure out why it was not working. This is where the memory model that I picked came in. Using CMM was ok for just plan coding but if you need to have something loop exactly and have it do it at processor speed you need to use the LMM code option. This solved a bunch of problems for me. It also help to build some timing loops that pulsed a pin up and down and watch them on the Prop scope only to see they were not as fast as they were supposed to be.

Now sampling the data with 25 bytes coming in was a little difficult so I had to build a simple one byte pulse program that I could use to adjust my timing loops. One issue was finding code that would wait for a short while and then sample the input pin to determine is value. I was trying to avoid coding in Assembly language if possible.

By starting a cog with the one byte sample code running in it I was able to read that data back in using another cog. One pin out and another pin in. I also had the sample code change another pin that I hooked the Prop scope to just so I could see how the sample program was handling the input data.

There is nothing worse than having your sample code drift off and start sampling in the wrong frame.

Now if this all wasn't difficult enough now comes the SBus decoding fiasco. First the data bits are transmitted high to low which is the opposite of serial data and signal values are inverted so high is low and low is high. Then the data is transmitted 8 bits at a time but are really 11 bits so some of the bits are in the next byte. Not only that but once you put the 11 bits together you have to reverse the order because the values are in little Indian format.

So some questions are is the next 3 bits on the top of the next byte or on the bottom? Well they are normal and are the top 3 bits.

So you read in 25 bytes of data then convert them to 16 channels of data. Also the first byte of data is always 0xf0. This lets you know where the start of a frame is.

The first channel is the next 8 bits plus 3 bits of the next byte. The next channel is 5 bits of this byte and 6 bits of the next byte of data and so on until you get to 8 or 16 channels of data.

Now that the data is converted you see that the values coming out are not what you expected. Normal PWM signals are between 1000 and 2000 where SBus are in the range of 200 to 1800. So now you have to scale theses values so that they fit the range required by servos. Obviously if you're going to just use them with the propeller chip you can use the raw values.

Well the library is now complete and will allow me to use my FrSky X8R receiver to control my boe-bot.
void doRecSBus(void *par)
{
  int rxmask;
  int rxbits;
  int rxdata;
  int baud;
  int wcnt;
  int c;
  
  rxmask = 1 << _SBPin;
  baud = 800;
  c = 0;
  
  while (1)
  {
    waitpne(0, rxmask);       // Wait for Start Bit
    rxbits = 10;
    rxdata = 0;
    wcnt = baud >> 1;
    wcnt = wcnt + CNT;
    while (rxbits > 0)
    {
      wcnt += baud;
      waitcnt2(wcnt, baud);   // Wait for Center of Next bit
      rxdata = rxdata << 1;
      if ((rxmask & INA) != rxmask)  // Reverse Bit value
        rxdata = rxdata | 1;

      rxbits--;
    }
    rxdata = rxdata >> 2;    // Dump Parity and Stop
    if (rxdata == 0xf0)      // SBus Start Byte Frame value
      c = 0;
    _Dc[c] = rxdata;
    if (c++ > 25)            // Just in case
      c = 25;
    if (c == 24)
      doChannel();
  }

void doChannel()
{
  int i;
  short c, b, bc;
  
  c = 0;         // Channel
  b = 0x80;      // Bit Position high -> low
  bc = 1;        // Bit Channel value
  i = 1;         // Should be 1
  _Cc[0] = 0;      // Clear Channel value
  while (c < 16) // Do 16 Channels
  {
    if ((_Dc[i] & b) == b)
      _Cc[c] |= bc;
    b = b >> 1;
    if (b == 0)
    {
      b = 0x80;
      i++;
    }
    bc = bc << 1;
    if (bc > 0x400)
    {
      bc = 1;
      c++;
      _Cc[c] = 0;
    }
  }
  _Er = 0;
  if (_Dc[23] == 0x20)
    _Er = 1;
  if (_Dc[23] == 0x30)
    _Er = -1;
}

Mike

Comments

Sign In or Register to comment.