Forum: Mikrocontroller und Digitale Elektronik RC5 - dekodierung PIC


von Tobias John (Gast)


Lesenswert?

Hallo zusammen,
ich versuche seit Wochen einen Fernbedienungs-Code mit Hilfe eines TSOP
auszuwerten.
Ich habe den Eingang des TSOP an RB0 meines PIC, den auch als Interrupt
eingerichtet habe.

Ich komme hierbei leider auf keinen grünen Zweig. Jetzt mögen viele
sagen "schau bei Sprut da ist alles erklärt". Das ist wohl auch
richtig, und ich habe auch schon auf vielen anderen Seiten geschaut,
aber deshalb weiß ich immer noch nicht wie ich an die Sache herangehen
soll.

Wie kann ich z.B. sicher gehen, dass der PIC nicht anfängt die Folge
von BITs mitten im Code auszuwerten?

Ein Flussdiagramm für die Auswertung habe ich nirgends gefunden.

Kann mir jemand unter die Arme greifen?

Ich programmiere übrigens in Assembler (ich weiß, C wäre einfacher,
aber wir programmieren in der Schule nur mit Assembler, und ich möchte
das einfach dafür lernen ;-) )

Danke im Vorraus!

 Tobias

von Peter Dannegger (Gast)


Lesenswert?

"Ich habe den Eingang des TSOP an RB0 meines PIC, den auch als
Interrupt
eingerichtet habe."

Damit wird die Sache nur unnötig kompliziert.

Einfacher ist die Abfrage in einem Timerinterrupt.
In der Codesammlung ist ein Beispiel in C mit Funktionsbeschreibung:

http://www.mikrocontroller.net/forum/read-4-74473.html#new

Wenn Du C verstehst, sollte es nicht schwer sein, das Prinzip in
Assembler umzusetzen.



"Wie kann ich z.B. sicher gehen, dass der PIC nicht anfängt die Folge
von BITs mitten im Code auszuwerten?"

Kein Problem, der Code prüft, ob genau 14 Bits empfangen wurden.


Peter

von Peter Schwarz (Gast)


Lesenswert?

Hallo Peter,

was spricht dagegen, den Externer Interrupt zu benutzen ?

Ich habe im Moment eine Basis-Station für eine Propeller-Uhr fertig und
werte dort eine JVC Fernbedienung aus. Da ich keine Beispiele gefunden
habe, habe ich folgendes selbst programmiert: ich benutze jetzt den
externer Int1, um dann den Timer2 losrennen zu lassen. Trudelt der
nächste Int1 ein, werte ich den Timer aus, um zu sehen, wie lange der
letzte Impuls gedauert hat.

Ich muß zugeben, so ganz 100%ig funktioniert es nicht, ich muß die
Taste teilweise öfters drücken, damit das Teil reagiert (angeschlossen
ist ein TSOP1738).

Würde es Vorteile bringen, alles in den Timer zu packen und den einfach
dauernd laufen zu lassen und zu gucken, wie das Signal momentan vom TSOP
ist (high oder low) und daraus die Länge des letzten Impulses zu
bestimmen ?

JVC verpackt die 1er in 2,10ms und die Nuller in 1,05ms Pakete, RC5
wäre mir lieber gewesen, aber leider habe ich bei 6 Fernbedienungen
daheim keine einzige die RC5 sendet ;)

Gruss, Peter

von André Weber (Gast)


Lesenswert?

Hi,

ich verwende auch die "Polling" / Timer Variante,
- ich habe in meinem aktuellen Projekt es so gemacht, da ich
vorhatte mehre verschiedene Protokolle erkennen zu können und
außerdem noch andere Interrupts zulassen wollte - USART,
& Timer0.

Im Hauptprogramm in einer Endlosschleife frage ich
immer den Port ab woran der TSOP1738 (ist für meisten IR
Signale geeignet) hängt - wenn dieser Eingang auf 0
geht - starte ich den Timer1 (16bit, TMR1H=TMR1L=0) - so wenn
jetzt der Eingang wieder auf 1 geht - kann ich anhand
der Länge dieses Startimpulses ermitteln welches Protokoll
empfangen wurde ... (so mit Pi * Daumen) +- einige us

- wenn jetzt die Zeit ca. 889us (850-920us) ist - ist es ja
RC5- (wenn die Zeit ca. 1778us beträgt kann es auch RC5 sein
wo das 2. Startbit zum 6. Befehlsbit gemacht wurde RC5+!)

danach gehts in die spezifische dekodierroutine:

- dort lese ich als erstes den aktuellen Zustand des TSOP
ein - ist dieser 1 (0)- warte ich in einer Schleife maximal 1000us
auf einen Pegelwechsel nach 0 (1) - wenn das nicht geschieht
breche ich das Lesen ab.
(dafür halte ich Timer1 zuvor an setzte TMR1L und TMR1H so das
nach ca. 1000 us ein Überlauf statt findet wenn ich den Timer neu
gestartet habe)

Habe ich jetzt einen 01 / 10 Pegelwechsel erhalten speichere
ich mir das jeweilige Bit in meiner Variablen (2 bytes).

Zu diesem Zeitpunkt wird noch die 2. Halbbit des aktuellen Bits
übertragen. Jetzt richte ich mir den Timer1 so ein daß er nach
1333 us (889+889/2) überläuft - dann müßte ich mich ja zeitlich
gesehen in der Mitte des nächste ersten Halbbits befinden - und
ich lese dessen Pegel ein - und das ganze beginnt vor vorne.
Bis ich meine 14 Bit zusammen habe.

Dieses Vorgehen mag kompliziert erscheinen im vergleich zu
anderen RC5 Routinen - welche NOPS und Warteschleifen verwenden
um das Timing zu treffen, diese Variante hat aber folgende
Vorteile:
 - andere IRQ's wie z.B. TMR0 und USART stören so gut wie über-
   haupt nicht - da Zeitmessung über Timer1 erfolgt
 - auch unsauber gesendete IR Befehle können noch erfolgreich
   dekodiert werden da er Empfang in der Mitte jedes Bits
   neu synchronisiert wird.

André

von Peter Dannegger (Gast)


Lesenswert?

@Peter Schwarz

"was spricht dagegen, den Externer Interrupt zu benutzen ?"

"Ich muß zugeben, so ganz 100%ig funktioniert es nicht"

Genau das.

Die TSOP haben eine AGC, um sich den Lichtverhältnissen anzupassen.
Dadurch kann es zu Pseudoimpulsen kommen. Die Timer-Poll-Version
filtert diese heraus.

Und wie gsagt, der Timer kann noch nebenbei LEDs multiplexen, Tasten
entprellen, Uhrzeit zählen usw., da er nicht angehalten wird.


Peter

von André Weber (Gast)


Lesenswert?

Hi,
JVC - hab ich mir gerade mal angeschaut ist ja
"fast" das gleiche wie Nec / Yamaha nur das der Repeat
ein wenig anders funktioniert.

würde ich meiner Logik so ausschauen:
wenn Zeit des StartImpulses ca. 8400us ist.
(da ich den 16 bit Timer verwende, mit Vorteiler 4 und
 bei 10mhz Pic-Takt - müßte ich immer 16 bit vergleiche
 mach deswegen teile ich die Zeit vor dem Vergleich immer
 durch 32 (rrf um 5 bit!) - dadurch wird zwar die Zeit
 nicht mehr ganz genau gemessen aber man kann trotzdem
 die IR's sequencen gut voneeinander unterscheiden)

Repeat-Code!
    (wenn Zeit des StartImpulses ca. 526us ist - und zuletzt
     ein JVC Code empfangen wurde d.h. kein Error oder ähnliches
     aufgetreten ist - gehe direkt zum Empfänger an "Stelle 1"
     vorher noch den BitZähler mit 16 initialisieren!

Decoder:

geht das Programm in den JVC decoder und initialisiert
Timer1 so das nach ca. 4400 us ein Überlauf eintritt
und wartet dann darauf das am TSOP das nächste mal
ein L Pegel auftritt - ist dies bis zum Überlauf nicht
der Fall wird das Lesen abgebrochen.

BitZähler=16;
LeseSchleife:
 ; die 526us "Pause" abwarten
  lade TMR1L und TMR1H so das nach maximal 600us ein überlauf eintritt
warte_auf_nächsten_Pulse:
  teste TMR1 auf überlauf wenn ja abbruch der Empfangsroutine
 wenn TSOP = 0 goto warte_auf_nächsten_Pulse

Stelle 1:

 halte Timer1 an
 löschen TMR1L und H
 starte Timer1
lese_bit:
 teste ob im TMR1 größer als 1574 us  wenn ja abbruch
 übertragungsfehler
 wenn TSOP = 1 goto lese_bit

 halte Timer1 an

 (um den vergleich auf 8 Bit zu begrenzen verwende
  ich hier den gleiche Trick wie beim Startpuls
  einfach den TMR1H und TMR1L um 3 Bit nach Rechts
  schieben -
     clrc
     rrf TMR1H,F
     rrf TMR1L,F
     clrc
     rrf TMR1H,F
     rrf TMR1L,F
     clrc
     rrf TMR1H,F
     rrf TMR1L,F
  dann kann der ganze vergleich mit TMR1L erfolgen,
  um wieviel Bit nach Rechts geschoben werden muß hängt
  vom Takt des PIC ab, dem Vorteiler welcher Timer1 inne
  hat und der maximalen Zeit die gemessen werden soll ab
  (3bit - sind es bei VT=4 und 10 Mhz)

 wenn Timer1 im Bereich 1500 bis 1650us dann
   eine 1 speichern
 wenn Timer1 im Bereich 480 bis 540us dann
   eine 0 speichern
 else
   übertragungsfehler

decfsz BizZähler,F
goto LeseSchleife

Wichtig!
-> warte jetzt noch solange bis der TSOP wieder H-Pegel
-> liefert - d.h. das Stop Bit zu Ende ist!





André

von till (Gast)


Lesenswert?

Nun, eindeutig für die Version mit externem Interrupt spricht, das man
so den PIC schlafen legen kann wenn er nichts tun soll. In einem Hifi
Vorverstärker minimiert man so störende HF Einstreuung aus dem MC.

von Tobias John (Gast)


Angehängte Dateien:

Lesenswert?

Hallo zusammen,
vielen Dank für die Tipps!

Ich habe mich dann mal an die Arbeit gemacht und mich an der
Beschreibung von "André Weber" entlanggehangelt.
Nun habe ich ein Problem bei dem ich nicht weiterkomme.

Nachdem das Erste Start-Bit bei mir angekommen ist und das 2te Startbit
auf High geht fange ich an den Code zu lesen.

Bei der Sprungmarke "S1" geht er zu "waitNull" weil eine "1" als
Startbit anliegt.
Dort springt er aber sofort wieder zu "Main" => bei "T1" weil die
Zeit abläuft.

Ich habe den Code mal im Anhang mitgesendet.

Kann mir da jemand sagen was ich falsch mache?

Gruß,
 Tobias

von André Weber (Gast)


Lesenswert?

Hallo,

wie ich sehe aktiviert dein Source den TMR0 IRQ und
den Externen Interrupt and RB0 - dein Source
enthält dafür aber keine Interruptroutine?
(GIE = 0? hoffe ich mal)
- deaktiviere doch mal komplett, aktiviere nur den Timer
und lasse ihn parallel laufen.

Das Hauptproblem was ich sehe - vermutlich habe ich es nicht
genau genug beschrieben - daß Deine Leseroutine nicht zwischen
den BITS wartet, das heißt du beginnst das nächste Bit schon zu lesen -
während der TSOP momentan noch die 2. Hälfte des vorigen Halbbits
ausgibt d.h. du müßtest zwischen

S2:
 ; weil wenn du S2 anspringst ist ja erst gerade der Flankenwechsel
 ; des aktuellen Bits geschehen - d.h. du mußt es noch ein wenig
 ; warten bis etwa die Zeit um es daß von nächsten Halbbit schon
 ; die hälfte der Zeit vorbei ist - bevor du ein neues Bit zu lesen
 ; beginnst -

 movlw 0x90  ; ca 889 + 889/2 us
 movwf TR0
wait_mitte_nächstes_halb_bit:
 btfsc TR0,7
 goto wait_mitte_nächstes_halb_bit

decfsz  R0
goto S1

weiterhin solltest du die Länge des Start Impulse messen
da deine Routine sonst viel zu häufig auf Störimpulse
anspringt - die oft nur wenige us lang sind.

Also würde ich den Teil welche du in W1 geschrieben hast
wie folgt ändern:
(TMR0 VT=8 Pic Takt=4mhz vorausgesetzt)

clrf TR0
wait_for_startbit2:
 btfss  PORTB, RB0 ;Skip wenn Pin0 wieder auf "eins" geht
 btfss TR0,7       ; ok. länger as 128 * 8 us gewartet da
                   ; kommt  nichts mehr abbruch
 goto  startbit2
movfw TR0          ; Timer0 wert sichern
movwf TMR_TMP      ; und zwischen speichern
movlw D'100' ; ca 800 us
subwf TMR_TMP,W
btfss STATUS,C
goto MAIN       ; wenn C Flag = 0, dann pulse kürzer als 800us

movlw D'121' ; ca 960 us
subwf TMR_TMP,F
btfsc STATUS,C
goto MAIN       ; wenn C Flag = 1, dann pulse länger als 960us

Weitere Probleme sehe ich im Moment nicht, müßte eigentlich
so gehen für RC5 - womit hast du es getestet, woher du
dir sicher bist das er aufgrund des Timers sofort wieder
zurück geht?
Hast du mal mit dem Timer die Zeit gemessen, wie lang
die Lücke wirklich ist? erhöhe doch einfach die mal die
Wartezeit um einige "us" - vielleicht ist deine FB auch
zu ungenau? - habe da schon teilweise Abweichungen in
den Timings bis zu 15% gesehen - d.h. 1022us wäre durchaus
auch noch ok. als Wartezeit - als initialisierte mal
TR0 mit 127 statt mit 130 in deiner Warteschleife
vielleicht sind es ja wirklich nur die paar us die
dir fehlen.
Oder verwende mal zur Überlaufprüfung nicht das T0IF Flag
sondern werde den Wechsel des Bit7 von TR0 von 1 auf 0 aus,
was auch sehr zuverlässig den überlauf anzeigt, speziell
wenn man den Timer frei laufen läßt, braucht man sich
dann auch nicht um das Interruptflag kümmern;-)

Wie hoch ist dein PIC getaktet?

Dein Timersetup geht von 4 Mhz aus?


André

von Tobias John (Gast)


Angehängte Dateien:

Lesenswert?

Hallo André,

vielen Dank für Deine Tatkräftige Unterstützung.

Interrupts habe ich nun komplett deaktiviert. Das waren z.T. noch
Relikte aus vorherigen Versionen.
Frage: Timer aktivieren? Ich kann doch nur die Source für den Timer
wählen (intern /extern) und den Prescaler einstellen - oder?

Zu "S2":
Wenn ich S2 anspringe habe ich einen eventuellen Flankenwechsel
erkannt. Was aber wenn dies 999µs dauert (ich warte ja max. 1000µs
darauf). Dann befinde ich mich doch schon auf dem nächsten Halbbit.
Wenn es schneller geht befinde ich mich davor - oder sehe ich das
falsch?

Ich habe meinen Schritt "S2" mal so wie du sagtest geupdatet.

Wie genau funktioniert die Überprüfung vom TR0 - Bit "7" ?

bei "wait_for_startbit2" steht unten "goto startbit2" ist damit die
Sprungmarke "wait_for_startbit2" gemeint?

ist hier nicht ein Fehler bei der Rechnung.
Wenn der Pulse kürzer als 800µs war, dann steht im TR0 ein Wert, sagen
wir mal ein Wert von 90d. Subtrahieren wir davon 100d dann ist der Wert
negativ, das heißt das Carry-Flag wird gesetzt (oder nicht?).
Wenn das gesetzt wird, dann müsste man doch zurück zu Main springen und
nicht wenn es "0" ist? Wenn das C Flag = 0, dann ist der Pulse länger
als 800µs - oder nicht?
-----------------------
movfw TR0          ; Timer0 wert sichern
movwf TMR_TMP      ; und zwischen speichern
movlw D'100' ; ca 800 us
subwf TMR_TMP,W
btfss STATUS,C
goto MAIN       ; wenn C Flag = 0, dann pulse kürzer als 800us
----------------------------
Und hier dann genau andersherum:
Nehmen wir mal an, der Pulse war ca. 1040µs =>130d lang.
Subtrahieren wir davon 121d, dann ist das Carry-Flag (weil nicht
negativ) nicht gesetzt. Oder habe ich das mit dem Carry-Flag falsch
verstanden?
----------------------------
movlw D'121' ; ca 960 us
subwf TMR_TMP,F
btfsc STATUS,C
goto MAIN       ; wenn C Flag = 1, dann pulse länger als 960us
--------------------------


"womit hast du es getestet, woher du dir sicher bist das er aufgrund
des Timers sofort wieder zurück geht?"
--
Ich habe einfach an bestimmten Stellen die Routine "RA0_ON"
aufgerufen, um zu sehen bis wohin er kommt und wo er hängen bleibt.



Oder verwende mal zur Überlaufprüfung nicht das T0IF Flag
sondern werde den Wechsel des Bit7 von TR0 von 1 auf 0 aus,
was auch sehr zuverlässig den überlauf anzeigt, speziell
wenn man den Timer frei laufen läßt, braucht man sich
dann auch nicht um das Interruptflag kümmern;-)
-----
Wie bereits oben gefragt. Ich Bit7 von TR0 ist doch nicht nur wenns
überläuft gesetzt - oder?


Wie hoch ist dein PIC getaktet?
Dein Timersetup geht von 4 Mhz aus?
----
Jupp, genau 4MHz ;-)


André

von Tobias John (Gast)


Lesenswert?

Ähm ja,
jetzt habe ich glatt vergessen Deinen Namen unten drunter zu löschen...
:D
Wenn man nicht alle Sinne beisammen hat...

Gruß,
 Tobias

von André Weber (Gast)


Lesenswert?

Hallo,

>Frage: Timer aktivieren? Ich kann doch nur die Source für den Timer
>wählen (intern /extern) und den Prescaler einstellen - oder?
sorry hab das mit Timer1 und 2 verwechselt die lassen sich mittels
TMR1ON bzw. TMR2ON ab und anschalten, d.h. TMR0 läuft immer
nur den Interrupt kann man abschalten.

>Zu "S2":
>Wenn ich S2 anspringe habe ich einen eventuellen Flankenwechsel
>erkannt. Was aber wenn dies 999µs dauert (ich warte ja max. 1000µs
>darauf). Dann befinde ich mich doch schon auf dem nächsten Halbbit.
>Wenn es schneller geht befinde ich mich davor - oder sehe ich das
>falsch?
nein - bei RC5 besteht ja jedes Bit aus je zwei Halbbits zu 889us
Da du ja den Flankenwechsel abgewartet hast kannst du dieses
Halbbit ja damit "verbringen" die 0 oder 1 in der Variable GBYTE
zu speichern, und zu warten das dieses Halbbit vorübergeht
das dauert 889us dann bis du erst genau an der Grenze zum nächsten
Halbbit welches Interessant ist, da aber 889us zu warten extrem
knapp ist um man evtl. zu früh den Pegel des TSOP abfragt
wartet man noch ein wenig mehr (889/2) um etwa in der Mitte
des übernnächsten Halbbits wieder aufzusetzen.

eine 1 besteht aus:
------------------
889us ohne IR-Impulse, gefolgt von 889us mit IR-Impulsen
- daraus ergibt sich am Ausgang des TOP zunächst
  -> ein 889us H-Pegel
  -> gefolgt von 889us L-Pegel

Also wenn der Wechsel länger als 1000us dauert, ist
die Übertragung sowieso fraglich - ob das ein gültiges RC5
Signal sein - kann deswegen auch der Abbruch!

eine 0 besteht aus:
-------------------
889us mit IR-Impulses, gefolgt von 889us ohne IR-Impulse
- daraus ergibt sich am Ausgang des TOP zunächst
  -> ein 889us L-Pegel
  -> gefolgt von 889us H-Pegel

(schau dir mal die Seite an: http://www.sprut.de/electronic/ir/rc5.htm
dort ist das mal
grafisch dargestellt wie ein RC5 Signal aussieht)

>Wenn ich S2 anspringe habe ich einen eventuellen Flankenwechsel
>erkannt.
das ist aber der Flankenwechsel zwischen 1. und 2. Halbbit
d.h. zu diesem Zeitpunkt vergehen noch ca. 889us
bis das nächste BIT beginnt. damit man nicht zu früh
ausliest habe ich darauf noch einmal die halbe Bitzeit
addiert. (889 + 889/2) - dann befindet man sich etwa in der
Mitte des 1. Halbbits des nächstes Bits.

Anmerkung:
Zwischen zwei Bits hast du nicht unbedingt einen Flankenwechsel
wenn z.B. bei RC5 ein Binär 01 übertragen wird hast du
am TSOP Ausgang 889us-L, 889us-H, 889us-H, 889us-L)
einen Flankenwechsel gibt es garantiert nur mitten im Bit
bei RC5.

> Wie genau funktioniert die Überprüfung vom TR0 - Bit "7" ?
da deine Zähler TMR0 Initialisierung in diesem Fall
größer als 128 ist ist ja beim setzen immer Bit 7 = 1 gesetzt.
jetzt wartet man einfach solange bis Bit 7 = 0 wird - das geschieht
beim Überlauf von 255 -> 256 (0) Beispiel:

warte:
 btfss TR0,7
 goto Ueberlauf
goto warte
Ueberlauf:

oder kürzer

warte:
 btfsc TR0,7
 goto warte

geschehen - die Schleife bricht erst ab wenn der Zähler
auf 0 überläuft.

--> wenn deine Zählerinitialisierung kleine als 128 ist mußt
du damit vorsichtig sein, und vorher darauf warten das bit 7 gesetzt
wird! -- im FALL RC5 und dem Timersetup ist dies aber nicht notwendig

>bei "wait_for_startbit2" steht unten "goto startbit2" ist damit
die
>Sprungmarke "wait_for_startbit2" gemeint?
genau (das passiert dann wenn man sowas blind schreibt)

> ist hier nicht ein Fehler bei der Rechnung.
- bitte den TMR0 mit Vorteiler 8 einrichten
(sonst kommt es vielleicht zu früh zum überlauf!)

> Wenn der Pulse kürzer als 800µs war, dann steht im TR0 ein Wert,
> sagen wir mal ein Wert von 90d. Subtrahieren wir davon 100d dann
> ist der Wert negativ, das heißt das Carry-Flag wird gesetzt (oder
> nicht?).
eher nicht - aufgrund wie der PIC ein Subtraktion vornimmt wird
das C-Flag durch einem unterlauf (sprich wenns <0 wird gelöscht).
Beispiel: 90 - 100 sieht binär so aus für den PIC

Carry
  1 0101 1010    (90)
- 0 0110 0100   (100)
-------------
  0 1111 0110   (246)
---------------------

>Ich habe einfach an bestimmten Stellen die Routine "RA0_ON"
>aufgerufen, um zu sehen bis wohin er kommt und wo er hängen bleibt.
ok - hab ich übersehen;-)

>Wie bereits oben gefragt. Ich Bit7 von TR0 ist doch nicht nur wenns
>überläuft gesetzt - oder?
- deswegen wertet man ja auch wechsel von 1 nach 0 des bits aus
sowie ich es geschrieben habe:

movlw 0x90  ; ca 889 + 889/2 us
movwf TR0
wait_mitte_nächstes_halb_bit:
btfsc TR0,7  ;>>>>>>> solange Bit 7 gesetzt ist goto_wait...
goto wait_mitte_nächstes_halb_bit

(gehen die Labels mit "ä" drin - wirklich? hab das nie probiert)



André

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.