Programmers

All on one SLOW page
Serial Port
Parallel Port
Smart Programmers
Serial Loaders

Projects

ATmega8 Serial LCD
ATtiny2313 Serial LCD
ATtiny4313 Serial LCD
ATmega328 SIRC
ATtiny2313 SIRC
40-pin Dev Board
28-pin Dev Board
AVR PS/2 Keyboard
AVR MAX232 RTS/CTS
AVR Dual RS232 Ports

Minimal Circuits

ATmega16
ATmega32
ATmega644
ATmega1284
ATmega8515
ATmega8535
ATmega8
ATmega48
ATmega88
ATmega168
ATmega328
ATmega162
ATmega128
ATtiny13
ATtiny2313
ATtiny4313
ATtiny24
ATtiny84
ATtiny25
ATtiny45
ATtiny85

Other Stuff

ATtiny13 vs ATtiny85
ATmega8 vs ATmega88
ATmega16 vs ATmega164
ISP and SPI
MAX232 Arduino
A small FAQ
Hardware Info

SIRC using an ATmega328

Page: 1 2 3 4 5

Library

This is the code that receives and decodes the IR from the universal remote.

To read the pulses, we set an I/O pin up as an input with the ability to interrupt on an edge. A timer is set on the first, and all subsequent falling edges. The pulse width is measured by the count of the timer when the line goes back high. When we start the timer, we also flip the interrupt to trigger on the rising edge. We let the timer continue to run, though. If the timer, which is set to expire in around 4 mS, ever does expire, it will signal the end of the received frame. During this time, we receive bits, rotate them into the appropriate bucket, depending on which bit we are on, and we count the bits so we know where we are in the frame. Remember, the timer will fire when the end of frame is reached, so we don't need to track the total bits received for that purpose. We just receive bits until the timer fires.

#ifndef __SIRC_H__
#define __SIRC_H__

#ifndef cbi
#define cbi(sfr, bit) (_SFR_BYTE(sfr) &= ~_BV(bit))
#endif
#ifndef sbi
#define sbi(sfr, bit) (_SFR_BYTE(sfr) |= _BV(bit))
#endif


class sirc
{
	public:
		sirc( );

		unsigned char address;
		unsigned char command;
		bool commandReady;
		bool commandBusy;
		bool commandLength;
		unsigned char programmedAddress;
		unsigned char error;
		unsigned char extra;

		void risingEdgeISR();
		void fallingEdgeISR();
		void timerISR();

	private:
		bool _inbit;
		unsigned char _bit_time;
		unsigned char _bit_count;
		unsigned char _command;
		unsigned char _last_command;
		unsigned char _first_command;
		unsigned char _address;
		unsigned char _bit_value;
		unsigned char _extra;
		unsigned char _pin;
};
extern sirc Sirc;

#endif

#include 
#include 
#include 
#include 

sirc Sirc;

ISR( TIMER2_OVF_vect )
{
	Sirc.timerISR( );
}

void rising_edge_routine( )
{
	Sirc.risingEdgeISR( );
}

void falling_edge_routine( )
{
	Sirc.fallingEdgeISR( );
}

sirc::sirc( void ) {

	_pin = 2;
	programmedAddress = 1;
	command = 0;
	address = 0;
	_bit_count = 0;
	extra = 0;
	_inbit = false;

	TCCR2A = 0;
	sbi( TCCR2B, CS22 );
	sbi( TCCR2B, CS21 );
	cbi( TCCR2B, CS20 );

	attachInterrupt( 0, falling_edge_routine, FALLING );
	sei( );
	pinMode( 2, INPUT );
	digitalWrite( 2, HIGH );
}
void sirc::fallingEdgeISR( )
{
	TCNT2 = 1;
	attachInterrupt( 0, rising_edge_routine, RISING );
	sbi( TIMSK2, TOIE2 );
	_inbit = true;
}

void sirc::risingEdgeISR( )
{
	_bit_time = TCNT2;
	attachInterrupt( 0, falling_edge_routine, FALLING );
	_inbit = false;
	_bit_value = 0;
	if (( _bit_time > 70 ) && ( _bit_time < 82 )) {
		_bit_value = 128;
	}
	if (( _bit_time > 150 ) && ( _bit_time < 160 )) {
		_bit_count = 0;
		return;
	}
	if ( _bit_count < 7 )
		_command = (_command >> 1) + _bit_value;
	if ( _bit_count < 12 )
		_address = (_address >> 1) + _bit_value;
	if ( _bit_count < 20 )
		_extra = (_extra >> 1) + _bit_value;
	++_bit_count;
}

void sirc::timerISR( )
{
	cbi( TIMSK2, TOIE2 );
	_command >>= 1;
	_address >>= 3;
	if ( _bit_count == 15 )
	{
		_address &= _extra;
	}
	if ( _address == programmedAddress )
	{
		if (( _first_command ^ _last_command ^ _command ) == _command )
		{
			if ( !commandBusy )
			{
				address = _address;
				command = _command;
				commandReady = true;
				commandLength = _bit_count;
			}
		}
		_last_command = _first_command;
		_first_command = _command;
	}
}

The timer ISR keeps track of the two previous commands, and only passes the command along if it matches those. Three consecutive matches is a good indication that you are getting the correct code. The commands are sometimes only partially received, causing the command to flip from one to another rapidly. This triple check makes sure that doesn't happen.

If the address matches, and the command passes the triple check, and the user has not put commands on hold, the new command is presented to the user and a flag is set telling him. It is a very good idea not to have one button toggle the state of something important, because the number of messages sent varies depending on how long the button is pushed. You may get one, or you may get 15. You must design your application to handle that possibility. For instance, on a mute functino, you should perform the action, then lock out commands for a second or so to prevent the function from being repeated (reversed) a few mS after being done correctly.

Page: 1 2 3 4 5