Continue to Site

Welcome to EDAboard.com

Welcome to our site! EDAboard.com is an international Electronics Discussion Forum focused on EDA software, circuits, schematics, books, theory, papers, asic, pld, 8051, DSP, Network, RF, Analog Design, PCB, Service Manuals... and a whole lot more! To participate you need to register. Registration is free. Click here to register now.

Intrrupt driven RC5 infra red decoder for Pic16f

Status
Not open for further replies.

btbass

Advanced Member level 5
Advanced Member level 5
Joined
Jul 20, 2001
Messages
1,896
Helped
438
Reputation
880
Reaction score
288
Trophy points
1,363
Location
Oberon
Activity points
12,887
It takes about 30mS to decode an RC5 transmission, which is a comparatively long time in terms of processor time.
Attached is a RC5 decoder project that is interrupt driven. It is written for a Pic16F690 although it should work with most pic16f variants.
It uses the external interrupt pin for the IR code stream, which on the 690 is RA2.
The internal oscillator is used and it is set to 8MHz.
It also uses timer 0. So timer 0 can't be used for anything else.

It only uses the negative edge interrupt so that minimum processor time is taken when decoding an RC5 data stream. It decodes the RC5 data using pulse lengths. The transmitted pulse time is calibrated on every transmission received, so should work with most RC5 handsets.
A lot of cheap handsets use ceramic resonators that are temperature sensitive, so the pulse times can vary widley from the RC5 spec.
 

Attachments

  • rc5_interrupt.zip
    17.5 KB · Views: 242

I forgot to include this basic information:

;------ RC5 code is 14 bits each of 1.8mS duration with carrier frequency of 36KHz.
;------ A '0' is a mid pulse transition from high to low.
;------ A '1' is a mid pulse transition from low to high.
;------ The first 2 bits are always 1 to calibrate timing.
;------ The next bit is the toggle bit, indicating a new keypress.
;------ Then 5 bits of address and 6 bits of command code.
 

One comment about the first version of the decoder is that it makes 3 nested function calls and uses up stack space.
Can't argue with that!

So, I have moved all the functions inline so that the interrupt calls no other functions.
This slightly increases the code size but improves the speed and saves the stack space used.
 

Attachments

  • rc5_interrupt_V2.zip
    17.9 KB · Views: 219

thanks again mate , but are you familiar wit the use of interrupt on chage feature on the same pic16f690????
 

hi btpass i wrote this code to run two stepper motors, with control input coming from a remote transmitter which transmits 5V pulses to the interrupt on change pins RA0, RB4, BR5, and RB6 but i am still not getting any response from the pic can u look at it and see if u can please spot the error. thanks in advance


#include <htc.h>
#include <pic.h>
#define stepcnt 300 //max number of steps
#define _XTAL_FREQ 20000000


void Run_Motor1_cw(void);
void Run_Motor2_cw(void);
void Run_Motor1_ccw(void);
void Run_Motor2_ccw(void);
void interrupt a(void);

char Motor_Select(char);
enum MotorID {Motor1, Motor2};
enum Bool {False, True};
enum Direction {Clockwise, Anticlockwise};
int iSpeed=0;

struct Motor
{
enum MotorID MotorName;
enum Direction ActiveDirection;
enum Bool Active;
int iMotorSpeed;
};

void main(void)
{

struct Motor PrimaryMotor, SecondaryMotor;
PrimaryMotor.MotorName=Motor1;
SecondaryMotor.MotorName=Motor2;
PrimaryMotor.ActiveDirection=Clockwise;
SecondaryMotor.ActiveDirection=Clockwise;
PrimaryMotor.Active=True;
SecondaryMotor.Active=False;

PrimaryMotor.MotorName=Motor1;
PrimaryMotor.MotorName=Motor2;

if(PrimaryMotor.Active ==True)
{

if(PrimaryMotor.ActiveDirection==Clockwise)
Run_Motor1_cw();
else
Run_Motor1_ccw();
}

if(SecondaryMotor.Active ==True)
{

if(SecondaryMotor.ActiveDirection==Clockwise)
Run_Motor2_cw();
else
Run_Motor2_ccw();
}

ANSEL = 0x00;
PORTA = 0x00; //SET PORT A AS INPUT
TRISA = 1; //INITIALISE PORTA AS 0

PORTB = 0xFF; //SET PORT B AS INPUT PORT
TRISB = 1; //INITIALISE PORT B AS 0

PORTC = 0x00;
TRISC = 0; //SET PORT C AS AN OUTPUT PORT

INTE = 1;
INTF = 0;
INTEDG = 1;
GIE = 1;
RABIE = 1;//SET INTERRUPT
IOCA0 = 1;
IOCB4 = 1;
IOCB5 = 1;
IOCB6 = 1;
}



void Run_Motor1_cw(void) //function to run motor 1 at constant speed
{
int b;
PORTC = 0x32; //reset motor phases to initial position 0101 and light up led to indicate motor 1 is active and wait for the step signal
__delay_ms(10);

for(b=0;b<=stepcnt;b++) //loop to step 100 times running motor1
{
PORTC = 0xB2;
__delay_ms(100);
PORTC= 0xB3;
__delay_ms(100);
}
}

void Run_Motor2_cw(void) //function to run motor 2 at constant speed
{
int d;
PORTC = 0x52; //turn on led to indicate motor 2 is about to start running and wait for the step signal
__delay_ms(10);

for(d=0;d<=stepcnt;d++) //loop to run motor 2 for 100 steps
{
PORTC = 0xD2;
__delay_ms(100);
PORTC = 0xD6;
__delay_ms(100);
}
}

void Run_Motor1_ccw(void) //function to run motor 1 counter clockwise
{
int n;
PORTC = 0x30; //initialise motor 1 and turn on led for motor 1
__delay_ms(10);
for(n=0;n<=stepcnt;n++) // loop to run motor one at contstan speed counter clockwise
{
PORTC = 0xB0;
__delay_ms(100);
PORTC = 0xB1;
__delay_ms(100);
}
}

void Run_Motor2_ccw(void)
{
int n;
PORTC = 0x50;
__delay_ms(10);
for(n=0;n<=stepcnt;n++)
{
PORTC = 0xD0;
__delay_ms(100);
PORTC = 0xD4;
__delay_ms(100);
}
}

void interrupt a(void)
{
if (RBIF = 1)

if(IOCA0 =~ 1)Run_Motor1_cw();
if(IOCB4 =~ 1)Run_Motor2_cw();
if(IOCB4 =~ 1)Run_Motor1_ccw();
if(IOCB6 =~ 1)Run_Motor2_ccw();
PORTA = 0x02; //SWITCH ON AN LED
__delay_ms(450); //DELAY (AROUND 1SEC)
PORTA = 0x00; //WITCH LED OFF
//if(INTF = 1)

INTF = 0;
}


hi
 

This only sets RB0 as an input?

Code:
  PORTB = 0xFF;     //SET PORT B AS INPUT PORT
  TRISB = 1;        //INITIALISE PORT B AS 0

Not sure about this?

Code:
void interrupt a(void)
 {
 if (RBIF = 1) ??? RBIF == 1


 if(IOCA0 =~ 1)Run_Motor1_cw();
 if(IOCB4 =~ 1)Run_Motor2_cw();
 if(IOCB4 =~ 1)Run_Motor1_ccw();
 if(IOCB6 =~ 1)Run_Motor2_ccw();

I assume you are trying to detect a pin change?
Would something like this work?

Code:
void interrupt a(void)
 {
 static unsigned char this_reading, last_reading = 0;

 if(RABIF == 1)
  {
  this_reading = PORTB; /* reading the port clears the mismatch */

  if((last_reading & 0x10) != (this_reading & 0x10)){  /* Has RB4 changed? */
    Run_Motor2_cw();
    }
  else if((last_reading & 0x20) != (this_reading & 0x20)){  /* Has RB5 changed? */
    Run_Motor1_ccw();
    }
  else if((last_reading & 0x40) != (this_reading & 0x40)){  /* Has RB6 changed? */
    Run_Motor2_ccw();
    }

  last_reading = this_reading; 
  
  PORTA = 0x02; //SWITCH ON AN LED
  __delay_ms(450); //DELAY (AROUND 1SEC)
  PORTA = 0x00; //WITCH LED OFF
  

  RABIF = 0;
  }
 }

You also need a loop somewhere, as at the moment the code comes to an end and then the micro will reset and start again?

It would be better to not call the run motor functions from within the interrupt, but to just set flags and call the run motor routines from within the loop.
 
Last edited:

thanks btpass here's the code again i am calling the motor functions using the struct names from the main am gonna try it later on today, but i want to put in another interrupt at the RA2 interrupt pin this time. i was intending to watch a signal at the port, the signal will be falling at the pin but it should will not stay low beyond 1 clock cycle of 20MHz , if it stays low beyond 1 clock cycle there should be motor change, can u give please help me with this piece of code thanks alot.


void interrupt a(void) //interrrupt function
{
static unsigned char this_reading, last_reading = 0;
struct Motor PrimaryMotor, SecondaryMotor;
if(RABIF == 1)
{
this_reading = PORTB; /* reading the port clears the mismatch */

if((last_reading & 0x10) != (this_reading & 0x10)){ /* Has RB4 changed? */
PrimaryMotor.Active=True;
SecondaryMotor.Active =False;
}
else if((last_reading & 0x20) != (this_reading & 0x20)){ /* Has RB5 changed? */
PrimaryMotor.Active=False;
SecondaryMotor.Active=True;
}
else if((last_reading & 0x40) != (this_reading & 0x40)){ /* Has RB6 changed? */
if (PrimaryMotor.Active==True)
PrimaryMotor.ActiveDirection=Clockwise;
else
PrimaryMotor.ActiveDirection=Anticlockwise;
}
else if ((last_reading & 0x30) != (this_reading & 0x30)){ /* Has RB7 changed? */
if(SecondaryMotor.Active==True)
SecondaryMotor.ActiveDirection=Clockwise;
else
SecondaryMotor.ActiveDirection=Anticlockwise;
}
RABIF = 0;
}

if(INTF == 1)
{
if(PORTA.F2 == 0)????


}
 

You know it must be low to generate the interrupt,
can't you just wait for the time and then check it again?

Code:
unsigned char delay;

if(INTF == 1)
  {
  delay = 2;  /* delay time */

  while(delay--){
    ;
    }

  if(PORTA.F2 == 0){
    motor_change = True;
    }
  else{
    motor_change = False;
   }

  INTF = 0;
 }
 

Hello btbass,

Do you confirm the code you posted in Post #5 ( rc5_interrupt_V2.zip‎ ) is for PIC C18 compiler ?

What PIC familly did you use with it ?

Many thanks,
 

The code was written for the Pic16f690 and I used the Hi-Tech compiler.
As it is written in C it should compile ok with the C18 compiler.
To port the code to a different pic variant, you would only have to modify the external interrupt pin used for the IR input if it was different and maybe change the timer prescale value to suit the osc frequency.
 
Would you please explain me further, how you set up the timer for the RC5 application ?

I've understood the code checks the 1st high edge with an interrupt but i do not understand well what comes next with the timer.
Do you use CCP or PWM then ?

Sorry for newbie's questions . :roll:

Best regards,
 
The timer is used to time the bit lengths and to detect the end of the transmission. The absolute time is not critical as the decoder measures and calibrates the bit lengths on every transmission.

The first two bits of a standard RC5 transmission are always one, the first two bits are used for the calibration of the bit times. The timer used is 8 bit timer 0.

The code has these comments:

/* Timer should have an overflow period of greater than 5mS and less than 15mS */
// Overflow period = (1 / (oscfreq / 4)) * 256 * prescaler

//OPTION = 0x04; /* Pull ups on, Timer 1:32 prescaler for 4MHz oscillator */

OPTION = 0x05; /* Pull ups on, Timer 1:64 prescaler for 8MHz oscillator */

The easiest thing to do is try it out and adjust the prescaler till it works consistantly.
 

I'm studying your code but i do not understand well some cases into the interrupt loop :

Here is what i understood so far :

START_S :
inits interrupt flags and directly points to the next step CALIBRATE_S

CALIBRATE_S :
loads the timer 0 value, this means the first edge happened
What is the purpose of tolerance ?

DATA_S:
I do not understand what happens here. :roll:

Do you have a flow chart that explains all steps you perform in order to decode the RC5 frame ?
This could help me a lot.

Many thanks,
 

;------ RC5 code is 14 bits each of 1.8mS duration with carrier frequency of 36KHz.
;------ A '0' is a mid pulse transition from high to low.
;------ A '1' is a mid pulse transition from low to high.
;------ The first 2 bits are always 1.
;------ The next bit is the toggle bit, indicating a new keypress.
;------ Then 5 bits of address and 6 bits of command code.

It takes about 30mS to decode an RC5 transmission, which is a comparatively long time in terms of processor time.
So the decoder uses the interrupt to minimise the processor time.

The IR receiver inverts the RC5 data stream, so the positive edge becomes a negative edge.
The decoder only uses the negative edge interrupt so that minimum processor time is taken when decoding an RC5 data stream. It decodes the RC5 data using bit lengths.

A lot of IR handsets use ceramic resonators that are temperature sensitive and drift over time, so the bit times can vary widley from the RC5 spec. To overcome this, the transmitted bit time is calibrated on every transmission received.

The first two bits of a standard RC5 transmission are always one. Knowing this, the timer is used to measure the bit length. As the transmission will suffer some jitter and not all transmitted bits will have the exact same timings, a ~12% tolerence is added to the bit time. (a divide by 8 as this compiles as a right shift sequence).

If you analyse a RC5 data transmission, using only the negative edge, you will see that there are three possible pulse lengths, (the time between two consecutive negative edges).
They are the calibrated pulse length, a pulse 1.5 times the calibarted pulse length and a pulse two times the calibrated pulse length.

It turns out that to know what each pulse length means, you have to know what the last received bit was.
As the data stream always starts with two one's, we can keep track of the last received bit and decode the data.

Timer_0 is used to count the time between negative edges. The timer count is stored and the timer reset to 0 on each negative edge.

The pulse length is then determined from the timer count, and using the last bit received, the data is decoded and added to the received data variable and the last bit received updated.

It also turns out that if the RC5 data stream ends with a '0', there is no last negative edge.

Timer_0 is also used to detect the end of the data stream. The timer prescaler is set so that the timer overflow interrupt occurs if no negative edge has been received to reset the timer. (Typically 5mS to 15mS).

On timer overflow interrupt, the bit count is then checked. If it is the correct number of bits, the data is ok, if it is one short, a '0' is added to the end of the data stream.

The 8-bit pic's have a limited stack space, so no function calls are made and all the code is contained in the interrupt routine to save stack space.

This does make it a litle harder to understand.
 
Last edited:

whats wrong with me
Code:
/*** HISTORY OF CHANGE ********************************************************* 
  * 
  *   07.10.2011 Initial Version 
  * 
  * 
  * 
  * 
  ******************************************************************************/ 
 #include <RC5 IR Remote.h> 
 #include "types.h" 

 /* --- RC5 driver configuration --- */ 
 #define RC5_DATA_PIN          PIN_A4        /* IR sensor (SFH5110-36) connected to RB1 */ 
 #define RC5_TICKS_PER_MS      (1000/32)      /* timer increments every 31.25 us */ 
                                             /* i.e. around 1000 ticks per millisecond */ 
 #define RC5_GetTimer()        get_timer0()  /* timer0 shall be used for RC5 decoding */ 
                           

 /* --- macros to switch on/off LED --- */ 
 #define Led_On()        output_high(PIN_A0) 
 #define Led_Off()       output_low(PIN_A0) 
   
 /* RC5 driver include */ 
 #include <RC5_driver.h> 

 /******************* 
 //PORT C Register = 0x07 
 //PORT A Register = 0x05 
 *********************/ 
 #BIT RL1 = 0x07.0 
 #BIT RL2 = 0x07.1 
 #BIT RL3 = 0x07.2 
 #BIT RL4 = 0x07.3 
 #BIT RL5 = 0x07.4 
 #BIT RL6 = 0x07.5 
 #BIT RL7 = 0x05.1 
 #BIT RL8 = 0x05.2 

 /****************************************************************************** 
  Key Codes 
  *****************************************************************************/ 
 #define RC5_NUMERIC_KEY_0 0x00 /* NUMERIC KEY 0 */ 
 #define RC5_NUMERIC_KEY_1 0x01 /* NUMERIC KEY 1 */ 
 #define RC5_NUMERIC_KEY_2 0x02 /* NUMERIC KEY 2 */ 
 #define RC5_NUMERIC_KEY_3 0x03 /* NUMERIC KEY 3 */ 
 #define RC5_NUMERIC_KEY_4 0x04 /* NUMERIC KEY 4 */ 
 #define RC5_NUMERIC_KEY_5 0x05 /* NUMERIC KEY 5 */ 
 #define RC5_NUMERIC_KEY_6 0x06 /* NUMERIC KEY 6 */ 
 #define RC5_NUMERIC_KEY_7 0x07 /* NUMERIC KEY 7 */ 
 #define RC5_NUMERIC_KEY_8 0x08 /* NUMERIC KEY 8 */ 
 #define RC5_NUMERIC_KEY_9 0x09 /* NUMERIC KEY 9 */ 

 /*****************************************************************************/ 
 /* Interrupt_RA                                                              */ 
 /*                                                                           */ 
 /* Port A change interrupt service routine.                                  */ 
 /* Used to decode RC5 IR signal.                                             */ 
 /*****************************************************************************/ 
 #INT_RA 
 void Interrupt_RA(void) 
 { 
    Led_On(); 
    RC5_InterruptHandler(); 
    Led_Off(); 
     
    clear_interrupt(INT_RA4); 
 } 

 /*****************************************************************************/ 
 /* main                                                                      */ 
 /*                                                                           */ 
 /* Configures timer0 as time base for RC5 decoding and enables port A        */ 
 /* interrupt on change.                                                      */ 
 /* Main loop checks if a RC5 code has been received and processes the code   */ 
 /* in the switch-case statement                                              */ 
 /*****************************************************************************/ 
 /* NOTE: Currently it only works if PIC is reset after programming? */ 
  void main() 
 { 
    unsigned int16 rc5code; 
    unsigned int rc5cmd; 

    setup_comparator(NC_NC);      // All Ports Digital 

     /* FOSC/4 is timer source */ 
     
     /* Original Configuration: 
     /* FOSC = 20MHz => 5MHz, i.e. timer increment every t = 1/(FOSC/4) = 200ns */ 
     /* with prescaler of 128 timer will increment every 25.6us */ 
     
     // 16F676 Running at Internal 4 Mhz Configuration: 
     // Timer0 increments every 1 us (4 Mhz/4 = 1 Mhz) 
     setup_timer_0(RTCC_INTERNAL | RTCC_DIV_32); 
     
     
     // TRIS Ports set by #use fixed_io in RC5 IR Remote.h 
     /* read port A to clear mismatch condition */ 
     input_a();                      
     
     /* configure port A interrupt on change */ 
     clear_interrupt(INT_RA);        /* clear port A interrupt flag */ 
     enable_interrupts(INT_RA);     /* enable RA4 interrupt on change */ 
       
     /* global interrupt enable */ 
     enable_interrupts(GLOBAL); 
     
     // Main Loop 
     while (1) 
     { 
       /* check if new data is available */ 
         if (RC5_CodeReady()) 
         { 
             /* get code */ 
             rc5code = RC5_GetCode(); 
             
             // Extract command from RC5 code 
             rc5cmd = RC5_GetCmd(rc5code); 
             
            switch (rc5cmd) 
            { 
                case RC5_NUMERIC_KEY_0: 
                     RL1 = ~RL1; 
                     break; 
                   
                  case RC5_NUMERIC_KEY_1: 
                     RL2 = ~RL2; 
                     break; 
                   
                  case RC5_NUMERIC_KEY_2: 
                     RL3 = ~RL3; 
                     break; 
                   
                  case RC5_NUMERIC_KEY_3: 
                     RL4 = ~RL4; 
                     break; 
                   
                  case RC5_NUMERIC_KEY_4: 
                     RL5 = ~RL5; 
                     break; 
                     
                  case RC5_NUMERIC_KEY_5: 
                     RL6 = ~RL6; 
                     break; 
                     
                  case RC5_NUMERIC_KEY_6: 
                     RL7 = ~RL7; 
                     break; 
                     
                  case RC5_NUMERIC_KEY_7: 
                     RL8 = ~RL8; 
                     break; 
                     
                  case RC5_NUMERIC_KEY_8: 
                     RL1 = 1; 
                     RL2 = 1; 
                     RL3 = 1; 
                     RL4 = 1; 
                     RL5 = 1; 
                     RL6 = 1; 
                     RL7 = 1; 
                     RL8 = 1; 
                     break; 
                       
                  case RC5_NUMERIC_KEY_9: 
                     RL1 = 0; 
                     RL2 = 0; 
                     RL3 = 0; 
                     RL4 = 0; 
                     RL5 = 0; 
                     RL6 = 0; 
                     RL7 = 0; 
                     RL8 = 0; 
                     break; 
                     
          }    
         } 
         
         /* this function increments the RC5 timeout timer */ 
         /* NOTE: Decoding will also work without calling this function, but */ 
         /*       it could happen that RC5 codes are sometimes not getting */ 
         /*       recognized because of decoding state machine stucks due */ 
         /*       to erroneous RC5 signal. */ 
         RC5_TimeoutIncrement();    
    } 
}
 

Status
Not open for further replies.

Similar threads

Part and Inventory Search

Welcome to EDABoard.com

Sponsor

Back
Top