You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

153 lines
4.3 KiB

/** @file ir_serial.c
@author M. P. Hayes, UCECE
@date 23 August 2010
@brief Infrared serial driver.
*/
/* This should probably be compiled with optimisation enabled
otherwise some of the timing may be dodgy.
For documentation of the protocol see ir_serial.h */
#include "ir_serial.h"
#include "delay.h"
#define IR_SERIAL_DIT_PERIOD 0.6e-3
#define IR_SERIAL_MODULATIONS_PER_DIT ((int)(IR_SERIAL_DIT_PERIOD * IR_MODULATION_FREQ))
#define IR_SERIAL_MODULATIONS_PER_START (IR_SERIAL_MODULATIONS_PER_DIT * 4)
#define IR_SERIAL_MODULATIONS_PER_ONE (IR_SERIAL_MODULATIONS_PER_DIT * 2)
#define IR_SERIAL_MODULATIONS_PER_ZERO (IR_SERIAL_MODULATIONS_PER_DIT)
#define IR_SERIAL_MODULATIONS_PER_BREAK (IR_SERIAL_MODULATIONS_PER_DIT)
#define IR_SERIAL_DELAY_US 10
#define IR_SERIAL_DIT_COUNT (1e6 * IR_SERIAL_DIT_PERIOD / IR_SERIAL_DELAY_US)
#define IR_SERIAL_START_COUNT_MAX ((int)(4.5 * IR_SERIAL_DIT_COUNT))
#define IR_SERIAL_ONE_COUNT_MAX ((int)(2.5 * IR_SERIAL_DIT_COUNT))
#define IR_SERIAL_ZERO_COUNT_MAX ((int)(1.25 * IR_SERIAL_DIT_COUNT))
#define IR_SERIAL_BREAK_COUNT_MAX ((int)(1.5 * IR_SERIAL_DIT_COUNT))
/** Transmit a start code. */
static inline void ir_serial_tx_start (void)
{
ir_tx_set (1, IR_SERIAL_MODULATIONS_PER_START);
ir_tx_set (0, IR_SERIAL_MODULATIONS_PER_BREAK);
}
/** Transmit a data bit. */
static inline void ir_serial_tx_bit (uint8_t state)
{
ir_tx_set (1, state ? IR_SERIAL_MODULATIONS_PER_ONE
: IR_SERIAL_MODULATIONS_PER_ZERO);
ir_tx_set (0, IR_SERIAL_MODULATIONS_PER_BREAK);
}
/** Transmit a stop code. */
static inline void ir_serial_tx_stop (void)
{
ir_tx_set (0, IR_SERIAL_MODULATIONS_PER_BREAK);
}
/** Transmit 8 bits of data over IR serial link.
@param data byte to transmit
@note No error checking is performed. This function blocks until
the frame is transmitted. */
void ir_serial_transmit (uint8_t data)
{
int i;
/* Send start code. */
ir_serial_tx_start ();
/* Eight data bits LSB first. */
for (i = 0; i < 8; i++)
{
ir_serial_tx_bit (data & 1);
data >>= 1;
}
/* Send stop code. */
ir_serial_tx_stop ();
}
/** Receive 8 bits of data over IR serial link.
@param pdata pointer to byte to store received data
@return status code
@note No error checking is performed. If there is no activity on the
IR serial link, this function returns immediately. Otherwise, this
function blocks until the entire frame is received. */
ir_serial_ret_t ir_serial_receive (uint8_t *pdata)
{
int i;
int count;
uint8_t data;
bool data_err;
/* Check for start code; if not present return. */
if (!ir_rx_get ())
return IR_SERIAL_NONE;
/* Wait for end of start code or timeout. */
for (count = 0; ir_rx_get (); count++)
{
if (count >= IR_SERIAL_START_COUNT_MAX)
return IR_SERIAL_START_ERR;
DELAY_US (IR_SERIAL_DELAY_US);
}
/* We may have received a rogue short pulse but we may have been
called closed to the falling transition. */
data_err = 0;
data = 0;
/* Eight data bits LSB first. */
for (i = 0; i < 8; i++)
{
data >>= 1;
/* Wait for IR modulation to start or timeout (indicating
detection of a false start code). */
for (count = 0; !ir_rx_get (); count++)
{
if (count >= IR_SERIAL_BREAK_COUNT_MAX)
return IR_SERIAL_BREAK_ERR;
DELAY_US (IR_SERIAL_DELAY_US);
}
/* If there is another transmission in this slot we will get
too small a value for dit_count. This is likely to trigger
a bit error. */
for (count = 0; count < IR_SERIAL_ONE_COUNT_MAX
&& ir_rx_get (); count++)
{
DELAY_US (IR_SERIAL_DELAY_US);
}
if (count >= IR_SERIAL_ONE_COUNT_MAX)
data_err = 1;
if (count >= IR_SERIAL_ZERO_COUNT_MAX)
data |= 0x80;
}
/* Perhaps should check for stop code. If not found, there is
likely to have been interference from another transmitter. */
*pdata = data;
return data_err ? IR_SERIAL_DATA_ERR : IR_SERIAL_OK;
}
/** Initialise IR serial driver. */
void ir_serial_init (void)
{
ir_init ();
}