# Pic Lab, PIC16, Experiment #21, The IR remote protocol (RC5)

It is time to put my hands on the IR protocol.

The goal: To study IR protocol and gain the skills to work with it

Tools: a microcontroller PIC16f877A, an IR receiver TSOP1736(1738), devboard

Now, a piece of the information about the RC5 protocol as such.

The frequency: of the carrier is 36 KHz, so the period is about 27.8 us.

A modulation: in the RC5 protocol is realized in a next way: each single bit is divided by half in time. We seek for a transition, or the edge direction: the rising edge is one, the falling edge is 0 – the Manchester code.

One important thing we need to take into account – the transmitter amplifier usually is the common emitter configuration, so the logic should be inverted on a receiver side:

That’s the very basis of the coding scheme. Now to the modulation: each single bit is represented by 64 pulses, 32 which represent a high level will be switching with clock about 36KHz, and the low level you will get a constant level, no switching.

The data packet representation consists of 14 bits and has the next view:

Where:

S1, S2 – starting bits, always equal to 1, they will serve as an anchor to catch a start of transmission.

T – kind of checkbox, showing that the button on a remote is being hold, when it happened the T bit is toggling. An example of application: user wants to switch the tv to channel #2, without a T bit if user will hold the button long enough, it will produce 22 or 222 or 2222.

A4-A0 – an address, by this one system will determine what kind of device is receiving the signal. Here we have a list of devices and their addresses:

0 - TV

1 - TV2

2 - Teletext

3 - An extension for first two TVs

4 - A laser video player

5 - Videorecorder1

6 - Videorecorder2

7 - Reserved

8 - Satellite equipment?(SAT1)

9 - Extension for video recorders

10 - SAT2

11 - Reserved

12 - CD video player

13 - Reserved

14 - CD photo (no idea, what the heck is that)

15 - Reserved

16 - Pre-amplifier

17 - Reciever/tuner

18 - tape/vhs?

19 - Preamplifier 2

20 - CD

21 - Audio stand

23 - DCC record (?)

24 - Reserved

25 - Reserved

26 - Recording CD

27 - 31 - Reserved

Finally, С5-С0 – the command by itself, which also has the own specification. Everything has specification, smart guys developed the protocol.

0-9 - just numbers

12 - waiting mode

13 - Mute

14 - Setup

16 - Volume +

17 - Volume -

18 - Brightness +

19 - Brightness -

20 - Saturation +

21 - Saturation -

22 - Bass +

23 - Bass -

24 - Treble +

25 - Treble -

26 - Balance right

27 - Balance left

48 - Pause

50 - Fastforward

51 - Fastbackward

53 - Play

54 - Stop

55 - Record

63 - System choice (?)

71 - Display switch off(?)

77 - Linear function +

78 - Linear function -

80 - Up

81 - Down

84 - Display the A/V status

85 - Left

86 - Right

87 - OK

88 - Picture in picture

89 - Picture shift

90 - Picture in picture - swapping pictures

91 - Strobe

92 - Multistrobe

93 - Image blocking

94 - Scan

95 - Choose picture in picture

96 - Mosaic

97 - Picture DNR(?)

98 - Save

99 - Strobe of the picture-in-picture

100 - Call for the main image

101 - Freeze of the picture-in-picture

102 - ? up

103 - ? down

118 - Alternative mode

119 - Options (different mode)

123 - Connect

124 - Disconnect


Time to move to the practice, that what I had:

The Chinese remote, I got it together with a TV tuner somebody gave away. There are different IR protocols, but by accident I found that this remote works with a Philips TV, so I made an “educated guess”.

To check my guess I brought the remote to my work lab and connected the LED leg to the scope:

You can think “why do I see such a weird waveform?”. Well this is because the signal we see there is from the transmitting LED, not from the receiver. So far so good, the theory seems to have an agreement with the practice – we have 14 bits in total, lets check the carrier frequency now:

The frequency is 38KHz! It was a quite wise decision to connect the remote to the scope first – it means that I need to buy 38KHz receiver, not 36KHz. Plus that something you certainly would be good to be aware of before coding the uC. Also, I stumbled at such words somewhere in the web:

The command data is a Manchester coded bitstream modulating a 36 kHz carrier. (Often the carrier used is 38 kHz or 40 kHz, apparently due to misinformation about the actual protocol.)

Due to misinformation, huh! Anyway, I do not care much, and I have a single remote, so not quite much of choices available for me.

The next movement for me is a building of the receiver. I had bought the TSOP1738 analogue, in the next package:

In that package the pinout is mirrored with respect to TSOP1736. The application diagram can be checked in the datasheet:

Good to have a scope handy, but sadly I did not have at the moment. If I would have it would be so easy to connect to the receiver input and check the signal type. Oh wait I have the one, built-in in Pickit2.

Connected, and pushing the “8” button:

There are clear two starting bits and the one could see 0b001000 = 8. The address is 0, since it is a remote from TV tuner. Now, how we can force our microcontroller to work with all this stuff? I developed my algorithm based on INTE and TMR1 interrupts.

The main difficulty in the program I met – we can’t trigger the absolute transmission starting point, what we see is is the edge, or already the middle of the Manchester coded bit S1. My algorithm is the next:

1. Connect the receiver leg to the RB0 pin. Set INTEDG = 0, falling edge detection
2. If the INTE interrupt is detected very first time then:
• Launch timer for the interval 1.25 ms (3/4 of one bit modulated at 38 KHz)
• Change INTEDG value on the inverted one
3. If INTE interrupt happened not in the first time, then:
• Change INTEDG to the inverted one
• If INTEDG = bufer, launch TMR1 for 1.250 ms
4. If TMR1 interrupt is detected, capture the RB0 state and put it to the buffer and save to the variable used for memory records

To make it simpler for understanding (for me as well), I made the next picture:

Put some attention to this picture: we have received byte 11000001001000. Using beforementioned algorithm we will get a bit different number: 10000010010001, the MSB 1 is lost, but we gained another 1 in the end, and the end always will be 1.

So what we need to do now is to simply remove this extra 1 in LSB part and parse the byte to determine an address and the command.

Okay, time to expose the code:

#include <htc.h>
#include <stdio.h>
#include "usart.h"

#define _XTAL_FREQ 4000000
#define IRpin RB0

volatile static bit direction;    //1 - rising, 0 - falling
volatile unsigned char IRbyte;    //recieved byte
volatile bit buffer;              //буфер для значений таймера
volatile unsigned char bytecount; //количество байт

__CONFIG(WDTDIS & UNPROTECT & LVPDIS & HS);

void StartTimer()                 //refresh timer procedure
{
T1CKPS1 = 0;                      //no dividers
T1CKPS0 = 0;

T1OSCEN = 0;                      //internal ocs
TMR1CS = 0;                       // Fosc/4
TMR1IE = 1;                       // TMR1 overflow interrupt
TMR1H = 0b11111011; TMR1L = 0b00010110;       //1.257ms

TMR1ON = 1;                       // enable timer
}

void main()
{
TRISB0 = 1;                        //RB0 input
init_comms();                      //uart initialization
printf("\r\nStart");               //debug message
buffer = 0;                        //initial conditions
IRbyte = 0;
bytecount = 0;
direction = 0;
INTEDG = 0;                        //waiting fallinge edge interrupt

GIE = 1;                           //go go go
PEIE = 1;
INTE = 1;

while(1)
{
if (bytecount>13)                 //if we recieved already 14 bits
{
GIE = 0;                        //blocking interrups for a while
printf("\r\nPressed button is %d", IRbyte);  //throwing command to uart
IRbyte = 0;                     //and back to 0 state
bytecount=0;
INTEDG = 0;
buffer = 0;
GIE = 1;
}
}//while(1)

}

void interrupt isr()
{
if (INTF)                       //RB0
{
if (bytecount<1)             //if this is a first time
{
direction = 1;           //waiting rising edge interruption
INTEDG = 1;
StartTimer();            //launch the timer
bytecount++;             //increment counter of bits count
} else                     //if not in the first time
{
direction = !direction; //change type of the edge interrup on the inverted one
if (direction==buffer) //check if we can already relaunch the timer
{
StartTimer();
bytecount++;
} //if (direction==buffer)
INTEDG = direction;
} //else
INTF = 0;
} //INTF

else {                            //RB0 interrupt has higher priority
if (TMR1IF)
{
buffer = IRpin;                // RB0 state to buffer
TMR1ON = 0;                    //disable timer
TMR1IF = 0;
if ((bytecount>7)&&(bytecount<14))   //grabbing command only
{
IRbyte <<= 1;
IRbyte |= buffer;
}
}
}

}

Example of the work:

All works fine, just I don’t have the toggle bit processing so I can have some false commands without proper handling.

The sources