The joy of bit-banging, part 1, Serial Transmit

Originally posted June 4, 2009. Updated May 11, 2019.

Need a good bit-bang? Sometimes you need one more UART than what your microcontroller has. In that case you need to bit-bang it out. "Bit-banging" sounds much more vulgar than it actually is - it just means that instead of using the built-in shift register, you are manually turning on/off the bits. For this example I implement it using a method that uses blocking waits, between bits. A better implementation would be to use a timer ISR to generate the bit timings. You can bit bang most serial protocols: SPI, I2C, or plain old asynchronous serial.

The most common asynchronous serial data format is 8N1: 8 data bits, no parity bits, one stop bit. This only tells part of the story. Actually there are a total of 10 bits: (see wikipedia)

  • One start bit (a "1")

  • Eight data bits

  • One stop bit (a "0")

For 9600 baud, bit spacing is 104uSec. The calculation of this is left as an exercise for the reader. To perfect the bit spacing I first just twiddled a bit on and off with the delay loop between bits until I got exactly 104uSec.

On the hardware side, I ran the output of this through a simple level converter and then into an RS-232 port on a PC to verify that everything was working ok. One level shifter I like is available from sparkfun.com here. Surprisingly, this part is still available in 2019. It will be more difficult to find a PC with a serial port though. However, we usually use the FTDI USB to serial converter.

Notes on the code: nowadays we exclusively use the C fixed width integer data types (uint16_t, etc.) and not integers with unspecified size.

/**
* Bit-Bang UART transmit
* Transmits one byte out the specified pin
* Baud rate is 9600 (104uSec bit timing)
* 8-N-1: 8 data bits, no parity, 1 stop bit
*
* Line is nominally at '1' (high)
* 1 Start bit = '0'
* Data, MSB first, LSB last. '1' = line high, '0' = line low
* 1 Stop bit = '1' (idle, or high)
*
* PRECONDITION: LINE IS high (idle) and port/bit configured for output!
*/

#define BIT_BANG_TX_PORT P4OUT
#define BIT_BANG_TX_BIT BIT6
#define BIT_LENGTH_9600 160
void bitBangOutput(unsigned char byte)
{
//start bit - pull line down
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT;
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

//LSB
if (byte & BIT0)
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //'1' = line high
else
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT; //'0' = line low
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

if (byte & BIT1)
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //'1' = line high
else
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT; //'0' = line low
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

if (byte & BIT2)
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //'1' = line high
else
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT; //'0' = line low
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

if (byte & BIT3)
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //'1' = line high
else
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT; //'0' = line low
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

if (byte & BIT4)
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //'1' = line high
else
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT; //'0' = line low
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

if (byte & BIT5)
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //'1' = line high
else
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT; //'0' = line low
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

if (byte & BIT6)
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //'1' = line high
else
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT; //'0' = line low
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

//MSB
if (byte & BIT7)
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //'1' = line high
else
BIT_BANG_TX_PORT &= ~BIT_BANG_TX_BIT; //'0' = line low
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay

//Stop bit
BIT_BANG_TX_PORT |= BIT_BANG_TX_BIT; //let line go high
for (unsigned int i = 0; i < BIT_LENGTH_9600; i++) ; //104uSec delay
}

Derek Smithfirmware