Hallo Alle,
es wird hier eine Routine beschrieben zum Dekodieren des RC-5-Codes. Gundlage ist eine, von einem vorhandenen Timer verfügbare Zeitbasis. Der Timer kann ansonsten für andere, konstante Zeitaufgaben benutzt werden. Ein solcher Timer ist in vielen Programmen bereits vorhanden.
Hintergrund:
Mein bisher vorhandener RC-5-Dekoder läuft als Hauptprogramm - ich muss nach dem Aufruf warten, bis eine Taste gedrückt wird. Damit kann ich z.B. verschieden Tasks bzw. Modi in meinem MiniD0 oder WALL R starten/schalten. Ich wollte immer schon eine ähnliche Routine im Interruptbetrieb. Ich kenne den Code im RNWissen von SprinterSB - wollte aber mal versuchen, einen schlankeren Code zu schreiben, die knappe Variante von PDannegger aus mikrocontroller-nett ist mir zu undurchsichtig. Ausserdem interessierte mich die tatsächliche Codegestaltung (die einzelnen Bits) und die problemlose Ausgabe dieser Dinge auf ein übliches Terminal. Na ja, ansonsten: es gibt besser und schöner geschriebene C-Passagen.
Der folgende Code beschreibt diese Routine(n), der "nackte" Code der Dekoder-IRS hat 23 Zeilen. Die Routine stellte das Befehlsbyte zur Verfügung, derzeit wird es in der Timer-IRS "gelöscht" - dort könnte es in eine FIFO o.ä. übertragen werden. Das ist hier nicht geschehen.
Vielleicht nutzt jemandem das Ganze.
Gegebenheit:
Ein vorhandener Timer-Interrupt der als Bordzeit eingeführt wurde zur Zeitnahme verschiedener Sensoren und derzeit verschiedene Zeitabläufe steuert. Interruptabstand 50 µs, eine Zeitscheibe (zwischen zwei Interrupts) nenne ich tupsi =: Time Unit Per Sensor Interrupt.
Der Sensor ist ein SFH5110, der in ähnlicher Schaltung wie beim asuro verwendet wird. Damit wird das Signal der Infrarotfernsteuerung invertiert auf den Controllerpin geschaltet.
Der Code wurde für die RNControl mit mega1284/20 MHz geschrieben, ein Anpassung sollte leicht/wird möglich sein.
Aufgabenstellung:
RC-5-Dekoder im Interrupt
Interruptquelle ist ein externer Interrupt, hier extINT2 am mega1284. Interruptauslöser ist ein SFH5110 in der Schaltung à la asuro.
Erweiterte Aufgabe:
Im Testbetrieb werden die Codebits und der Befehlscode dezimal über UART ausgegeben.
Code:
Das erste Codefenster zeigt die "nackte" Interruptroutine
Das zweite Codefenster zeigt >>auszugsweise<< - aber hoffentlich komplett ALLE erforderlichen Definitionen und Deklarationen (keine Funktionsprototypen) und Codesequenzen.
Anmerkungen:
Die Zeit für 1 Codebit beträgt beim RC-5 1,778ms, in meinen tupsi à 50 µs sind das etwa 33 .. 38 Zeitscheiben, genauer: 35,56. Wegen der Toleranzen habe ich das entsprechende Zeitfenster - heisst bei mir RCbit_zt, auf 26 bis 44 tupsi gesetzt. Entsprechende Anpassungen sind erforderlich, wenn beim Verwenden der Routinen eine andere Zeitbasis zur Verfügung steht. Wenn innerhalb dieses zulässigen Zeitfensters eine Interrupt erfolgt, wird er ausgewertet und die Messzeit für das nächste Bit wieder auf Null gesetzt. Durch dieses Vorgehen "justiert" sich die Routine in weiten Grenzen auf die Fernsteuerung.
Im Code wird über USART das Bitmuster des Codes ausgegeben. Es wird anschließend NUR das Befehlsbyte weiter verarbeitet (meine Billigst-IR-RC hat garkein Adressbyte) und auch das Togglebit wird nicht ausgewertet. Das Befehlsbyte wird ebenfalls auf USART ausgegeben - damit ist es einfach möglich, den Tastencode vorhandener Fernsteuerungen zu analysieren. Schließlich wird die Gesamtdauer eines Signalzyklus einer Fernsteuerung im Rahmen der Zeitbasis angegeben, damit die mögliche Reaktionszeit des Codes und Abweichungen der Fernsteuerung von der Standardfrequenz abgeschätzt werden können.
Für Risiken und Nebenwirkungen wird nicht gehaftet. Eine kommerzielle Nutzung ist ausschließlich nur nach Rücksprache und meiner Zustimmung erlaubt.
Codefenster eins
Code:
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Start RC5-Decoding: Level (RC5prt & (1<<RC5pin)) ist low
// ===== RC-5-DECOding-Byte (RCDECO) ist Null
// RCvorsc >= 99 -- nur dann ist "vorher" länger high
// Es folgen 23 Zeilen relevanter Code zum Dekodieren
if ((!(RC5prt & (1<<RC5pin))) && (!RCDECO) && (RCvorsc > 99)) //
{ //
RCges_zt = 18; // ?? Gesamtzeit in tupsi f EINEN kplt CodeSATZ
RCBptr = 13; // RC-5-Code: Bitpointer (0..13) f RC-5-Code-Word
RCDECO |= ((uint16_t)1<<13 ); //
sendUSART ("\t1"); //
RCBptr = 12; // ... und pointer auf nächstes Bit
RCbit_zt = 0; // Zeit für 1 Codebit 1,778ms, in tupsi 33 .. 38
} //
if (RCBptr >= 0) //
{ //
if ((RCbit_zt>26) && (RCbit_zt < 44)) // <<## gute Funktion, so bleibts
{ // Ist ein gültiges Bit erkannt worden ?
if (!(RC5prt & (1<<RC5pin))) // High oder low level ?
{ //
RCDECO |= ((uint16_t)1<<RCBptr ); // Bei LOW schreib "1" weg
sendUSART ("1"); //
} // Ende if (!(RC5prt & (1<<RC5pin))) : High oder low
if (RC5prt & (1<<RC5pin)) sendUSART ("0");
RCBptr -- ; // ... und pointer auf nächstes Bit
RCbit_zt = 0; // Zeit für 1 Codebit 1,778ms, in tupsi 33 .. 38
} // Ende if ((RCbit_zt>26) && (RCbit_zt < 48))
} // Ende if (RCBptr >= 0)
Codefenster zwei
Code:
#define LCg 6 // gnLED2 auf PC6 - onboard RNControl
volatile int16_t Izeit_1; // Wertbereich int16: 32.767. uint16: 65.535
volatile int16_t Izthrznt; // Der zeitliche Horizont, z.B. 20000 für 2 sec
volatile int16_t Isecundn; // Sekunden Programmlaufzeit, 32.767 sec sind
// 546,117 Minuten bzw. 9 Std
volatile int16_t icntdwn; // Countdownzähler (max 32767 = 9 Std)
// ============================================================================== =
// === RC-5_Daten, Daten fürs RC-5 Decoding
// ============================================================================== =
#define RC5prt PINB // Eingangsport für RC5-Decoding
#define RC5pin PB2 // Pointer auf gewählten Sensorpinn für RC-5
volatile int16_t RCCODE [12]; // 12 Words für komplette Codes + Flags + Reserve
volatile uint8_t RCCptr; // Pointer auf den aktuellen Code
volatile int16_t RCDECO; // Word für EINEN komplett dekodierten Code
volatile int8_t RCBptr; // RC-5-Code: Bitpointer für RC-5-Code-Word
// beim Dekodieren. Beginn: 14 für Startbit 1
volatile uint8_t RC5roff; // RC5-read-off aus- (=1) / ein- (=0) -schalten
// roff = 0 <=> Lesen ist ein, 1 <=> Lesen ist aus
// - - - - - - - - - - - - - - -
// Zeiten
volatile int16_t RCbit_zt; // Zeit für EIN Codebit 1,778ms, in tupsi 35,56
// <=> mit Toleranz 33 .. 38
volatile int16_t RCges_zt; // ?? Gesamtzeit in tupsi für einen kplt. CodeSATZ
volatile uint16_t RCvorsc; // Vorstartcounter - TupsiZeit vor dem ersten Bit
volatile uint16_t RCzeit1; // RC-5-zeit. Wird im main genullt und im timer
// freilaufend+unkontrolliert hochgezählt wird bis zum Überlauf
// ============================================================================== =
// ============================================================================== =
// ============================================================================== =
// === Initialisierung fuer Timer2 mega168, m328, m1284 und Ähnliche
void TC2TMR_init(void) // Init Tmr/Cntr 2, 8-Bit auf 20 kHz = 50 µs
{ //
TCCR2A |= (1<<WGM21); // Timer im CTC-Mode, Top=OCR2A doc S 157
TCCR2B |= (1<<CS21); // Prescaler 1/8 / Clock <- CPU doc S 158
OCR2A = 124; // Preset 124 für 50µs bei 20Mhz
TIMSK2 |= (1<<OCIE2A); // Tmr/Cntr2 CompareA interrupt enabled
Izeit_1 = 0; // Laufzeit nullen - unabhängig von Isecndn !!
} // Ende void TC2TMR_init(void)
// ============================================================================== =
// ============================================================================== =
// === Nicht unterbrechbare ISR für timer2 => zählt hoch im Takt 20 kHz = 50 µs
ISR(TIMER2_COMPA_vect) // Vektor 7
{ //
if (Izeit_1 <= Izthrznt) //Interrupt-Timer = 1 ... 20 000 ... (1 sec blink)
{ //
Izeit_1 ++; // Izeit_1 bleibt bis 32000 in der int16-Grenze
} //
else // in if (Izeit_1 <= Izthrznt)
{ // ... Eine Sekunde ist voll =>
Izeit_1 = 1; // ansonsten: Rückstellen auf Eins
icntdwn = icntdwn + 1; // Countdownzähler hoch(!!)zählen
PORTC ^= (1<<LCg); // gnLED auf Pin PC6 toggeln <=> Heartbeat
Isecundn ++; // Sekundenzähler hochtackern, max 9 Std
} // Ende if (Izeit_1 < Izthrznt)
// - - Ende der eigentlichen Timer2-ISR
// - - - - - - - - - - - - - - -
// - - Jetzt für RC-5-Analyse Zeiten etc.
if (RC5prt & (1<<RC5pin)) // WENN RC5-pinn high; Encoder empfängt nichts
{ // => zähle Vorstartcounter hoch
// Vorstartcounter in IRS(INT2_vect) nullen
RCvorsc ++; // RC-5-Vor-Sequenz-Counter bis 100 hochzählen
if (RCvorsc > 100) RCvorsc = 100; // Begrenze RCvors
} // Ende if (RC5prt & (1<<RC5pin))
// - - - - - - - - - - - - - - -
RCbit_zt ++; // Zeit für 1 Codebit 1,778ms, in tupsi 33 .. 38
RCzeit1 ++; // Tupsicounter uint16_t für RC-5-Decoding
RCges_zt ++;
if (RCges_zt >= 490 && RCDECO) // Endemarkierung RC-5-Deco 27Nov2012-15:22
{ //
RCDECO = 0; //
} //
if ( RCzeit1 > 2000) // "Reset" Dekodierung bei einer zehntel Sekunde
{ //
RC5roff = 1; //
RCzeit1 = 0; //
} //
// - - - - - - - - - - - - - - -
return; //
} // Ende ISR(TIMER2_COMPA_vect)
// ============================================================================== =
// ============================================================================== =
// === Initialisierung fuer EXT_INT2 auf Pin PB2 bei m1284 für
// Vector 4 {1-35}, Progr.addr. $0006 INT2 External Interrupt Request 2
// ============================================================================== =
void XTI_2_init( void ) // Init. INT 2 auf any edge für RC-5
{ // d.h. EICRA ISC20 = 1 doc S67
// - - - - - - - - - - - - - - - -
EICRA |= (1<<ISC20); // Interrupt auf any edge
EIMSK |= (1<<INT2); // und erlaube INT2 in EIMSK
} // Ende void XTI_2_init( void )
// ============================================================================== =
// ============================================================================== =
// === ISR für EXT_INT2 auf Pin PB2 zum Dekodieren von RC-5
ISR (INT2_vect) //
{ //
int16_t RCbb;
char wortadc[12]; // Übersetzungsfeld für Werteausgabe
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Start RC5-Decoding: Level (RC5prt & (1<<RC5pin)) ist low
// ===== RC-5-DECOding-Byte (RCDECO) ist Null
// RCvorsc >= 99 -- nur dann ist "vorher" länger high
if ((!(RC5prt & (1<<RC5pin))) && (!RCDECO) && (RCvorsc > 99)) //
{ //
RCges_zt = 18; // ?? Gesamtzeit in tupsi f EINEN kplt CodeSATZ
// hier ein offset, weil ja 1/2 Bit vorbei ist
RCBptr = 13; // RC-5-Code: Bitpointer (0..13) f RC-5-Code-Word
// fängt auf Position 14! an mit >>13<< (0..13) für Startbit 1!
// Hauptaufgabe: Schreibe Bit ins Target
RCDECO |= ((uint16_t)1<<13 ); //
sendUSART ("\t1");
RCBptr = 12; // ... und pointer auf nächstes Bit
RCbit_zt = 0; // Zeit für 1 Codebit 1,778ms, in tupsi 33 .. 38
} //
// - - - - - - - - - - - - - - - -
if (RCBptr >= 0) //
{ //
if ((RCbit_zt>26) && (RCbit_zt < 44)) // <<## gute Funktion, so bleibts
{ // Ist ein gültiges Bit erkannt worden ?
if (!(RC5prt & (1<<RC5pin))) // High oder low level ?
{ //
RCDECO |= ((uint16_t)1<<RCBptr ); // Bei LOW schreib "1" weg
sendUSART ("1"); //
} // Ende if (!(RC5prt & (1<<RC5pin))) : High oder low
if (RC5prt & (1<<RC5pin)) sendUSART ("0");
RCBptr -- ; // ... und pointer auf nächstes Bit
RCbit_zt = 0; // Zeit für 1 Codebit 1,778ms, in tupsi 33 .. 38
} // Ende if ((RCbit_zt>26) && (RCbit_zt < 48))
} // Ende if (RCBptr >= 0)
// - - - - - - - - - - - - - - - -
// TESTWEISE Ausgabe Codewort und Zeitbedarf
if ((RCBptr < 0) && (RCges_zt < 1000))
{ //
RCBptr = 0; // doppelte Ausgabe verhindern
sendUSART(" => dez: "); // Ausgabezeile eröffnen
// lo = word & 0x7f; 0x7F wegen 7 Bit Befehlsbyte
RCbb = RCDECO & 0x7F;
itoa(RCbb, wortadc, 10); // aktueller Wert
sendUSART(wortadc); // ... ausgeben.
sendUSART(" , RCges_zt = "); // Neue Zeile
itoa (RCges_zt, wortadc, 10); // aktueller Wert
sendUSART(wortadc); // ... ausgeben.
sendUSART("\r\n"); // Neue Zeile
} // Ende if (RCBptr < 0)
// - - - - - - - - - - - - - - - -
} // Ende ISR (INT2_vect)
// ============================================================================== =
So wie im Codefenster unten sieht ein Testlauf aus mit Ausgabe am Terminal von br@y. Die Ausgabe nach "... Aktion" wird vom vorgestellten Code geliefert : Bitmuster der gesamten Codesequenz, dezimale Entsprechung des sechsbittigen Befehlsbytes, Zeitdauer der Dekodierung vom Beginn des Codes bis zum Ende. Man kann auch schön die Funktion des Toggelbits (Bit 3) erkennen.
Zum Zeitbedarf, hier in 50µs-tupsi: 482 tupsi zu 50 µs sind 24,2 ms; da das erste Halbbit fehlt - es kann ja erst auf die erste Flanke des Datentelegramms getriggert werden, die ist aber in der Mitte vom Startbit 1 - komme ich ziemlich genau auf die fast 25 ms der gesamten Telegrammlänge.
Code:
C501 R5M_x15 m1284p/20MHz 27Nov2012 15:28
I2C >>400kHz [t05], I2C mit Taste [gelb], dann [OK] zu MoCo328
Motoren rauf+runter mit Taste [P100]
I2C-Slave ist Adresse : 132 dez = 0x84 ,
Gute Funktion mit extINT2 für RC-5
Aktuell - Zur-Verfügung-Stellung des RC-5-Code
Aktiv : Taste [MIX], [P100], [9] und [gelb]
Bitte um Aktion _
11000000010100 => dez: 20 , RCges_zt = 483
11100000011001 => dez: 25 , RCges_zt = 482
11000000100011 => dez: 35 , RCges_zt = 482
11100000011011 => dez: 27 , RCges_zt = 482
11000000001010 => dez: 10 , RCges_zt = 484
11100000000110 => dez: 6 , RCges_zt = 483
Rückmeldungen sind erwünscht.
Lesezeichen