Aus RN-Wissen.de
Wechseln zu: Navigation, Suche
Rasenmaehroboter fuer schwierige und grosse Gaerten im Test

K (rc5.c: #ifndef F_CPU)
(WebLinks)
Zeile 449: Zeile 449:
 
</pre>
 
</pre>
  
 +
===WebLinks===
 +
* [http://www.atmel.com/dyn/resources/prod_documents/DOC1473.PDF AVR410 (Application Note): ''RC5 IR Remote Control Receiver'', pdf (en)]
 +
===WebLinks===
 
[[Kategorie:Software]]
 
[[Kategorie:Software]]

Version vom 4. Dezember 2005, 04:23 Uhr

Dieser Artikel befindet sich möglichrweise im Aufbau. Siehe Diskussion:Codesammlung_avr-gcc

IR-Empfang

RC5 Decoder für AVR Mega

Beschreibung

Dieser Code impementiert einen interrupt-getriebenen RC5-Empfänger.

Auf eine Flanke an einem externen INT hin werden die nachfolgenden Pulslängen gemessen und in einer Struktur bereitgestellt, falls es sich um RC5-Code handelt und die Empfängeradresse übereinstimmt.

Nicht alle Fernbedienungen halten sich genau an die RC5-Spezifikation und haben oft einen mehr oder weniger starken Jitter auf dem Signal. In konstanten Zeitabständen auf den Port zu schauen und anhand des gelesenen Wertes das RC5-Signal aufzubauen, funktioniert daher nicht zuverlässig.

Resourcen

Resource Verbrauch (mit -Os)
I/O Timer0, 1 Pin für extern INT
Interrupts Timer0 Overflow, 1 externer IRQ
Flash ~ 0x180
SRAM statisch: 8
Stack: 11
Laufzeit  ?
externe Hardware IR-Empfänger wie TSOP17xx, TSOP18xx o.ä.
Schaltplan
Anschluss TSOP17xx an AVR, R1 ca 10kΩ
IR-Empfänger vom Typ TSOP17xx an AVR
R1 ca 10 kΩ

Interface

#define RC5_INT0 0
#define RC5_INT1 1

#define RC5_ALL 0xff

typedef struct
{
	uint8_t code;
	uint8_t addr;
	volatile char flip;
} rc5_t;

extern rc5_t rc5;
extern void rc5_init (uint8_t addr);
void rc5_init (uint8_t addr)
Initialisiert die Hardware für RC5-Empfang. Akzeptiert wird Code, der an Adresse addr geschickt wird. Falls addr = RC5_ALL bzw Bit 7 von addr gesetzt ist, werden alle Adressen akzeptiert.
extern rc5_t rc5
In rc5 wird der empfangene RC5-Code geliefert. Über dieses Objekt wird zudem der RC5-Empfang gesteuert. Wird ein Code mit der gewünschten Adresse ampfangen und ist rc5.flip = -1, dann wird der Code gespeichert und rc5.flip gesetzt wie gesendet. Danach wird der Empfänger solange inaktiv, bis der Anwender wieder rc5.flip auf -1 setzt.
rc5.code
der empfangene RC5-Code, falls rc5.flip != -1
rc5.addr
die Adresse, an die gesendet wurde, falls rc5.flip != -1
rc5.flip
das Flip-Bit
rc5.flip = 0
Code empfangen, RC5-Empfang inaktiv
rc5.flip = 1
dito
rc5.flip = -1
RC5-Empfang aktiv, wartet auf nächste Übertragung
Define default Werte Beschreibung
RC5_INT RC5_INT0 RC5_INT0, RC5_INT1 Code wird generiert für INT0 resp. INT1, zB gcc-Aufruf mit -DRC5_INT=RC5_INT1 erzeugt Code für INT1
RC5_PRESCALE 1024 64, 256, 1024 Legt den Prescaler für Timer0 fest. Standardeinstellung auf 1024, was zu F_CPU=16000000 passt. Für kleinere CPU-Frequenzen muss evtl ein kleinerer PRESCALE gewählt werden; das geht noch nicht automatisch.
F_CPU Gibt die CPU-Frequenz in Hz an

Seiteneffekte

SFRs

Der Code verwendet folgende SFRs und ändert deren Inhalt in den ISRs:

MCUCR: MCU Control Reg
GICR: Global Interrupt Control Reg
TCNT0: Timer0 Counter Reg
TIMSK: Timer Interrupt Mask Reg

Falls eines dieser SFRs veränder wird, nachdem RC5-Empfang aktiviert wurde, muss diese Änderung atomar erfolgen!

#include <avr/io.h>
#include <avr/interrupt.h>

   ...
   {
      sreg = SREG;
      cli();     
      TIMSK |= ...
      SREG = sreg;
   }
   ...

Prescaler

Timer0 verwendet den Prescaler. Ein Prescaler-Reset sollte aufgrund der langsamen Übertragung bei RC5 unkritisch sein.

Code

ANSI-C Nein (C++ Kommentare, anonymous struct)
Dateien rc5.h, rc5.c
getestet für ATMega8-16 @ 16MHz, Vcc = 5V
Portierung ATMegaXX: sollte ohne Anpassung laufen
ATTiny, Classic: Anpassungen erforderlich
Comment-Style

rc5.c

#include <avr/io.h>
#include <avr/signal.h>

#include "rc5.h"

#ifndef RC5_INT
#define RC5_INT      RC5_INT0
#endif  // RC5_INT

#ifndef RC5_PRESCALE
#define RC5_PRESCALE 1024
#endif  // RC5_PRESCALE

//////////////////////////////////////////////////////////////////////////////

rc5_t rc5;

//////////////////////////////////////////////////////////////////////////////

#ifndef F_CPU
#error Please define F_CPU
#endif // !F_CPU

// µs for a whole bit of RC5 (first & second part)
#define RC5_BIT_US   (64*27)

#define RC5_TICKS \
	((uint8_t) ((uint32_t) (F_CPU / 1000 * RC5_BIT_US / 1000 / RC5_PRESCALE)))
	
#define RC5_DELTA \
	(RC5_TICKS / 6)
	
typedef union 
{
	uint16_t w;
	uint8_t b[2];
	
	struct
	{
		unsigned code:6;
		unsigned addr:5;
		unsigned flip:1;
		unsigned agc:4;
	} __attribute__ ((packed));
	
} code_t;

static code_t code;
static uint8_t rc5_addr;

// Number of Bits received so far
// Number of Interrupts occured so far;
static uint8_t nbits;
static uint8_t nint;

//////////////////////////////////////////////////////////////////////////////
	
void 
rc5_init (uint8_t addr)
{
	nint = 0;
	nbits = 0;
	rc5.flip = -1;
	
	rc5_addr = addr;
	
#if (RC5_PRESCALE==1024)
	TCCR0 = (1 << CS02) | (1 << CS00);
#elif	(RC5_PRESCALE==256)
	TCCR0 = (1 << CS02);
#elif	(RC5_PRESCALE==64)
	TCCR0 = (1 << CS01) | (1 << CS00);
#else
#error This RC5_PRESCALE is not supported
#endif // RC5_PRESCALE
	
	// INTx on falling edge
	// clear pending INTx
	// enable INTx interrupt
#if (RC5_INT == RC5_INT0)		
	MCUCR |=   (1 << ISC01);
	MCUCR &= ~ (1 << ISC00);
	GIFR = (1 << INTF0);
	GICR |= (1 << INT0);
#elif (RC5_INT == RC5_INT1)		
	MCUCR |=   (1 << ISC11);
	MCUCR &= ~ (1 << ISC10);
	GIFR = (1 << INTF1);
	GICR |= (1 << INT1);
#else
#error please define RC5_INT
#endif // RC5_INT
}

//////////////////////////////////////////////////////////////////////////////

SIGNAL (SIG_OVERFLOW0)
{
	TIMSK &= ~(1 << TOIE0);
	
	uint8_t _nbits = nbits;
	code_t _code = code;
	
	if (26 == _nbits)
	{
		_nbits++;
		_code.w <<= 1;
	}
	
	if (27 == _nbits 
		&& 3 == _code.agc 
		&& 0 > rc5.flip)
	{
		uint8_t _rc5_code;
		uint8_t _rc5_addr;
		// we do the bit manipulation stuff by hand, because of code size
		_rc5_code = _code.b[0] & 0x3f; // 0b00111111 : #0..#5
		_code.w <<= 2;
		_rc5_addr = _code.b[1] & 0x1f; // 0b00011111 : #6..#10
		
		if (rc5_addr & 0x80
			|| rc5_addr == _rc5_addr)
		{
			rc5.code = _rc5_code;
			rc5.addr = _rc5_addr;
			char flip = 0;
			if (_code.b[1] & 0x20) // 0b00100000 : #11
				flip = 1;
			rc5.flip = flip;
		}
	}
	
	nint = 0;
	nbits = 0;
	
	// INTx on falling edge
	// clear pending INTx
	// enable INTx interrupt
#if (RC5_INT == RC5_INT0)		
	MCUCR |=   (1 << ISC01);
	MCUCR &= ~ (1 << ISC00);
	GIFR = (1 << INTF0);
	GICR |= (1 << INT0);
#elif (RC5_INT == RC5_INT1)		
	MCUCR |=   (1 << ISC11);
	MCUCR &= ~ (1 << ISC10);
	GIFR = (1 << INTF1);
	GICR |= (1 << INT1);
#endif
}

//////////////////////////////////////////////////////////////////////////////

#if (RC5_INT == RC5_INT0)		
SIGNAL (SIG_INTERRUPT0)
#elif (RC5_INT == RC5_INT1)		
SIGNAL (SIG_INTERRUPT1)
#endif // RC5_INT
{
	code_t _code = code;
	uint8_t _nint = nint;
	
	if (0 == _nint)
	{
		// INTx on both edges
		// clear pending INTx
#if (RC5_INT == RC5_INT0)		
		MCUCR &= ~ (1 << ISC01);
		MCUCR |=   (1 << ISC00);
		GIFR = (1 << INTF0);
#elif (RC5_INT == RC5_INT1)		
		MCUCR &= ~ (1 << ISC11);
		MCUCR |=   (1 << ISC10);
		GIFR = (1 << INTF1);
#endif // RC5_INT
	
		TCNT0 = 0;
		TIFR = (1 << TOV0);
		TIMSK |= (1 << TOIE0);
		_code.w = 0;
	}
	else
	{
		uint8_t tcnt0 = TCNT0;
		
		TCNT0 = 0;
		
		// Number of bits of the just elapsed period;
		uint8_t n = 1;
		// Bits received so far
		uint8_t _nbits = nbits;
	
		// is TCNT0 close to RC5_TICKS or RC5_TICKS/2 ?
		if (tcnt0 > RC5_TICKS + RC5_DELTA)
			goto invalid;
		else if (tcnt0 < RC5_TICKS/2 - RC5_DELTA)
			goto invalid;
		else if (tcnt0 > RC5_TICKS - RC5_DELTA)
			n = 2;
		else if (tcnt0 > RC5_TICKS/2 + RC5_DELTA)
			goto invalid;
		
		//	store the just received 1 or 2 bits
		do
		{
			_nbits++;
			if (_nbits & 1)
			{
				_code.w <<= 1;
				_code.b[0] |= _nint & 1;
			}
		} 
		while (--n);
		
		if (0)
		{
			invalid:
			
			// disable INTx, run into Overflow0
#if (RC5_INT == RC5_INT0)		
			GICR &= ~(1 << INT0);
#elif (RC5_INT == RC5_INT1)		
			GICR &= ~(1 << INT1);
#endif // RC5_INT			

			_nbits = 0;
		}
		
		nbits = _nbits;
	}

	code = _code;
	nint = 1+_nint;
}

rc5.h

#ifndef _RC5_H_
#define _RC5_H_

#include <inttypes.h>

#define RC5_INT0 0
#define RC5_INT1 1

#define RC5_ALL 0xff

typedef struct
{
	uint8_t code;
	uint8_t addr;
	volatile char flip;
} rc5_t;

extern rc5_t rc5;
extern void rc5_init (uint8_t addr);

#endif /* _RC5_H_ */

Beispiele

Beispiel: Anwendung

#include <avr/io.h>
#include <avr/interrupt.h>

#include "rc5.h"

	...
	// Code atomar machen
	uint8_t sreg = SREG;
	cli();
	// Gibt's was Neues?
	if (-1 == rc5.flip)
	{
		// Nein, dann zurück	
		SREG = sreg;
		return;
	}
	// Ja, dann code merken
	uint8_t code = rc5.code;
	// und auf nächstes Zeichen warten
	rc5.flip = -1;
	SREG = sreg;
	...

Beispiel: Initialisierung

#include <avr/io.h>
#include <avr/interrupt.h>

#include "rc5.h"

	...
	// RC5 initialisieren, alle Adressen zulassen
	rc5_init (RC5_ALL);

	// Interrupts zulassen
	sei();
	...

WebLinks

WebLinks


LiFePO4 Speicher Test