Sie sind auf Seite 1von 10

SONY SIRC Protocol.

Infra-red remote control has been arond for a very long time now. Each manufacturer use different set of protocols. For example, RC80 is used by Panasonic, RC5 is used by Philips, and SIRC is used by SONY, which is one of the simplest to decode. In order to eliminate ambient light sources from interfering with the data stream transmited modulated light is used. This modulation is center around different frequencies depending on manufacturer and varies from 32KHz to 56KHz. In the case of SONY, the modulation center is 40KHz which means we need a IR-receiver that can receive the modulation infra-red light and convert it to a TTL signal for a PIC. There are a number of IR-receivers available, each having a specific center frequency that they are more sensitive too. In this example, I use RPM7140 which is has 40KHz center frequency. The figure shown below is the internal block diagram of the RPM7140.

Output (Rout) is normally high as you can see from the block diagram, it has a pull-up resister that keep logic high when no signal present. When infra-red signal is detected, output is low. SONY protocol, SIRC (Serial Infra-Red Control) protocol is an infra-red light remote control communication that uses a form of pulse width modulation (PWM) to build serial interface.The most common protocol is 12-bit interface but 15-bit and 20_bit version are also available. The figure shown below is a series of pulses build up 12-bit packet.

The Header is 2.4ms in length, logic 1 is 1.8ms ( 1.2ms high + 0.6ms low), logic 0 is 1.2ms ( 0.6ms high + 0.6ms low). The packet consists of Header, Command code (7-bit) which presents the actual button pressed on the remote control, and Device code (5-bit) which presents a TV, VCR, CD player and etc. Those signal is inverted to Rout of the RPM7140. When the data is sent, 45ms delay time before the next packet is sent and it is repeated for as long as the key is pressed. Table shown below is some list of key Command code for TV (Device code = 1).

Command Code (Hex) 0 1 2 3 4 5 6 7 8 9 0x10

Function Key 1 Key 2 Key 3 Key 4 Key 5 Key 6 Key 7 Key 8 Key 9 Key 0 Channel +

Command Code (Hex) 0x11 0x12 0x13 0x14 0x15 0x0B 0x25 0x74 0x75 0x33 0x34

Function Channel Volume + Volume Mute Power Enter Input UP DOWN RIGHT LEFT

The Schematic.
In this example, all I need is to toggle turn on and off an LED co-ordinate to the key on the remote control. The schematic is shown below.

The Code.
/* * Project name: SONY SIRC decoding * Copyright: Nicholas Sirirak * Description: * Test configuration: MCU: PIC18F2620 Dev.Board: Oscillator: HS, 16.0000 MHz Ext. Modules: SW: mikroC PRO V1.65 * NOTES: */ #define #define #define #define #define #define #define LED1 LED2 LED3 LED4 LED5 LED6 LED7 LATB.F1 LATB.F2 LATB.F3 LATB.F4 LATB.F5 LATB.F6 LATB.F7

unsigned counter = 0; unsigned input_data, bit_count;

enum { Idle, Start_bit, Capture_bit }; char Current_state = Idle; char got_data = 0; char Command_code, Device_code; void interrupt(){ if(INTCON.INT0IF){ switch (Current_state){ case Idle: INTCON2.INTEDG0 = 1; //interrupt on rising edge. counter = 0; Current_state = Start_bit; break; //found the rising edge, check lenght for 2.4ms case Start_bit: //correct signal, move on to next state if(counter == 4) { counter = 0; bit_count = 0; input_data = 0; Current_state = Capture_bit; } else { //fault signal, reset to Idle Current_state = Idle; } break; case Capture_bit: //check plus length for 0 or 1 if(counter == 2){ input_data >>= 1; // add 0 to received data bit_count++; }else { if(counter == 3){ input_data >>= 1; input_data |= 0x8000; //add 1 to received data bit_count++; } else { //error occurs, reset to Idle state INTCON2.INTEDG0 = 0; //interrupt on falling edge. Current_state = Idle; } } //compleat 12 bit if(bit_count >= 12){ got_data = 1; input_data >>= 4; INTCON2.INTEDG0 = 0; //interrupt on falling edge. Current_state = Idle; } counter = 0; break; default: Current_state = Idle; } INTCON.INT0IF = 0; //clear interrupt flag. } if(PIR1.TMR2IF){ counter++;

if(counter > 5) { Current_state = Idle; counter = 0; INTCON2.INTEDG0 = 0; //interrupt on falling edge. } PIR1.TMR2IF = 0; //clear interrupt flag } } //****************************************************************************** // MAIN MAIN MAIN MAIN //****************************************************************************** void main() { TRISC = 0; //portc is output TRISB = 0; //portb is output //****************************************************************************** // RB0 interrupt set up //****************************************************************************** INTCON.INT0IE = 1; //enable RB0 interrupt. INTCON2.INTEDG0 = 0; //interrupt on falling edge. TRISB.F0 = 1; //RB0 = input. ADCON1 = 0x0F; //all digital I/O //****************************************************************************** // Timer2 interrupt set up, interrupt every 600us //****************************************************************************** T2CON = 2; //timer off, prescaler 1:16 PR2 = 149; //preload timer2 comparator value. TMR2 = 0; //reset value timer2 PIR1.TMR2IF = 0; //clear interrupt flag. PIE1.TMR2IE = 1; //enable timer2 interrupt. IPR1.TMR2IP = 1; //timer2 interrupt high priority //****************************************************************************** // Global interrupt enable //****************************************************************************** INTCON.PEIE = 1; //enable interrupt INTCON.GIE = 1; //enable global interrupt T2CON.TMR2ON = 1; //timer2 is on while(1){ if(got_data){ Command_code = input_data & 0x7F; Device_code = input_data >> 7; got_data = 0; if(Device_code == 1){ switch (Command_code){ case 0: LED1 = ~LED1; break; case 1: LED2 = ~LED2; break; case 2: LED3 = ~LED3; break; case 3: LED4 = ~LED4; break; case 4: LED5 = ~LED5; break; case 5: LED6 = ~LED6; break; case 6: LED7 = ~LED7; break; } PIE1.TMR2IE = 0; //disable timer2 interrupt. Delay_ms(100); TMR2 = 0; //reset value timer2 PIE1.TMR2IE = 1; //enable timer2 interrupt. } } } }

Testing.

Video.

Philips RC5.
The RC5 protocol is a type of Manchester encoded data packet. Manchester data is unique in that a data is signified by a transition in the middle of the bit. A 1 is received by the IR receiver (after inversion by the IR sensor) as a high-to-low transition, and a 0 as a low-to-high transition. The RC5 IR packet consists of 14 bits: two start bits (S1, S0), one control bit (C), five address bits (A4 to A0), and a six bit command code (C5 to C0). The entire 14-bit packet is received MSB first, starting with two start bits. Figure shown below is a data packet seen by IR receiver.

The start bits are always transmitted as 1. The control bit toggles whenever a new key is received. The five address bits represent 32 different potential addresses of the equipment for which the packet is intended. The six command bits represent 64 commands that can be transmitted. The bit period for RC5 is 1.78 ms long, with half of that period high and the other half low. The duration for the complete 14-bit packet is approximately 25 ms.

RC5 decoding example code.


/* * Project name: RC5 decoding * Copyright: Nicholas Sirirak * Description: * Test configuration: MCU: PIC18F2620 Dev.Board: Oscillator: HS, 16.0000 MHz Ext. Modules: SW: mikroC PRO V1.65 * NOTES: */ #define #define #define #define #define #define #define LED1 LED2 LED3 LED4 LED5 LED6 LED7 LATB.F1 LATB.F2 LATB.F3 LATB.F4 LATB.F5 LATB.F6 LATB.F7

unsigned input_data, bit_count; char logic_interval, logic_change; char old_invert = 0; char invert_bit = 0; enum { Idle, Start_bit, Capture_bit

}; char Current_state = Idle; char got_data = 0; char Command_code, Address_code; void interrupt(){ if(INTCON.INT0IF){ //****************************************************************************** // RB0 changed, toggle interrupt edge bit and increase logic_change for detect // fault signal purpose // If it's a first edge detected, initialize data and turn on timer2 //****************************************************************************** INTCON2.INTEDG0 = ~INTCON2.INTEDG0; //toggle edge logic_change++ ; if (current_state == Idle){ logic_interval = 0; logic_change = 0; delay_us(200); TMR2 = 0; PIR1.TMR2IF = 0; //clear interrupt flag T2CON.TMR2ON = 1; //timer2 is on Current_state = Start_bit; } INTCON.INT0IF = 0; //clear interrupt flag. } //********* End RB0 interrupt routine ***************************************** if(PIR1.TMR2IF){ logic_interval++; switch (Current_state){ //****************************************************************************** // Check for (second) start bit, Logic on RB0 must change in 890us or considers // as a fault signal. //****************************************************************************** case Start_bit: if((logic_interval == 1)&& (logic_change == 1)){ logic_interval = 0; logic_change = 0; bit_count = 0; input_data = 0; current_state = Capture_bit; //move on to capturing state }else { Current_state = Idle; T2CON.TMR2ON = 0; //timer2 is off INTCON2.INTEDG0 = 0; //interrupt on falling edge. } break; case Capture_bit: //****************************************************************************** // Capturing state. Sampling RB0 logic every 1780 us (logic_interval = 2). // Data is valid if logic on RB0 was change. // Data is store in Command_code and Address_code //****************************************************************************** if(logic_interval == 2){ if(logic_change <= 2){ logic_interval = 0; logic_change = 0; if(bit_count < 12){ bit_count++; input_data <<= 1; if(PORTB.F0 == 1) { input_data |= 1; }

} else { Command_code = input_data & 0x3F; input_data >>= 6; Address_code = input_data & 0x1F; input_data >>= 5; if(old_invert != input_data) { invert_bit = 1; old_invert = input_data; }else invert_bit = 0; got_data = 1; Current_state = Idle; T2CON.TMR2ON = 0; //timer2 is off INTCON2.INTEDG0 = 0; //interrupt on falling edge. } }else { Current_state = Idle; T2CON.TMR2ON = 0; //timer2 is off INTCON2.INTEDG0 = 0; //interrupt on falling edge. } } break; default: Current_state = Idle; INTCON2.INTEDG0 = 0; //interrupt on falling edge. } PIR1.TMR2IF = 0; } } //****************************************************************************** // MAIN MAIN MAIN MAIN //****************************************************************************** void main() { TRISC = 0; //portc is output TRISB = 0; //portb is output LATB = 0; //****************************************************************************** // RB0 interrupt set up //****************************************************************************** INTCON.INT0IE = 1; //enable RB0 interrupt. INTCON2.INTEDG0 = 0; //interrupt on falling edge. TRISB.F0 = 1; //RB0 = input. ADCON1 = 0x0F; //all digital I/O //****************************************************************************** // Timer2 interrupt set up, interrupt every 890us //****************************************************************************** T2CON = 0b00100001; //postscal 1:5, timer off, prescaler 1:4 PR2 = 177; //preload timer2 comparator value TMR2 = 0; //reset value timer2 PIR1.TMR2IF = 0; //clear interrupt flag. PIE1.TMR2IE = 1; //enable timer2 interrupt. IPR1.TMR2IP = 1; //timer2 interrupt high priority //****************************************************************************** // Global interrupt enable //****************************************************************************** INTCON.PEIE = 1; //enable interrupt INTCON.GIE = 1; //enable global interrupt while(1){ if(got_data){ got_data = 0; if((Address_code == 0)&&(invert_bit == 1)){ switch (Command_code){ case 0: LED1 = ~LED1; break; //clear interrupt flag

case case case case case case } } } } }

1: 2: 3: 4: 5: 6:

LED2 LED3 LED4 LED5 LED6 LED7

= = = = = =

~LED2; ~LED3; ~LED4; ~LED5; ~LED6; ~LED7;

break; break; break; break; break; break;

Note.