AVR-Assembler: IR-Fernbedienungs-Empfang Pulse-Distance

#7438285
Lesenswert?

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?

Angehängte Dateien:
#7438614
Lesenswert?

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:

1
#define ROUND(X) (int(1.0*(X)+0.5))
2
#define IR_MIN_INTR(T) ROUND((1.0-IR_TOLERANCE)*(T)/IR_DELAY)
3
#define IR_MAX_INTR(T) ROUND((1.0+IR_TOLERANCE)*(T)/IR_DELAY)+1
#7438724
Lesenswert?

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.

#7438833
Lesenswert?

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

1
#define IR_TIMER_OCR ROUND(1.0*F_CPU/IR_TIMER_PRESC/IR_INTR_FREQ-1)

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

#7438842
Lesenswert?

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

#7438888
Lesenswert?

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.

#7439440
Lesenswert?

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.

Angehängte Dateien:
#7439482
Lesenswert?

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?

#7439501
Lesenswert?

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

#7439638
Lesenswert?

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:

1
#define IR_DELAY (1.0e6*IR_TIMER_PRESC*(IR_TIMER_OCR+1)/F_CPU)

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.

Angehängte Dateien:
#7439660
Lesenswert?

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.

Angehängte Dateien:
Moderator Persönliche Seite #7439663
Lesenswert?

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.

1
#define NEC_PULSE_LEN_MIN   ((uint_fast8_t)(F_INTERRUPTS * NEC_PULSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
2
#define NEC_PULSE_LEN_MAX   ((uint_fast8_t)(F_INTERRUPTS * NEC_PULSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
3
#define NEC_1_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NEC_1_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
4
#define NEC_1_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NEC_1_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)
5
#define NEC_0_PAUSE_LEN_MIN ((uint_fast8_t)(F_INTERRUPTS * NEC_0_PAUSE_TIME * MIN_TOLERANCE_30 + 0.5) - 1)
6
#define NEC_0_PAUSE_LEN_MAX ((uint_fast8_t)(F_INTERRUPTS * NEC_0_PAUSE_TIME * MAX_TOLERANCE_30 + 0.5) + 1)

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.

#7439679
Lesenswert?

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.

Moderator Persönliche Seite #7439689
Lesenswert?

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! :)

#7439726
Lesenswert?

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

#7439952
Lesenswert?

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

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren