A simple software timer system

March 31, 20132 comments Coded in C
/**
 * @file
 * Software timer facility.
 *
 * This module implements an unlimited number of 8-bit down-counting 10ms and 
 * 100ms timers.  Timers are actually held in various places by the application
 * code and are registered with this module for service from the system's 
 * timekeeping interrupt.
 *
 * A down-counting timer starts out set to a time interval and is
 * automatically decremented via the system's periodic interrupt.  Check for a
 * zero value to know when the timer has expired:
 *
 * <pre>uint8_t my_timer = 10;
 * timer_register_100ms(&my_timer);
 *
 * for (;;)
 * {
 *   if (my_timer == 0)
 *   {
 *     do_something();
 *     my_timer = 10;
 *   }
 * }</pre>
 *
 * Down-counting timers are restricted to 8 bits so that they can be
 * atomically manipulated outside interrupt code on 8-bit architectures
 * without resorting to disable interrupts.
 *
 * @warning All variables used as timers must be declared
 *          <code>volatile</code>, because they are modified from an interrupt
 *          context that may not be understood by the compiler.  GCC in
 *          particular is known to optimize away timer variables that aren't
 *          declared <code>volatile</code>.
 *
 * <h2>Configuration</h2>
 * The number of available 10ms and 100ms timer slots is set using
 * {@link MAX_100MS_TIMERS} and {@link MAX_10MS_TIMERS}.
 */

#include <stdlib.h>    /* for NULL */
#include <stdint.h>    /* uint8_t, etc. */
#include <stdbool.h>   /* bool type, true, false */

#include "timer.h"

/** Maximum number of 100ms timers that can be registered. */
#define MAX_100MS_TIMERS 10

/** Maximum number of 10ms timers that can be registered. */
#define MAX_10MS_TIMERS  10

/** The polling frequency for the 10ms timers is scaled by this factor to
    service the 100ms timers. */
#define PRESCALE_100MS   10

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

/** 10ms timer array.  These are pointers to the actual timers elsewhere in
    the application code. */
static volatile uint8_t *timers_10ms [MAX_10MS_TIMERS];

/** 100ms timer array.  These are pointers to the actual timers elsewhere in
    the application code. */
static volatile uint8_t *timers_100ms [MAX_100MS_TIMERS];

bool timer_register_10ms (volatile uint8_t *t)
{
  uint8_t k;
  
  for (k = 0; k < MAX_10MS_TIMERS; ++k)
  {
    if (NULL == timers_10ms[k])
    {
      /* Success--found an unused slot */
      timers_10ms[k] = t;
      return false;
    }
  }
  
  /* Failure */
  return true;
}

bool timer_register_100ms (volatile uint8_t *t)
{
  uint8_t k;
  
  for (k = 0; k < MAX_100MS_TIMERS; ++k)
  {
    if (NULL == timers_100ms[k])
    {
      /* Success--found an unused slot */
      timers_100ms[k] = t;
      return false;
    }
  }
  
  /* Failure */
  return true;
}

void timer_poll (void)
{
  static uint8_t prescaler = PRESCALE_100MS;
  volatile uint8_t *t;
  uint8_t k;
  
  /* Service the 10ms timers */
  for (k = 0; k < MAX_10MS_TIMERS; ++k)
  {
    t = timers_10ms[k];
    
    /* First NULL entry marks the end of the registered timers */
    if (t == NULL)
    {
      break;
    }
    
    if (*t > 0)
    {
      -- *t;
    }
  }
  
  /* Now divide the frequency by 10 and service the 100ms timers every 10th
     time through. */
  if (--prescaler == 0)
  {
    prescaler = PRESCALE_100MS;

    for (k = 0; k < MAX_100MS_TIMERS; ++k)
    {
      t = timers_100ms[k];
      
      if (t == NULL)
      {
        break;
      }
      
      if (*t > 0)
      {
        -- *t;
      }
    }
  }
}

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

/**
 * @file
 */

#include <stdbool.h>
#include <stdlib.h>

/**
 * Registers a 10-millisecond timer for service.
 *
 * @param[in]  t  pointer to the variable used for timing
 *
 * @retval     true   if registration failed
 * @retval     false  if registration succeeded (normal return)
 */
bool timer_register_10ms (volatile uint8_t *t);

/**
 * Registers a 100-millisecond timer for service.
 *
 * @param[in]  t  pointer to the variable used for timing
 *
 * @retval     true   if registration failed
 * @retval     false  if registration succeeded (normal return)
 */
bool timer_register_100ms (volatile uint8_t *t);

/**
 * Maintains all registered timers.
 *
 * This function should be called from a stable 10-millisecond time base,
 * preferably from an interrupt.
 */
void timer_poll (void);

#endif /* TIMER_H */

Integer PI control with integrator anti-windup

March 31, 2013 Coded in C
/**
 * @file
 * Proportional-integral (PI) control law.
 *
 * This module implements a simple position-type PI controller:
 * <pre>
 *   u = [ kp * e + ki * sum(e) ] >> shift
 * </pre>
 * <tt>shift</tt> is a right bit shift used to scale the output of the
 * controller down from the 32-bit intermediate result.
 *
 * An anti-windup provision is implemented on the PI integrator to prevent
 * deep saturation (aka integrator windup):
 * - The new control output with the latest integrator value is computed.
 * - If the control output exceeds either output limit, <i>and</i> the latest
 *   change in the integrator is in the same direction, then the new integrator
 *   value is not saved for the next call.
 * - Otherwise, the integrator is saved for the next call.
 */

#include <stdbool.h>
#include "pi_control.h"

/**
 * Proportional-integral (PI) control law.
 *
 * @param[in,out]  p    control parameter and state structure
 * @param[in]      e    error signal
 *
 * @return              control output <code>u</code>
 */
int pi_control (struct PIControl *p, int e)
{
  bool int_ok;      /* Whether or not the integrator should update */
  long new_i;       /* Proposed new integrator value */
  long u;           /* Control output */
  
  /* Compute new integrator and the final control output. */
  new_i = p->i + e;
  u = (p->kp * (long)e + p->ki * new_i) >> p->shift;

  /* Check for saturation.  In the event of saturation in any one direction,
     inhibit saving the integrator if doing so would deepen the saturation. */
  int_ok = true;
     
  /* Positive saturation? */
  if (u > p->max)
  {
    /* Clamp the output */
    u = p->max;

    /* Error is the same sign? Inhibit integration. */
    if (e > 0)
    {
      int_ok = false;
    }
  }
  /* Repeat for negative sign */
  else if (u < p->min)
  {
    u = p->min;
    
    if (e < 0)
    {
      int_ok = false;
    }
  }
  
  /* Update the integrator if allowed. */
  if (int_ok)
  {
    p->i = new_i;
  }

  return (int)u;
}

/**
 * Initializes the PI control.
 *
 * This function resets the PI integrator to zero.
 *
 * @param[in,out]  p  control parameter structure
 */
void pi_control_init (struct PIControl *p)
{
  p->i = 0L;
}

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

/**
 * @file
 * Proportional-integral (PI) control law header file.
 */

/** PI control data structure.  This structure contains configuration (the
    proportional and integral gain, plus a final divisor), output limits, and
    an integration accumulator (the PI controller's state variable). */
struct PIControl
{
  int kp;              /**< Proportional gain constant */
  int ki;              /**< Integral gain constant */
  unsigned char shift; /**< Right shift to divide */
  int max;             /**< Maximum value */
  int min;             /**< Minimum value */
  long i;              /**< Current integrator value */
};

/* Prototypes */
int pi_control (struct PIControl *p, int e);
void pi_control_init (struct PIControl *p);

#endif /* _PI_CONTROL_H */

1D and 2D table lookup

March 31, 2013 Coded in C
/**
 * @file
 * Table lookup with interpolation (1-D and 2-D).
 *
 * This is a 1/2-D table lookup facility.  Each routine looks up data in a table 
 * structure, interpolating as needed between data points.  The 2-D version
 * looks up along 2 axes and interpolates in two dimensions.
 *
 * <h2>Limitations</h2>
 * - The table axes (input values) must monotonically increase, or the lookup
 *   will fail.
 * - The index data type is nominally 8 bits, limiting the table length to
 *   256 elements.  Change <code>index_t</code> if larger tables are needed.
 */

#include <stdint.h>
#include <stdbool.h>
#include "lookup.h"

/** Index data type */
typedef uint8_t index_t;

/**
 * 1-D table lookup.
 *
 * This function performs a 1-D table lookup with interpolation.  The output
 * value is clamped to either of the table end values when the input value is
 * out of bounds.
 *
 * @param[in]   t      table data structure
 * @param[in]   ix     input (X-axis) value
 * @param[out]  o      output data
 *
 * @retval      true   if the lookup result is suspect due to clipping
 * @retval      false  on successful lookup
 */
bool lookup1d (Table1d *t, int ix, int *o)
{
  index_t i;
  
  /* ------------------------------------------------------------------------ */
  /* Off the end of the table */
  if (ix > t->columns[t->ncols - 1])
  {
    *o = t->table[t->ncols - 1];
    return true;
  }
  
  /* Off beginning of the table */
  else if (ix < t->columns[0])
  {
    *o = t->table[0];
    return true;
  }

  /* Within the bounds of the table */
  for (i = 0; i < t->ncols - 1; ++i)
  {
    if (   ix >= t->columns[i]
        && ix <= t->columns[i + 1])
    {
      /* Output (table) low value */
      int o_low   = t->table[i];
      /* Input (X-axis) low value */
      int i_low   = t->columns[i];
      /* Spead between the two adjacent input values */
      int i_delta = t->columns[i + 1] - t->columns[i];
      /* Spread between the two adjacent table output values */
      int o_delta = t->table[i + 1]   - t->table[i];
      
      /* Prevent division by zero.  We could get here if two consecutive
         input values in the table are the same. */
      if (o_delta == 0)
      {
        *o = o_low;
        return true;
      }
      
      *o = o_low + ((ix - i_low) * (long)o_delta) / i_delta;
      return false;
    }
  }

  /* Didn't find it (we shouldn't ever get here). */
  return true;
}

/**
 * 2-D table lookup.
 *
 * This function performs a 2-D table lookup with interpolation.  The output
 * value is clamped to either of the table end values when the input value is
 * out of bounds.
 *
 * @param[in]   t      table data structure
 * @param[in]   ix     input (X-axis) value
 * @param[in]   iy     input (Y-axis) value
 * @param[out]  o      output value
 *
 * @retval      true   if the lookup result is suspect due to clipping
 * @retval      false  on successful lookup
 */

bool lookup2d (Table2d *t, int ix, int iy, int *o)
{
  /* The lower X and Y coordinates of the interpolation box */
  index_t i, j;
  /* Set whenever one of the lookups goes off the end of the table */
  bool is_fault = false;
  
  /* ------------------------------------------------------------------------ */
  /* X axis coordinate lookup */

  /* Off the end of the table */
  if (ix > t->columns[t->ncols - 1])
  {
    /* Pretend the input value is right at the table edge so that interpolation
       works as expected */
    ix = t->columns[t->ncols - 1];
    i = t->ncols - 1;
    is_fault = true;
  }

  /* Off beginning of the table */
  else if (ix < t->columns[0])
  {
    ix = t->columns[0];
    i = 0;
    is_fault = true;
  }

  /* Within the bounds of the table */
  else
  {
    for (i = 0; i < t->ncols - 1; ++i)
    {
      if (   ix >= t->columns[i]
          && ix <= t->columns[i + 1])
      {
        break;
      }
    }
  }

  /* ------------------------------------------------------------------------ */
  /* Y axis coordinate lookup */

  /* Off the bottom of the table */
  if (iy > t->rows[t->nrows - 1])
  {
    iy = t->rows[t->nrows - 1];
    j = t->nrows - 1;
    is_fault = true;
  }

  /* Off the top of the table */
  else if (iy < t->rows[0])
  {
    iy = t->rows[0];
    j = 0;
    is_fault = true;
  }

  /* Within the bounds of the table */
  else
  {
    for (j = 0; j < t->nrows - 1; ++j)
    {
      if (   iy >= t->rows[j]
          && iy <= t->rows[j + 1])
      {
        break;
      }
    }
  }

  /* ------------------------------------------------------------------------ */
  /* 2-D interpolation */

  /* At this point we know that the input X value is between
     column[i] and column[i+1] and that the input Y value is between
     row[j] and row[j+1].  Therefore we have a rectangle in which we need
     to interpolate. 
     
     To do the interpolation, we first interpolate between column i and
     column i+1 on the upper row j.  Then, we interpolate between the same
     columns on row j+1.  Finally, we interpolate vertically between the two
     rows based on the input Y value.
     
     row0 is the upper row data and row1 is the lower (higher subscript) row
     data. */
  {
    const int *row0 = &t->table[j * t->ncols];
    const int *row1 = &row0[t->ncols];
    /* Difference between the two adjacent column values */
    int i_delta = t->columns[i + 1] - t->columns[i];
    /* Difference between the two adjacent row values */
    int j_delta = t->rows[j + 1] - t->rows[j];
    /* Low column value */
    int i_low = t->columns[i];
    /* Low row value */
    int j_low = t->rows[j];
    /* Interpolation results for the upper and lower rows */
    int o0, o1;
    
    /* Prevent division by zero if the input values aren't increasing.
       If no division by zero, interpolate between columns in the upper and
       lower row. */
    if (i_delta == 0)
    {
      o0 = row0[i];
      o1 = row1[i];
      is_fault = true;
    }
    else
    {    
      /* Interpolate the upper row */
      {
        int o_low   = row0[i];                 /* Row value at low column # */
        int o_delta = row0[i + 1] - row0[i];   /* Difference from next column */
  
        o0 = o_low + ((ix - i_low) * (long)o_delta) / i_delta;
      }

      /* Interpolate the lower (higher subscript) row */
      {
        int o_low   = row1[i];                 /* Row value at low column # */
        int o_delta = row1[i + 1] - row1[i];   /* Difference from next column */
  
        o1 = o_low + ((ix - i_low) * (long)o_delta) / i_delta;
      }
    }

    /* Guard against division by zero in the row axis.  If all is well,
       interpolate between the two row interpolation results from earlier. */
    if (j_delta == 0)
    {
      *o = o0;
      is_fault = true;
    }
    else
    {
      *o = o0 + ((iy - j_low) * (long)(o1 - o0)) / j_delta;
    }
  }
 
  return is_fault;
}

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

/**
 * @file
 * Table lookup with interpolation (1-D and 2-D) header file.
 */

#include <stdbool.h>

/** One dimensional lookup table. */
typedef const struct
{
  /** Number of elements in the table.  This must be at least 2. */
  unsigned char ncols;
  /** List of input values. */
  int *columns;
  /** Table data (output values).  The output values list must have the same
      length as the input list. */
  int *table;
} Table1d;

/** Two dimensional lookup table. */
typedef const struct
{
  /** Number of columns (X values) in the table.  Must be at least 2. */
  unsigned char ncols;
  /** Number of rows (Y values) in the table.  Must be at least 2. */
  unsigned char nrows;
  /** X-axis input values list. */
  int *columns;
  /** Y-axis input values list. */
  int *rows;
  /** Table data.  This is an array of <code>columns</code>X<code>rows</code>,
      arranged in rows.  For example, <code>table[1]</code> is the second 
      column in the first row. */
  int *table;
} Table2d;

/* Prototypes */
bool lookup1d (Table1d *t, int ix, int *o);
bool lookup2d (Table2d *t, int ix, int iy, int *o);

#endif

Software UART receiver

March 31, 2013 Coded in C
/**
 * @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.  However, 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