Forum: Mikrocontroller und Digitale Elektronik IR Fernbedienung Signale auf µC verarbeiten


von Thomas M. (Firma: keine) (tommy1169)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,
ich habe ein KEIL Board MCB2300 mit LPC2378 und würde gern ankommende 
IR-Signale von einer Fernbedienung mittels TSOP empfangen und auswerten.

Leider schaffe ich es nicht den Empfänger(MCB2300) mit den Pulsen von 
der FB zu synchronisieren. Die Details und Signalzeiten wären:
-JVC FB für Hifianlage (RM-RXUV9RMD)
-Startbit     = 8,4ms
-Pause        = 4,2ms
-Gerätecode   = 8Bit
-Kommandocode = 8Bit
-Stopbit      = 1Bit

-Gesamtdauer inkl. Startbit, Pause und Stopbit = 58,98ms (einmalig)
-Gesamtdauer exkl. Startbit und Pause = 46,33ms (wiederholend)


Ich hatte einfach eine Abtastung ab Drücken einer Taste veranlasst und 
bei einem gewissen Abtastintervall den Inputwert vom IR EMpfänger 
ausgelesen.
Eine Unterscheidung zweier Tasten würde mir reichen.

Doch die Sache "rutscht" einfach durch. Es wird nicht synchron mit dem 
FB-Signal.

Wie kann man soetwas angehen? Geschrieben wird in c.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Thomas Mönig schrieb:
> Hallo zusammen,
> ich habe ein KEIL Board MCB2300 mit LPC2378 und würde gern ankommende
> IR-Signale von einer Fernbedienung mittels TSOP empfangen und auswerten.

Schau Dir den Artikel mal an:

  http://www.mikrocontroller.net/articles/IRMP

Die eingesetzte Software läuft auf PICs und AVRs, die Anpassung an 
anderes µCs ist minimal und gekapselt. Wie wärs mit einer Portierung auf 
LPC2378?

> Die Details und Signalzeiten wären:
> -Startbit     = 8,4ms
> -Pause        = 4,2ms
> -Gerätecode   = 8Bit
> -Kommandocode = 8Bit
> -Stopbit      = 1Bit

http://www.mikrocontroller.net/articles/IRMP#Die_IR-Protokolle_im_Detail 
gibt für das JVC-Protokoll etwas abweichende Werte an - sowohl vom 
Timing als auch vom Verhältnis der Geräte-/Kommando-Bits.

von Thomas M. (Firma: keine) (tommy1169)


Lesenswert?

Danke erstmal für die Hinweise.

Du hast Recht, das Geräte/Kommandobits Verhältnis ist 4/12 nicht 8/8.

Eine Portierung auf den LPC2378 wäre sicherlich interessant, doch leider 
weiss ich gar nicht recht wo ich da anfangen soll.
Würde mich aber, wenn wieder mehr Zeit ist, gern damit beschäftigen.

Da mir allerdings gerade die Zeit wegläuft habe ich erst einmal ein paar 
Verständnisfragen die Du evtl. beantworten kannst:

-Ist eine Abtastung des TSOP-Outputs mit 1ms sinnvoll oder sollte man 
lieber mit 100µs den Pegel abfragen?

-Ich bekomme das empfangene Signal nicht synchronisiert, daher habe ich 
eine wandernde Abtastung und somit nur Müll.

-Kennst Du eine einfache Methode zur Synchronisation des Empfängers mit 
der FB? Evtl. einen zweiten Timer beim Empfang des Startbits 
einschalten?
Ich hatte ganz simpel die Gesamtzeiten gemessen, durch die Frequenz der 
Abtastung geteilt und dann gesagt: Ok hier ist Abtastung 15, wenn da 
eine 1  ist dann... und so weiter. Aber ohne Sync keine Chance.

Aber ich schaue jetzt schon so lange auf den c-code und sehe den Wald 
nicht mehr. Müsste doch machbar sein zu sagen, wenn der Pegel 1 über 
mehr als 6 Abtsatungen anliegt dann ist dass das Startbit... Aber 
anscheinend ist es nicht ganz so simpel.

Gruß
Tom

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Thomas Mönig schrieb:

> -Ist eine Abtastung des TSOP-Outputs mit 1ms sinnvoll oder sollte man
> lieber mit 100µs den Pegel abfragen?

1ms ist auf jeden Fall zu lang, denn die eigentlichen Datenbits sind ja 
wesentlich kürzer.

Auszug aus der Tabelle im IRMP-Artikel für JVC:

Start-Bit    9000µs Puls, 4500µs Pause, 6000µs Pause bei Tasten-
             Wiederholung
0-Bit        560µs Puls, 560µs Pause
1-Bit        560µs Puls, 1690µs Pause
Stop-Bit     560µs Puls

IRMP pollt den TSOP mit 10kHz (oder in den neueren SVN-Versionen sogar 
mit 15kHz), das entspricht einer Abtastrate von 100µs. Damit sollte es 
bei JVC auf jeden Fall gehen.

Du solltest dann "sehen"

   - ca. 80-100 mal Puls  beim Startbit
   - ca. 40-60  mal Pause beim Startbit

   - ca. 4-7    mal Puls  beim Datenbit 0
   - ca. 4-7    mal Pause beim Datenbit 0

   - ca. 4-7    mal Puls  beim Datenbit 1
   - ca. 14-20  mal Pause beim Datenbit 1

Da die Fernbedienungen von den dokumentierten Timing-Werten unter 
Umständen bis zu 40% abweichen können, solltest Du die obigen 
Minimum-Maximum-Werte abfragen, um damit dann die Bitfolge aus dem Frame 
zu ermitteln. Wichtig wäre noch: JVC sendet LSB first.

> -Ich bekomme das empfangene Signal nicht synchronisiert, daher habe ich
> eine wandernde Abtastung und somit nur Müll.

Bei 1ms kannst Du die eigentlichen Datenbits auch gar nicht abtasten, 
diese sind kürzer, siehe oben.

> -Kennst Du eine einfache Methode zur Synchronisation des Empfängers mit
> der FB? Evtl. einen zweiten Timer beim Empfang des Startbits
> einschalten?

Achso, Du denkst, das wäre ein bitserielles Protokoll? Nein, ist es 
nicht, denn bei IR-Fernbedienungen steckt in jedem Bit die 
Synchronisation ("Puls-Distance"), siehe auch entsprechenden Abschnitt 
im IRMP-Artikel:

   http://www.mikrocontroller.net/articles/IRMP#Protokolle

Aber fangen wir von vorne an, dann wird das klarer:

IRMP lässt einen 10kHz-Timer laufen und zählt die Pulse und die Pausen. 
Sind diese für das Startbit innerhalb der obigen Grenzen, "weiss" IRMP, 
dass es sich um das JVC-Protokoll handelt, stellt seine Timing-Tabellen 
auf die obigen Werte für die Datenbits um und zählt dann einfach die 
nachfolgenden Puls-/Pausenlängen.

Da die meiste Zeit überhaupt kein Signal gesendet wird, belastet die ISR 
auch überhaupt nicht den Controller. Okay, man könnte es auch mit einem 
PIN-Change-Interrupt machen und die Zeiten zwischen den Interrupts 
"zählen". Das wäre etwas ressourcen-schonender. Aber die Polling-Methode 
ist halb so wild.

> Ich hatte ganz simpel die Gesamtzeiten gemessen, durch die Frequenz der
> Abtastung geteilt und dann gesagt: Ok hier ist Abtastung 15, wenn da
> eine 1  ist dann... und so weiter. Aber ohne Sync keine Chance.

Das ist auf jeden Fall die falsche Methode, denn die Bits sind 
unterschiedliche lang:

  Bit 0: 567µs Puls + 567µs Pause  = 1134µs Länge
  Bit 1: 567µs Puls + 1690µs Pause = 2257µs Länge

Hättest Du Dir den Link

   http://www.mikrocontroller.net/articles/IRMP#Die_IR-Protokolle_im_Detail

aus meiner ersten Antwort auch mal angeschaut, hättest Du das direkt 
erkannt ;-)

Der Sync steckt in jedem Datenbit. Für jedes(!) Bit kommt ja am Anfang 
ein Puls... und dann eine Pause. Nur die Pausen sind unterschiedlich 
lang, siehe oben. Anhand der Pausenlängen weisst Du dann, ob es eine 0 
oder eine 1 ist. Dieses Verfahren nennt man Pulse-Distance-Protokoll und 
hat den Vorteil, dass es Dir im Timing gar nicht "davonlaufen" kann... 
eben weil jedes Bit im Frame auch gleichzeitig für den Empfänger zum 
Synchronisieren da ist. Alternativ zum Puls-Distance-Protokoll wird auch 
teilweise das Manchester-Protokoll ("Bi-Phase") genutzt. Auch hier kann 
bei jedem Bit neu synchronisiert werden.

> Aber ich schaue jetzt schon so lange auf den c-code und sehe den Wald
> nicht mehr. Müsste doch machbar sein zu sagen, wenn der Pegel 1 über
> mehr als 6 Abtsatungen anliegt dann ist dass das Startbit... Aber
> anscheinend ist es nicht ganz so simpel.

Vielleicht schaust Du Dir einfach mal den IRMP-Artikel näher an, denn da 
steht auch jede Menge an geballter Info zu den IR-Protokollen drin. 
Allein das Zusammensuchen/Ermitteln der Eckwerte für die mittlerweile 
über 30 IR-Protokolle, die IRMP "versteht", hat mich insgesamt fast mehr 
Zeit als das eigentliche Programmieren gekostet ;-)

Manchmal ist ein wenig Studium der Theorie vor der eigentlichen 
Programmiererei gar nicht so unsinnig....

von Thomas M. (Firma: keine) (tommy1169)


Lesenswert?

Hallo Frank,
ich habe mir jetzt so einiges durchgelesen.
Ich denke das mit der Bitsync ist mir jetzt klarer.

Beim IRMP ist doch das IR-Logging für die Routine da, die die Pulse 
einliest, die als Basis der ganzen Auswertung des IR-Signales dienen.
Ich habe jetzt meinerseits versucht eine Routine zuschreiben, 
allerdingszugeschnitten auf meine verwendete JVC Fernbedienung.

Mein Ansatz war jetzt die Bitsynchrionisation sowie die Tatsache dass 
der oben dargestellte Signalverlauf eingelesen und verarbeitet werden 
soll.

Hierbei hatte ich folgende Überlegungen:
Wenn die FB sendet kommt IMMER zuerst das Startbit gefolgt von 16 
Datenbits und einem Stopbit. Dann die Burstpause und dann wieder 16 
Daten plus Stopbit. Bekannt sind die zeitliche Länge des Starbits mit 
Puls- und Pausenzeiten sowie die immer gleiche Pulsdauer der Daten- und 
Stopbits.
Da der Abstand der Bursts ebenfalls immer gleich ist, ergeben sich 
daraus eine minimale Burstpausendauer und eine maximale, da ja die 
Einsen und Nullen unterschiedliche Pausenzeiten haben.

Der Timer steht auf 100µs(also 10000mal/pro sek) und durchläuft mit 
dieser Schrittzeit den folgenden Code.

Meine Idee war erstmal dafür zu sorgen, dass solange der Eingangspegel 
vom IR-Empfänger 1 ist, eine Variable x auch 1 wird. Da aber ein Zähler 
dann bei jedem x=1 Wert zählen würde, musste eine Hilfsvariable a her, 
mit der eine Bedingung geschaffen wird, bei der nur gezählt wird wenn 
eine pos. oder neg. Flanke, das heisst ein Zustandswechsel 0->1 oder 
1->0, kommt.
1
if(x==1 && a==1){         //positive Flanke bei Betätigen der FB
2
      a=0; 
3
}
4
   
5
if(x==0 && a==0){         //negative Flanke bei Betätigen der FB
6
      a=1;
7
          
8
}

Ich kann doch jetzt beim ersten if mit c++ loslegen zu zählen. Ich weiß 
dass der Burst 17 steigende Flanken hat und zusätzlich noch die Flanke 
des Startbits, allerdings nur einmal. Also starte ich c bei -1, wenn die 
erste steigende Flanke des Startbits kommt ist p=0 und wenn der 
Datenblock anfängt ist c=1. Wenn c den Wert 17 überschreitet, lasse ich 
c zu 1 werden und die Zählung beginnt von vorn beim ersten Datenbit. 
damit habe ich eine Variable die mir jeden empfangenen Puls indexiert, 
damit ich genau weiß wo ich bin wenn ich dann anschliessend die Länge 
der Pulse inkl. Pausen messen muss, um sagen zu können ob bei Puls(n) 
eine log. 1 oder 0 kommt.

Nun dachte ich, man könnte sagen: wenn c gleich 1 ist, zähle im 0,1ms 
Takt. Wenn c zu 2 wird speichere den gezählten Wert in einer Variable 
"takt" ab und setze den Zähler wieder auf 0.
Damit würde ich doch das erste Datenbit auslesen und müsste immer 
denselben wert erhalten und zwar 21-22 für eine 1 oder 10-11 für eine 0.
1
    
2
if(x==1 && a==1){         //positive Flanke bei Betätigen der FB
3
      a=0; 
4
      c++;                //c ist die Variable für den Pulszähler Schrittzeit 0,1ms
5
      t=0;
6
      s=1;
7
    }
8
//--------------------------------------------------------------------------------------------------------------------------------------------------------------    
9
if(x==0 && a==0){         //negative Flanke bei Betätigen der FB
10
      a=1;      
11
    }
12
//--------------------------------------------------------------------------------------------------------------------------------------------------------------    
13
      if(c>17){           //mit c werden die Pulse vom ersten bis zum letzten in einem Burst gezählt
14
        b++;              //Da ein Burst 17 Pulse hat, wird b nach 17 Pulsen um 1 erhöht und gibt somit die gesendete Burstanzahl an
15
        c=1;
16
      }
17
//--------------------------------------------------------------------------------------------------------------------------------------------------------------    
18
    if(s==1){             //Schalter s für das Starten der Zähler 
19
      t++;
20
      if(c==1){           //Beispiel: Taktzählung bei Puls Nr.1
21
        r++;
22
      }        
23
      if(c==2){
24
        takte=r;          //speichert die gezählte Taktzahl für eine Puls-Pausenzeit in -takte- ab wenn der zweite Puls errreicht ist
25
        r=0;
26
      }
27
      if(t>=300){         //Bedingung zum Zurücksetzten der Zähler wenn Taste losgelassen wird
28
        t=0;              //Taktzähler wird bei Loslassen einer FB Taste auf 0 gesetzt
29
        b=0;              //Burstzähler wird zurückgesetzt wenn keine Taste betätigt wird
30
        s=0;              //Schalter s wird auf Off geschaltet wenn über 300 Takte ohne Puls vergangen sind
31
        p=0;              //Pulszähler p nach Loslassen einer Taste = 0
32
      }
33
    }

Doch leider haut das nicht so recht hin. Die Werte nehmen entweder 0, 
11, 21 oder 208 an, schnell durchlaufend. Es sieht so aus als würde der 
Zähler nicht fest die erste Puls-Pausezeit zählen, sondern durch die 
Datenbits  inkl. der Burstpause laufen, das kann aber eigentlich nicht 
sein.
Ich weiß nicht warum dort nicht der korrekte Wert für beispielsweise das 
erste Datenbit ausgegeben wird...

Grüße
T.M.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Thomas Mönig schrieb:
> Beim IRMP ist doch das IR-Logging für die Routine da, die die Pulse
> einliest, die als Basis der ganzen Auswertung des IR-Signales dienen.

Das IR-Logging wird von IRMP nicht benötigt, normalerweise ist das im 
IRMP abgeschaltet. Ich benötige das nur, um unbekannte Protokolle zu 
scannen und anschließend zu analysieren.

> Ich habe jetzt meinerseits versucht eine Routine zuschreiben,
> allerdingszugeschnitten auf meine verwendete JVC Fernbedienung.

In 3 Jahren wirst Du eine andere FB mit einem anderen Protokoll 
verwenden... aber ok, zum Lernen ist das ja richtig, sich mit einem 
konkreten Protokoll auseinanderzusetzen.

> Wenn die FB sendet kommt IMMER zuerst das Startbit gefolgt von 16
> Datenbits und einem Stopbit.

Korrekt.

> Dann die Burstpause und dann wieder 16 Daten plus Stopbit.

Ja.

> Bekannt sind die zeitliche Länge des Starbits mit Puls- und Pausenzeiten
> sowie die immer gleiche Pulsdauer der Daten- und Stopbits.

Jepp.

> Da der Abstand der Bursts ebenfalls immer gleich ist, ergeben sich
> daraus eine minimale Burstpausendauer und eine maximale, da ja die
> Einsen und Nullen unterschiedliche Pausenzeiten haben.

Du meintest nicht "Abstand der Bursts", sondern "Dauer der Bursts", 
nicht wahr? Ja, dann stimmt es.

Ich an Deiner Stelle würde es so machen:

1. Startbit-Pulsdauer zählen (Pin = 0)
2. Startbit-Pausendauer zählen (Pin = 1)
3. Passt Burst-/Pause zu JVC-Startbit?
      Nein, zurück zu 1
      Ja, Weiter zu 4
4. Datenbit-Pulsdauer zählen
5. Datenbit-Pausendauer zählen
6. Passt Pulsdauer zu JVC-Datenbit?
      Nein, zurück zu 1
      Ja, weiter zu 7
7. Passt Pausendauer zum Bit == 0?
      Nein, Passt Pausendauer zum Bit == 1?
          Nein, zurück zu 1
          Ja, Bit == 1 speichern
      Ja, Bit == 0 speichern
8. 16 Bits eingelesen?
9.    Nein, weiter bei 4
10.   Ja, wir haben die 16 Bit, diese aufteilen in Adresse + Kommando

> Der Timer steht auf 100µs(also 10000mal/pro sek) und durchläuft mit
> dieser Schrittzeit den folgenden Code.

Finde ich gut, Das macht IRMP genauso ;-)

> Meine Idee war erstmal dafür zu sorgen, dass solange der Eingangspegel
> vom IR-Empfänger 1 ist, eine Variable x auch 1 wird. Da aber ein Zähler
> dann bei jedem x=1 Wert zählen würde, musste eine Hilfsvariable a her,
> mit der eine Bedingung geschaffen wird, bei der nur gezählt wird wenn
> eine pos. oder neg. Flanke, das heisst ein Zustandswechsel 0->1 oder
> 1->0, kommt.

Man muss halt die Pulse (0en) und die Pausen (1en) zählen und sich in 
einer Variablen den letzten Zustand merken.

Also so:

1. Pin einlesen in Variable state
2. Ist state == 0, also Puls?
      Ja, ist state == last_state?
         Ja, dann pulsecounter++;
         Nein, Es gab eine Flanke 1->0! Pulse-/Pausenzählung fertig!
      Nein, dann pausecounter++

last_state initialisiert man mit 1.

> [Jede Menge C-Code...]

Tut mir leid, ich habe versucht, durch Deinen Code durchzusteigen, aber 
Deine nichtssagenden Variablennamen a, x, t, c, r machen es mir 
unmöglich, Deinen Gedankengängen über mehrere Zeilen zu folgen.

Zerlege die Aufgabenstellung in kleine Module und setze jedes einzeln 
um, als da wären:

   A. Ein "Paar" Pulsdauer + Pausendauer erkennen.
   B. Startbit anhand A) erkennen
   C. Datenbit anhand A) erkennen
   D. Speichern der Bits
   E. Aufteilung in Adresse und Kommando

Ein weiteres Problem ist, dass dieses alles in einer ISR abläuft. Diese 
muss man als Statemachine konzipieren, um ein IR-Telegramm einzulesen. 
Dazu muss man sich merken:

   1. Bin ich ganz am Anfang, also bei der Startbit-Erkennung?
   2. Bin ich in einer Pulszählung?
   3. Bin ich in einer Pausenzählung?
   4. Liegt eine Flanke 1->0 vor, habe ich also ein Bit
      (Puls-/Pausenpaar) zusammen, das ich speichern kann?
   5. Habe ich alle 16 bits eingelesen, habe ich also gerade die
      Flanke des Stop-Bits erwischt?

Die Flanke 0->1 interessiert übrigens nicht, da ist keine "Action" 
nötig. Das gilt jedenfalls bei der Familie der 
Pulse-Distance-Protokolle. Bei Manchester-Codierungen (RC5, RC6 und 
anderen) sieht das wieder ganz anders aus....

Viel Glück und viel Spaß beim Programmieren!

Frank

P.S.
Gewöhne Dir bitte längere und aussagekräftigere Variablennamen an, sonst 
steigst Du später (schon nach einigen Wochen) nicht mehr durch Deinen 
eigenen Code durch ;-)

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Noch ein Tipp:

Schau Dir mal den Quellcode von sndrx.c aus dem SOUNDRX-Programmpaket 
an. Da findest Du eine ganz einfache ISR vor, die genau das oben 
beschriebene innerhalb von 200 Codezeilen macht. Der einzige Unterschied 
ist, dass hier nur 8 Bit statt 16 Bit eingelesen werden. Da muss der 
Datentyp einer Variablen auf uint16_t statt uint8_t erweitert und eine 
Bedingung (8 -> 16) abgeändert werden. Ausserdem ist da noch ein 
Ringpuffer enthalten, da bei einer Soundkarten-Datenübertragung die 
Signale wesentlich flotter reinkommen als bei einer Infrarot-FB. Der 
Ringbuffer kann natürlich für Deine Zwecke raus.

Aber sonst ist diese ISR 1:1 für Deine Zwecke adaptierbar. Die 
Preprozessor-Konstanten, die die Puls-/Pausenlängen für Start- und 
Datenbits beschreiben, müssten natürlich an diejenigen Deiner FB 
angepasst werden...

Link:

  http://www.mikrocontroller.net/articles/SOUNDRX

Natürlich kannst Du das auch alles selbst schreiben, sonst könntest Du 
ja auch direkt IRMP nehmen, welches ja als Programmbibliothek gedacht 
ist.

Zum Studium, wie mans macht, ist es auf keinen Fall verkehrt, in die 
SOUNDRX-ISR mal reinzuschauen.

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.