Your algorithm works like a charm ! Fantastic code !
I converted it to MikroC with no diffuculty. I had to make some changes to the names of some registers because I use a different PIC, but other than that little or no changes
For those interested, I included the MikroC code below.
KR,
Rudy
Code: Select all
/************************************************************************************
* MAIN PROGRAM for PHILIPS RC5 IR RECEIVER *
************************************************************************************
* Original MikroPascal version : D. Rosseel *
* http://users.edpnet.be/rosseel01/DRO/PIC/index.htm *
* *
* Converted to MikroC by Rudolf Lapie *
* *
* Original version : 15-FEB-2009 Last change : 16-FEB-09 *
* MikroC version : 18-FEB-2009 Last change : 18-FEB-09 *
* *
* For Rc5 receivers with inverse output (= output active low), like the TSOP1736. *
* The output of the Rc5 receiver is to be connected to the PIC's external *
* interrupt pin (PortB.0). *
*
* The timer0 requires a clock (after prescaler) of 62.5 KHz. This means, when the *
* CPU clock is 4 MHz a prescaler value of 16 is required: 4 MHz / 4 / 16 =62.5 KHz *
* The time/timerstep is then 16 microsecs. (1/62500) *
* For other CPU clock speeds the prescaler has to be adjusted accordingly, or *
* if not possible both prescaler and "RC5_TIME_..." constants have to be adapted. *
* e.g. for 8Mhz : use 1:32 as prescaler *
************************************************************************************
* Algorithm : *
* ~~~~~~~~~~~ *
* Everytime the input on pin RC5 changes an interrupt is generated. In the handler *
* the time is measured between 2 subsequent changes. Based on this difference a *
* decision is made on what the previous bit was : a short pulse or a double pulse *
* If the timing is out of range the code is considered invalid and the handler *
* resets itself to the initial state. A state machine is used with 3 states : *
* IDLE, DECODING & COMPLETED *
************************************************************************************
* Hardware config : 18F452 @ 8Mhz + TSOP1736 + LED + LCD *
* -------------------------------------------------------------------------------- *
* LED on PortD.F2 (active when port low) *
* TSOP-1736 : Output pin connected to PORTB.F0 (active when low) *
* LCD : HD44780 on PORTB : bits 1..4 = D4...D7 / 5 = EN / 6 = RS / 7 = RW *
* Note : when using a different Crystal speed, you will need to change prescaler *
* in the main program and potentially the counters in the definition section *
************************************************************************************
*/
// Remove next line for production code !
#define DEBUG
//-----------------------------------------------------------------------------
// Hardware specific settings
//-----------------------------------------------------------------------------
#define LED_On() (LATD.F2=0)
#define LED_Off() (LATD.F2=1)
#define LED_Toggle() (LATD.F2=~LATD.F2)
#define IR_PIN (PORTB.F0)
//-----------------------------------------------------------------------------
// Predefined constants
//------------------------------------------------------------------------------
#define RC5_NR_Bits 14
//In total 14 Rc5_Bits are to be captured (the first "startbit" is already there
//when the first interrupt occurs)
// The format of the received bits is:
// - 2 startbits (both "1")
// - 1 toggle bit (changes every time when a non repeated command is sent)
// - 5 "System" bits, most significant bit first
// - 6 "Command" bits, most significant bit first
// The bits are recognized by the time between 2 "edges" of the RC5 signal:
// - a "long time" means: bit received that differs from the previous one
// - two RC5_times "short" means: bit received that equals the previous one
// the previous bit to start with is always "1"
#define RC5_TIME_BOUNDARY 83 // below 83 = short, above = long (0.75 bittime)
#define RC5_TIME_TOO_SHORT 28 // (0.25 bittime)
#define RC5_TIME_TOO_LONG 140 // (1.25 bittime)
// 1 bit time = 1778 uS
// "short" time = 1/2 bittime = 889 uS, = timer value 55.5 (= 889/16)
// "long" time = 1 bittime = 1778 uS, = timer value 111.12 (=1778/16)
// Note : constant values depend on the speed of your crystal !
//-----------------------------------------------------------------------------
// Global variables
//-----------------------------------------------------------------------------
enum {
RC5_IDLE,
RC5_DECODING,
RC5_COMPLETED
} RC5_state = RC5_IDLE ;
#ifdef DEBUG
unsigned char RC5_times[26] ; // array for the Rc5_Times measured
unsigned char RC5_timesIndex; // index/count for above
#endif
unsigned int RC5_bits ; // will hold the received bits
unsigned char RC5_bitCount ; // nb of bits processed
unsigned char RC5_prevBit ; // the value of the previous bit
unsigned char RC5_shortPulses ; // nr of subsequent short pulses measured
struct
{
toggle : 1 ;
device : 5 ;
command : 6 ;
received : 1 ;
} IR ;
//-----------------------------------------------------------------------------
// Forward Declarations
//-----------------------------------------------------------------------------
void Lcd_Custom_INT (int row, int col, int x, unsigned char len);
//------------------------------------------------------------------------------
// Interrupt handler
//------------------------------------------------------------------------------
void interrupt()
{
unsigned char time, i ;
// RB0 interrupt
if (INTCON.INT0IF)
{
LED_Toggle () ;
INTCON2.INTEDG0 = 1-INTCON2.INTEDG0 ; // toggle the INTEDG (interrupt edge)
INTCON.INT0IF = 0;
time = TMR0L; // get time
TMR0L = 0; // restart timer
switch (RC5_state)
{
////////// IDLE STATE. NOTHING RECEIVED YET ////////////////////////
case RC5_IDLE :
#ifdef DEBUG
RC5_timesIndex = 0;
#endif
RC5_bits = 1; // already 1 bit (with value "1") received
RC5_bitCount = 1;
RC5_shortPulses = 0;
RC5_prevBit = 1;
RC5_state = RC5_DECODING;
INTCON.T0IF = 0; // reset Timer0 overflow flag
INTCON.T0IE = 1; // enable timer0 interrupt
break ;
////////// CHANGES DETECTED. WE START PROCESSING BITS////////////////
case RC5_DECODING : // Execution time is about 52 uS at 4 Mhz CPU clock
#ifdef DEBUG
RC5_times[RC5_timesIndex] = time; // get timer value
RC5_timesIndex ++ ;
#endif
if ((time > RC5_TIME_TOO_SHORT) && (time < RC5_TIME_TOO_LONG))
{
if (time < RC5_TIME_BOUNDARY) // short pulse detected
{
RC5_shortPulses ++ ;
if (RC5_shortPulses == 2)
{
RC5_bits <<= 1; // store the bit received
RC5_bits |= RC5_PrevBit;
RC5_bitCount ++ ;
RC5_shortPulses = 0;
}
} else { // long pulse detected
RC5_prevBit = 1- RC5_prevBit; // toggle the bit
RC5_bits <<= 1; // store the bit received
RC5_Bits |= RC5_PrevBit;
RC5_bitCount ++ ;
RC5_shortPulses = 0;
}
if (RC5_bitCount == RC5_NR_Bits)// all bits are received
{
RC5_state = RC5_COMPLETED;
INTCON2.INTEDG0 = 0; // next RC5 interrupt is negative going
INTCON.T0IE = 0; // disable timer0 interrupt
}
} else { // something is wrong about timing
RC5_state = RC5_IDLE; // start all over
INTCON2.INTEDG0 = 0; // next RC5 interrupt is negative going
INTCON.T0IE = 0; // disable timer0 interrupt
}
break ;
/////////// ALL BITS RECEIVED CORRECTLY. Store the data /////////////
case RC5_COMPLETED :
INTCON2.INTEDG0 = 0; // next RC5 interrupt is negative going
IR.toggle = ((RC5_bits>>11) & 0x01) ? 0 : 1 ;
IR.device = (RC5_bits>>6) & 0b00011111 ;
IR.command= (RC5_bits & 0b00111111) ;
IR.received = 1 ;
RC5_state = RC5_IDLE ;
#ifdef DEBUG
for (i= 0; i < sizeof(RC5_times); i++) RC5_times[i] = 0;
#endif
break ;
} // end switch
} // end INTCON.INT0IF
// Timer 0 interrupt
if (INTCON.T0IF) // timer 0 overflow, timeout
{
if (RC5_state == RC5_DECODING) // something is wrong
{
RC5_state = RC5_IDLE; // start all over
INTCON2.INTEDG0 = 0; // next RC5 interrupt is negative going
INTCON.T0IE = 0; // disable timer0 interrupt
}
}
INTCON.T0IF = 0; // always reset timer0 overflow flag
} // end interrupt routine
//-----------------------------------------------------------------------------
// MAIN PROGRAM
//------------------------------------------------------------------------------
void main ()
{
unsigned int dev, cmd ;
unsigned int repeat ;
unsigned char last_toggle ;
int i ;
// Initialize state machine
RC5_state = RC5_IDLE ;
#ifdef DEBUG
for (i=0; i< sizeof (RC5_times); i ++) RC5_times[i] = 0 ;
#endif
// INITIALISE all registers
ADCON1 = 0x00 ; // No ADC convertors
// CMCON = 0x07 ; // turn off comparators - Not available in 18F452
LATB = 0;
TRISB = 0b00000001; // set PORTB D7-D1 = LCD / D0 = IR receiver
LATC = 0 ;
TRISC = 0b11111111 ; // set PORTC as input except for bits 0 : LED
LATD = 0 ;
TRISD = 0b1111000 ; // set PORTD as D0-D3 output , D4-D7 = input
// Setup Timer 0, but don't enable it yet --> Settings depend on Crystal
// Initialise TIMER0 : Prescaler= 1:32 - 1 tick = 16 usec / 62.5 Khz
T0CON = 0b11000100; // enable timer, 8 bit mode, 1:32 prescaler
TMR0L = 0; // preset for timer register
INTCON.PEIE = 1;
INTCON.T0IF = 0;
INTCON.T0IE = 0; // disable timer 0 interrupt for now
// Enable external interrupt
INTCON2.INTEDG0 = 0; // start with downgoing edge
INTCON.INT0IF = 0; // Clear interrupt
INTCON.INT0IE = 1; // Enable RB0 interrupt
INTCON.GIE = 1; // bit7 global interrupt enable
// Initialise LCD
Lcd_Custom_Config(&PORTB, 4,3,2,1,&PORTB,6,7,5); // Initialize LCD on PORTB
Lcd_Custom_Cmd(Lcd_CURSOR_OFF); // Turn off cursor
Lcd_Custom_Out (1, 1, "Philips RC5") ;
// Main program loop
IR.received = 0 ;
last_toggle = 2 ; // Impossible
while (1)
{
if (IR.received)
{
dev = IR.device ;
cmd = IR.command;
IR.received = 0 ; // Processed the command
if (last_toggle == IR.toggle)
{
repeat ++ ;
} else {
repeat = 0 ;
last_toggle = IR.toggle;
}
LCD_Custom_Out (2, 1, "DEV=") ;
LCD_Custom_INT (2, 5, dev, 3) ;
LCD_Custom_Out (2, 9, "CMD=") ;
LCD_Custom_INT (2, 13, cmd, 3) ;
LCD_Custom_Chr (2, 17, IR.toggle?'T':' ') ;
LCD_Custom_INT (2, 19, repeat, 3) ;
}
#if 0
else {
if (RC5_timesIndex)
{
INTCON.GIE = 0; // bit7 global interrupt disable
LCD_Custom_INT (2, 20, RC5_bitcount, 2) ;
for (i=0 ; i<RC5_timesIndex; i++)
{
LCD_Custom_Out (2, 1, "IDX=") ;
LCD_Custom_INT (2, 5, i, 3) ;
LCD_Custom_Out (2, 9, "TMR=") ;
LCD_Custom_INT (2, 13, RC5_times[i], 3) ;
Delay_ms(2000) ;
}
LCD_Custom_Out (2, 1, " ") ;
RC5_timesIndex = 0 ;
INTCON.GIE = 1; // bit7 global interrupt enable
}
}
#endif
Delay_ms (250) ;
}
} // end main
//------------------------------------------------------------------------------
// AUXILIARY ROUTINES
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
void Lcd_Custom_INT (int row, int col, int x, unsigned char size)
{
static char txt[7] ;
int i ;
IntToStr (x, txt) ;
for (i=0; i<7; i++) if (txt[i] == ' ') txt[i] = '0' ;
Lcd_Custom_Out(row, col, txt+(6-size)) ;
}
////////////////////////////////////////////////////////////////////////////////
// THAT'S ALL, FOLKS
////////////////////////////////////////////////////////////////////////////////