////////////////////////////////////////////////////////////////////////////////
// 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);
}
Copyright © 2002-2006 SourceBoost Technologies