(→WebLinks) |
K (→WebLinks) |
||
| Zeile 451: | Zeile 451: | ||
===WebLinks=== | ===WebLinks=== | ||
* [http://www.atmel.com/dyn/resources/prod_documents/DOC1473.PDF AVR410 (Application Note): ''RC5 IR Remote Control Receiver'', pdf (en)] | * [http://www.atmel.com/dyn/resources/prod_documents/DOC1473.PDF AVR410 (Application Note): ''RC5 IR Remote Control Receiver'', pdf (en)] | ||
| − | + | ||
[[Kategorie:Software]] | [[Kategorie:Software]] | ||
Version vom 4. Dezember 2005, 04:24 Uhr
Dieser Artikel befindet sich möglichrweise im Aufbau. Siehe Diskussion:Codesammlung_avr-gcc
Inhaltsverzeichnis
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
|
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
addrgeschickt wird. Fallsaddr = RC5_ALLbzw Bit 7 vonaddrgesetzt ist, werden alle Adressen akzeptiert. extern rc5_t rc5- In
rc5wird der empfangene RC5-Code geliefert. Über dieses Objekt wird zudem der RC5-Empfang gesteuert. Wird ein Code mit der gewünschten Adresse ampfangen und istrc5.flip = -1, dann wird der Code gespeichert undrc5.flipgesetzt wie gesendet. Danach wird der Empfänger solange inaktiv, bis der Anwender wiederrc5.flipauf-1setzt.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
