Forum: Compiler & IDEs uC mit Lego Infrarotfernbedienung steuern


von David M. (dave48859)


Lesenswert?

Hallo,

Ich habe probiert das Signal einer Lego Powerfunctions Fernbedienung 
abzufangen.
Leider wird der Interrupt an dem der IR Empfänger hängt nur einmal 
ausgelößt.
Hier ist der link in dem das Protokoll beschrieben 
wird:http://www.philohome.com/pf/LEGO_Power_Functions_RC_v110.pdf

Hier der Code:
1
#include <avr/io.h>
2
#include "lcdlib.h"
3
#include <avr/interrupt.h>
4
#include <stdlib.h>
5
6
7
volatile uint8_t bitnr=0;
8
volatile uint16_t t;
9
volatile uint16_t datent=0;
10
volatile uint16_t daten=0;
11
volatile uint8_t x=0;
12
volatile uint8_t nib1=0;
13
volatile uint8_t nib2=0;
14
volatile uint8_t nib3=0;
15
volatile uint8_t nib4=0;
16
17
char datenchar[20];
18
19
20
21
ISR(INT0_vect)
22
{
23
      
24
    
25
      t=(TCNT1)*32;                    //Eine inkrementierung des Timers entspricht 32us
26
      if((t>316)&&(t<=526)&&(bitnr>=2)&&(bitnr<=17))    //Überprüfen ob ein high oder low gesendet wird (low: 316-526us bis zur nächsten steigenden Flanke
27
      {
28
        datent &=~(1<<(17-bitnr));            //Zwischenvariable damit nur am ende des Sendevorgangs 
29
      }
30
      
31
      if((t>526)&&(t<=947)&&(bitnr>=2)&&(bitnr<=17))    //High: 516-947us bis zur nächsten steigenden Flanke
32
      {
33
        datent |=(1<<(17-bitnr));
34
      
35
        nib1=datent>>12;
36
        nib2= (datent>>8)&0x0FFF;
37
        nib3= (datent>>4)&0x00FF;
38
        nib4=datent&0x000F;
39
      }
40
      
41
      
42
      if((bitnr==17)&&(((0xF)^nib1^nib2^nib3)==nib4))    //Nibble1 xor Nibble2 xor Nibble3 xor 0xF muss gleich Nibble4
43
      {
44
        daten=0;
45
        daten=datent;                  //Zwischenvariable damit nur am ende des Sendevorgangs die Daten Freigegeben werden
46
        bitnr=0;                            
47
        datent=0;
48
      }
49
      
50
      else
51
      {
52
        bitnr=0;
53
        datent=0;
54
      }
55
    
56
    TCNT1=0;                          //Timerregister auf null setzen
57
    bitnr++;                        //Bitnr inkrementieren
58
  
59
  
60
  
61
}
62
63
int main(void)
64
65
{
66
  
67
  DDRC=0xff;
68
  DDRA=0x07;
69
  DDRB=0xff;
70
  DDRD=0b11111011;
71
  
72
  
73
  TCCR1B=(1<<CS12);        //prescaler 256
74
  
75
  
76
  GICR|=(1<<INT0);
77
  MCUCR = (1<<ISC00)|(1<<ISC01);  // Trigger INT0 on rising edge
78
  
79
  lcd_init(LCD_DISP_ON);
80
  lcd_clrscr();
81
  
82
83
              
84
  
85
    while(1)
86
    {
87
    lcd_clrscr();        //Ausgeben eines Datenblocks am LCD
88
    lcd_gotoxy(0,0);
89
    char datenchar[20];
90
    itoa(bitnr,datenchar,2);
91
    lcd_puts(datenchar);
92
    _delay_ms(100);
93
    
94
      
95
    
96
    
97
    
98
    }
99
}


Was ist der Fehler?

von Joachim B. (jar)


Lesenswert?

David Märzinger schrieb:
> Was ist der Fehler?

k.a.

evtl. weil nicht IRMP benutzt :-)

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

: Bearbeitet durch User
von Eric (Gast)


Lesenswert?

David Märzinger schrieb:

> if((bitnr==17)&&(((0xF)^nib1^nib2^nib3)==nib4))    //Nibble1 xor
> Nibble2 xor Nibble3 xor 0xF muss gleich Nibble4
>       {
>         ...
>         bitnr=0;
>         datent=0;
>       }
>
>       else
>       {
>         bitnr=0;
>         datent=0;
>       }

An dieser Stelle angekommen ist bitnr immer 0, da es sowohl im if als 
auch im else-Zweig auf 0 gesetzt wird.

>     TCNT1=0;                          //Timerregister auf null setzen
>     bitnr++;                        //Bitnr inkrementieren

und hier ist bitnr dann 1.

von Eric (Gast)


Lesenswert?

David Märzinger schrieb:

>       if((t>316)&&(t<=526)&&(bitnr>=2)&&(bitnr<=17))    //Überprüfen ob
> ein high oder low gesendet wird (low: 316-526us bis zur nächsten
> steigenden Flanke

Da bitnr immer 1 ist (siehe obigen Beitrag), liefert dies Abfrage immer 
FALSE und der Code darunter wird nie ausgeführt.

>       if((t>526)&&(t<=947)&&(bitnr>=2)&&(bitnr<=17))    //High:
> 516-947us bis zur nächsten steigenden Flanke

Hier genau so. Der ISR kann also 1000x aufgerufen werden, wird aber nur 
bei der 1. Durchlauf irgendwas machen. Danach passiert da nix mehr.

von David M. (dave48859)


Lesenswert?

danke für den tipp!

von Karl H. (kbuchegg)


Lesenswert?

mal abgesehen davon.

Ich will dich ja nicht bevormunden, aber
1
      t=(TCNT1)*32;                    //Eine inkrementierung des Timers entspricht 32us
2
      if((t>316)&&(t<=526)

da in TCNT1 nur eine ganze Zahl stehen kann, kann daher t nur ein 
ganzzahliges Vielfaches von 32 sein. Das Ergebnis kann daher gar nicht 
zb 317 sein, da 317 kein Vielfaches von 32 ist. Die nächst kleinere 
Zahl, die bei der Multiplkation entstehen kann, ist 288. Dann kommt 
lange nichts und die nächste größere Zahl, die entstehen kann ist 320. 
(das eine mal hat TCNT1 den Wert 9 und das andere mal den Wert 10). Alle 
Werte zwischen 288 und 320 können überhaupt nicht auftreten. Das ist 
mathematisch überhaupt nicht möglich.

Jetzt muss man sich aber fragen: was bringt mir dann eigentlich die 
Multiplikation mit 32?
Und die Antwort drauf lautet: Überhaupt nichts. Gar nichts.

Denn ob du
1
      t=(TCNT1)*32;                    //Eine inkrementierung des Timers entspricht 32us
2
      if((t>316)&&(t<=526)
schreibst, oder ob du
1
      t= TCNT1;
2
      if( (t > 9) && (t < 17)
schreibst, kommt wegen des eingeschränkt möglichen Wertebereichs aufs 
selbe raus.

Mit einem kleinen Unterschied, der mglw. entscheidend sein kann. Das 
Eregbnis der Multiplikation mit 32 kann dir unter Umständen, wenn der 
TCNT1 Wert hoch genug wird, den Zahlenbereich eines int16_t sprengen. Ab 
dann rechnest du dann mit falschen Werten weiter.

von David M. (dave48859)


Lesenswert?

Aufgrund des Prescalers von bei einer Taktfrequenz von 8MHz, erhöht sich 
der Timer 31250 mal pro Sekunde daher enspricht ein Timerschritt 1/31250 
= 32us
Um die Zeit von einem Impuls zum anderen in us zu bekommen muss ich den 
Timerwert mit 32 multiplizieren.

Kennt ihr eine bessere Methode um die Zeiten zwischen mehreren Impulsen 
exakt zu messen? Das Signal, welches die Lego IR Fernbedienung sendet 
besteht aus einem Startbit, 16 Datenbits und einem Stoppbit:

Da die Information über die Dauer von der letzten Steigenden Flanke bis 
zur nächsten Steigenden Flanke übertragen wird:
Startbit, Stoppbit Dauer: 947-1579 us
Low                Dauer: 316-526 us
High               Dauer: 526-947 us

(Daher kommen diese Grenzen)
Habt ihr eine Idee wie man das einfacher machen könnte?

von Eric (Gast)


Lesenswert?

David Märzinger schrieb:

> Um die Zeit von einem Impuls zum anderen in us zu bekommen muss ich den
> Timerwert mit 32 multiplizieren.

Oder halt die Werte mit denen du vergleichst durch 32 dividieren,
wie kbuchegg oben beschrieben hat. Dann rechnest du halt nicht in us
sondern in timerticks.

von Joachim B. (jar)


Lesenswert?

David Märzinger schrieb:
> Habt ihr eine Idee wie man das einfacher machen könnte?

man könnte ja bei Frank (ukw) schmulen IRMP aber das schrieb ich schon.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

David Märzinger schrieb:
> Um die Zeit von einem Impuls zum anderen in us zu bekommen muss ich den
> Timerwert mit 32 multiplizieren.

Warum willst Du die Zeiten in µs? Tatsächlich brauchst die Zeiten in 
Takteinheiten Deines Timers.

> Da die Information über die Dauer von der letzten Steigenden Flanke bis
> zur nächsten Steigenden Flanke übertragen wird:
> Startbit, Stoppbit Dauer: 947-1579 us
> Low                Dauer: 316-526 us
> High               Dauer: 526-947 us

Besser:

Startbit, Stoppbit Dauer: 30 - 50 Timereinheiten
Low                Dauer: 10 - 16 Timereinheiten
High               Dauer: 16 - 30 Timereinheiten

Das hat auch den Vorteil, dass Du mit einem uint8_t-Zähler auskommst.

Genauso "zählt" übrigens IRMP - mit Unterstützung des Preprocessors 
in der gewählten Timereinheit, je nachdem, ob man 10000, 15000 oder 
20000 mal pro Sekunde das TSOP-Signal einliest.

Du solltest Dir wirklich mal IRMP (<-- draufklicken) anschauen, denn 
es unterstützt u.a. auch das LEGO-Protokoll, kann es einwandfrei 
verstehen und mittels IRSND sogar wieder senden - falls gewünscht.

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.