软件UART接收器

2013年3月31日 编码为 C

该模块通过周期性中断在软件中解码异步串行数据。 当您必须从第二个串行源接收数据但微控制器只有一个硬件串行端口时,这很有用。 该代码被实现为直接向量状态机,即函数指针指向当前状态的决策函数,并直接从系统的周期性中断中调用。 对于1200波特数据速率,大约200微秒的中断是合适的,如本示例所示。

在每个状态下,如果已接收到字符,该函数将返回true,否则返回false。 每当函数返回true时,soft_uart_rx_buf中都会立即提供最新字符。 

/**
 * @file
 * Software serial (UART) receiver
 *
 * This module implements the receive engine for asynchronous serial
 * communications using polling ("bit banging").  Transmission capability
 * is not provided.
 *
 * The data format is <tt>8-N-1</tt>:
 * - Eight data bits
 * - No parity
 * - One stop bit
 *
 * <h2>Structural overview</h2>
 * The receiver is implemented as a polled finite state machine.  The state
 * of the I/O pin is passed as an argument to the state machine animation
 * function <code>soft_uart_rx()</code>.  The polling function must be called
 * on a stable timebase at a frequency at least three times
 * the bit rate.  The function returns a flag to indicate that a character has
 * been received and places the received character in a fixed buffer.
 *
 * <h2>Timing</h2>
 * The baud rate of the transmitter constrains the ossortment of possible
 * interrupt rates.  怎么样ever, this receiver is designed to be configurable so
 * as to maximize those choices.
 *
 * Any frequency multiple of at least 3 is suitable.  Is this example, the
 * sample rate is four times the serial data bit rate:
 *
 * <pre>
 *  Given
 *  =====
 *  Baud rate specification: 1200 +/- 4%
 *  System interrupt rate:   5 kHz (200 us)
 *
 *  Selecting a sample rate
 *  =======================
 *  Chosen multiplier:       samples per bit
 *  Sample rate:             5 kHz / 4 == 1250 baud (4.16% high)
 * </pre>
 *
 * Since the baud rate is high in this example, We will have a tendency to
 * sample earlier and earlier on each successive bit.  Therefore it is desirable
 * to sample slightly later in the bit time if possible.
 * <pre>
 * \#define SOFT_SOFT_UART_RX_BIT_TIME  5
 * \#define SOFT_UART_RX_START_SAMPLES  2
 * </pre>
 * The diagram below shows the resultant timing.  The actual bit times are 4%
 * slower, owing to the fact that the system interrupy is not an exact multiple
 * of the bit time.
 *
 * The sample timing error at the stop bit is (4% X 9) = 36% too early.
 * <pre>
 *  _______                 _______________                     _______________
 *         \\_______________/               \\...________________/
 * +-------+---+---+---+---+---+---+---+---+...+---+---+---+---+---+---+---+---+
 * | Cycle | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |   | 8 | 9 | A | B | C | D | E | F |
 * +-------+---+---+---+---+---+---+---+---+...+---+---+---+---+---+---+---+---+
 * | Data  | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |   | 0 | 0 | 0 | 0 | 1 | 1 | 1 | 1 |
 * |       |   Start bit   |  Data bit 0   |   |  Data bit N   |   Stop bit    |
 * | Samp. | X | X |   |   |   |   | X |   |   |   |   | X |   |   |   | X |   |
 * +-------+---+---+---+---+---+---+---+---+...+---+---+---+---+---+---+---+---+
 *           ^   ^                                       |<------------->|
 *           |   |                                               |
 *           |   |                  SOFT_UART_RX_BIT_TIME -------+
 *           |   |
 *           +---+---- SOFT_UART_RX_START_SAMPLES
 * </pre>
 * Here is an explanation of how a character is received:
 * -# We sample the line continuously until the START (logic zero) bit is seen.
 * -# Just to make sure it wasn't noise, we sample the line a second (or third
 *    or fourth, depending on the setting) time with the expectation that the
 *    state hasn't changed.
 * -# We continue to sample the start bit until we have reached the center of
 *    the bit time.  The line must stay in the low state.  This shifts us to
 *    safety away from edges.
 * -# We delay (frequency multiplier) cycles, ignoring the state of the line.
 *    This puts us in the middle of the first data bit.
 * -# We sample and save the data bit, then wait (frequency multiplier - 1)
 *    cycles.
 * -# We repeat until we have sampled all data (payload) bits.  The last bit
 *    is sampled and must be a logic one.
 *
 * <h2>Limitations</h2>
 * For speed, the receive buffer is implemented as a global variable that is
 * to be accessed directly by the calling code.  Also, the state variable
 * is private to this module.  Therefore, only one instance of the soft
 * UART receiver is supported in a given project. 
 *
 * @author Justin Dobbs
 */

#include <stdbool.h>

/** The number of times to sample the start bit.

    This defines the phase shift of subsequent samples.  If the interrupt rate is
    a bit high relative to the baud rate, we want to sample late to
    minimize cumulative timing error. */
#define SOFT_UART_RX_START_SAMPLES  3

/** The inter-bit delay time, a.k.a. the frequency multiplier */
#define SOFT_UART_RX_BIT_TIME       4

/* State definitions */
static bool st_idle (bool);
static bool st_start_bit (bool);
static bool st_delay_rx0 (bool);
static bool st_delay_rx1 (bool);
static bool st_delay_rx2 (bool);
static bool st_delay_rx3 (bool);
static bool st_delay_rx4 (bool);
static bool st_delay_rx5 (bool);
static bool st_delay_rx6 (bool);
static bool st_delay_rx7 (bool);
static bool st_delay_stop (bool);
static bool st_abort_wait_for_idle (bool);

/**
 * Soft UART receiver polling function.
 *
 * This function implements the receiver.  It should be called on a stable
 * timebase at a fixed multiple of the bit rate.
 *
 * @note This is implemented as a pointer to a function to handle the current
 *       state.  The caller need only invoke the function using the pointer.
 *
 * @param[in]   x      the state of the input line:
 *                     - <code>true</code>: the line is high
 *                     - <code>false</code>: the line is low
 *
 * @retval      true   if a character is ready in <code>soft_uart_rx_buf</code>
 * @retval      false  otherwise
 */
bool (*soft_uart_rx)(bool) = st_idle;

/** Serial recieve buffer.  This should be immediately read after
    <code>soft_uart_rx()</code> returns <code>true</code>. */
unsigned char soft_uart_rx_buf;

/** Cycle counter, for timing. */
static unsigned char i;

/**
 * Sampling continuously, waiting for the start bit.
 */
static bool st_idle (bool x)
{
  if (!x)
  {
    i = SOFT_UART_RX_START_SAMPLES - 1;
    soft_uart_rx = st_start_bit;
  }
  return false;
}

/* -------------------------------------------------------------------------- */

/**
 * Sampling the start bit a few more times to make sure it's solid.  This also
 * provides time offset for sampling future bits in the middle of the bit time.
 */
static bool st_start_bit (bool x)
{
  /* Reject if the start bit does not last long enough */
  if (x)
  {
    soft_uart_rx = st_idle;
  }
  else if (--i == 0)
  {
    i = SOFT_UART_RX_BIT_TIME;
    soft_uart_rx_buf = 0;
    soft_uart_rx = st_delay_rx0;
  }
  return false;
}

/* -------------------------------------------------------------------------- */

/**
 * Waiting one bit time, then sampling the LSb (bit 0).
 */
static bool st_delay_rx0 (bool x)
{
  /* When it's time, shift in the data to the RX buffer.  If we have
   received all the data, go wait for the STOP bit. */
  if (--i == 0)
  {
    if (x)
    {
      soft_uart_rx_buf |= 0x01;
    }
    i = SOFT_UART_RX_BIT_TIME;
    soft_uart_rx = st_delay_rx1;
  }
  return false;
}

/* -------------------------------------------------------------------------- */

/**
 * Waiting one bit time, then sampling bit 1.
 */
static bool st_delay_rx1 (bool x)
{
  if (--i == 0)
  {
    if (x)
    {
      soft_uart_rx_buf |= 0x02;
    }
    i = SOFT_UART_RX_BIT_TIME;
    soft_uart_rx = st_delay_rx2;
  }
  return false;
}

/* -------------------------------------------------------------------------- */

/**
 * Waiting one bit time, then sampling bit 2.
 */
static bool st_delay_rx2 (bool x)
{
  if (--i == 0)
  {
    if (x)
    {
      soft_uart_rx_buf |= 0x04;
    }
    i = SOFT_UART_RX_BIT_TIME;
    soft_uart_rx = st_delay_rx3;
  }
  return false;
}

/* -------------------------------------------------------------------------- */

/**
 * Waiting one bit time, then sampling bit 3.
 */
static bool st_delay_rx3 (bool x)
{
  if (--i == 0)
  {
    if (x)
    {
      soft_uart_rx_buf |= 0x08;
    }
    i = SOFT_UART_RX_BIT_TIME;
    soft_uart_rx = st_delay_rx4;
  }
  return false;
}

/* -------------------------------------------------------------------------- */

/**
 * Waiting one bit time, then sampling bit 4.
 */
static bool st_delay_rx4 (bool x)
{
  if (--i == 0)
  {
    if (x)
    {
      soft_uart_rx_buf |= 0x10;
    }
    i = SOFT_UART_RX_BIT_TIME;
    soft_uart_rx = st_delay_rx5;
  }
  return false;
}

/* -------------------------------------------------------------------------- */

/**
 * Waiting one bit time, then sampling bit 5.
 */
static bool st_delay_rx5 (bool x)
{
  if (--i == 0)
  {
    if (x)
    {
      soft_uart_rx_buf |= 0x20;
    }
    i = SOFT_UART_RX_BIT_TIME;
    soft_uart_rx = st_delay_rx6;
  }
  return false;
}

/* -------------------------------------------------------------------------- */

/**
 * Waiting one bit time, then sampling bit 6.
 */
static bool st_delay_rx6 (bool x)
{
  if (--i == 0)
  {
    if (x)
    {
      soft_uart_rx_buf |= 0x40;
    }
    i = SOFT_UART_RX_BIT_TIME;
    soft_uart_rx = st_delay_rx7;
  }
  return false;
}

/* -------------------------------------------------------------------------- */

/**
 * Waiting one bit time, then sampling bit 7.
 */
static bool st_delay_rx7 (bool x)
{
  if (--i == 0)
  {
    if (x)
    {
      soft_uart_rx_buf |= 0x80;
    }
    i = SOFT_UART_RX_BIT_TIME;
    soft_uart_rx = st_delay_stop;
  }
  return false;
}

/* -------------------------------------------------------------------------- */

/**
 * Waiting one bit time, then sampling the stop bit.
 * @note The reception is aborted if the stop bit does not arrive on schedule.
 */
static bool st_delay_stop (bool x)
{
  if (--i == 0)
  {
    /* STOP bit is always logic ONE by definition */
    if (x)
    {
      soft_uart_rx = st_idle;
      return true;  /* Got a character */
    }
    else
    {
      /* Stop bit didn't happen when we expected it.  Go sit and wait 
         indefinitely for the line to go high. */
      soft_uart_rx = st_abort_wait_for_idle;
      return false;
    }
  }
  /* Haven't sampled the stop bit yet! */
  return false;
}

/* -------------------------------------------------------------------------- */

/**
 * Reception aborted; waiting as long as required for the line to idle high
 * again.
 */
static bool st_abort_wait_for_idle (bool x)
{
  /* NOW the line is finally high/idle again.  Start the receive process over.
     We did not get a character. */
  if (x)
  {
    soft_uart_rx = st_idle;
  }
  return false;
}

/* Header file */
#if !defined(_SOFT_UART_RX_H)
#define _SOFT_UART_RX_H

/**
 * @file
 * Soft UART receiver header file
 *  
 * This file implements the interface to the software UART reciever module.
 * The full documentation is located in @ref soft_uart_rx.c.
 *
 * @author Justin Dobbs
 */

#include <stdbool.h>

/* Actually a function pointer, but this is supposed to be opaque.  This is
   called from a periodic interrupt. 

  @param[in]  x  the state of the serial line (true == high) */
extern bool (*soft_uart_rx) (bool x);

/* The receive buffer */
extern unsigned char soft_uart_rx_buf;

#endif