Inhaltsverzeichnis
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
addr
geschickt wird. Fallsaddr = RC5_ALL
bzw Bit 7 vonaddr
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 istrc5.flip = -1
, dann wird der Code gespeichert undrc5.flip
gesetzt wie gesendet. Danach wird der Empfänger solange inaktiv, bis der Anwender wiederrc5.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(); ...