3 RC Kanäle Auswerten

Gast #2683210
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
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.
Gast
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
Gast
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
}
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.
Gast
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.
Gast
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 ;-)
Gast
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
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.
Gast
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

Antwort schreiben

Eine eigene Frage? Dieser Thread ist über 14 Jahre alt. Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.

Neuen Thread starten

Eine Lösung, Ergänzung oder Rückmeldung zu diesem Thread? Dann antworte hier.

Formatierung
  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] – Link zu einem Artikel
  • Verweis auf einen anderen Beitrag: Rechtsklick auf dessen Beitrags-ID (#8078628), „Adresse kopieren“, in den Text einfügen

Mehr zur Formatierung

Längeren Sourcecode als Anhang, nicht in den Text. JPG nur für Fotos und Scans; Zeichnungen und Screenshots als PNG oder GIF. Siehe Bildformate.

Schon registriert? Anmelden

Wird nicht angezeigt. Wir schicken dir einen Link; sobald du damit bestätigst, erscheint dein Beitrag.

Mit dem Abschicken legst du einen kostenlosen Account an und erkennst die Nutzungsbedingungen und die Datenschutzerklärung an.