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

a TSOP1736 receiver on the 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.

Manchester code states

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:

Inverted Manchester code states

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:

RC5 protocol data packet structure


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
22 - Satellite audio receiver(?)
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
82 - Menu
83 - Exit from menu
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 remote I had in my stash

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:

The scope waveform from the remote LED leg

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 zoomed in waveform, frequency is measured

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:

the package for TSOP1738

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

the application diagram from 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:

number 8 is transferred to TSOP

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:

An algorithm described in diagram

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; //количество байт
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;
 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
   INTEDG = 0;
   buffer = 0;
   GIE = 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
               } //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

1 thought on “Pic Lab, PIC16, Experiment #21, The IR remote protocol (RC5)

  1. Pingback: Remote control for the fan | diymicro.org

Leave a Reply

Your email address will not be published.