Notes on bit-banging async serial

Real UARTS typically sample the received signal at 16 times the baud rate, so that they can accurately detect the leading edge of the start bit and hence sample the actual data bits very near the center of the bit time. This allows for a fair bit of slop between the transmiter and receiver bit rates. This diagram illustrates the best and worst case timing:
-----                /-------------\-/-------------\-/-------------\-/----
    |  start bit    /  data bit 0   X  data bit 1   X  data bit 2   X
    |--------------/---------------/-\-------------/-\-------------/-\----

------                /-------------\-/-------------\-/-------------\-/----
     |  start bit    /  data bit 0   X  data bit 1   X  data bit 2   X
     |--------------/---------------/-\-------------/-\-------------/-\----

     |                       |               |               |
     0000000000111111111122222222223333333333444444444455555555556666666666
     0123456789012345678901234567890123456789012345678901234567890123456789
     ^                       ^               ^               ^
start bit detected      first data bit   subsequent data
within first 1/16 bit   sampled 24       bits sampled every
time of leading edge    intervals later  16 sample intervals
                                         thereafter
Most UARTs will actually sample the middle of the start bit (eight sample periods after the leading edge is recognized) and verify that it is low in case the detected leading edge was just a glitch. Some UARTs will also take multiple samples in the middle of the bit time (i.e, at 23, 24, and 25 in the diagram above) and set error flags for noise if they don't all match. Many application notes on implementing software UARTs, including the Microchip ap notes, suggest sampling at two times the bit rate. This results in the following:
-----                /-------------\-/-------------\-/-------------\-/----
    |  start bit    /  data bit 0   X  data bit 1   X  data bit 2   X
    |--------------/---------------/-\-------------/-\-------------/-\----

-------------                /-------------\-/-------------\-/-------------\-
            |  start bit    /  data bit 0   X  data bit 1   X  data bit 2   X
            |--------------/---------------/-\-------------/-\-------------/-

            |               |               |               |               |
            0       1       2       3       4       5       6       7       8
            ^               ^               ^               ^               ^
start bit detected     first data bit    subsequent data
within first 1/2 bit   sampled 2         bits sampled every
time of leading edge   intervals later   2 sample intervals
                                         thereafter
This results in a 1/2 bit time uncertainty regarding when the start bit actually arrived. As a consequence the data bit sampling can occur any time within the first half of the data bit, including right at the leading edge. This allows no margin for rate mismatch. An alternative would be to sample the first data bit three sample periods after the start bit is detected, and every two sample periods thereafter. This is no better as the window is then from the middle to the end of the data bit, so the bits could be sampled right at the trailing edge, again allowing no margin for rate mismatch. The Microchip ap note does at least suggest waiting 1.25 bit times from the leading edge of the start bit to sample the first data bit, which results in the sample window being in the middle 50% of the bit. That's great unless you want to do full duplex serial and use the same time base for transmit. Since you can't get a 1.25 bit time delay using a 0.5 bit time timebase, you maintain proper transmitter timing during that 1.25 bit time delay. The correct solution (IMHO) is to sample at 3 times the bit rate:
-----               /------------\-/------------\-/------------\-/-----------
    |  start bit   /  data bit 0  X  data bit 1  X  data bit 2  X
    |-------------/--------------/-\------------/-\------------/-\-----------

----------               /------------\-/------------\-/------------\-/-------
         |  start bit   /  data bit 0  X  data bit 1  X  data bit 2  X
         |-------------/--------------/-\------------/-\------------/-\-------

         |                   |              |              |              |
         0    0    0    0    0    0    0    0    0    0    1    1    1    1
         0    1    2    3    4    5    6    7    8    9    0    1    2    3
         ^                   ^              ^              ^              ^
start bit detected     first data bit    subsequent data
within first 1/3 bit   sampled 4         bits sampled every
time of leading edge   intervals later   3 sample intervals
                                         thereafter
As you can see, this reduces the uncertainty of the timing of the start bit to 1/3 of a bit time, and guarantees that the sampling of the data bits will occur within the middle 1/3 of the bit time, thus providing better tolerance to speed variation than the 1/2 bit time scheme provides (even with the 1.25 bit time delay). It also uses fewer CPU cycles than would be required to sample at 4 times the bit rate. I've implemented this scheme on a PIC16C84 and it seems to work quite well. I use the RTCC timer to generate interrupts at three times the bit rate, and the interrupt routine has simple receive and transmit state machines to do the work. I'll make the code available after I do some more testing.
Back to my PIC Projects page
Back to my home page

Last updated June 5, 1995

Copyright 1995 Eric Smith

eric@brouhaha.com