ir_biphase_decode.c - Decodes IR bi-phase bitstream from IR remote controlΒΆ

Decodes bi-phase bitstream from IR remote control as output by IR receiver Protocol is Phillips VCR control, 13 bit command (start bit, toggle bit, 5-bit address, 6-bit data) Timer2 divider must be set such that one bit time does not exceed the timer period. To configure this example to run with an external 8 MHz crystal for for a FCY=40MHz, define the C preprocessor macro: CLOCK_CONFIG=PRIPLL_8MHzCrystal_40MHzFCY and have an external crysal + 2 capacitors on the OSC1/OSC2 pins. Typical crystal accuracy for through hole is +/-20 pmm, so for a 100000 us pulse width measurement this is +/- 2 us.

#include "pic24_all.h"

/** \file
 * Decodes bi-phase bitstream from IR remote control as output by IR receiver
 * Protocol is Phillips VCR control, 13 bit command (start bit, toggle bit, 5-bit address, 6-bit data)
 * Timer2 divider must be set such that one bit time does not exceed the timer period.
*/

#define IR_FIFO_SIZE 32
volatile uint8_t au8_irFIFO[32];
volatile uint16_t u16_irFifoHead = 0;
volatile uint16_t u16_irFifoTail = 0;

void irFifoWrite(uint8_t u8_x) {
  u16_irFifoHead++;
  if (u16_irFifoHead == IR_FIFO_SIZE) u16_irFifoHead = 0;
  au8_irFIFO[u16_irFifoHead] = u8_x;
}
uint8_t irFifoRead() {
  while (u16_irFifoHead == u16_irFifoTail) {
    doHeartbeat();
  }
  u16_irFifoTail++;
  if (u16_irFifoTail == IR_FIFO_SIZE) u16_irFifoTail = 0;
  return au8_irFIFO[u16_irFifoTail];
}
 

no interrupt for Timer2, must be configured so that one bit time does not exceed the Timer2 period.

void  configTimer2(void) {
  T2CON = T2_OFF | T2_IDLE_CON | T2_GATE_OFF
          | T2_32BIT_MODE_OFF
          | T2_SOURCE_INT
          | T2_PS_1_64 ;   //at 40 MHz, approx 420 ms max, 1 tick = 1.6 us
  PR2 = 0xFFFF;                    //must be long enough so that one bit time does not overflow this
  TMR2  = 0;                       //clear timer2 value
  _T2IF = 0;                        //clear interrupt flag
  T2CONbits.TON = 1;               //turn on the timer
}

#define TWOTHIRDS_PERIOD_US 1100      //two thirds expected bit period, in microseconds
#define COMMAND_LENGTH  13            //number of bits expected in IR command
#define IR_INPUT _RB7                 //using RB9 for IR input

volatile uint16_t u16_lastCapture, u16_thisCapture,u16_delta, u16_twoThirdsPeriodTicks;
volatile uint8_t u8_bitCount,u8_bitCountTotal,u8_currentByte;
//some one-bit flags
typedef struct tagFLAGBITS {
unsigned u1_bitEdge:
  1;
unsigned u1_bitValue:
  1;
} FLAGBITS;
volatile FLAGBITS flags;

typedef enum  {
  STATE_START_PULSE_FALL = 0,
  STATE_START_PULSE_RISE,
  STATE_BIT_CAPTURE,
  STATE_LAST_EDGE,
} ICSTATE;

ICSTATE e_isrICState;

void _ISRFAST _IC1Interrupt() {
  _IC1IF = 0;
  u16_thisCapture = IC1BUF ;  //always read buffer to prevent overflow
  u16_delta = computeDeltaTicks(u16_lastCapture,u16_thisCapture,PR2);
  u16_lastCapture = u16_thisCapture;
  switch (e_isrICState) {
    case STATE_START_PULSE_FALL:
      e_isrICState = STATE_START_PULSE_RISE;
      break;
    case STATE_START_PULSE_RISE:
      if (u16_delta > u16_twoThirdsPeriodTicks) {
        //error, unexpected long pulse, reset back to start state
        e_isrICState = STATE_START_PULSE_FALL;
      } else {
        //received start pulse, start accumulating bits
        flags.u1_bitEdge = 1;   //next edge contains a bit
        u8_bitCount = 0;
        u8_currentByte = 0;
        flags.u1_bitValue = 1; //first bit is always a '1'
        u8_bitCountTotal = 0;
        e_isrICState = STATE_BIT_CAPTURE;
      }
      break;
    case STATE_BIT_CAPTURE:
      if ((u16_delta > u16_twoThirdsPeriodTicks) || flags.u1_bitEdge) {
        //record this bit
        if ((u16_delta > u16_twoThirdsPeriodTicks)) {
          //bit value has changed if wide pulse
          flags.u1_bitValue = !flags.u1_bitValue;
        }
        if (u8_bitCount != 0)u8_currentByte = u8_currentByte << 1;;
        if (flags.u1_bitValue) u8_currentByte = u8_currentByte | 0x01;
        u8_bitCount++;
        u8_bitCountTotal++;
        flags.u1_bitEdge = 1;  //this was a bit edge
        if (u8_bitCount == 7) {  //received start, toggle, address
          irFifoWrite(u8_currentByte);
          u8_currentByte = 0;
          u8_bitCount = 0;
        }
      }
      flags.u1_bitEdge = !flags.u1_bitEdge;  //next edge is opposite
      if (u8_bitCountTotal == COMMAND_LENGTH) {
        if (u8_bitCount != 0) irFifoWrite(u8_currentByte);  //save last byte
        if (IR_INPUT) e_isrICState = STATE_START_PULSE_FALL;
        else e_isrICState = STATE_LAST_EDGE;   //one more edge to come
      }
      break;
    case STATE_LAST_EDGE:
      e_isrICState = STATE_START_PULSE_FALL;
      break;

    default:
      e_isrICState = STATE_START_PULSE_FALL;
  }
}


//configure input capture.
void configInputCapture1(void) {
  CONFIG_RB7_AS_DIG_INPUT();   //use RB7 for IR Input (must be 5 V tolerant)
  CONFIG_IC1_TO_RP(RB7_RP);
  e_isrICState = STATE_START_PULSE_FALL;
  u16_irFifoHead = 0;
  u16_irFifoTail = 0;
  u16_twoThirdsPeriodTicks = usToU16Ticks(TWOTHIRDS_PERIOD_US, getTimerPrescale(T2CONbits));
#ifdef IC1CON                  //older familes
  IC1CON = IC_TIMER2_SRC |     //Timer2 source
           IC_INT_1CAPTURE |   //Interrupt every capture
           IC_EVERY_EDGE;      //Capture every edge
#endif
#ifdef IC1CON1                  //PIC24E/dsPIC33E
  IC1CON1 = IC_TIMER2_SRC |     //Timer2 source
            IC_INT_1CAPTURE |   //Interrupt every capture
            IC_EVERY_EDGE;      //Capture every edge
cascade off, sync mode, sync to timer 2
  IC1CON2 = IC_IC32_OFF| IC_SYNC_MODE | IC_SYNCSEL_TIMER2;
#endif
  _IC1IF = 0;
  _IC1IP = 1;
  _IC1IE = 1;   //enable
}

int main (void) {
  uint8_t u8_x, u8_y;
  configBasic(HELLO_MSG);
  configTimer2();
  configInputCapture1();

  while (1) {
    u8_x = irFifoRead();
    u8_y = irFifoRead();
    if (u8_x & 0x20) outString("Toggle = 1, ");
    else outString("Toggle = 0, ");
    outString("Addr: ");
    outUint8(u8_x & 0x1F);
    outString(",Cmd: ");
    outUint8(u8_y);
    outString("\n");
  }
}