Get Adobe Flash player
FacebookTwitterGoogle+
English Arabic French German Italian Portuguese Russian Spanish

Did you know?

In 1957 a battery was discovered in Bagdad. It was made by the Parthians, who ruled Bagdad from 250 B.C.E. to 224 C.E., and was used to electroplate silver.
 

Help us stay online:

small donate

Ir receiver and AVRIn this example we will make project that uses any standard IR remote control from your TV, Audio system... to receive and recognize code with AVR microcontroller. There are many different predefined IR protocols as written in Ultimate guide through IR Remote Control article. Despite this fact receiving and decoding which key is pressed is very simple.

This article will teach you how to add IR reception to your projects. Imagine having control of your light, turning ON/OFF your device... all over your old (or new :) ) remote controller form any device you have.

 

 

On picture below is displayed wave form at output of standard IR receiver

DS0004

RPM7138 is used and you can download its datasheet here.

Any IR receiver will do the same job. They are cheap to buy and if you cannot find one you can take it from any broken TV, audio system or any other device with IR remote control.

Ir receiver

As shown on picture above all you need to do is connect VCC pin to +5V, GND pin to ground of microcontroller and Rout pin to digital input pin of microcontroller used. In this example we used Port A pin 3, but this can be changed just in one line of the code

{code}

#define IR_pin PINA.3

{/code}

And by setting other used pin as digital input pin.

As you can see on signal waveform above changes of IR signal occur approximately every 500uS or more. This means that we will get correct results if we measure pin state every 50uS.

There are many different ways to get IR functionality on your project. Way that is presented here is best way to start. It is universal and does not depend on protocol type used. Down side is that it is consuming a lot of memory. Still it is best way to start from and you can easily free some memory when you see what kind of codes you are receiving which we will discuss later.

In this example we configured timer0 to interrupt every 50uS. Inside timer we check IR receiver output state and if it is changed remember duration of logic “1” or logic “0”. This way no matter what standard or bit duration is used, AVR will decode what key is pressed on the remote control. When microcontroller is started it will store in RAM memory string first two keys pressed. After that it will write durations of each bit for key pressed and compare weather it is one of keys stored in memory at startup. If you add external memory for memorized keys then you will resolve memory issue and still have universal reception that reacts on every IR remote control you can find.

Here is code for this project that is written in Codevision C compiler for AVR atMega8535.

/*****************************************************
This program was produced by the
CodeWizardAVR V2.03.4 Standard
Automatic Program Generator
© Copyright 1998-2008 Pavel Haiduc, HP InfoTech s.r.l.
http://www.hpinfotech.com
 
Project :   universal IR AVR command readout
Version :
Date   : 2/19/2012
Author : www.Electronics-Base.com
Company : For all questions and much more details and explanation on this project visit our site
Comments:   Detailed comments as well as video and pisture presentation of its operation you can find on www.Electronics-Base.com
  
Chip type           : ATmega8535
Program type       : Application
Clock frequency     : 8.000000 MHz
Memory model       : Small
External RAM size   : 0
Data Stack size     : 128
*****************************************************/
 
#include <mega8535.h>
 
// Standard Input/Output functions
#include <stdio.h>
#include <stdlib.h>
 
#define IR_pin PINA.3
unsigned char bit_duration,bit_index,IR_state=1,ended,i,started,pass_ok;
unsigned char bit_array[101];
 
unsigned char memory[101][2];
unsigned char memory_load;
 
unsigned char buffer[5];
// Declare your global variables here
// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
// Reinitialize Timer 0 value
TCNT0=0xCD;
// Place your code here
PORTA.0=!PORTA.0;//just inverting pin for test indication
if (IR_state==1)
  {
  if(IR_pin==1)
   {
   if(bit_duration<203)
   bit_duration++;
   if ((started==1)&&(bit_duration>200))
   //no change for long time meaning code reception is over
    {
    ended=1;
    started=0;
    bit_index=0;
    }
   }
   else
   {
   IR_state=0;
   bit_array[bit_index]=bit_duration;
   bit_index++;
   bit_duration=0;
   started=1;
   }
  }
else
{
if(IR_pin==0)
  {
  if(bit_duration<201)
   bit_duration++;
  if (bit_duration>200)
   {
   ended=1;
   bit_index=0;
   }
  }
  else
   {
   IR_state=1;
   bit_array[bit_index]=bit_duration;
   bit_index++;
   bit_duration=0;
   }
  }
}
void main(void)
{
// Declare your local variables here
 
// Input/Output Ports initialization
// Port A initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=out
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=1
PORTA=0x01;
DDRA=0x01;
 
// Port B initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTB=0x00;
DDRB=0x00;
 
// Port C initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTC=0x00;
DDRC=0x00;
 
// Port D initialization
// Func7=In Func6=In Func5=In Func4=In Func3=In Func2=In Func1=In Func0=In
// State7=T State6=T State5=T State4=T State3=T State2=T State1=T State0=T
PORTD=0x00;
DDRD=0x00;
 
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 1000.000 kHz
// Mode: Normal top=FFh
// OC0 output: Disconnected
TCCR0=0x02;
TCNT0=0xCD;     //interrupt every 50uS
OCR0=0x00;
 
// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;
 
// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2 output: Disconnected
ASSR=0x00;
TCCR2=0x00;
TCNT2=0x00;
OCR2=0x00;
 
// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// INT2: Off
MCUCR=0x00;
MCUCSR=0x00;
 
// Timer(s)/Counter(s) Interrupt(s) initialization
TIMSK=0x01;
 
// USART initialization
// Communication Parameters: 8 Data, 1 Stop, No Parity
// USART Receiver: Off
// USART Transmitter: On
// USART Mode: Asynchronous
// USART Baud Rate: 9600
UCSRA=0x00;
UCSRB=0x08;
UCSRC=0x86;
UBRRH=0x00;
UBRRL=0x33;
 
// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
SFIOR=0x00;
 
// Global enable interrupts
#asm("sei")
 
while (1)
     {
     // Place your code here
       if(ended)
//for longer time period there was no change so code is received
//this ensures that we can receive codes
//of different lengths with no change in code
       {
       putsf("RECEIVED:");
       for (i=0;bit_array[i]!=0;i++)
       {
       itoa(bit_array[i],buffer);
       putchar(' ');
       puts(buffer);
       }
       putchar(13);
      
           if (memory_load<2)
//if we haven’t already memorized two keys we copy received code to string
           {
           for (i=0;bit_array[i]!=0;i++)
               {
               memory[i][memory_load]=bit_array[i];
               }
 
       //and write both memorized codes
           putsf("MEMORY 0:");
           for (i=0;memory[i][0]!=0;i++)
               {
               itoa(memory[i][0],buffer);
               putchar(' ');
               puts(buffer);
               }
           putchar(13);
      
           putsf("MEMORY 1:");
           for (i=0;memory[i][1]!=0;i++)
               {
               itoa(memory[i][1],buffer);
               putchar(' ');
                puts(buffer);
               }
           putchar(13);
           memory_load++;
           }
           else
//we have already memorized two codes
//so now we will only compare received code with ones we remembered
           {
           for (memory_load=0;memory_load<2;memory_load++)
               {
               pass_ok=1;
              for (i=0;memory[i][memory_load]!=0;i++)
                   {
                   if (abs(bit_array[i]-memory[i][memory_load])>1)
//we have some 10% tolerance on bit duration.
                   pass_ok=0;
                   }
               if(pass_ok)
                   {
//we write which memorized key is recognized
                   putsf("RECOGNIZED KEY FROM MEMORY: ");
                   putchar(memory_load+'0');
                   putchar(13);
                   }
               }
           }
           for (i=0;i<101;i++)
           {
           bit_array[i]=0;
           }
       bit_index=0;
       bit_duration=0;
       ended=0;
       }
 
       };
}

Complete source code with project can be downloaded from this link.

Now let us discuss how we can save some memory.

  1. 1.We see that every bit have only two different durations so we can name them “1” or “0”. By doing this we will need only two bits instead of 2 bytes we are using now! That is 8 times less. We made this example this way because is is completely universal and not dependable on protocol or command length that is used.. You can easily adopt it to your needs if necessary.
  2. 2.Use external EEPROM memory so you will store all key codes to be remembered when device have no power. Today it is very cheap to but for example 4Kbit EEPROM memory and it is plenty of memory for this purpose.

On video below you can see above listed code in action:

As you can see on video microcontroller will write duration of each bit measured in x50uS because that is interrupt period. For first two buttons pressed on remote it will store them in memory and write memory content. After that only received bit timings will be written to serial port as well as position of stored command if it was previously stored. You can see more detailed picture of data being outputted to terminal on picture below. You can click on the image to enlarge it.

Terminal example output