/*
rc5dec.c 0.2.0 - Interrupt driven RC5 decoder for Atmel AVR µC's.
Website: **broken link removed**
Copyright (C) 2003 Mark Haemmerling <rc5@markh.de>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
Preconfigured for INT1 (PD3) and Timer/Counter0.
*/
#include <inttypes.h>
#include <avr/signal.h>
#include <avr/interrupt.h>
#include <avr/io.h>
// set your clock speed here
#define XTAL 1000000
// you may have to experiment a little with this
// in case of heavy disturbance, try values around 60
// try to use odd values (for symmetrical level detection)
#define RC5RESAMPLE 11
// this autoconfigures all timer values:
#if XTAL < 285000
# define PRESCALEDIV 1
# define TIMERCR (1<<CS00)
#elif XTAL < 2300000
# define PRESCALEDIV 8
# define TIMERCR (1<<CS01)
#elif XTAL < 18000000
# define PRESCALEDIV 64
# define TIMERCR (1<<CS01) | (1<<CS00)
#else
# define PRESCALEDIV 256
# define TIMERCR (1<<CS02)
#endif
#define RC5TIMERFIRST 0x100-((uint8_t) (XTAL/PRESCALEDIV*0.0008890))
#define RC5TIMERSECOND 0x100-((uint8_t) (XTAL/PRESCALEDIV*0.0004445))
#define RC5TIMERCANCEL 0x100-((uint8_t) (XTAL/PRESCALEDIV*0.0008890))
volatile uint16_t rc5_code = 0;
volatile uint8_t rc5_first_half;
SIGNAL (SIG_INTERRUPT1) {
/* external interrupt handler
edge from IR receiver detected.
this is assumed to be the middle of a bit.
*/
uint8_t i;
// resample to filter out spikes
if (MCUCR & (1<<ISC10)) {
// we were waiting for a rising edge, so cancel if we sample low
for (i = RC5RESAMPLE; i; i--) if (!(PIND & (1<<PIND3))) return;
} else {
// vice versa...
for (i = RC5RESAMPLE; i; i--) if ((PIND & (1<<PIND3))) return;
}
TCNT0 = RC5TIMERSECOND; // preset timer to sample (ovf int) at second half (3/4 bit time)
rc5_first_half = 0; // next sample will be in the second half
sbi(TIMSK, TOIE0); // enable timer ovf int
cbi(GICR, INT1); // disable ext int (this handler)
}
SIGNAL (SIG_OVERFLOW0) {
/* timer overflow handler
sample the present level of the interrupt line.
*/
static uint16_t rc5_shift, rc5_temp;
static uint8_t rc5_bit = 0, level, first_level = 1;
uint8_t i = 0, temp;
// resample to filter out spikes
for (temp = RC5RESAMPLE; temp; temp--) if (PIND & (1<<PIND3)) i++;
level = (i > ((uint8_t) (RC5RESAMPLE/2)));
if (rc5_first_half == 2) {
// this is a timeout, cancel operation
goto rc5_cancel;
} else if (rc5_first_half == 1) {
// this sample is taken in the first half of the bit (1/4 bit time)
first_level = level; // save current level
if (level) cbi(MCUCR, ISC10); // currently at high level, wait for falling edge
else sbi(MCUCR, ISC10); // currently at low level, wait for rising edge
rc5_first_half = 2; // special code, means: next ovf int is a timeout
sbi(GICR, INT1); // much better: wait for edge ;)
TCNT0 = RC5TIMERCANCEL; // at timer ovf (in around 1/2 bit time) we should get a timeout
} else {
// this sample is taken in the second half of the bit (3/4 bit time)
TCNT0 = RC5TIMERFIRST; // next sample at first half (1/4 bit time) of next bit
rc5_first_half = 1;
if (first_level != level) {
// levels differ -> valid manchester encoded bit detected
if (!rc5_bit) {
// this is the first bit, reset values
rc5_temp = 0;
rc5_shift = 0x8000;
}
if (!level) rc5_temp |= rc5_shift; // low level (low-active) so store a '1'
else if (rc5_bit < 2) goto rc5_cancel; // high level ('0' bit) as startbit -> error
if (++rc5_bit == 14) {
// reception complete, stop operation
rc5_code = rc5_temp;
goto rc5_cancel;
}
if (rc5_shift == 0x0100) rc5_shift = 0x0020;
else rc5_shift >>= 1;
} else {
// error in manchester stream -> cancel operation
goto rc5_cancel;
}
}
return;
rc5_cancel:
cbi(MCUCR, ISC10); // falling edge
sbi(GICR, INT1); // ext. int. enabled
GIFR = (1<<INTF1); // clear an eventually set ext. int. flag
cbi(TIMSK, TOIE0); // disable timer int.
first_level = 1; // as the next ext. int. appears at the falling edge, assume the first level as high
rc5_bit = 0;
}
void rc5_init(void) {
/* init for ext. int. and timer.
call this once to activate the rc5 decoder.
*/
TCCR0 = TIMERCR;
cbi(DDRD, PD3); // pin is input
cbi(PORTD, PD3); // pullup disabled (receiver has defined output)
cbi(TIMSK, TOIE0); // timer ovf disabled. gets enabled by ext. int.
sbi(MCUCR, ISC11); // ext. int. activated by falling edge
cbi(MCUCR, ISC10);
GIFR = (1<<INTF1); // clear ext. int. flag
sbi(GICR, INT1); // enable ext. int.
}
int main(void) {
// example main program:
rc5_init(); // activate RC5 detection
DDRB = 0xff; // PORTB is output
PORTB = 0x00; // all pins low
// start main loop
for (;;) {
if (rc5_code) {
// ...do some action here...
PORTB = (uint8_t) rc5_code; // output low-byte at PORTB
rc5_code = 0; // clear code
}
}
}
// EOF