Forum: Compiler & IDEs 3 RC Kanäle Auswerten


von Toby (Gast)


Lesenswert?

Hallo,

ich versuche grade drei RC Kanäle mit einem ATMega168 auszuwerten.
Dazu nutze ich die drei vorhandenen PinChange Interupts.
Also je RC Kanal einen anderen Port.
1
#define RX_PIN_B  PINB
2
#define RX_PORT_B  PORTB
3
#define RX_DDR_B  DDRB
4
#define RX_1    0
5
6
#define RX_PIN_C  PINC
7
#define RX_PORT_C  PORTC
8
#define RX_DDR_C  DDRC
9
#define RX_2    0
10
11
#define RX_PIN_D  PIND
12
#define RX_PORT_D  PORTD
13
#define RX_DDR_D  DDRD
14
#define RX_3    2
15
16
volatile uint16_t timerValue[3]  = {0,0,0};
17
volatile uint16_t channel[3]  = {1500,1500,1500};
18
19
/* INIT */
20
  
21
   RX_DDR_B  &= ~(1<<RX_1_B);
22
   RX_DDR_C  &= ~(1<<RX_1_C);
23
   RX_DDR_D  &= ~(1<<RX_1_D);
24
  
25
   TCCR1B = _BV(CS11) ;
26
   TIMSK1 = 0;
27
  
28
   //PinChange Interupt an PB 0
29
   PCMSK0 |= (1<<PCINT0);
30
  
31
   //PinChange Interupt an PC 0
32
   PCMSK1 |= (1<<PCINT8);
33
  
34
   //PinChange Interupt an PD 2
35
   PCMSK2 |= (1<<PCINT18);
36
  
37
   //enable pinchange interrupt;
38
   PCICR  |= ((1<<PCIE2) | (1<<PCIE1) | (1<<PCIE0));
39
40
   sei();
41
42
/* INIT ENDE */
43
44
ISR(SIG_PIN_CHANGE0)
45
{
46
  uint16_t timer = TCNT1;
47
48
  if(RX_PIN_B & (1<<RX_1))
49
  {
50
    timerValue[0] = timer;
51
  }
52
  else
53
  {
54
    channel[0] = timer - timerValue[0];
55
  }
56
}
57
58
ISR(SIG_PIN_CHANGE1)
59
{  
60
  uint16_t timer = TCNT1;
61
  
62
  if(RX_PIN_C & (1<<RX_2))
63
  {
64
    timerValue[1] = timer;
65
  }
66
  else
67
  {
68
    channel[1] = timer - timerValue[1];
69
  }
70
}
71
72
ISR(SIG_PIN_CHANGE2)
73
{  
74
  uint16_t timer = TCNT1;
75
  
76
  if(RX_PIN_D & (1<<RX_3))
77
  {
78
    timerValue[2] = timer;
79
  }
80
  else
81
  {
82
    channel[2] = timer - timerValue[2];
83
  }  
84
}

Nun möchte ich gerne alle drei RC Kanäle an nur einem Port auswerten.
Da habe ich dann aber nur einen PinChange Interupt.
Hat jemand eine Idee wie ich das lösen kann?
1
#define RX_PIN  PINB
2
#define RX_PORT  PORTB
3
#define RX_DDR  DDRB
4
#define RX_1  0
5
#define RX_2  1
6
#define RX_3  2
7
8
volatile uint16_t timerValue[3]  = {0,0,0};
9
volatile uint16_t channel[3]  = {1500,1500,1500};
10
11
/* INIT */
12
  
13
   RX_DDR  &= ~((1<<RX_3) | (1<<RX_2) | (1<<RX_1));
14
  
15
   TCCR1B = _BV(CS11) ;
16
   TIMSK1 = 0;
17
  
18
   //PinChange Interupt an PB 0,1,2
19
   PCMSK0 |= ((1<<PCINT2) | (1<<PCINT1) | (1<<PCINT0));
20
  
21
   //enable pinchange interrupt;
22
   PCICR  |= (1<<PCIE0);
23
24
   sei();
25
26
/* INIT ENDE */
27
28
ISR(SIG_PIN_CHANGE0)
29
{
30
   uint16_t timer = TCNT1;
31
  
32
   //Hier fehlt`s ;-)  
33
}

Gruß Toby

von Karl H. (kbuchegg)


Lesenswert?

Toby schrieb:

> Nun möchte ich gerne alle drei RC Kanäle an nur einem Port auswerten.
> Da habe ich dann aber nur einen PinChange Interupt.
> Hat jemand eine Idee wie ich das lösen kann?

In der ISR den PIN-Zustand einlesen und mit einem vorhergehenden Zustand 
vergleichen? Der Vergleich bringt zutage, welcher Pin sich verändert 
hat.

von Toby (Gast)


Lesenswert?

Hallo,

irgendwie stehe ich grade auf dem Schlauch.

Ich speichere nun den Wert von PinB in einer Variable.
1
ISR(SIG_PIN_CHANGE0)
2
{
3
   uint16_t timer = TCNT1;
4
   uint8_t rx_state = RX_PIN;  
5
}

Jetzt muss ich den Wert mit den voherigen vergleichen.
Also eine globale Variable, sagen wir mal rx_state_old deklarieren.
1
volatile uint8_t rx_state_old;
2
3
ISR(SIG_PIN_CHANGE0)
4
{
5
   uint16_t timer = TCNT1;
6
   uint8_t rx_state = RX_PIN;
7
8
   //neuen Wert mit altem Wert vergleichen
9
   //Wie?!   
10
11
   //alten Wert mit neuem Wert beschreiben
12
   rx_state_old = rx_state;     
13
}

Muss ich jetzt Bitweise sehen welches Bit sich geändert hat und dann 
dementsprechend handeln?

Gruß Toby

von Toby (Gast)


Lesenswert?

Hallo,

nun bin ich so weit, das die ersten beiden Kanäle gehen.
Nur der dritte nicht. :-(
1
ISR(SIG_PIN_CHANGE0)
2
{
3
  uint16_t timer = TCNT1;
4
  uint8_t rx_state = RX_PIN;
5
  uint8_t i;
6
  
7
  i = rx_state | rx_state_old;
8
  
9
  if(i & 0x01)
10
  {
11
    timerValue[0] = timer;
12
  }
13
  else if(i & 0x02)
14
  {
15
    channel[0] = timer - timerValue[0];
16
    timerValue[1] = timer;
17
  }  
18
  else if(i & 0x04)
19
  {
20
    channel[1] = timer - timerValue[1];
21
    timerValue[2] = timer;
22
  }  
23
  else if( //hier muss ich auf gelöschtes Bit3 testen )
24
  {
25
    channel[2] = timer - timerValue[2];
26
  }
27
  
28
  rx_state_old = rx_state;
29
}

von Karl H. (kbuchegg)


Lesenswert?

Toby schrieb:
> Hallo,
>
> nun bin ich so weit, das die ersten beiden Kanäle gehen.
> Nur der dritte nicht. :-(
>
> [c]
> ISR(SIG_PIN_CHANGE0)
> {
>   uint16_t timer = TCNT1;
>   uint8_t rx_state = RX_PIN;
>   uint8_t i;
>
>   i = rx_state | rx_state_old;

so kriegst du aber nicht die Pins raus, die sich VERÄNDERT haben!
Du brauchst ein XOR

    i = rx_state ^ rx_state_old;

Jetzt hast du in i genau an den Stellen ein 1 Bit, an denen sich 
rx_state von rs_state_old unterscheidet.

i sagt dir, welcher Pin den Pin Change Interrupt ausgelöst hat und 
rx_state sagt dir, ob der Pin jetzt 0 oder 1 ist.

von Rolf Magnus (Gast)


Lesenswert?

Ich würde auch das else überall weglassen. Wenn sich mal mehrere Pins 
nahezu gleichzeitig ändern, bekommt man sonst nicht alle Änderungen mit, 
da der Code ja nur dann was macht, wenn sich exakt einer der drei Pins 
geändert hat.

von Toby (Gast)


Lesenswert?

Hallo,

Also in etwa so:

Wenn i = 1
    Wenn rx_state = 1 Pulsmessung starten
    Sonst Pulsmessung auswerten

Wenn i = 2
   Wenn rx_state = 2
...
Wenn i = 4
   Wenn rx_state = 4
...

Oder kann/muss ich i und rx_state nochmal vergleichen
Um den Flankenwechsel zu erkennen?

Wenn ich jeden 2. RC Kanal vom Empfänger nehme,
Hab ich ja eindeutige Pulslücken zwischen den Kanälen.

Nehme ich Kanal 1-3 hab ich keine Lücken.
Am Ende des 1. Pulses kommt sofort der zweite Puls.
Kann man das überhaupt Erkennen?

Gruß Toby

Entschuldigt die Schreibweise, aber geht mit dem Handy
so einfacher ;-)

von Toby (Gast)


Lesenswert?

Hallo,

ich habs jetzt so gemacht und es funktioniert:
1
ISR(SIG_PIN_CHANGE0)
2
{
3
  Timer1_stopp;
4
  uint16_t timer = TCNT1;
5
  Timer1_run;
6
  
7
  uint8_t rx_state = RX_PIN;
8
  uint8_t i;
9
  
10
  i = rx_state ^ rx_state_old;
11
  
12
  if(i & 0x01)
13
  {
14
    if (rx_state & 0x01)
15
    {
16
      timerValue[0] = timer;
17
    }
18
    else
19
    {
20
      channel[0] = timer - timerValue[0];
21
    }
22
  }
23
  
24
  if(i & 0x02)
25
  {
26
    if (rx_state & 0x02)
27
    {
28
      timerValue[1] = timer;
29
    }
30
    else
31
    {
32
      channel[1] = timer - timerValue[1];
33
    }
34
  }
35
  
36
  if(i & 0x04)
37
  {
38
    if (rx_state & 0x04)
39
    {
40
      timerValue[2] = timer;
41
    }
42
    else
43
    {
44
      channel[2] = timer - timerValue[2];
45
    }
46
  }
47
    
48
  rx_state_old = rx_state;
49
}

Danke für die Hilfe!

Gruß Toby

von Karl H. (kbuchegg)


Lesenswert?

Toby schrieb:

> ich habs jetzt so gemacht und es funktioniert:

Gut,

Und wenn du jetzt noch deinen 'magischen Konstanten' 0x01, 0x02 ein paar 
schöne Makrokonstanten spendierst, dann kann man das auch leicht auf 
andere Pinbelegungen vom µC anpassen. Anstatt i noch einen vernünftigen 
Variablennamen nehmen (Frage: Was bedeuten die Bits in dieser Variable? 
Antwort: Das sind die Pins die sich verändert haben. Also sollte man 
dann auch einen Namen wählen, der das ausdrückt)
1
#define RX_KANAL_1     PB0
2
#define RX_KANAL_2     PB1
3
#define RX_KANAL_3     PB2
4
5
....
6
7
    pinsChanged = pinsNow ^ pinsPrevious;
8
 
9
    if( pinsChanged & (1<<RX_KANAL_1) )
10
    {
11
      if (pinsNow  & (1<<RX_KANAL_1)
12
      {
13
        timerValue[0] = timer;
14
      }
15
      else
16
      {
17
        channel[0] = timer - timerValue[0];
18
      }
19
    }
20
21
    if( pinsChanged & (1<<RX_KANAL_2) )
22
    {
23
      if (pinsNow  & (1<<RX_KANAL_2)
24
      {
25
26
...

liest sich doch gleich viel besser. Je weniger man beim Code lesen 
erraten oder sich merken muss, umso besser. Im Idealfall ist der Code 
schon fast ein vollständiger Satz, dem lediglich ein paar Füllwörter 
fehlen.


   wenn sich der Pin gändert hat, an dem der RC Kanal 1 hängt

   if( pinsChanged & (1<<RX_KANAL_1) )

Ist fast die gleiche Information, nur das das eine ein deutscher Satz 
ist und das andere dasselbe in C ausdrückt.

von STK500-Besitzer (Gast)


Lesenswert?

Wenn alle abzufragenden Pins sich an einem Port befinden, könnte man 
auch eine Schleife benutzen...

von Toby (Gast)


Lesenswert?

Hallo Karl-Heinz,

Als ich den Beitrag abgeschickt habe, hatte ich auch noch den Gedanken
die Konstanten zu ändern.

Deine Ausführung ist da natürlich sehr elegant.

Gruß Toby

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.