The joy of bit-banging, part 2, Serial Receive

I previously discussed how to implement the Tx side of a bit-bang serial port, in the post “The joy of bit-banging, part1, Serial Transmit. This time I'll describe how to implement the receive side. This implementation uses blocking waits, as a result it should not be used unless you can tolerate delays. If you don’t want to use blocking waits then you can sample with a timer, but that becomes much more complicated.

In this implementation the receive pin is P2.5 on an MSP430, but the underlying concept should be easy to replicate on any microcontroller.

Required definitions, etc:

#include "msp430x24x.h"
#define BIT_BANG_RX_PORT P2IN
#define BIT_BANG_RX_BIT BIT5
#define DISABLE_BIT_BANG_RX_INTERRUPT() (P2IE &= ~BIT5)
#define ENABLE_BIT_BANG_RX_INTERRUPT() (P2IE |= BIT5)
//Timings calculated using DCO_8_MHZ - modify if different oscillator speed or different hardware
#define BIT_LENGTH_9600 160
#define HALF_BIT_LENGTH_9600 76
#define BIT_LENGTH_19200 75
#define HALF_BIT_LENGTH_19200 27

/** Simple delay for bit width. Used in transmit and receive. */
void delayFullBit() {for (unsigned int i = 0; i < BIT_LENGTH_19200; i++) ; } //104uSec delay

/** Simple delay for 1/2 of bit width. Used in receive. */
void delayHalfBit() {for (unsigned int i = 0; i < HALF_BIT_LENGTH_19200; i++) ; } //104uSec delay

#define NUMBER_OF_DATA_BITS 8 
#define TOGGLE_DEBUG_PIN() (P4OUT ^= BIT5)  //debugging only

Output function:

/**
* Blocking UART receiver. Called by BIT_BANG_RX_PORT Interrupt Service Routine upon detection of start bit (high to low transition).
* BIT_BANG_RX_BIT should be configured as high-low edge triggered interrupt.
* Receives a byte and then processes it.
* @note BIT_BANG_RX_PORT, BIT_BANG_RX_BIT, DISABLE_BIT_BANG_RX_INTERRUPT(), and ENABLE_BIT_BANG_RX_INTERRUPT() must be #defined. Modify accordingly for other hardware.
* @pre Oscillator configured for 8MHz
* @pre A high-to-low transition has occurred on BIT_BANG_RX_BIT
* @return the byte received
*/
char bitBangInput()
{
    unsigned char receivedBitIndex = 0;
    unsigned char receivedByte = 0;
   
    DISABLE_BIT_BANG_RX_INTERRUPT();
    delayHalfBit();     //Since this method was called on detection of an edge, need to wait until we're in the middle of the bit width.
   
    TOGGLE_DEBUG_PIN();
    //Read this start bit - should be zero
    if (BIT_BANG_RX_PORT & BIT_BANG_RX_BIT)
    {
        //error - re-enable interrupt and return  
    }

    for (int i =0; i    {
        delayFullBit();
        TOGGLE_DEBUG_PIN();
        receivedByte |= (BIT_BANG_RX_PORT & BIT_BANG_RX_BIT) ? (1 << receivedBitIndex) : 0;     //Sample the bit, and if '1' then increase receivedByte accordingly
        receivedBitIndex++;
    }
        delayFullBit();   
        TOGGLE_DEBUG_PIN();
    //THIS IS STOP BIT (line should be back high)
    if (!(BIT_BANG_RX_PORT &amp; BIT_BANG_RX_BIT))
    {
     //error - line is low; should be high because this is stop bit  
    }
    //all bits have been received!
    ENABLE_BIT_BANG_RX_INTERRUPT();
    return receivedByte;
}





Derek Smith