All pastes #1493895 Raw Edit

Stuff

public text v1 · immutable
#1493895 ·published 2009-07-13 19:28 UTC
rendered paste body
/*
   rc5dec.c 0.2.0 - Interrupt driven RC5 decoder for Atmel AVR \u013eC's.
   Website: http://markh.de/rc5dec/
   Copyright (C) 2003  Mark Haemmerling <rc5(at)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 8000000

// 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 25

// 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 32//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) {
ISR(INT1_vect) {
/*   external interrupt handler
   edge from IR receiver detected.
   this is assumed to be the middle of a bit.
*/
   uint8_t i;
   PORTD ^= ( 1 << PD7);
  
   // 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
   TIMSK |= ( 1 << TOIE0);
   GICR &= ~( 1 << INT1);
   //sbi(TIMSK, TOIE0); // enable timer ovf int
   //cbi(GICR, INT1); // disable ext int (this handler)
}

//SIGNAL (SIG_OVERFLOW0) {
ISR(TIMER0_OVF_vect) {
/*   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) MCUCR &= ~( 1 << ISC10);// cbi(MCUCR, ISC10); // currently at high level, wait for falling edge
      else MCUCR |= ( 1 << ISC10);//sbi(MCUCR, ISC10); // currently at low level, wait for rising edge
      rc5_first_half = 2; // special code, means: next ovf int is a timeout
      GICR |= ( 1 << INT1);//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:
   MCUCR &= ~( 1 << ISC10); //cbi(MCUCR, ISC10); // falling edge
   GICR |= ( 1 << INT1); //sbi(GICR, INT1); // ext. int. enabled
   GIFR = (1<<INTF1); // clear an eventually set ext. int. flag
   TIMSK &= ~( 1 << TOIE0); //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;
   DDRD &= ~( 1 << PD3); //cbi(DDRD, PD3); // pin is input
   PORTD &= ~( 1 << PD3); //cbi(PORTD, PD3); // pullup disabled (receiver has defined output)
   TIMSK &= ~( 1 << TOIE0); //cbi(TIMSK, TOIE0); // timer ovf disabled. gets enabled by ext. int.
   MCUCR |= ( 1 << ISC11); //sbi(MCUCR, ISC11); // ext. int. activated by falling edge
   MCUCR &= ~( 1 << ISC10); //cbi(MCUCR, ISC10);
   GIFR = (1<<INTF1); // clear ext. int. flag
   GICR |= ( 1 << INT1); //sbi(GICR, INT1); // enable ext. int.
}

int main(void) {
   // example main program:
   rc5_init(); // activate RC5 detection
   DDRD |= ( 1 << PD7);
   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
      }
   }
}