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?
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.
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.
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 %.
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.
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.
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.
... 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?
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 ...
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.
(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.
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.
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. :-)
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.
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).