PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Mein Dekoder für RC-5 in C im Interruptbetrieb



oberallgeier
27.11.2012, 18:08
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 (http://www.mikrocontroller.net/topic/12216#new) 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

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

#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.


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.

oberallgeier
26.06.2020, 19:28
Ein paar Schräubchen gedreht an der IR-Fernsteuerung (Schlechtwetterbeschäftigung).

Seit meinen ersten Robotern – Dottie über WALL R und archie – erfolgt deren Steuerung mit ner alten TV-Fernbedienung von einem längst entsorgten Fernseher. Dessen IR-Code ist Philips RC-5, siehe z.B. Unterlagen von San Bergmans hier (https://www.sbprojects.net/knowledge/ir/rc5.php) – mit den Feinheiten zum Manchestercode auf 36 kHz-Basis.


/* ================================================== =========================== =
================================================== =========================== =
Beispiele für RC-5-Signalgang/Manchester-Codierung

| | | | Bitdauer 2x889 µs (2*17,78 tupsi) => 1,778 ms/35,56 tupsi
+---+ | | +---+ Für den Wert des Bits ist Übergang in Bitmitte massgebend
|HHH| | | |HHH|
|HHH+---+ +---+HHH| ==> Übergang von 1 nach Null <=> Bitwert 0
|Logic 0| |Logic 1| ==> Übergang von 0 nach 1 <=> Bitwert 1

Beispiel (Graphik zeigt neun Bits) :
| | | | | | | | | |
| +-+-+ | +-+-+ | +-+ +-+-+ +-+ +-+ | 1 Bit 1,778 ms / 35,56 tupsi < s.u.
| | | | | | | | | | | | | | | | | | | 14 Bit 24,892 ms / 497,84 tupsi
+-+ | +-+-+ | +-+-+ +-+ | +-+ +-+ +-+ Gap 114 ms / 2800 tupsi
Bit- | | | | | | | | | |
Wert 1 0 1 0 1 1 0 0 0
// - - - - - - - - - - - - - - -
Der Rest der RC-Variablen (die folgenden) ist im Prinzip unnötiger Schotter
und wurde am 08 Nov 2013 entfernt (bis Rev. ~x30 enthalten)
>>>> Messung (26.6.2020) mit DISCOVERY_2 am Steckbrett mit zwei parallel <<
>>>> geschalteten SFH5110 zeigt eine Varianz der Pulse zwischen <<
>>>> ca. 850 µs und 980 µs >> entsprechend 17 (34) und 19,6 (39) tupsi <<
tupsi sind 50µs-Zeitscheiben einer Interruptroutine für heartbeat etc.
================================================== =========================== =
================================================== ===========================*/

Die Bedienung ist ab archie angepasst an die übliche TV-Fernbedienung: es gibt einerseits Ein-Tasten-Befehle (mute, START/STOP, vor(auf), zurück(ab), links, rechts, swap uvm). Implementiert ist aber auch die übliche 3-Ziffern-Kombination, die ich RC5-3 nenne. RC5-3 läuft bei archie wie beim Fernseher – wird eine Ziffer gedrückt, wartet der (Befehls-) Empfänger ne Weile auf eine zweite, danach ne Weile auf ne dritte Ziffer. Wird statt einer Ziffer eine andere Taste gedrückt, wird die laufende 3-Ziffern-Eingabe abgebrochen. Ist der 3ziffrige Befehl eingetippt, wird er sofort angesteuert/eingeleitet. Dauert die Pause nach einer Zifferneingabe zu lange, wird die laufende Eingabe abgebrochen. Als optische Eingabekontrolle dient eine grobpixelige LED-anzeige (12x10-Matrix – ne ehemalige PingPongPlatine (http://www.elektronik-labor.de/Lernpakete/Pingong.html)), die auch in der untersten LED-Reihe einen kleinen „Strich“ als Busymarke beim RC5-3 zeigt.

Zusätzlich ist bei archie natürlich zu wünschen, dass Befehle aus beliebigen Richtungen (rundum) empfangen werden. Es ist daher auf einen zuverlässigen Rundum-Empfang der Steuercodes zu achten. Dies erfolgt durch mehrere IR-Empfängerplatinchen. Verwendet werden die ICs SFH5110 oder nach dessen Abkündigung TL1838.

Weiter soll der Decoder das Doppeln von Tasten verhindern.

Grobskizze des Decoders:
- IR-Decoding vom IR-Empfänger auf einem PCINT
- Start Timer sobald ein Signal (LOW) erkannt wird nach einer Pause von mind. 4,95 ms.
..Durch diese Pause ist sichergestellt, dass kein Zeichen "kurz vorher" akzeptiert wird.
- Decodieren der 14 Bits des Manchestercodes
- Wird danach ein Zifferncode erkannt –
..weiter zu RC5-3 Abschnitt, sonst Befehlsaufruf je nach Codewert.
Probleme machte mir bisher z.B. das Doppeln – wenn man zu lange drückt oder die Fernsteuertasten „doppeln“. Der Code wurde dahin überarbeitet. Dazu wurden einige längst beabsichtigte Messungen mit zwei verschiedenen IR-Empfängertypen durchgeführt. Der Signalausgang dieser Empfänger wurde am Eingangsport des Controllers zusammengeschlossen.

Die Messungen (ANALOG-DIGILENT2) zeigen Signalgänge:
1. Zwei TL1838 an EINEM Controllerport – Abgriff am Porteingang; 1 ms/DIV (https://dl.dropbox.com/s/03c5e3fj2zar10a/W10_RC5-3_2xTL1838_x01.jpg?dl=0).

2. Zwei TL1838 an EINEM Controllerport – Abgriff am Porteingang; 100 µs/DIV (https://dl.dropbox.com/s/58ehtxw5qav0rjh/W10_RC5-3_2xTL1838_x02.jpg?dl=0).

3. High-Low-Übergang von TL1838-bl und SFH5110-ge; 1 µs/Div. Der moderner (https://dl.dropbox.com/s/76t4ep9lsd2t7kw/W10_RC5-3_TL1838bl-vers-SFH5110ge_x01.jpg?dl=0)e
..…TL1838 (blaue Linie) schaltet viel sauberer.

4. IR-Sendediode (in der Fernbedienung) und TL1838. Der IR-Empfänger schalte (https://dl.dropbox.com/s/9fbjez1m9wwnoq5/W10_RC5-3_TL1838versRC-Ctrl_x01.jpg?dl=0)t
…..nach ca. 8 36-kHz-Pulsen auf low. 100 µs/DIV.

5. IR-Sendediode (in der Fernbedienung) und SFH5110. Der IR-Empfänger schalte (https://dl.dropbox.com/s/7l3l8or1h2miugt/W10_RC5-3_SFH5110versRC-Ctrl_x01.jpg?dl=0)t
..…nach ca. 6 36-kHz-Pulsen auf low. 100 µs/DIV.

Schaltung des IR-Empfängers :
. . . . . Vcc . . . . > . . 100R . . - . . > . .SFH5110/Vcc . . . sinngemäß mit TL1838
. . . . . GND . . . . > . . SFH5110/GND . . . . . . . . . . . . . . sinngemäß mit TL1838
. . . . . SigOut . . .> . .SFJ5110/SigOut < 22k>Vcc-in . . .. sinngemäß mit TL1838
=>=>=> Diese Schaltung mit dem kleinen Vorschaltwiderstand und dem 22k-PullUp bietet eine sehr sichere Funktion.
Kondensatoren (z.B. lt. Datenblatt) stören ! ! !

Vielleicht interessiert´s.

Klebwax
27.06.2020, 19:03
Rückmeldungen sind erwünscht.

Nur mal eine Sache: Du bist sehr großzügig mit volatile. Ist das wirklich nötig? Das bremst den Optimizer komplett aus. Und du gibst Strings im Interrupt-Handler über die serielle aus. Das könnte dir deinen 50µs Interrupt ziemlich aus dem Tritt bringen.

MfG Klebwax

oberallgeier
28.06.2020, 10:41
Danke Klebwax für Deine Rückmeldung.


.. Du bist sehr großzügig mit volatile. Ist das wirklich nötig? Das bremst den Optimizer komplett aus ..Es ist sicher nicht immer nötig. Ach wenns nur das wäre. Meine C-Kenntnisse gehen ja zurück auf einen autodidaktischen Crashkurs vor etlichen Jahren. Wenig gelernt und inzwischen manches vergessen; ich nenne das ja eher Cäh als C. Ja, vergessen ist keine Entschuldigung - und manchmal mühe ich mich wegen irgendwelcher (mir) unerklärlicher Fehler lange ab . . . In meinem Fall macht selbst Übung nicht mehr den Meister. Deiner Anmerkung werde ich aber mal gewissenhaft nachgehen.


.. du gibst Strings im Interrupt-Handler über die serielle aus. Das könnte dir deinen 50µs Interrupt ziemlich aus dem Tritt bringen ..Das tuts sicher - auch wenn ich z.B. mein PingPong-Display mit 256 KBd anspreche. Dabei ist der Standardstring drei Zeichen lang - an die 120 µs - siehe hier (https://dl.dropbox.com/s/krqktc26zgnzoxo/UART-PPong-256k_28052020-0943.JPG?dl=0). Abgesehen von dieser Display-Anzeige wird im Standardbetrieb kaum was über UART gesendet, da läuft fast alles per I²C - sicher auch nicht problemlos. Allerdings meine ich, dass meine Zeitmessung den Verlust von manchem Takt verkraften kann bzw. verkraftet - jedenfalls merke ich den meisten Bewegungen von archie nicht wirklich irgendwelche Taktlosigkeiten an.


.. Das könnte dir deinen 50µs Interrupt ziemlich aus dem Tritt bringen ..Noch schlimmer: meine Regelungsroutine läuft alle 10 ms >innerhalb< der ansonsten sehr kurzen Timer-ISR. Dazu mal n Zitat aus meinem Laborbuch (-file) :

............30. August 15; MoC4_x26 Messung Umfangsgeschwindigkeit
........................// Der Zeitbedarf für die Regelung wurde gemessen: MoC4_tmr26.c, GOULD 20 MHz
........................// rgl_nn-Aufruf und regelpre..=0 ohne Regelung 1,5 µs, mit Regelung 5,1 µs
............Bei Aufruf Regelung void rgl_12(void) OHNE Geschwindigkeitsvorgabe (bzw sspeed <= 3) wird
............ja die Funktion sofort wieder verlassen, siehe Quellcode in ~mot~.
............Die gemessene Zeit ist unabhängig davon, ob in den Regelfunktionen rgl_nn der Interrupt erlaubt
............wird (sozusagen nested interrupts) oder nicht ! ! !

Aber meine Controller laufen ja mit 20 MHz; fast durchwegs Atmel, auch nanoclones etc.

Danke Klebwax!

Moppi
28.06.2020, 12:18
Meine Faustregel:

Volatile immer bei allen Variablen, die in einer Interrupt-Service-Routine genutzt werden, außerhalb der ISR deklariert sind (Zählervariablen z.B.) und deren Inhalt nicht verloren gehen darf, wenn die ISR beendet wird.

Variablen die nur innerhalb der ISR gebraucht werden, solange die ISR ausgeführt wird, würde ich, für mich, innerhalb der ISR deklarieren (ohne volatile). Dann könnten sie vom Compiler optimiert werden, dass evtl. nur Register- statt Speicherzugriffe stattfinden.

Ich habe ein RETURN gefunden, ich glaube, das ist nicht notwendig:


// - - - - - - - - - - - - - - -
return; //
} // Ende ISR(TIMER2_COMPA_vect)


MfG

- - - Aktualisiert - - -



Aber meine Controller laufen ja mit 20 MHz; fast durchwegs Atmel, auch nanoclones etc.!

Würde ich prüfen, ob das notwendig ist. Ich meine, mit steigernder Geschwindigkeit steigt i.R. auch der Strombedarf. Muss man mal im Datenblatt schauen.

oberallgeier
28.06.2020, 17:02
Nur mal eine Sache: Du bist sehr großzügig mit volatile. Ist das wirklich nötig? Das bremst den Optimizer komplett aus ..So, die Geschichte mit dem volatile hab ich mal (wieder :-/ ) durchgeackert. Ich hoffe, dass ich dieses Typenattribut (type qualifier) zukünftig auf wirklich notwendige Fälle beschränken werde und mit der Zeit die vorhandenen, unnötigen Fälle vollständig entferne. Ich halte es im Auge.


.. Ich habe ein RETURN gefunden, ich glaube, das ist nicht notwendig ..Stimmt, unnötig. Ein Relikt aus gaaanz frühen Jahren. Aber ein Vergleich der *.lls-Datei zeigt, dass der Compiler sich damit zu keiner unnötigen Aktion verleiten lässt.


.. Controller laufen ja mit 20 MHz; .. prüfen, ob das notwendig ist .. mit steigernder Geschwindigkeit steigt i.R. auch der Strombedarf ..Was heißt notwendig? Die ganze Controllersammlung im archie (aktuell neun Platinen - ein Master und 8 Slaves) hat ne Menge zu tun. Zusammen mit den Motoren ist der gesamte Leistungsbedarf bei knapp 10 Watt - in Ruhe, bei Bewegung (Gestik) bis über vierzig Watt, im Fahrbetrieb darüber - da sind ein paar Milliwatt mehr ohne Bedeutung. Der Vorteil liegt nicht zuletzt darin, dass z.B. die Kommunikation UART und I²C durch den gleichen, hohen Quarztakt nach meiner Erfahrung ziemlich sicher wurde - trotz der Durchseuchung mit Interrupts.

Moppi
28.06.2020, 17:15
Was heißt notwendig?
Manchmal braucht man die hohe Frequenz für die Aufgaben gar nicht, da tut es auch weniger.
Wenn es um Störanfälligkeit geht, wird es mit zunehmender Frequenz i.R. nicht besser, sondern eher ist das Gegenteil der Fall.


MfG

Klebwax
28.06.2020, 19:30
Volatile immer bei allen Variablen, die in einer Interrupt-Service-Routine genutzt werden, außerhalb der ISR deklariert sind (Zählervariablen z.B.) und deren Inhalt nicht verloren gehen darf, wenn die ISR beendet wird.

Da hat volatile nichts zu suchen und bewirkt auch nichts. Variablen, die die Laufzeit einer Funktion überleben sollen ansonsten aber nur von ihr verwendet werden, erzeigt man local und macht sie static.

Volatile ist dann nötig, wenn sowohl main() als auch der Interrupthandler sie verwenden. Oder wenn zwei Interrupthandler auf sie zugreifen. Allgemeiner, wenn man damit rechnen muß, daß sich eine Variable außerhalb des gerade laufenden Programmkontextes ändert, muß sie volatile (unbeständig) deklariert werden.

MfG Klebwax

oberallgeier
28.06.2020, 19:57
.. Volatile ist dann nötig, wenn .. Oder wenn zwei Interrupthandler .. Allgemeiner, wenn man ..Ich freue mich. Ich habs ja nur mit den Quellen Kernighan/Ritchie, im microcontroller.net (z.B. hier (https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Anforderungen_an_Interrupt-Routinen)), im RNWissen, z.B. hier (https://rn-wissen.de/wiki/index.php?title=C-Tutorial#Speicherklassen) und bei den AVR-Freaks durchgearbeitet. Aber ziemlich so hatte ich das verstanden (Verwendung in UND ausserhalb einer ISR und so . . .).

oberallgeier
17.07.2020, 16:16
Die Redewendung ".. hinten keine Augen haben .." ist dudenkonform und beschreibt eine Eigenschaft die uns zu Eigen ist - und manchmal hinderlich.

Meist oder immer hinderlich ist diese Eigenschaft bei Robotern - wenn man die mit einer optischen Fernsteuerung bedienen will. Bei meinem Archie hatte ich mich rausgewunden durch zwei getrennte Sensoren deren Signalausgänge ich miteinander verbunden hatte (wired or), siehe hier (https://www.roboternetz.de/community/threads/64853-IR-Receiver-5110-als-Rundumsensor). Beide "linsten" in zwei entgegengesetzte Richtungen, aber ihre Signale gingen auf ein und denselben Eingangspin. Es gab manchmal Störungen, einige Störungen konnten auf Abschattungen zurückgeführt werden, meist lief es.

Den abgekündigten SFH5110 habe ich ja mittlerweile durch den TL1838 ersetzt. Die etwas dünne Dokumentation habe ich für mich durch eigene Messungen (und Variation der Beschaltung) erweitert. Beste Ergebnisse bekam ich mit nem Vorschaltwiderstand von etwa 100 Ω und einem Pullup von (16 bis) 20 kΩ. Nun noch ein zweites Auge hintendran, beide Schaltungen auf ein "Brettchen montiert", ein dreipoliges Kabel dran: GND, Vcc und SigOut (sozusagen Belegung à la Modellbauservo) - und schon läufts. Sauber, fast keine toten Winkel, offensichtlich besser als vorher.

......https://dl.dropbox.com/s/1yozw9pm7l9p08w/RC5_Sensor-neu-Teil-25%25_3260.jpg?dl=0 (https://dl.dropbox.com/s/6xqg05suq3d1sua/RC5_Sensor-neu-Teil_3260.jpg?dl=0)
......© 2020 by oberallgeier (höhere Auflösung im Bild verlinkt)

Vielleicht hilft das dem einen oder andern.

oberallgeier
03.11.2020, 11:49
Hallo Klebwax, danke für Deine Hinweise:
Nur mal eine Sache: Du bist sehr großzügig mit volatile. Ist das wirklich nötig? Das bremst den Optimizer komplett aus ..Puhhh - so. Die unnötigen (unsauberen) Deklarationen "volatile" sind bereinigt. Schon einiges Arbeit (bis man draufkommt wie das schnell geht).
Dann compiliert mit -Os - wie zuvor. Der Vergleich "vorher" "nachher" gab keine Unterschiede :confused: aber selbst wenns so wäre - sauber(er) programmiert ist die Sache den Aufwand dann doch wert.
Zusätzlich wurden unnötige Definitionen bereinigt/entfernt - War schon längst angedacht . . .


.. Und du gibst Strings im Interrupt-Handler über die serielle aus. Das könnte dir deinen 50µs Interrupt ziemlich aus dem Tritt bringen ..Ich hatte (einige, wenige) Stellen die mir kritisch vorkamen getestet und hatte die Zeit für diese Stringausgaben gemessen (Ausgang war low, Testanfang - Ausgang high - Stringausgabe - Ausgang low) und Oskar angeguckt (ok ok, *lls Auswerten wäre direkter). Ich blieb dabei im Zeitrahmen. Übrigens hatte ich manche Programmaufrufe (z.B. Regelung!!) auf Zeitbedarf getestet (Oszi, *.lls) um Störungen im "eigentlichen" Betrieb möglichst auszuschließen. 100%igen Erfolg kann ich natürlich nicht garantieren. Es wäre mir auch nicht klar, wie ich das sicherstellen könnte. UND - wenns möglich ist (z.B. beim PingPong-Display) wird UART auch mit ziemlichem Speed gefahren (PingPong wurde extra auf Quarzbetrieb umgelötet).

Die Regelung. Es werden ja beide Antriebe voneinander getrennt geregelt - Geradeauslauf z.B. wird durch gleiche Strecken-/Speed-Vorgaben angestrebt (erhofft - leider nicht garantiert aus vielerlei Gründen). Und genau da spielt die Laufzeit ne Rolle und genau deswegen wird die 100/s-Regelfrequenz dadurch erreicht dass mit 200/sec geregelt wird - aber eben jeweils alternierend nur einer der beiden Antriebe. Die Regelfrequenz liegt dann im Bereich der Motorzeitkonstanten von 10 ms .. 12 ms der Antriebe (die "as build" gemessen wurden). Insbes. beim MiniD0 (die 0,15-Liter-Coladose) ist der Gleichlauf ziemlich gut zu sehen [/Selbstlob].

Bestimmte Ausgaben dauern länger als dieser 50µs Interrupt. Das kommt z.B. vor bei Testfahrten von u.A. Servo von A nach B fahren um Positionen zu testen und Ähnliches. Dabei wurden natürlich auch der Zeitbedarf für bestimmte Gesten erfasst. Aber für das Auge spielen >im "Normalbetriieb"< evtl. Interrupt-Störungen keine Rolle (sprich: ich seh da nix).