This article shows how to decode IR remote controls which use Philips RC-5 protocol using PIC16F887 microcontroller and CCS PIC C compiler where the results are displayed on 1602 LCD screen.
The RC5 has 14 bits per 1 code transmission, the 14 bits can be divided into 4 parts:
The first 2 bits are start bits and they are always logic 1.
The third bit called toggle bit, it can be logic 1 or logic 0.
The next 5 bits are address bits, each device type has its address number for example TV address number is 0, CD player address = 20 …………
And the last 6 bits are command bits, each button has its command number.
For the same device for example TV all the remote control buttons has the same address but each button has its command.
The toggle bit changes whenever a button is pressed.
The RC5 protocol uses Manchester coding, a logic 0 is represented by a logic high in the first half and a logic low in the second half, whereas a logic 0 is represented by a logic low in the first half and a logic high in the second half. The length of each half is 889µs which means the length of 1 bit is 1778µs.
Hardware required:
- PIC16F887 microcontroller
- RC5 IR remote control
- 16×2 LCD screen
- IR receiver
- 47µF capacitor
- 0.1µF ceramic capacitor (optional)
- 10K ohm variable resistor
- 10K ohm resistor
- 5V Power source
- Protoboard
- Jumper wires
RC5 Decoder with PIC16F887 circuit:
PIC16F887 uses its internal oscillator which is configured in the C code.
As shown in the circuit diagram, the output of the IR receiver is connected to external interrupt pin which is RB0. A 10K ohm resistor is connected between the IR receiver output and +5V in order to minimize noise which comes from it.
RC-5 Decoder with PIC16F887 CCS C code:
Before writing the C code of the decoder, I drew a simple state machine of the RC5 protocol which helped me a lot in the code. The state machine is shown below.
I made the code as simple and small as I can.
Where:
SP : Short Pulse (About 889µs)
LP : Long Pulse (About 1778µs)
SS: Short Space (About 889µs)
LS : Long Space (About 1778µs)
The resolution of the code is good as we’re working with interrupts.
Rest of code description is below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 | //////////////////////////////////////////////////////////////////////////////////////////////// // // // RC5 IR decoder with PIC16F887 CCS C code. // // The message of the RC5 protocol is 14-bit long, 2 start bits (always 1), toggle bit, // // address (5 bits) and command (6 bits). // // The length of 1 bit is 1778µs which can be divided into two parts of 889µs. A logic 1 // // is represented by 889µs low and 889µs high. A logic zero is represented by 889µs high // // and 889µs low. // // The RC5 protocol has 4 states: start1, mid1, start0 and mid0. // // The IR receiver output is logic high at idle state and when it receives a // // burst it changes the output to logic low. // // The output of the IR receiver is connected to the external interrupt pin (RB0) // // and every change in the pin status generates an interrupt and Timer1 starts calculating, // // Timer1 value will be used in the next interrupt, this means Timer1 calculates the time // // between two interrupts which is pulse time or space time. // // Timer1 time step is 1µs (Timer1 increments every 1µs). If you use mcu frequency other // // than 8MHz, make sure to keep Timer1 time step to 1µs, otherwise time intervals (short_ // // time, med_time and long_time) have to be changed. // // After every interrupt the edge of the external interrupt is changed. // // Also, Timer1 interrupt is used for time out (very long pulse or very long space). This // // interrupt resets the decoding process. // // The decoding results are displayed on 1602 LCD screen connected to PORTD. // // Internal oscillator used @ 8MHz // // // //////////////////////////////////////////////////////////////////////////////////////////////// //LCD module connections #define LCD_RS_PIN PIN_D0 #define LCD_RW_PIN PIN_D1 #define LCD_ENABLE_PIN PIN_D2 #define LCD_DATA4 PIN_D3 #define LCD_DATA5 PIN_D4 #define LCD_DATA6 PIN_D5 #define LCD_DATA7 PIN_D6 //End LCD module connections #define short_time 700 // Used as a minimum time for short pulse or short space (in µs) #define med_time 1200 // Used as a maximum time for short pulse or short space (in µs) #define long_time 2000 // Used as a maximum time for long pulse or long space (in µs) #include <16F887.h> #fuses NOMCLR NOBROWNOUT NOLVP INTRC_IO #use delay(clock = 8MHz) #include <lcd.c> #use fast_io(B) short rc5_ok = 0, toggle_bit; unsigned int8 rc5_state = 0, j, address, command; unsigned int16 rc5_code; #INT_EXT // External interrupt void ext_isr(void){ unsigned int16 time; if(rc5_state != 0){ time = get_timer1(); // Store Timer1 value set_timer1(0); // Reset Timer1 } switch(rc5_state){ case 0 : // Start receiving IR data (initially we're at the beginning of mid1) setup_timer_1( T1_INTERNAL | T1_DIV_BY_2 ); // Enable Timer1 module with internal clock source and prescaler = 2 set_timer1(0); // Reset Timer1 value rc5_state = 1; // Next state: end of mid1 j = 0; ext_int_edge( L_TO_H ); // Toggle external interrupt edge return; case 1 : // End of mid1 ==> check if we're at the beginning of start1 or mid0 if((time > long_time) || (time < short_time)){ // Invalid interval ==> stop decoding and reset rc5_state = 0; // Reset decoding process setup_timer_1(T1_DISABLED); ext_int_edge( H_TO_L ); // Toggle external interrupt edge return; } bit_set(rc5_code, 13 - j); j++; if(j > 13){ // If all bits are received rc5_ok = 1; // Decoding process is OK disable_interrupts(INT_EXT); // Disable the external interrupt return; } if(time > med_time){ // We're at the beginning of mid0 rc5_state = 2; // Next state: end of mid0 if(j == 13){ // If we're at the LSB bit rc5_ok = 1; // Decoding process is OK bit_clear(rc5_code, 0); // Clear the LSB bit disable_interrupts(INT_EXT); // Disable the external interrupt return; } } else // We're at the beginning of start1 rc5_state = 3; // Next state: end of start1 ext_int_edge( H_TO_L ); // Toggle external interrupt edge return; case 2 : // End of mid0 ==> check if we're at the beginning of start0 or mid1 if((time > long_time) || (time < short_time)){ rc5_state = 0; // Reset decoding process setup_timer_1( T1_DISABLED); // Disable Timer1 module return; } bit_clear(rc5_code, 13 - j); j++; if(time > med_time) // We're at the beginning of mid1 rc5_state = 1; // Next state: end of mid1 else // We're at the beginning of start0 rc5_state = 4; // Next state: end of start0 ext_int_edge( L_TO_H ); // Toggle external interrupt edge return; case 3 : // End of start1 ==> check if we're at the beginning of mid1 if((time > med_time) || (time < short_time)){ // Time interval invalid ==> stop decoding setup_timer_1(T1_DISABLED); // Disable Timer1 module rc5_state = 0; return; } else // We're at the beginning of mid1 rc5_state = 1; // Next state: end of mid1 ext_int_edge( L_TO_H ); return; case 4 : // End of start0 ==> check if we're at the beginning of mid0 if((time > med_time) || (time < short_time)){ // Time interval invalid ==> stop decoding setup_timer_1(T1_DISABLED); // Disable Timer1 module rc5_state = 0; // Reset decoding process ext_int_edge( H_TO_L ); // Toggle external interrupt edge return; } else // We're at the beginning of mid0 rc5_state = 2; // Next state: end of mid0 ext_int_edge( H_TO_L ); // Toggle external interrupt edge if(j == 13){ // If we're at the LSB bit rc5_ok = 1; // Decoding process is OK bit_clear(rc5_code, 0); // Clear the LSB bit disable_interrupts(INT_EXT); // Disable the external interrupt } } } #INT_TIMER1 // Timer1 interrupt (used for time out) void timer1_isr(void){ rc5_state = 0; // Reset decoding process ext_int_edge( H_TO_L ); // External interrupt edge from high to low setup_timer_1(T1_DISABLED); // Disable Timer1 module clear_interrupt(INT_TIMER1); // Clear Timer1 interrupt flag bit } void main(){ setup_oscillator(OSC_8MHZ); // Set internal oscillator to 8MHz lcd_init(); // Initialize LCD module lcd_putc('\f'); // LCD clear enable_interrupts(GLOBAL); // Enable global interrupts enable_interrupts(INT_EXT_H2L); // Enable external interrupt clear_interrupt(INT_TIMER1); // Clear Timer1 interrupt flag bit enable_interrupts(INT_TIMER1); // Enable Timer1 interrupt lcd_gotoxy(1, 1); lcd_putc("ADS:0x00 TGL: 0"); lcd_gotoxy(1, 2); lcd_putc("CMD:0x00"); while(TRUE){ if(rc5_ok){ // If the mcu receives RC5 message with successful rc5_ok = 0; // Reset decoding process rc5_state = 0; setup_timer_1(T1_DISABLED); // Disable Timer1 module toggle_bit = bit_test(rc5_code, 11); // Toggle bit is bit number 11 address = (rc5_code >> 6) & 0x1F; // Next 5 bits are for address command = rc5_code & 0x3F; // The 6 LSBits are command bits lcd_gotoxy(16, 1); printf(lcd_putc,"%1u",toggle_bit); // Display toggle bit lcd_gotoxy(7, 1); printf(lcd_putc,"%2LX",address); // Display address in hex format lcd_gotoxy(7, 2); printf(lcd_putc,"%2LX",command); // Display command in hex format enable_interrupts(INT_EXT_H2L); // Enable external interrupt } } } |
RC-5 Decoder with PIC16F887 microcontroller video:
Useful link:
http://en.wikipedia.org/wiki/RC-5
Discover more from Simple Circuit
Subscribe to get the latest posts sent to your email.