IR Remote Decoder

In this post we look for some actual data input to send to the 1602 LCD display we built in the 3-wire, 8-bit LCD Interface post.  A quick look in the old electronics junk box turned up a couple of long forgotten remote controls along with some front panel boards from broken DVD players.  Just then a light bulb went off over my head (a low power LED bulb, of course).  Why not make a simple IR decoder?  After all, IR remote controls are probably the most ubiquitous appliance in every home.  So off I went to fire up my soldering iron and my computer.


PIC IR Interface 

The hardware connections are shown in the diagram above.  The interface to the 1602 LCD display is based on the hardware and software detailed in Episode 1 so that detail is not shown here.  The only difference is that the PIC pins used to interface to the LCD and the shift register were moved so that one special purpose PIC pin could be used for this application.  The IR Receiver I used came from one of those broken DVD front panel boards I have.  If you don’t have one lying around then you can buy an IR Receiver for less than a dollar.  One of my favorite online places for hobby parts is Tayda Electronics but there are lots of good places out there.  Make sure that the IR Receiver has the three pins shown in the diagram and that it receives on the 38-kHz frequency.  It should be easy to find because that’s the most common version available.

IR Code Formats

IR Waveform

There are a few different IR code formats out there but the majority of manufacturers use the NEC format or some variation of that format.  That’s what we will be decoding here.  What is so cool about the cheap IR Receiver is that it receives the 38-kHz IR pulse bursts and decodes them into a nice TTL-level serial bit stream.  The partial waveform diagram above is typical of what is output from the receiver, starting with the sequence used to indicate the beginning of the message.  If you look around online you may see that the actual waveform is the inverse of this one but the waveform above is what comes out of the IR Receiver.  Or at least that’s what came out of the three different ones I scrounged.  Typically, one transmission consists of the two long pulses on the front end, 32 data pulses, and an end of transmission pulse.  The low-level start pulse lasts for 9ms and is followed by a space (high-level) of 4.5ms.  A logic “0” is a 562.5us pulse followed by a 562.5us space.  A logic “1” is a 562.5us pulse followed by a 1.6875ms space. 

In the original NEC data format, the first 8 bits were the address of the target device (e.g.: your TV), the second 8 bits were the inverse of the address, the third 8 bits were the command, and the fourth 8 bits were the inverse of the command.  That only allowed 256 address combinations so the format was revised to use the second 8 bits to extend the address combinations to 65,536.  I’ve seen remotes of both kinds around the house.  Some manufacturers also use the fourth 8 bits for their own purposes.  A typical example of that is Tivo.  What this project does is to simply decode the four bytes into ASCII format for display.  You could easily extend the software to decode the different commands and use them to control something, like maybe that robot you’ve been meaning to build.


The software link is listed below.  While it is targeted for the 12F683, it is easily ported to bigger versions of the PIC.  Mostly it requires changing names like TRISIO to TRISA, and GPIO to PORTA.  You will also need to change the line that identifies the PIC version (LIST=) and the INCLUDE file but those are intuitive changes.  The __CONFIG line may also need tweaking just because one or two of the labels used are spelled differently in some of the INCLUDE files.  Just make sure that the PIC you use has a pin that allows an External Interrupt input (usually labeled EXT).

Other than the LCD control pin changes, this program has a couple of basic differences from the simple one in Episode 1.  In particular, it uses an interrupt handler so the defined memory locations for the program reset and the start of the interrupt handler have been added.  The reset vector (address 0) has a simple GOTO instruction to jump to the real start of the program.  That’s necessary because the interrupt handler always starts at address 4.  One quirk I found is that the MPE X IDE program that compiles the code does not like a GOTO jumping directly to a BANKSEL statement.  That’s why the first instruction in “MAIN” is a NOP.  The “Init” routine also includes instructions to use the internal 8-MHz clock instead of the default 4-MHz clock.  That also means that the delay routines must have different values than those in the Episode 1 routines.

The LCD_Init routine adheres pretty much to the specified initialization sequence for the 1602 chip.  The LCD_Line1 routine displays “ADDRESS/COMMAND” on line 1.  The LCD_Line2 routine sets up for writing the received IR codes to line 2 of the LCD.  Line 2 gets overwritten each time a new IR message is received.

The heart of the IR message capture code is inside of the interrupt handler.  Remember from the wiring diagram that we have connected the serial data output of the IR Receiver to the External Interrupt input of the PIC.  This allows us to sync with the message start and to decode each data bit by simply measuring pulse widths.  The External Interrupt input is set to interrupt on the falling edge which always indicates a bit start.  The interrupt routine checks Timer1 to see how long it has been since the last interrupt and then resets the timer.  We could measure the various pulses exactly but the lazy approach works quite well.  As we saw in the previous section, the start pulse is easily identifiable but the data bits are differentiated by the space time following the data bit.  By measuring from falling edge to falling edge, we include both the bit and the space time.  That way we don’t have to be too precise when testing pulse widths.  Given that the PIC clock frequency is set to 8-MHz, Timer1 will increment every 500ns.  To simplify the software we ignore the lower 8 bits of Timer1 (127us maximum) and just test the upper 8 bits.  Each count in the upper 8 bits equals 128us.  A “0” data bit will have a count greater than 5 but less than 9 and a “1” data bit will have double that range.  That’s why the counts checked in the software are set to 6 for a data bit “0” and 12 for a data bit “1”.  The start bit check is for a count greater than 24 (over 3ms).  Not too precise but highly effective.

The routine Main_Lp1 is where the data bits decoded by the interrupt routine are packed into the expected four bytes.  The call to Make_ASCII is sent one nibble at a time of each byte and converts the 0-15 numerical value to a readable character from 0-F.  It uses a slick process that allows a programmer to build a lookup table and then set the Program Counter to jump into the appropriate location in the table.  Each table location is a special subroutine return statement (RETLW) that carries a specified literal value in the W register back to the calling routine.  After all four bytes have been received and sent to the display there is a call to LCD_Line2 to reset the display pointer in anticipation of the next IR message.  It does not include a command to clear the display because the next message will just overwrite the current display locations.





 Here are a couple of (bad) pictures that show typical results.  The first one shows the result from a cheap remote that adheres to the original NEC standard of Address, Inverse Address, Command, and Inverse Command.  The second picture is from a Tivo remote and shows the extended address as well as two different command bytes.  That’s it for this post.  Check out my other electronics projects.