Forum: Mikrocontroller und Digitale Elektronik Viele if-Schleifen hintereinander


von mcb (Gast)


Lesenswert?

Hallo,

ich bin gerade dabei, eine Abfrage für eine Tastatur zu schreiben. Die 
Funktion read_keypad_if soll dabei die Wertigkeit der Taste zurückgeben.
Funktion funktioniert, wenn man sie nur für zwei Tasten verwendet, also:
1
int debounce_if (const volatile uint8_t *debounce_if_port, uint8_t debounce_if_pin){
2
  if ((*debounce_if_port & (1 << debounce_if_pin)) == 0){
3
    while ((*debounce_if_port & (1 << debounce_if_pin)) == 0){
4
    }
5
    return 1;
6
  }
7
  return 0;
8
}
9
10
int read_keypad_if (void){
11
  if (debounce_if (&PIND, 5) == 1){ //Taste 1
12
    return 1;
13
  }
14
  if (debounce_if (&PINC, 1) == 1){ //Taste 2
15
    return 2;
16
  }
17
  return 0;
18
}
Wenn man allerdings alle Tasten miteinbezieht, wird gar keine Taste 
erkannt:
1
int debounce_if (const volatile uint8_t *debounce_if_port, uint8_t debounce_if_pin){
2
  if ((*debounce_if_port & (1 << debounce_if_pin)) == 0){
3
    while ((*debounce_if_port & (1 << debounce_if_pin)) == 0){
4
    }
5
    return 1;
6
  }
7
  return 0;
8
}
9
10
int read_keypad_if (void){
11
  if (debounce_if (&PIND, 5) == 1){ //Taste 1
12
    return 1;
13
  }
14
  if (debounce_if (&PINC, 1) == 1){ //Taste 2
15
    return 2;
16
  }
17
  if (debounce_if (&PINC, 5) == 1){ //Taste 3
18
    return 3;
19
  }
20
  if (debounce_if (&PIND, 4) == 1){ //Taste 4
21
    return 4;
22
  }
23
  if (debounce_if (&PINC, 0) == 1){ //Taste 5
24
    return 5;
25
  }
26
  if (debounce_if (&PINC, 4) == 1){ //Taste 6
27
    return 6;
28
  }
29
  if (debounce_if (&PIND, 3) == 1){ //Taste 7
30
    return 7;
31
  }
32
  if (debounce_if (&PIND, 7) == 1){ //Taste 8
33
    return 8;
34
  }
35
  if (debounce_if (&PINC, 3) == 1){ //Taste 9
36
    return 9;
37
  }
38
  if (debounce_if (&PIND, 2) == 1){ //Taste * = 10
39
    return 10;
40
  }
41
  if (debounce_if (&PIND, 6) == 1){ //Taste 0 = 11
42
    return 11;
43
  }
44
  if (debounce_if (&PINC, 2) == 1){ //Taste # = 12
45
    return 12;
46
  }
47
  return 0;
48
}
Aufruf und Auswertung im Hauptprogramm:
1
while (1){
2
    if (read_keypad_if () == 2){
3
      PORTA &= ~(1 << 5);
4
      _delay_ms (1000);
5
      PORTA |= (1 << 5);
6
    }
7
  }
Liegt das Problem bei den vielen if-Schleifen?

Danke im Voraus.

von if-Schleife (Gast)


Lesenswert?


von Lukas K. (carrotindustries)


Lesenswert?

if-Schleife schrieb:
> http://www.if-schleife.de/

Das wollte ich auch schreiben :(
statt
>(debounce_if (&PIND, 5) == 1){
und
>if ((*debounce_if_port & (1 << debounce_if_pin)) == 0){
schreibst du besser
(debounce_if (&PIND, (1<<5)) == 1){
und
if ((*debounce_if_port & debounce_if_pin) == 0){
Das kann der Compiler schon beim Kompilieren ausrechnen und der µC muss 
nichtmehr um n schieben, wofür es keine spezielle Instruktion gibt -> 
langsam

von Uhu U. (uhu)


Lesenswert?

mcb schrieb:
> Liegt das Problem bei den vielen if-Schleifen?

Daß das keine Schleifen sind, wurde schon gesagt...

Nein, es liegt daran, daß du für jede if-Abfrage die debounce-Funktion 
ein weiteres mal aufrufst und die wartet, bis einen weiteres mal eine 
Taste gedrückt wurde.

von mcb (Gast)


Lesenswert?

Die wartet doch nur, wenn eine Taste gedrückt wird, bis sie losgelassen 
wird.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Einmal abgesehen davon, dass die Funktion debounce_if entgegen ihrem
Namen die Taster nicht entrpellt, fällt mir an dem Programmstück nichts
Verdächtiges auf. Die paar if-Anweisungen hintereinander bringen den
Compiler nicht zum Verzweifeln.

Bist du sicher, dass während des Tests nicht die ganze Zeit Taster 1
geschlossen oder irgendwie überbrückt ist?

Die Taster schalten doch alle gegen GND und sind jeweils mit einem
Pullup-Widerstand (im Controller oder extern) versehen?

von Uhu U. (uhu)


Lesenswert?

mcb schrieb:
> Die wartet doch nur, wenn eine Taste gedrückt wird, bis sie losgelassen
> wird.


Stimmt. Aber debouncen tut die Funktion auch nicht.

Zudem fragst du denselben Pin für verschiedene Tasten ab, z.B. 2 und 5.

von Ralph B. (rberres)


Lesenswert?

Klärt mal einen Oberdau in Programmieren auf. Muss nicht nach jeder If 
Then else Abfrage ein End If folgen?
Zumindest kenne ich das von meinen HP-Instrumentbasic so.

Ralph Berres

von Uhu U. (uhu)


Lesenswert?

Ralph Berres schrieb:
> Klärt mal einen Oberdau in Programmieren auf. Muss nicht nach jeder If
> Then else Abfrage ein End If folgen?

In C nicht.

von mcb (Gast)


Lesenswert?

Ich frage dieselben PINs an unterschiedlichen PORTs ab! Siehe &PINC und 
&PIND!

von Daniel -. (root)


Lesenswert?

solange die Taste nicht gedrückt wird, blockiert doch
1
  if ((*debounce_if_port & (1 << debounce_if_pin)) == 0){
2
    while ((*debounce_if_port & (1 << debounce_if_pin)) == 0){
3
    }
4
    return 1;
5
  }

wenn man jetzt sukzessive die einzelnen Tasten abfragt, bleibt
man bei der ersten "gefangen" .. ganz unabhängig davon ob
die anderen gedrückt sind. Das ganze stellt doch eine Art
priorisierte Tastenabfrage dar. Ich verstehe ausserdem nicht
ganz warum dieselben Tasten mehrfach abgefragt werden.

von Daniel -. (root)


Lesenswert?

mcb schrieb:
> Ich frage dieselben PINs an unterschiedlichen PORTs ab! Siehe &PINC und
> &PIND!

ah gütiger :)
was spricht dagegen so ein Schema anzuwenden

// PORTA Schalter
#define TASTE_LICHT 0
#define TASTE_MOTOR 1
...
#define TASTE_XYZ   7

// PORTB Schalter
#define TASTE_ABC 0
...
#define TASTE_SOUNDSO 7

der Programmcode muss "reden" ;)

von Uhu U. (uhu)


Lesenswert?

Da die zweite Version von read_keypad_if sich von der ersten nur durch 
das unterscheidet, was nach der Abfrage der Taste 2 kommt, müssen beide 
Programme auf einen Druck der Taste 2 gleich reagieren.

Also funktionieren für die 2 entweder beide Versionen, oder keine.

von mcb (Gast)


Lesenswert?

Zur allgemeinen Schaltinformation. Alle Taster der Tastatur sind mit den 
COM-Anschluss gegen GND geschalten. Die einzelnen PINs sind dann an die 
I/Os des Controllers angeschlossen. In der Software werden dann für 
diese PINs die internen Pull-Ups aktiviert. Bei der Funktion debounce_if 
wird abgefragt, ob die Taste gedückt ist. Wenn ja, wird gewartet, bis 
die Taste losgelassen wurde und dann 1 zurückgegeben, wenn nein, wird 0 
zurückgegeben.

von Daniel -. (root)


Lesenswert?

soso, wenn das so ist, dann stimmt meine Argumentation von oben nicht.
Denn ich habe positive Logik angenommen .. 1=gedrückt

while (1){
    if (read_keypad_if () == 2){
      PORTA &= ~(1 << 5);
      _delay_ms (1000);
      PORTA |= (1 << 5);
    }
  }

Du erwartest nicht zufällig ein Blinken zur Bestättigung hier?

von mcb (Gast)


Lesenswert?

Doch. Wenn die Taste 2 gedrückt wird, soll die LED kurz (1s) aufblinken. 
Das funktioniert ja auch beim 1.Fall (also nur mit den Tasten 1 und 2 in 
der Funktion). Beim 2.Fall aber nicht.

von Frank B. (frank501)


Lesenswert?

Ich habe von C nun nicht gerade viel Ahnung, aber in den meisten 
richtigen Programmiersprachen gibt es die Case-Abfrage.
Wenn man nun die Taster einmal einliest und dann mit Case die 
Möglichkeiten abfragt dürfte das zumindest um einiges übersichtlicher 
sein.

Frank

von Daniel -. (root)


Lesenswert?

mcb schrieb:
> Doch. Wenn die Taste 2 gedrückt wird, soll die LED kurz (1s) aufblinken.
> Das funktioniert ja auch beim 1.Fall (also nur mit den Tasten 1 und 2 in
> der Funktion). Beim 2.Fall aber nicht.

eine 0 bzw Löschen des Pines am PORT lässt die LED leuchten?
(kann gut sein, ohne schematic lässt sich das nicht ersehen)

hmm, was könnte noch als Ursache in Betracht kommen ...
eine Taste, die dauernd "an" (0) ist und die Abfragekette blockiert?
Dennoch, wenn das nicht gerade die erste Taste ist, sollten die
am Kopf der Abfragekette stehenden Tasten funkionieren.

mir ist übrigens die Übergabeform &PIND als Zeiger nicht bekannt.
ich glaube zwar, dass es so richtig ist, und volatile ist dein
Zeiger ja auch. Wenn ich gar nicht weiterwusste würde ich die Funktion
umschreiben .. ohne Zeiger und den Code einfach duplizieren, je
nach Port
1
int debounce_if (uint8_t port_id, uint8_t debounce_if_pin){
2
  switch(port_id) {
3
      case PORTD_ID:
4
        if ((PIND & (1 << debounce_if_pin)) == 0){
5
          while ((PIND & (1 << debounce_if_pin)) == 0);
6
          return 1;
7
        }
8
        return 0;
9
       case PORTC_ID: /* the same */
10
       default: return 0;
11
}

von Uhu U. (uhu)


Lesenswert?

Schmeiß erst mal diese komische debounce_if raus - die funktioniert doch 
sowieso nicht.

Wenn du dann die Tasten bekommst, baust du eine Funktion, die wirklich 
entprellt.

von mcb (Gast)


Lesenswert?

Ich habe mir nochmal den AVR-Tutorial Eintrag zur Tastenentprellung 
angesehen (siehe: 
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#.28Tasten-.29Entprellung). 
Warum ist da die Funktion als inline deklariert?

von mcb (Gast)


Lesenswert?

Ich hab jetz mal die Funktion wait (Nachfolger von debounce) durch 
das Aufblinken einer weiteren LED anzeigen lassen, dass die Taste 
gedrückt und wieder losgelassen wurde:
1
int wait(volatile uint8_t *wait_port, uint8_t wait_pin){
2
  if ((*wait_port & (1 << wait_pin)) == 0){
3
    while ((*wait_port & (1 << wait_pin)) == 0){
4
    }
5
    PORTA |= (1 << 4);
6
    _delay_ms (200);
7
    PORTA &= ~(1 << 4);
8
    return 1;
9
  }
10
  else return 0;
11
}
Lässt man nun beim Aufruf im Hauptprogramm (vorerst ohne Auswertung) die 
letzte Spalte der Tastatur (also Tasten 3, 6, 9, #) weg, und drückt dann 
irgendeine andere Taste, blinkt die LED auf:
1
int main (void){
2
  //DDRs definieren
3
  DDRA = 0b11111111;
4
  DDRB = 0b11111111;
5
  DDRC = 0b11000000;
6
  DDRD = 0b00000011;
7
  //PORTs definieren
8
  PORTA = 0b00000000;
9
  PORTB = 0b00000000;
10
  PORTC = 0b00111111; //interne Pull-ups für Tasten aktivieren
11
  PORTD = 0b11111100; //interne Pull-ups für Tasten aktivieren
12
  while (1){
13
    wait (&PIND, 5);
14
    wait (&PINC, 1);
15
    //wait (&PINC, 5);
16
    wait (&PIND, 4);
17
    wait (&PINC, 0);
18
    //wait (&PINC, 4);
19
    wait (&PIND, 3);
20
    wait (&PIND, 7);
21
    //wait (&PINC, 3);
22
    wait (&PIND, 2);
23
    wait (&PIND, 6);
24
    //wait (&PINC, 2);
25
  }
26
  return 0;
27
}
Ich kann mir nicht erklären, warum das nicht funktionieren sollte, die 
anderen Tasten auch abzufragen.

von Peter D. (peda)


Lesenswert?

Erstmal Fusseln vom Mund wisch.

- Deine CPU stoppt komplett, solange eine Taste gedrückt ist

- Beim Loslassen macht sie dann noch nen Hickup von elendig langen 200ms 
(4 Millionen CPU-Zyklen).

- Die Reaktion erfolgt erst beim Loslassen. Ergonomisch wäre aber eine 
Reaktion beim Betätigen.


Falls Deine Programmnutzer mit diesen Nachteilen leben können und ein 
laienhaftes Feeling tolerieren, meinetwegen.


Peter

von mcb (Gast)


Lesenswert?

So besser? Allerdings weiß ich nicht welchen Wert ich bei *_delay_ms 
(xx)* verwenden soll.
1
uint8_t debounce(const volatile uint8_t *debounce_port, uint8_t debounce_pin){
2
  if ((*debounce_port & (1 << debounce_pin)) == 0){
3
    _delay_ms (50);
4
    return 1;
5
  }
6
  return 0;
7
}
Aufruf und Auswertung auf die übliche Form:
1
if ((debounce (&PIND, 5) == 1)){ //PD5 =^ Taster 1
2
  //irgendetwas machen
3
}

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.