Rainboard NewSoftSerial

During testing an issue appeared while using version 11b of NewSoftSerial.

In order to make sure everything was going correctly, the arduino was set up to echo back any bytes that it received via the software serial connection. This presented an error. In total, 16 bytes were send in one burst and 15 bytes were received back.

f0:7f:7f:03:00:00:7f:01:7f:00:00:02:00:7f:00:f7 // Data Sent
f0:7f:7f:03:80:80:bf:80:bf:80:80:81:80:01:dd //Data received

As you can see by the data received the first few bytes work fine, but as time goes on the bytes are not correct but still seem to be in some sort of order.

Lets look closer at the binary version of the data

Byte Sent Hex Received Hex Sent Binary Received Binary
1 0xF0 0XF0 11110000 11110000
2 0x7F 0X7F 01111111 01111111
3 0x7F 0X7F 01111111 01111111
4 0x03 0X03 00000011 00000011
5 0x00 0X80 00000000 10000000
6 0x00 0X80 00000000 10000000
7 0x7F 0XBF 01111111 10111111
8 0x01 0X80 00000001 10000000
9 0x7F 0XBF 01111111 10111111
10 0x00 0X80 00000000 10000000
11 0x00 0X80 00000000 10000000
12 0x02 0X81 00000010 10000001
13 0x00 0X80 00000000 10000000
14 0x7F 0X01 01111111 00000001
15 0x00 0XDD 00000000 11011101
16 0xF7 11110111

Everything up to bytes 5 is identical. Lets layout the binary in a stream.

|  byte5| byte 6| byte 7| byte 8| byte 9| byte10| byte11| byte12| byte13| byte14| byte15| byte16|
000000000000000001111111000000010111111100000000000000000000001000000000011111110000000011110111 //sent
1000000010000000101111111000000010111111100000001000000010000001100000000000000111011101 //recv

For byte 5 and bytes 6 the only problem is that the first bit of each packet has been changed to a 1 from a 0. Byte 7 received then begins with a 1 when it should also start with a 0. However, if we were to erase the first bit of byte 7, things start lining up again:

|  byte5| byte 6| byte 7| byte 8| byte 9| byte10| byte11| byte12| byte13| byte14| byte15| byte16|
000000000000000001111111000000010111111100000000000000000000001000000000011111110000000011110111 //sent
100000001000000001111111000000010111111100000001000000010000001100000000000000111011101 //recv
                1 was erased

Now bytes 7, 8, and 9 are correct but start going bad at byte 10 again. At this point we can assume that maybe the timing of reading data is slightly off (as we are using software not hardware). During Serial Communication [1] there are actually 10 bits sent per byte. The first or "start" bit signals the start of a transmitting byte and occurs when the line goes from 1 to 0 and the last bit (stop bit) is a 1. If the process of grabbing the data off the line was slightly slow, it would be possible to accidentally read the stop bit as if it were actually part of the data. At first you might think that if this was the case the bad 1 value would occur at the end of the byte. However, the serial line is transferring the bits with the least significant bit (lsb) first.

For example:

Sending 0x00 // hex
                    Byte: 00000000  //binary
With Start and stop Bits:0000000001 //

First we change the serial line from 1 to 0 this causes the receiver to know a byte is coming:

0000000001 
|

Then we start sending data from right to left (not including the stop byte)
0000000001 
        |
0000000001 
       |
0000000001 
      |
0000000001 
     |
0000000001 
    |
0000000001 
   |
0000000001 
  |
0000000001 
 |
0000000001 
         |

Another example:
If we number all the bits: 12345678

We send the data like so: Start Bit 8 7 6 5 4 3 2 1 Stop Bit

So the end result that if the timing is off and the stop bit is accidentally read, it will cause the most significant bit (msb) of the byte to be a 1.


Solution

The solution is to alter the timing of the NewSoftSerial parameters to work slightly faster than they are.

In our particular example we are operating at 57600 baud on a 16Mhz processor. If we look in the cpp file for NewSoftSerial library we can find this table.

static const DELAY_TABLE PROGMEM table[] = 
{
  //  baud    rxcenter   rxintra    rxstop    tx
  { 115200,   1,         17,        17,       12,    },
  { 57600,    10,        37,        37,       33,    },
  { 38400,    25,        57,        57,       54,    },
  { 31250,    31,        70,        70,       68,    },
  { 28800,    34,        77,        77,       74,    },
  { 19200,    54,        117,       117,      114,   },
  { 14400,    74,        156,       156,      153,   },
  { 9600,     114,       236,       236,      233,   },
  { 4800,     233,       474,       474,      471,   },
  { 2400,     471,       950,       950,      947,   },
  { 1200,     947,       1902,      1902,     1899,  },
  { 300,      3804,      7617,      7617,     7614,  },
};

There are three timings we are interested in.

rxcenter: This is the time it takes to go from the edge of the start bit to about half way through the bit (in order to center reading).

rxintra: This is the time to wait between sampling bits of the data

rxstop: This is the time to wait for the stop bit to finish being sent.

In order to speed things up slightly the rxcenter has been modified to be 8 so the bits are caught slightly closer to the start and the rxstop has been altered so we don't take quite as long waiting for the stop bit. This solves the problem by kicking out of the interrupt slightly quicker which allows it to be called again faster in situations where a lot of data is being sent all at once.

static const DELAY_TABLE PROGMEM table[] = 
{
  //  baud    rxcenter   rxintra    rxstop    tx
  { 115200,   1,         17,        17,       12,    },
  { 57600,     8,        37,        30,       33,    }, //Changed rxCenter to 8 and rxstop to 30
  { 38400,    25,        57,        57,       54,    },
  { 31250,    31,        70,        70,       68,    },
  { 28800,    34,        77,        77,       74,    },
  { 19200,    54,        117,       117,      114,   },
  { 14400,    74,        156,       156,      153,   },
  { 9600,     114,       236,       236,      233,   },
  { 4800,     233,       474,       474,      471,   },
  { 2400,     471,       950,       950,      947,   },
  { 1200,     947,       1902,      1902,     1899,  },
  { 300,      3804,      7617,      7617,     7614,  },
};

The new values were just found by trial and error. Perhaps with some more time and greater insight more accurate values could be used.

External Links

  1. http://en.wikipedia.org/wiki/Asynchronous_serial_communication


Comments

blog comments powered by Disqus