top of page

Decoding IR Remote Control

1. Introduction

The 1838 integrated infrared receiver is our most commonly used infrared receiver. It is widely used in televisions, air conditioners, refrigerators, TV set-top boxes and other appliances that require infrared remote control . Of course, all the MCU development boards with infrared remote control function also have the same feature. Let's study how to decode infrared remote control through this LPC1114 development PCB board.

Infrared Remote Control - Dusun
Infrared receiver on a development PCB - Dusun

2. Infrared Receiver and Electrical Connection

Schematic drawing of infrared receiver - Dusun

In the above figure, CON3 stands for the infrared receiving tube, and the 1 2 3 pins are defined as OUT , GND and VCC . Among them, VCC can be connected to 5V , or can be connected to 3.3V , mainly depends on the working voltage of the connected MCU. In the circuit, R12 is used to pull the resistor to the MCU pin connected to the OUT pin, and C16 is used for power supply filtering to improve the stability of the infrared remote control. The circuit connection is simple. At first glance, it is known that it uses a single bus communication method with the microcontroller.

3. Process Of Receiving Infrared Signals

How do you change the OUT pin of the IR receiver when you hold an infrared remote control and aim at the IR receiver? This is determined by the remote control because it only plays a role in decoding. In the present, China, basically the remote control is used to follow the NEC coding method. Therefore, we need to know the encoding method of NEC infrared remote control. 

NEC infrared remote control coding mode: its command format is, in order, dock + custom code high + custom code low + data code + data inverse. The dock is used to notify the arrival of the infrared remote control signal, consisting of a low level of 9 milliseconds plus a high level of 4.5 milliseconds; the custom code high, the custom code low, the data code, and the data inverse code are all 1 byte. Custom code high and low timing code form a 16 -bit custom code, generally used to identify the infrared remote control, that is, if your air conditioner remote control custom code is your TV remote control custom code is 55, this TV remote control does not work for the air conditioner. If the TV remote control and the air conditioner remote control have the same custom code, then you can take the remote control of the TV and press it against the air conditioner. Maybe something goes wrong. The data code is used to identify the user's functions. For example, the data codes sent by the buttons such as adding and subtracting sounds and changing channels are different. The data inverse code and the data code are inverse codes relationship and are used to verify whether the data code is received correctly or not.88.

After infrared remote control is pressed, the OUT pin receiver changes: First 9ms low level, high level and then is 4.5ms, and custom code will appear high, low custom code, data code, the data Inverse, the logic 1 of these 4 codes is 560us low level + 1680us high level, logic 0 is 560us low-level +560us high level.

Decoding IR Remote Control - Dusun

The picture above shows the pulse signal from the remote control. We'll see that its level is exactly the opposite of the level of the signal received by the receiver I just dictated.

In addition, if the user keeps pressing a button, the resend code will be sent. The resend code is followed by the key code. It consists of 9ms low level + 2.5ms high level +560us low level +97ms high level. By the law of retransmission code, we can see that when the MCU observes When it is low to 9ms , if it is 4.5ms high, it is the first press key. If 9ms is followed by 2.5ms high, it is resent code.

4.0 How To Detect These Pulses and Calculate The Size?

Use the following two functions:

  •                Timer

  •                Interrupt

Basic idea: Connect the OUT pin of the infrared receiver to the interrupt pin of the MCU, use the timer to record the time between each level jump, and obtain the code value by judging the time. So you know which button the remote control pressed.

Through the above ideas, first of all, it is best to use the double-edge interrupt, the double-edge interrupt, that is, the rising edge and the falling edge will be interrupted. Then, we think of the capture function of the single-chip microcomputer (CAP). We now use STC51, LPC1114, STM32. The microcontrollers all have CAPcapture and can be set to both edges interrupts. Below we take LPC1114 as an example to write a program.

4.1 MCU CAP Capture Initialization Configuration Program

LPC1114 our example, there are four pins CAP, we use a pin P1.8 wherein, as shown below:

MCU CAP capture initialization - Dusun

Void IR_Init (void)
{
     LPC_SYSCON->SYSAHBCLKCTRL |= (1<<16);   / / Open the pin function module IOCON clock
     LPC_IOCON->PIO1_8 &= ~0x07;
     LPC_IOCON->PIO1_8 |= 0x01;      /* CT16B1 CAP0 */ Configure P1.8 pin for CT16B1_CAP pin
     LPC_SYSCON->SYSAHBCLKCTRL &= ~(1<<16);   / / Turn off the IOCON module clock, configure the pin function, turn off the clock to save power
     LPC_SYSCON->SYSAHBCLKCTRL |= (1<<8);      / / Turn on the CT16B1 timer clock
     LPC_TMR16B1->TCR = 0x02;          / / Reset timer
     LPC_TMR16B1->PR   = 49;          / / Configure the prescaler, making 1us TC +1
     LPC_TMR16B1->IR   = 0x10;          / / interrupt reset
     LPC_TMR16B1->CCR = 0x07;      / / Configure the CAP pin bilateral edge interrupt
     LPC_TMR16B1->TCR = 0x01; // Open the timer and start timing
     NVIC_EnableIRQ (TIMER_16_1_IRQn);      / / Open the NVCI interrupt entry
}
The above function is used to configure the CAP pin. Because the PIN9 of the LPC1114 defaults to the GPIO function, P1.8 , we switch the pin to the CAP pin function.

4.2 Variables to Be Defined

uint8_t pulse_start = 0;      // pulse start sign

uint8_t pulse_bnum = 0;          // pulse counter

uint8_t pulse_ok = 0;          / / The first key code mark

uint8_t key_repeat = 0;          / / button times

uint16_t tc_buf = 0;      // pulse width storage

uint16_t ir_buf [64];          64 // 16-bit variable used to store 9ms + values of the four level code after 4.5ms

uint8_t user_code_hi;   / / Custom code high

uint8_t user_code_lo; // custom code low

uint16_t user_code; // custom code

uint8_t key_code; // data symbols

uint8_t key_code_lo; // inverted data symbols

uint8_t ir_sign;              / / Receive the button flag

When the microcontroller finds a low level of 9ms , pulse_start is set to 1 . When 9ms is found followed by a 4.5ms high level, pulse_ok is set to 1. When 9ms is found followed by a 2.5ms high level, key_repeat is set to 1 . The four code values that appear after that , a total of 4 bytes, 8 bits per byte , each bit is composed of a low level and a high level. The value of this level is 560us and 1680us , so 16 bits are required . the value is stored, a total of 4 bytes of 32 bits, each bit of a low to a high level, it is necessary to define the array 64 can put a 16-bit data. After successfully receiving a button value, ir_sign is set to 1 .

4.3 Interrupt Function

Void TIMER16_1_IRQHandler(void)

{

     If((LPC_TMR16B1->IR&0x10)==0x10) //Check if it is a CAP interrupt

     {

         Tc_buf = LPC_TMR16B1->TC; // give the TC value to tc_buf

         LPC_TMR16B1->TC = 0; j// clear the TC value to 0 , count from 0 again

         If(( tc_buf >8500)&&( tc_buf <9500))           // Found 9ms

         {

             Pulse_start =1;

             LPC_TMR16B1->IR = 0X10; // Clear CAP0 interrupt bit

             Return;

         }

         If( pulse_start ==1) // If found after 9ms

         {

          If(( tc_buf >4000)&&( tc_buf <5000))      // Found 4.5ms

             {

                 Pulse_ok =1; // flag pressed button

                 LPC_TMR16B1->IR = 0X10; // Clear CAP0 interrupt bit

                 Pulse_start =0; // clear this bit to prepare for the next button

                 Key_repeat =1; // flag the first button

                 Return;

             }

             Else if(( tc_buf >2000)&&( tc_buf <3000))      // Found 2.5ms

             {

                 Key_repeat ++; // increase the number of keys

                 LPC_TMR16B1->IR = 0X10; // Clear CAP0 interrupt bit

                 Pulse_start =0; // clear 0

                 Ir_sign =1; //The flag key value is still valid

                 Return;

             }

         }

         If( pulse_ok ==1) // If 9ms and 4.5ms are found

         {

             Ir_buf [ pulse_bnum ]= tc_buf ; // Start storing 64 levels

             Pulse_bnum ++;

             If( pulse_bnum ==64)

             {

                 Pulse_ok =0;

                 Pulse_bnum =0;

                 Ir_sign =1; // After receiving 64 levels, this variable is set to 1, indicating that a button value has been received.

             }     

         }     

     }

     LPC_TMR16B1->IR = 0X10; // Clear CAP0 interrupt bit

}

When the OUT pin of the infrared receiving head has a pulse signal, an edge transition occurs, and an interrupt is entered. After entering the interrupt, the value of TC is recorded , and then the TC is cleared to 0 , starting from 0 . In front of the pin configuration function that CAP, TC 1us every plus 1. After 9ms+4.5ms is found , the next 4 code values are stored in the data. When 9ms+2.5ms is found , the number of keystrokes is +1 . This is the work done by this interrupt function.

4.4 Decoding Function

In the previous interrupt function, we obtained the level of 4 code values. Now we use a function to calculate these 4 bytes. The function is as follows:

uint8_t ir_process (void)

{

     Uint8_t i ;

     Uint16_t buf ;

 

     For( i =0;i<16;i++) // calculate custom code high

     {

         User_code_hi <<=1;

         Buf = ir_buf [ i ]+ ir_buf [++ i ]; // Add high and low levels

         If(( buf >2100)&&( buf <2450)) // Is it 2.25ms, that is , whether it is logic 1

         {

             User_code_hi +=1;

         }

     }

     For( i =16;i<32;i++) // calculate custom code low

     {

         User_code_lo <<=1;

         Buf = ir_buf [ i ]+ ir_buf [++ i ]; // Add high and low levels

         If(( buf >2100)&&( buf <2450)) // Is it 2.25ms , that is, whether it is logic 1

         {

             User_code_lo +=1;

         }

     }

     For( i =32;i<48;i++) // calculate the data code

     {

         Key_code <<=1;

         Buf = ir_buf [ i ]+ ir_buf [++ i ];

         If(( buf >2100)&&( buf <2450))

         {

             Key_code +=1;

         }

     }

     For( i =48;i<64;i++) // calculate the data code inversion

     {

         Key_code_lo <<=1;

         Buf = ir_buf [ i ]+ ir_buf [++ i ];

         If(( buf >2100)&&( buf <2450))

         {

             Key_code_lo +=1;

         }

     }

 

     If( key_code ==(uint8_t)~ key_code_lo ) // Check if the received data is correct

     {

         User_code = ( user_code_hi <<8) + user_code_lo ;

         Return 1;     

     }

     Else return 0;

}

Decode function with return value. If 1 is returned, the reception is correct, and if 0 is returned, it is a reception error. Whether or not the reception is correct is obtained by judging whether the data code and the data complement are complementary.

As mentioned above, logic 1 is 560us low level + 680us high level, a total of 2.25ms; logic 0 is 560us low level +560us high level, a total of 1.125ms. So we add the two levels before and after to judge, if it is 2.25ms, it is logic 1, if it is 1.125ms, it is logic 0. In the program, the program that judges if it is low level is omitted, and if the program that determines the low level is added, it is like this:

     ......

For( i =0;i<16;i++) // calculate custom code high

     {

         User_code_hi <<=1;

         Buf = ir_buf [ i ]+ ir_buf [++ i ]; // Add high and low levels

         If(( buf >2100)&&( buf <2450)) // Is it 2.25ms, that is , whether it is logic 1

         {

             User_code_hi +=1;

         }

Else(( buf >1100)&&( buf <1300)) // Is it 1.125ms , that is, whether it is logic 0

{

            User_code_hi +=0;

}

     }

.....

However, after careful study, you find that the statement that determines the low level, and the statement that does not judge the low level, the result is the same, because the low level is added to 0.

4.5 Calling In The Main Function

In the main function, you first need to call IR_Init () to initialize the configuration function, and then you can detect if there is a button press in the while.

   Int main()

{

     IR_Init ();

While(1)

{

If(( ir_sign ==1)&&( ir_process ()==1))       / / Determine whether there is a button press

         {

......

             Ir_sign =0;

         }

}

5. Summary

The first time you get a remote control, you can first use the custom code of the remote control and the data code of each button to output the serial port, and then you can write the program to judge that they are used in the actual product. 

About DUSUN ELECTON LTD.

 

More than 16+ years of designing, developing and manufacturing, Dusunremotes has been created and delivered billions of high-quality and cost-effective remote control devices worldwide.  Our innovative remote control solutions designed based on a broad wireless technology portfolio, like BLE, RF4CE, 2.4G, IR and interactive paradigms: Air-mouse, Voice, Touch, Motion, Pointing and Virtual Reality control. Customers came from a wide range of industries, such as pay TV, PC/OTT, home automation, automobile, consumer electronics, IOT and so on. We always try our best to bring customers with value-added and different design experiences for their essential products and brand. Learn more

You May Also Like to Read

bottom of page