x10rf.c

PIC16F84 and RF interface (by www.ant.org).



////////////////////////////////////////////////////////////////////////////////
// X10rf.c - X10 all-housecode RF interface & CM12U relay
//           (c) ant.org 2004
//
//           This interface replaces the spectacularly poor TM12U RF interface
//           from X10. Its advantages are that it interfaces directly to the PC
//           (so there's less delay), it operates on all house-codes (the TM12U
//           only responds to a single housecode) and it can be daisy-chained to
//           a CM12U PC interface, so that both can be used with a single PC
//           serial port.
//
//           X10 RF reception takes priority over the serial port relay function
//
//           The LED is on to show power and flickers as packets are received
//           
//           The TEST_PIN goes high when a valid start bit is detected (handy
//           for scope debugging as the RF module kicks out a lot of noise)
//
//           The meat of this code is an asynchronous state machine (ASM) driven
//           from the external interrupt hooked up to the RF module's output.
//           Since X10 RF is PWM, the ASM uses the TIMER0 module to measure
//           pulse widths and decode the bytestream. It then performs a simple
//           integrity check on the data before passing it to a FIFO for output
//           to the PC.
//
//           Serial output is of the form "X%2X %2X\n" where the two bytes are
//           the X10 RF payload. These are in the same format as the X10 CM17A
//           protocol.
////////////////////////////////////////////////////////////////////////////////

#include <system.h> // platform 16f84.h
#include "x10rf.h"

#ifdef THIRTY_TWO_BIT_SUPPORT
unsigned int rf_data;
#else
unsigned char rf_data[4];
#endif
unsigned char fifo[2];
unsigned char fifo_bytes = 0;
unsigned char bits = 0;
unsigned char rf_state = WAIT_START_BIT;
unsigned char timer_high = 0;

#pragma CLOCK_FREQ 4000000
#pragma RS232_RXPORT PORTB
#pragma RS232_TXPORT PORTA
#pragma RS232_RXPIN  4      // pc serial rx on RB4
#pragma RS232_TXPIN  0      // pc serial tx on RA0
#pragma RS232_BAUD 28800

// Define device config block
asm {
  list p=16F84A
    __config H'3FF5' ;UNPROTECT & XT & WDTEN & PWRTEN & BOREN & LVPEN & DUNPROT & WRTEN & DEBUGDIS
}

////////////////////////////////////////////////////////////////////////////////
// function prototypes
////////////////////////////////////////////////////////////////////////////////
void interrupt(void);
void timer0_isr(void);
void portb_isr(void);
void ext_isr(void);
void put_hex_nibble(unsigned char x);
void put_hex(unsigned char x);

////////////////////////////////////////////////////////////////////////////////
// MAIN entry point
// - do initial setup
// - ISR bottom-half relays data from the ISR top-half to the PC via serial port
////////////////////////////////////////////////////////////////////////////////
void main(void)
{
  // careful when mucking about with prescaler - clear timer0 & watchdog
  tmr0 = 0;
  clear_wdt();

  // set options
  option_reg = PORTB_PULLUPS_ENABLED | INTEDGE_RISING | TIMER0_INTERNAL | PRESCALER_WDT | PRESCALER(7);

  // configure ports
  set_tris_a(0x1c); // RA0 and RA1 are outputs
  set_tris_b(0x7d); // RB1 and RB7 are outputs

  // enable interrupts
  enable_interrupt(GIE); // global ints on
  enable_interrupt(T0IE); // timer0 overflow int on
  enable_interrupt(INTE); // external interrupt on
  enable_interrupt(RBIE); // portB change interrupt on

  // our main program loops forever looking for bytes from the ISR 
  // to transfer to the PC
  while(1) {
      // does the ISR have anything for us?
    if(fifo_bytes) {
      // disable serial port relaying
      disable_interrupt(RBIE);
      delay_ms(10);

      putchar('X');
      put_hex(fifo[0]);
      putchar(' ');
      put_hex(fifo[1]);
      putchar('\n');
      fifo_bytes = 0;

      // restart serial port relaying
      enable_interrupt(RBIE);
    }
  }
}

////////////////////////////////////////////////////////////////////////////////
// Generic interrupt handler: dispatch interrupts to relevant ISRs
////////////////////////////////////////////////////////////////////////////////
void interrupt( void )
{
  // in each case, handle the interrupt then clear the flag

  // TIMER0 overflow
  if(intcon & TIMER_INTERRUPT) {
    timer0_isr();
    clear_bit(intcon, T0IF);
  }

  // external interrupt on RB0
  if(intcon & EXTINT_INTERRUPT) {
    ext_isr();
    clear_bit(intcon, INTF);
  }

  // portb change interrupt
  if(intcon & PORTB_INTERRUPT) {
    portb_isr();
    clear_bit(intcon, RBIF);
  }
}

////////////////////////////////////////////////////////////////////////////////
// TIMER0 interrupt - used as high precision timer to time events
////////////////////////////////////////////////////////////////////////////////
void timer0_isr(void)
{
  // interrupt occurs when timer trips from 0xff to 0x0
  // increment the high byte
  timer_high++;
}

////////////////////////////////////////////////////////////////////////////////
// PORTB CHANGE interrupt - used to relay RS232 signals between PC and CM12
//
// RF->X10 takes priority, and can lock out the rs232 relay function
////////////////////////////////////////////////////////////////////////////////
void portb_isr(void)
{
  unsigned char tmp;

  // whatever comes in from X10 goes out to PC ie RB4 -> RA0
  // whatever comes in from PC goes out to X10 ie RB5 -> RA1
  tmp = (portb >> 4) & 3;
  porta = (porta & 0xfc) | tmp;
}


////////////////////////////////////////////////////////////////////////////////
// EXT interrupt handler
//
// this handles all communication with the RF module
//
// implemented as a simple 3-state Asynchronous State Machine
// the ASM is triggered on every rising edge from the radio module
//
////////////////////////////////////////////////////////////////////////////////
void ext_isr(void)
{
  unsigned char barf = 0;
#ifdef THIRTY_TWO_BIT_SUPPORT
  unsigned short data_high, data_low;
#else
  unsigned char index  =0;
#endif

  // an EXT int has occured
  switch(rf_state) {

    // waiting for the edge which signals the preamble
    case WAIT_START_BIT:
      rf_state = WAIT_DATA_START;
      break;

      // waiting for the first data edge after the preamble
    case WAIT_DATA_START:
      if((timer_high >= 36) && (timer_high <= 67)) {
        // valid start sequence detected

        bits = 0;
        set_bit(LED);
        set_bit(TEST_POINT);

        rf_state = WAIT_DATA_BIT;
      } else {
        barf = 1;
      }
      break;

      // waiting for a data bit edge
    case WAIT_DATA_BIT:
      clear_bit(TEST_POINT);

#ifndef THIRTY_TWO_BIT_SUPPORT
      index = bits >> 3; // index into RF data array
#endif

      if((timer_high >=3) && (timer_high <= 5)) {
        // store a ZERO
#ifdef THIRTY_TWO_BIT_SUPPORT
        rf_data <<= 1;
#else
        //rf_data[index] <<= 1;
        rf_data[index] = rf_data[index] << 1;
#endif
        bits++;
      } else if((timer_high >=6) && (timer_high <= 11)) {
        // store a ONE
#ifdef THIRTY_TWO_BIT_SUPPORT
        rf_data = (rf_data << 1) | 1;
#else
        //rf_data[index] << = 1;
        //rf_data[index] |= 1;
        rf_data[index] = rf_data[index] << 1;
        rf_data[index] = rf_data[index] | 1;
#endif
        bits++;
      } else {
        barf = 1;
      }

      if(bits == 32) {
        // 32 bits arranged as A/!A/B/!B

#ifdef THIRTY_TWO_BIT_SUPPORT
        data_high = rf_data >> 16;
        data_low = rf_data & 0xffff;

        // check A/!A and B/!B
        data_high &= (data_high >> 8);
        data_low  &= (data_low >> 8);
        data_high &= 0xff;
        data_low  &= 0xff;

        if((data_high == 0) && (data_low == 0)) {
          // data integrity check passed
          fifo[0] = rf_data >> 24;
          fifo[1] = (rf_data >> 8) & 0xff;
          fifo_bytes = 2;
          barf = 1;
        } else {
          // data integrity check failed
          barf = 1;
        }
#else
        // 4 8bit values as rf_data[0..3] = !B/B/!A/A
        // check A/!A and B/!B
        if(!((rf_data[0] & rf_data[1]) | (rf_data[2] & rf_data[3]))) {
          fifo[0] = rf_data[0]; // A
          fifo[1] = rf_data[2]; // B
          fifo_bytes = 2;
        }
        barf = 1;
#endif
      }
      break;
  } // switch

  if(barf) {
    // invalid stuff happened
    set_bit(LED);
    clear_bit(TEST_POINT);
    rf_state = WAIT_START_BIT;
    barf = 0;
  }

  // reset the timer
  timer_high = 0;
  tmr0 = 0;

  return;
}

////////////////////////////////////////////////////////////////////////////////
// serial output a byte as its hexadecimal representation
////////////////////////////////////////////////////////////////////////////////
void put_hex(unsigned char x)
{
  put_hex_nibble(x >> 4);
  put_hex_nibble(x & 0xf);
}

////////////////////////////////////////////////////////////////////////////////
// serial output a nibble as its hexadecimal representation
////////////////////////////////////////////////////////////////////////////////
void put_hex_nibble(unsigned char x)
{
  if(x > 9) {
    x = x + 7;
  }
  x = x + 48;
  putchar(x);
}



http://www.sourceboost.com/home.html

Copyright © 2002-2006 SourceBoost Technologies