Forum: Mikrocontroller und Digitale Elektronik AVR-Assembler: IR-Fernbedienungs-Empfang Pulse-Distance


von Johannes T. F. (jofe)


Angehängte Dateien:

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?

von Harald K. (kirnbichler)


Lesenswert?

Johannes F. schrieb:
> #define IR_TOLERANCE 0.3

Ich glaube nicht, daß Du in Assembler mit Floatingpoint-Werten hantieren 
möchtest.

von Johannes T. F. (jofe)


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

von S. L. (sldt)


Lesenswert?

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?

von Daniel V. (danielv2)


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.

: Bearbeitet durch User
von Johannes T. F. (jofe)


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

von S. L. (sldt)


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

von S. L. (sldt)


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.

von Ben B. (Firma: Funkenflug Industries) (stromkraft)


Lesenswert?

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.

von Daniel V. (danielv2)


Lesenswert?

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.

von Johannes T. F. (jofe)


Angehängte Dateien:

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.

von S. L. (sldt)


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?

von Johannes T. F. (jofe)


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

von S. L. (sldt)


Lesenswert?

Ja, das haben 'kleine, unbedeutende Änderungen' hin&wieder so an sich - 
dann warte ich mal auf eine neue Version.

von S. L. (sldt)


Angehängte Dateien:

Lesenswert?

So viele Nullen - da scheint etwas bei der Änderung von IR_DELAY 
schiefgegangen zu sein.

von Johannes T. F. (jofe)


Angehängte Dateien:

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.

von Johannes T. F. (jofe)



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.

von Frank M. (ukw) (Moderator) Benutzerseite


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.

: Bearbeitet durch Moderator
von Johannes T. F. (jofe)


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.

von Frank M. (ukw) (Moderator) Benutzerseite


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

: Bearbeitet durch Moderator
von Daniel V. (danielv2)


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

: Bearbeitet durch User
von Johannes T. F. (jofe)


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

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.