Hallo,
als AVR-Assembler-Lernender habe ich mir die Aufgabe gestellt,
Infrarot-Fernbedienungs-Signale zu empfangen. Weil gerade entsprechende
Fernbedienungen vorhanden waren, habe ich mich zunächst auf die
"Protokolle" NEC und Samsung32 beschränkt, welche das
Pulse-Distance-Verfahren verwenden (Details dazu siehe IRMP).
Meinen Code habe ich angehängt; das Prinzip ist einfach: ein
Timerinterrupt inkrementiert ein 16-Bit-Register (ir_intrCntrH:L), und
falls der Zustand des IR-Eingangs sich änderte, wird bei fallender oder
steigender Flanke jeweils eine Routine aufgerufen, die ir_intrCntrH:L
auswertet und anschließend wieder auf null zurücksetzt. Abhängig von den
Puls- und Pausenzeiten wird ein Bit in den Datenregistern ir_data3..0
gesetzt oder, falls die Puls-/Pausendauer aus dem Toleranzbereich fällt,
der Pulszähler ir_pulseCntr zurückgesetzt und damit der aktuelle Empfang
verworfen.
Bei vollständig empfangenen Frames oder auch bei Abbruch eines Empfangs
werden die Daten bzw. ein Fehlercode über die UART gesendet.
Prinzipiell funktioniert es soweit - die empfangenen Daten stimmen mit
der Ausgabe von IRMP überein. Allerdings, und hier komme ich nicht
weiter, muss ich die Zeit-Toleranz auf ca. +/- 30 % einstellen (
1
#define IR_TOLERANCE 0.3
in der Datei ir-rx-pulse-dist.config.asm), was mir viel zu hoch
erscheint. An der Taktquelle kann es nicht liegen, da ich einen
Quarzoszillator verwende, dessen Frequenz ich auch schon nachgemessen
habe.
Ich vermute eher, dass mein Code noch Fehler enthält, die dazu führen,
dass ich die zulässige Abweichung so hoch einstellen muss.
Findet jemand etwas, oder kann mir anderweitig Tipps geben, was ich noch
an dem Code verbessern könnte?
Harald K. schrieb:> Johannes F. schrieb:>> #define IR_TOLERANCE 0.3>> Ich glaube nicht, daß Du in Assembler mit Floatingpoint-Werten hantieren> möchtest.
Mit den Floating-Point-Werten hantiert nur der Präprozessor des
Assemblers:
An welcher Programmstelle tritt der Fehler auf? Ich sehe einige
'..._discard' mit sehr unterschiedlichen Bedeutungen.
Der Interrupt läuft mit 49.9 us, richtig? Damit sollen dann Zeiten
detektiert werden von z.B. 560 oder 1690 us - ist das Absicht?
Kann natürlich ein Programmierfehler sein. Deine Sensordaten werden aber
fehlerbehaftet sein. Vielleicht zerreißt dein Sensor einen Impuls in
mehrere Einzelimpulse.
Außerdem könnten deine Annahmen falsch sein.
-
Setze doch einfach zum Fehlerzeitpunkt einen Debug-Pin auf 1. Dann
hättest du einen Trigger für dein Oszi.
S. L. schrieb:> An welcher Programmstelle tritt der Fehler auf?
Das weiß ich leider noch nicht ...
S. L. schrieb:> Ich sehe einige> '..._discard' mit sehr unterschiedlichen Bedeutungen.
Die ..._discard-Labels werden angesprungen, wenn das bisher Empfangene
verworfen werden soll, es wird dann eigentlich nur das Register
ir_pulseCntr gelöscht und, falls IR_DEBUG definiert ist, eine
Fehlermeldung gesendet.
S. L. schrieb:> Der Interrupt läuft mit 49.9 us, richtig? Damit sollen dann Zeiten> detektiert werden von z.B. 560 oder 1690 us - ist das Absicht?
Korrekt. Eigentlich sollten es genau 50 µs sein, die rund 0,1 µs
Abweichung entstehen durch einen Rundungsfehler bei
wie ich gerade festgestellt habe - den werde ich noch beseitigen, indem
ich das tatsächliche IR_DELAY aus dem gerundeten IR_TIMER_OCR berechne,
und dann mit diesem weiterrechne. Ursache des Problems kann es aber
nicht sein, denn es handelt sich ja nur um 0,2 %.
Daniel V. schrieb:> Deine Sensordaten werden aber> fehlerbehaftet sein. Vielleicht zerreißt dein Sensor einen Impuls in> mehrere Einzelimpulse.
Hmm ja, theoretisch sicher möglich, dann wäre es aber eher
unwahrscheinlich, dass das Programm überhaupt gültige Frames erkennen
würde; die Fehler dürften dann nur so liegen, dass die
Puls-/Pausenzeiten bei 30% Toleranz trotzdem passen.
Daniel V. schrieb:> Außerdem könnten deine Annahmen falsch sein.
Die Puls-/Pausenzeiten sind aus dem Artikel IRMP - ich nehme an,
dass die Zeiten bei handelsüblichen Fernbedienungen recht genau
eingehalten werden? Glaube nicht, dass mehrere getestete FBen davon mit
deutlich mehr als 10 % abweichen.
Daniel V. schrieb:> Setze doch einfach zum Fehlerzeitpunkt einen Debug-Pin auf 1. Dann> hättest du einen Trigger für dein Oszi.
Ich habe bisher nur ein analoges Oszi, weiß nicht, ob ich damit
gescheite Messungen hinbekomme. Muss mir mal ein DSO besorgen ...
> ... ja nur um 0,2 %.
Hmm - wie groß ist der Fehler, wenn 560 mit einer Auflösung von 50
gemessen werden?
> ... wenn das bisher Empfangene verworfen werden soll
Und da wäre eben interessant, wo das eigentlich passiert - so weiß man
nur ganz allgemein "Oh - Panne".
Vielleicht noch ein allgemeiner Tipp: wenn man die (allgemeine)
Fehlerroutine nicht anspringt, sondern (per (r)call) aufruft, so lässt
sich in dieser der 'Werdegang' des Fehlers durch Ausgabe des Stack und
anhand des Assemblerlistings zurückverfolgen.
Bau doch mal eine Ausgabe dafür ein, aus welchem Grund ein Frame
verworfen wurde, vielleicht lässt sich daraus schließen, an welcher
Stelle der Fehler auftritt.
Nur so an Rande:
Gleiche Flanken passen genau in ein Zeitraster. Ungleiche Flanken je
nach Sensor und Signalstärke mehr oder weniger und sind nicht so gut für
eine genaue Auswertung geeignet. Aber 30% - naja.
S. L. schrieb:>> ... ja nur um 0,2 %.>> Hmm - wie groß ist der Fehler, wenn 560 mit einer Auflösung von 50> gemessen werden?
Rund 7 %, also deutlich weniger als 30 %, wenn ich richtig rechne.
S. L. schrieb:>> ... wenn das bisher Empfangene verworfen werden soll>> Und da wäre eben interessant, wo das eigentlich passiert - so weiß man> nur ganz allgemein "Oh - Panne".
Ja, es erfolgt schon eine Ausgabe eines Fehlercodes, die Codes sind in
der Datei ir-rx-pulse-dist.asm definiert:
1
; == Debug message codes ==
2
.equ IR_PULSE_TOO_SHORT = $01
3
.equ IR_PULSE_TOO_LONG = $02
4
.equ IR_PULSE_TIMEOUT = $03
5
.equ IR_PAUSE_TOO_SHORT = $04
6
.equ IR_PAUSE_BETWEEN_0_1 = $05
7
.equ IR_PAUSE_TOO_LONG = $06
8
.equ IR_PAUSE_START_TOO_SHORT = $07
9
.equ IR_PAUSE_START_BETWEEN_R_D = $08
10
.equ IR_PAUSE_START_TOO_LONG = $09
11
.equ IR_PULSE_START_TOO_SHORT = $0A
12
.equ IR_PULSE_START_TOO_LONG = $0B
13
.equ IR_PULSE_COUNT_INVALID = $10
14
.equ IR_DATA_INVALID = $20
Ich bekam meistens den Fehler $02, also "Puls zu lang". Leider
funktioniert seltsamerweise die UART-Ausgabe momentan nicht (neuer PC
und ein paar Codezeilen geändert), sodass ich gerade keinen Screenshot
des Terminals zeigen kann ... Arbeite an der Behebung des Problems ...
Anbei noch die korrigierten Quelltexte; ich hatte zwischenzeitlich mal
ein paar Bezeichner geändert und scheinbar noch nicht alle Dateien
aktualisiert ... Jetzt sollte es auch assemblierbar sein.
> funktioniert seltsamerweise die UART-Ausgabe momentan nicht
Da sind wir zu zweit: ein (von mir eingefügtes Prompt-)Zeichen wird noch
angezeigt, aber wenn ich C0 mal mit GND, mal mit Ucc verbinde, tut sich
nichts - ich hatte eine Fehlermeldung erwartet; oder ist das von mir zu
einfach gedacht?
S. L. schrieb:> wenn ich C0 mal mit GND, mal mit Ucc verbinde, tut sich> nichts - ich hatte eine Fehlermeldung erwartet; oder ist das von mir zu> einfach gedacht?
Das ist richtig gedacht - dann müssten eigentlich Fehlermeldungen
erscheinen. Das hat auch schon mal funktioniert, ich hatte danach nur
ein paar kleine (eigentlich unbedeutende) Änderungen vorgenommen, und
nun funktioniert es nicht mehr :/ Ich habe schon einige Zeit nach
Fehlern gesucht, finde aber nichts ...
S. L. schrieb:> da scheint etwas bei der Änderung von IR_DELAY> schiefgegangen zu sein.
Ja, danke sehr, das war tatsächlich der Fehler. Diese Zeile muss
natürlich wie folgt lauten:
Nun funktioniert es wieder. Der Screenshot im Anhang zeigt den
erfolgreichen Empfang einiger Tastencodes einer alten LG-Fernbedienung,
Einstellung von IR_TOLERANCE ist nach wie vor 0.3.
Anbei noch der korrigierte Quelltext.
Ich habe es nun noch einmal mit der Einstellung
1
#define IR_TOLERANCE 0.15
(in der Datei ir-rx-pulse-dist.config.asm) versucht: tatsächlich
funktioniert es mit der alten LG-Fernbedienung, bei einer neueren
kleinen NoName-FB (aus einem RaspPi-Pico-Set) bekomme ich die in dem
angehängten Screenshot ersichtlichen Fehlermeldungen (Puls zu lang oder
Pause zu kurz).
Das heißt also, dass das Problem vermutlich doch an (zu?) großen
Toleranzen der neueren FB liegt.
Johannes F. schrieb:> muss ich die Zeit-Toleranz auf ca. +/- 30 % einstellen [...],> was mir viel zu hoch erscheint.
Wenn ich das richtig sehe, orientierst Du Dich bei der Entwicklung nicht
nur an dem IRMP-Artikel, sondern auch am IRMP-C-Source, denn
diese Preprocessor-Mimik für die Toleranzen gibt es auch schon seit
Anbeginn im Quellcode vom IRMP. (Ja, ich muss das wissen,
schließlich ist der IRMP von mir ;-)
Wenn ich da in die irmp.h schaue, sehe ich dort für NEC u.a.
Das heisst: Auch der IRMP arbeitet bei NEC mit einer Toleranz von 30
Prozent.
Und das hat auch seinen guten Grund: Gerade das NEC-Protokoll wird
weltweit von tausenden von Herstellern genutzt. Und alle braten dabei
einen Extra-Storch: Sie halten sich zwar an das Pulse-Pause-Protokoll
(Anzahl der Bits, Timing-Verhältnisse), aber nicht unbedingt an
absoluten Zeiten, sondern lassen diese im bestimmten Rahmen abweichen.
Vielleicht machen das die Hersteller (gerade NoName), um sich von
anderen abzusetzen, wer weiß. Ich weiß auch aus der Praxis, dass viele
NEC-kompatible FBs sehr genau arbeiten (das heißt zum Beispiel: die
Pulse sind bis auf 1% alle gleich lang, aber liegen längst nicht bei
560us), aber es gibt auch FBs, die da schlampig arbeiten und sich den
Arbeitstakt irgendwo raussaugen und dabei Signale mit viel Jitter
erzeugen. Bei denen können die Abweichungen schon mal im zweistelligen
Prozentbereich liegen.
Egal, wie es ist: Dass man hier im Decoder mit 30% Toleranz arbeitet,
ist nicht schlimm, nein, es ist sogar gut! Das NEC-Protokoll setzt sich
von den Timingverhältnissen (Start-Bits, Daten-Bits) dermaßen stark ab
von den anderen IR-Protokollen, dass es selbst bei einer so großen
Toleranz immer noch erkennbar und unterscheidbar ist. Genau deshalb ist
das NEC-Protokoll auch das meistverwendete überhaupt.
Eine größtmögliche Toleranz sehe ich daher nicht als Nachteil, sondern
als Vorteil: Je toleranter der IR-Decoder werden kann bei
gleichbleibender Erkennungsrate, desto besser in der Praxis. Aus diesem
Grund habe ich die Toleranzen für die einzelnen Protokolle immer
möglichst groß gewählt. Kam es dann zu Konflikten mit anderen
Protokollen, habe ich die Toleranzen immer solange gemächlich
runtergeschraubt, bis diese wieder unterscheidbar wurden.
Glücklicherweise konnte ich bei NEC immer noch bei 30% Toleranz bleiben
- auch wenn IRMP ca. 60 verschiedene IR-Protokolle detektieren (und
die meisten davon auch unterscheiden) kann.
Hallo Frank, vielen Dank für deine ausführliche Erläuterung!
Frank M. schrieb:> Wenn ich das richtig sehe, orientierst Du Dich bei der Entwicklung nicht> nur an dem IRMP-Artikel, sondern auch am IRMP-C-Source
Den C-Sourcecode habe ich mir tatsächlich bisher noch nie angesehen (bis
auf die Ausschnitte im Artikel), wir hatten also ähnliche Gedankengänge
unabhängig voneinander. :-)
Frank M. schrieb:> Das heisst: Auch der IRMP arbeitet bei NEC mit einer Toleranz von 30> Prozent.
OK, dann bin ich ja beruhigt. Ich dachte wirklich, ich hätte noch
irgendeinen Bug in meinem Code, der eine so relativ hohe Toleranz
notwendig macht.
Also dann ist dieses Problem wohl gelöst. Nochmals dankeschön an Frank
M. (ukw) und alle anderen, insbesondere an S.L. (sldt) für die Tipps.
Johannes F. schrieb:> Den C-Sourcecode habe ich mir tatsächlich bisher noch nie angesehen (bis> auf die Ausschnitte im Artikel), wir hatten also ähnliche Gedankengänge> unabhängig voneinander. :-)
Interessanterweise ähneln sich die Methoden der Berechnung (Runden der
Floating-Werte und anschließender Cast in Integer) und auch die
Exponentialschreibweise der Zeiten frappierend ;-)
Für Dich interessant sein könnte irmpprotocols.h - falls Du mal ein
weiteres Protokoll hinzufügen möchtest. Dort stehen die verwendeten
Timings aller unterstützten Protokolle drin. Die jeweiligen Toleranzen,
die individuell ausfallen, stehen in irmp.h. Das hat historische Gründe.
Viel Spaß mit Deinem Decoder! :)
Der Fehler war eigentlich, dass du ohne ausreichende Überprüfung gleich
von einem Programmfehler ausgegangen bist. Deshalb schrieb ich:
Daniel V. schrieb:> Außerdem könnten deine Annahmen falsch sein.
Leider hast hast du den Fehler wiederholt. Eine Überprüfung der
Eingangsdaten per Oszi hätte dir früher Klarheit verschafft.
Johannes F. schrieb:> OK, dann bin ich ja beruhigt. Ich dachte wirklich, ich hätte noch> irgendeinen Bug in meinem Code, der eine so relativ hohe Toleranz> notwendig macht.
Das ist übrigens auch nur eine Annahme.
(-> Nur so für den Hinterkopf: Annahmen prüfen.)
Frank M. schrieb:> Viel Spaß mit Deinem Decoder! :)
Dankeschön!
Daniel V. schrieb:> Eine Überprüfung der> Eingangsdaten per Oszi hätte dir früher Klarheit verschafft.
Ja sicher, aber wie ich bereits schrieb, verfüge ich derzeit über kein
DSO, lediglich ein analoges Oszi, und letzteres ist halt ungeeignet, um
das IR-Signal zu untersuchen (welches nur alle ca. 100 ms gesendet wird,
bei einer erforderlichen Auflösung im µs-Bereich).