Forum: Mikrocontroller und Digitale Elektronik Problem mit if Abfrage in C


von Jörg S. (mitchell)


Angehängte Dateien:

Lesenswert?

Hallo,
ich bin gerade dabei mich mit C-Programmierung zu befassen.
Dazu habe ich mir ein Test-Programm geschrieben.
Teste zur Zeit mit einem Tiny2313, 3 LEDs an PB0-PB2 und 3 Schalter an 
PD0-PD2.
LED sind gegen  +5V geschaltet, Die Pullup für die Schalter sind 
aktiviert.
Wird einer der Schalter betätigt ( schaltet gegen GND) soll die 
entsprechende LED an gehen.
Schalter 1 und 2 sollen statische Signale und Schalter 3 soll ein 1Hz 
Signal simulieren.
SW1 und SW2 funktioniert die Abfrage, die LEDs werden entsprechend 
angeschaltet.

Bei SW3 soll die LED angehen, dann soll in einer Schleife getestet 
werden, ob der SW3 noch betätigt ist, da es pulsiert.
Wenn ca. 1s lang kein Signal kommt wird der Block verlassen.

Bei der Abfrage von SW3 komme ich nur bis zu der Anweisung "PORTB = 
0b11111010 ;  // LED1 und LED3 an".
Das Z = 0 und alles danach wird einfach ignoriert.
Warum?
Es steht doch in dem Anweisungsblock mit { und }.
Was habe ich falsch gemacht oder übersehen?

Gruß Jörg

von Peter II (Gast)


Lesenswert?

Jörg S. schrieb:
> Was habe ich falsch gemacht oder übersehen?

es wird ignoriert weil es keine Bedeutung hat. Es ist weg optimiert 
wurden.

von Frank B. (f-baer)


Lesenswert?

Z als volatile deklarieren, dann sollte es gehen.
Der Compiler optimiert die for-Schleife weg.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Jörg S. schrieb:
> Dazu habe ich mir ein Test-Programm geschrieben
Meine C-Programme enden auf *.c
Warum endet deines auf *.txt?

Jörg S. schrieb:
> Was habe ich falsch gemacht oder übersehen?
Hier aändert sich der Wert von x nicht mehr, weil der Port nicht mehr 
eingelesen wird:
        if (( x & 0x07 ) == 3) { // ist SW3 noch an?
Und weil x vorher == 2 war, wird die Bedingung nie erfüllt und 
wegoptimiert.

: Bearbeitet durch Moderator
von Ingo L. (corrtexx)


Lesenswert?

1
  int x = 0;
2
  int z = 0;
Du benötigst hier keine "int" (16-Bit), eine 8Bit Variable tut es auch.

von Ralf G. (ralg)


Lesenswert?

zusätzlich (reine Geschmackssache):
Mir gefallen solche 
'wenn-dies-dann-das-wenn-anders-dann-so-wenn-ja-warum-nicht-Vergleiche' 
überhaupt nicht.
1
switch (PORTD & 0x07)
2
{
3
    case 6:
4
        //  ...
5
        break;
6
    case 4:
7
        //  ...
8
        break;
9
    case 3:
10
        //  ...
11
        break;
12
    case 2:
13
        //  ...
14
        break;
15
// eventuell:
16
    default:
17
        //  ...
18
        break;
19
}
... find' ich übersichtlicher.

: Bearbeitet durch User
von Jörg S. (mitchell)


Lesenswert?

Hallo,
danke für die schnelle Rückmeldung.

Peter II schrieb:
> Jörg S. schrieb:
>> Was habe ich falsch gemacht oder übersehen?
>
> es wird ignoriert weil es keine Bedeutung hat. Es ist weg optimiert
> wurden.

warum?
Bin gerade erst beim lernen und verstehe es nicht.


Frank B. schrieb:
> Z als volatile deklarieren, dann sollte es gehen.
> Der Compiler optimiert die for-Schleife weg.

Wo, vor dem Z in der Abfrage?


Lothar M. schrieb:
> Jörg S. schrieb:
>> Dazu habe ich mir ein Test-Programm geschrieben
> Meine C-Programme enden auf *.c
> Warum endet deines auf *.txt?
>
> Jörg S. schrieb:
>> Was habe ich falsch gemacht oder übersehen?
> Hier aändert sich der Wert von x nicht mehr, weil der Port nicht mehr
> eingelesen wird:
>         if (( x & 0x07 ) == 3) { // ist SW3 noch an?
> Und weil x vorher == 2 war, wird die Bedingung nie erfüllt und
> wegoptimiert.

1. ich habe es in eine .txt kopiert, das nächste mal nehme ich gleich 
die .c

2. bis dahin komme ich nicht, aber ich werde das ändern auf 2 und 
testen.

Gruß Jörg

von Peter II (Gast)


Lesenswert?

Jörg S. schrieb:
> Bin gerade erst beim lernen und verstehe es nicht.

welche Bedeutung (bzw Auswirkung) soll denn der code nach deiner Meinung 
haben?

von Karl H. (kbuchegg)


Lesenswert?

Jörg S. schrieb:

> warum?
> Bin gerade erst beim lernen und verstehe es nicht.

weil, wenn
1
    x & 0x07
wie weiter vorne gefordert den Wert 2 haben muss, damit dieser Teilzweig 
überhaupt betreten wird, dann kann derselbe Ausdruck x & 0x07 nicht den 
Wert 3 haben, wenn zwischendurch x keine Möglichkeit hatte seinen Wert 
zu verändern. Von da ausgehend, kann man aber das Pferd jetzt von hinten 
aufzäumen. Wenn der Ausdruck nicht 3 ergeben kann, dann kann er das auch 
nicht, wenn man das in einer Schleife immer wieder tut. Die Schleife 
macht also effektiv nichts. Eine Schleife die nichts tut, braucht 
keiner. Gibt es keine Schleife mehr, dann gibt es auch keinen Grund für 
eine Variable namens z, die sonst nirgends mehr vorkommt.

d.h. ausgehend von diesem Code
1
    else if (( x & 0x07 ) == 2)  {      // ist SW1 und SW3 close?
2
      PORTB = 0b11111010 ;        // LED1 und LED3 an
3
      z = 0 ;            // Zähler zurück setzen
4
        for (; z<=4; z++) {
5
        //_delay_ms(200);      // warte 200ms
6
          if (( x & 0x07 ) == 3) {  // ist SW3 noch an?
7
            z = 0;      // ja, Zähler zurück setzen
8
          }
9
        }
10
      }

und der simplen Beobachtung, dass
1
           if (( x & 0x07 ) == 3)
niemals wahr sein kann, ist bis auf
1
      PORTB = 0b11111010 ;        //
alles toter Code, der nichts bewirkt ausser Rechenzeit zu verbrauchen.
Also hat ihn der Compiler rausgeworfen.

: Bearbeitet durch User
von Sebastian V. (sebi_s)


Lesenswert?

Frank B. schrieb:
> Z als volatile deklarieren, dann sollte es gehen.
> Der Compiler optimiert die for-Schleife weg.

Volatile bringt hier gar nichts. Man sollte einfach PIND anstelle von x 
testen. Oder man ließt vor jedem Test in der for-Schleife nochmal den 
aktuellen Wert von PIND und schreibt ihn in die x Variable.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Jörg S. schrieb:
> Lothar M. schrieb:
>> Hier aändert sich der Wert von x nicht mehr, weil der Port nicht mehr
>> eingelesen wird:
>>         if (( x & 0x07 ) == 3) { // ist SW3 noch an?
>> Und weil x vorher == 2 war, wird die Bedingung nie erfüllt und
>> wegoptimiert.
>
> ...
>
> 2. bis dahin komme ich nicht, aber ich werde das ändern auf 2 und
> testen.

Ich glaube, du möchtest eher das x durch PIND ersetzen, damit der Port
an dieser Stelle (d.h. in jedem Schleifendurchlauf) erneut gelesen wird
und so auf das Loslassen der Taste reagiert werden kann.

von Jörg S. (mitchell)


Angehängte Dateien:

Lesenswert?

Hallo Ralf,
das mit Case habe ich auch schon getestet.
Aber nur mit 3 statischen Signalen.
Das funktioniert, auch bei If mit drei statischen Signalen.
Aber ich mochte das bei dem 3. Signal ein pulsierendes abgefragt wird 
und erst wenn dieses nicht mehr anliegt, die Abfrage verlassen.

Da kommt die selbe Problematik wie beim If.
Aber auch das werde ich mit dem Case testen.

von Karl H. (kbuchegg)


Lesenswert?

Du möchtest da jetzt keine Zahlenspielchen spielen, sondern du möchtest 
deinen COde ordentlich schreiben.

Ob ein Bit in einem Byte auf 0 ist oder nicht, testet man mit dem 
Vergleich
1
    !( Byte & ( 1 << Bitnummer ) )

dieser Ausdruck ergibt wahr, wenn das Bit auf 0 ist. Bei dir bedeutet 
das, dass die entsprechende Taste gedrückt ist.

Und genau so handhaben wir das dann auch im Programm.

Da ich mal davon ausgehe, dass es erst mal nicht besonders kritisch ist, 
dass die Bitabfragen tatsächlich gleichzeitig erfolgen (denn: so genau 
kannst du deine Tasten sowieso nicht händisch betätigen, dass wirklich 
beide Tasten in genau derselben Femtosekunde gleichzeitig schalten), 
schlage ich erst mal vor, du machst dir dafür ein Makro
1
#define KEY_PRESSED( Port, Bit )  ( !(( Port) & ( 1 << (Bit) )) )
und du machst dir auch ein paar Makros für deine Hardwarebezeichnungen
1
#define SWITCH_1     PD0
2
#define SWITCH_2     PD1
3
#define SWITCH_3     PD2

dann schreibt sich nämlich dieser Aspekt des COdes als:
1
  while(1)
2
  {
3
    if  (KEY_PRESSED( PIND, SWITCH_1 )) {        
4
      PORTB = 0b11111110 ;        // LED1 an
5
    }
6
    else if (KEY_PRESSED( PIND, SWITCH_1 ) &&
7
             KEY_PRESSED( PIND, SWITCH_2 ) ) {        
8
      PORTB = 0b11111100 ;        // LED1 und LED2 aon
9
    }
10
    else if (KEY_PRESSED( PIND, SWITCH_1 ) &&
11
             KEY_PRESSED( PIND, SWITCH_3 ) )  {      
12
      PORTB = 0b11111010 ;        // LED1 und LED3 an
13
      z = 0 ;            // Zähler zurück setzen
14
        for (; z<=4; z++) {
15
        //_delay_ms(200);      // warte 200ms
16
          if ( KEY_PRESSED( PIND, SWITCH_3 ) {
17
            z = 0;      // ja, Zähler zurück setzen
18
          }
19
        }
20
      }
21
    else {
22
      PORTB = 255;
23
    }
24
  }

nicht nur macht diese Modifikation einige Kommentare überflüssig, das 
Programm ist jetzt auch deutlich leichter zu lesen und zu verstehen. Und 
als Nebeneffekt verschwindet dann auch dein Logikfehler.

Und dasselbe machst du auch mit deinen LED
* erst mal ersetzt du die direkten Portzuweisungen durch die 
entsprechenden Bitoperationen. Und dann denkst du dir Namen für dein LED 
aus und machst dir Makros dafür. Wann immer du versucht bist, so etwas 
zu schreiben
1
      PORTB = 0b11111110 ;        // LED1 an
musst du dich sofort fragen, warum du da einen Kommentar brauchst. Wozu 
einen Kommentar schreiben, wenn du genausogut zb
1
     CLR_BIT( PORTB, LED1 );
schreiben kannst, oder noch besser
1
     LED_ON( LED1 );
was genau dasselbe aussagt, was bei dir im Kommentar steht. Alles was du 
dazu brauchst, ist nur ein wenig Makro-Magie, so dass sich durch die 
Makro.Textersetzungen dann wieder
1
     PORTB &= ~( 1 << PB0 );
ergibt, was genau das Bit 0 am Port B auf 0 setzt und sonst nichts 
anderes tut.

: Bearbeitet durch User
von Jörg S. (mitchell)


Lesenswert?

Hallo Karl Heinz,
danke für die anschauliche Erklärung, das hilft mir mehr als nur 
Begriffe.

das werde ich gleich mal testen

Gruß Jörg

von Karl H. (kbuchegg)


Lesenswert?

Jörg S. schrieb:

> das werde ich gleich mal testen

Ich hatte noch eine Klammerung vergessen.
So ist das Makro richtig
1
#define KEY_PRESSED( Port, Bit )  ( !((Port) & ( 1 << (Bit) )) )

von Jörg S. (mitchell)


Angehängte Dateien:

Lesenswert?

Hallo Karl Heinz,

ich habe jetzt einen Teil umgesetzt.

Die Tasten sind soweit klar.
aber bei den LEDs habe ich jetzt schon wieder ein Problem bzw. etwas 
nicht verstanden.

     LED_ON( LED1 );

dazu habe ich das definiert :
#define LED_ON( Bit )  ( (PORTB) &= ~(1 << (bit) ) )
#define LED1  PB0
#define LED2  PB1
#define LED3  PB2

und erhalte das:

Error  1  'bit' undeclared (first use in this function)

Gruß Jörg

von Yalu X. (yalu) (Moderator)


Lesenswert?

Groß- Kleinschreibung beachten in C.

von Jörg S. (mitchell)


Lesenswert?

Hallo,
genau, das passiert mir immer wieder, danke.
Jetzt werde ich das ganze mal testen.

Gruß Jörg

von Karl H. (kbuchegg)


Lesenswert?

:-)
Und ich hoffe, du hast dir auch überlegt, dass eine leuchtende LED nicht 
von alleine ausgeht.
1
   LED_ON( LED1 );

ist kein vollwertiger Ersatz für
1
      PORTB = 0b11111110 ;        // LED1 an

selbst wenn der Kommentar dies suggeriert.
Denn die letzte Anweisung macht noch mehr als nur die LED1 
einzuschalten. Sie schaltet auch alle anderen ab bzw. setzt sogar die 
Portpins an denen gar nichts hängt auf 1. Davon steht aber nichts im 
Kommentar!

(Und das ist auch einer der Gründe, warum du möglichst ohne derartige 
Komentare auskommen sollst. Denn meistens sind die sowieso falsch bzw. 
erzählen nicht die ganze Wahrheit. Sich auf sie zu verlassen ist daher 
sowieso oft Harakiri. Man kommt daher ohne exakte Analyse des Source 
Codes sowieso nicht aus. Ein
1
      LED_ON( LED1 );
2
      LED_OFF( LED2 );
3
      LED_OFF( LED3 );
hingegen erzählt mir auch ohne Kommentar die ganze Wahrheit und ist 
dabei recht einfach zu lesen und zu analysieren. Vor allen Dingen wenn 
die LED dann nicht LED1, LED2 und LED3 heissen, sondern sprechende Namen 
tragen, wie zb ERROR_LED oder READY_LED oder ...

: Bearbeitet durch User
von Jörg S. (mitchell)


Lesenswert?

Hallo,
und schon wieder ein neues Problem mit den Makros.
Ich komme nicht mehr aus der ersten IF raus, da es immer wahr ist.
Auch wenn die beiden anderen Tasten betätigt werden, ist der Taster 1 
bereits betätigt und lande bei der ersten IF.

Taster 1 ist so zu sagen der Hauptschalter und ist immer vor den anderen 
betätigt.
Es soll getestet werden:

ist nur SW1 on
 -Anweisung
ist SW1 und SW2 on
 -Anweisung
ist SW1 und SW3 oder SW1 und SW2 und SW3 on (SW2 ist hier egal)
 -Anweisung
sonst
 -Anweisung

das hat ohne die Makros funktioniert nur eben nicht die verschachtele 
Anweisung beim 3.Fall mit der Schleife.

Gruß Jörg

von Jörg S. (mitchell)


Lesenswert?

Hallo,
ja, das LED_OFF Makro fehlt noch.

Wenn man gerade C lernt, ist das mit dem Makros noch komplizierter als 
die direkte Zuweisung.
Aber man möchte es auch gleich richtig lernen.

Gruß Jörg

von Karl H. (kbuchegg)


Lesenswert?

Jörg S. schrieb:

> ist nur SW1 on

'nur' bedeutet in diesem Zusammenhang
* Taster1 ist betätigt    UND
* Taster2 ist nicht betätigt    UND
* Taster3 ist nicht betätigt

In Codeform
1
    if  (  KEY_PRESSED( PIND, SWITCH_1 ) &&
2
         ! KEY_PRESSED( PIND, SWITCH_2 ) &&
3
         ! KEY_PRESSED( PIND, SWITCH_3 ) )

schreib was du meinst. Aber überleg dir vorher, ob das was du meinst 
auch die Realität wiederspiegelt.

Und ja:
wir sind im täglichen Leben recht schlampig was strenge Logik angeht. 
Wir haben unsere Code-Wörter, die der Person gegenüber signalisieren, 
was er sich dazudenken muss.

: Bearbeitet durch User
von Jörg S. (mitchell)


Lesenswert?

Hallo,
alles klar, danke.

von Karl H. (kbuchegg)


Lesenswert?

Jörg S. schrieb:

> Taster 1 ist so zu sagen der Hauptschalter und ist immer vor den anderen
> betätigt.

Wenn es sich um einen Hauptschalter handelt, dann formulier das auch 
genau so
1
   if( KEY_PRESSED( PIND, SWITCH_1 ) )    // Hauptschalter auf "ein" ?
2
   {
3
4
     if( KEY_PRESSED( PIND, SWITCH_2 ) )
5
       ...
6
7
     if( KEY_PRESSED( PIND, SWITCH_3 ) )
8
       ...
9
   }


Ist der Hauptschalter nicht auf 'ein', dann interessieren dich auch die 
anderen Schalter nicht mehr. Wie im täglichen Leben. Ist im Auto die 
Zündung ausgeschaltet, dann interessiert dich die Stellung des 
Scheibenwischerschalters nicht weiter. Erst mal geht es um die Frage: 
Ist die Zündung überhaupt ein oder nicht? Und nur dann, wenn die 'ein' 
ist, erst dann willst du die Stellung des Scheibenwischerschalters 
wissen um zu entscheiden, ob gewischt werden sollte oder nicht. Aber 
solange die Zündung aus ist, funktioniert nicht sehr viel im Auto.

: Bearbeitet durch User
von Jörg S. (mitchell)


Lesenswert?

Hallo,
so ist es noch besser.

Gruß Jörg

von Jörg S. (mitchell)


Angehängte Dateien:

Lesenswert?

Hallo Karl Heinz,
noch mal danke für die Hilfe.
Es funktioniert jetzt so wie es soll.
Auch die Schleife um das pulsierende Signal anzuzeigen ohne das die LED 
mit blinkt funktioniert jetzt.
Das Ausschalten der LEDs habe ich in die else Anweisung gepackt.

Der Dateiname stimmt aber nicht mehr, es gibt keine else if mehr.

Gruß Jörg

von Karl H. (kbuchegg)


Lesenswert?

Kleiner Tip.

Das hier
1
      if (KEY_PRESSED( PIND, SWITCH_3)) {      
2
      LED_ON( LED3 );
3
      z = 0 ;                      // Zähler zurück setzen
4
        for (; z<=4; z++) {
5
        _delay_ms(200);                // warte 200ms
6
          if (KEY_PRESSED( PIND, SWITCH_3)) {    // ist SW3 noch an?
7
            z = 0;                // ja, Zähler zurück setzen
8
          }
9
        }
10
      }
lässt sich auch deutlich einfacher und ohne 'von hinten durch die Brust 
ins Auge'-Programmierung erreichen.

Was ist denn der Sinn der Sache?
Die LED3 soll eingeschaltet werden.
Danach soll solange gewartet werden, bis die Taste losgelassen wird. 
Weiter geht es erst, wenn die Taste 4 mal hintereinander als losgelassen 
gesehen wird, was sich in Summe bei 200ms pro Schleifendurchlauf zu 
800ms aufsummiert.
D.h. der Nettoeffekt ist: Die LED wird auf jeden Fall 800ms brennen, 
möglicherweise auch länger wenn die Taste gedrückt bleibt. Erst danach 
geht  es dann weiter (was ist eigentlich, wenn bei gedrückter Taste 3 
der Hauptschalter losgelassen wird? Im Moment kümmert das nicht - was 
aber eigentlich am Sinn eines Hauptschalters vorbei geht). 'Weiter geht' 
bedeutet aber in jedem Fall, dass die LED 3 wieder abgeschaltet wird.

Wie auch immer. Das hier
1
      if (KEY_PRESSED( PIND, SWITCH_3)) {      
2
        LED_ON( LED3 );
3
        delay_ms( 800 ); 
4
        while( KEY_PRESSED( PIND, SWITCH_3) )
5
          ;
6
        LED_OFF( LED3 );
7
      }
macht daher offensichtlich genau dasselbe. Bei deutlich einfacherem 
Code.

Edit:
stimmt nicht ganz. Im Original kann ich die Taste zum richtigen 
Zeitpunkt zwischendurch loslassen und wieder drücken, ohne dass es deine 
Warte auf Taste loslassen Schleife abbricht, was bei dieser Version 
nicht geht. Auf der anderen Seite habe ich dafür ganze 8 Zehntel 
Sekunden Zeit, was nicht wahnsinnig viel ist. Aber wer weiss: vielleicht 
bist du ja gerade darauf aus: Schnell genug (aber auch nicht zu schnell) 
auf die Taste hämmern, lässt die LED weiterleuchten.
Das hätte sich dann allerdings einen Kommentar verdient.

: Bearbeitet durch User
von Jörg S. (mitchell)


Angehängte Dateien:

Lesenswert?

Hallo,
da hast du Recht, wenn SW1 geöffnet wird, bleibt LED3 an.

Das habe ich gleich geändert und jetzt geht die LED3 nach Ablauf der 
Schleife auch aus.

SW3 simuliert ein pulsierendes Signal mit ca. 1HZ, kommt nicht von einem 
Quarz, daher ca.
Die LED soll aber nicht im Takt blinken, sondern dauernd leuchten bis 
das Signal nicht mehr anliegt.

Das hatte ich weiter oben schon mal erwähnt.

Gruß Jörg

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.