Forum: Mikrocontroller und Digitale Elektronik Taster auf "1" abfragen in AVR-C


von O. A. (sokrates1989)


Lesenswert?

Hallo Forumsmenschen,

ich brauche mal wieder Hilfe und zwar habe ich ein einfaches Programm 
gemacht, bei dem ich per Tasterdruck die Leds nacheinander einschalte..

Leider funktioniert es nicht so wie erhofft.

Aus einem Buch habe ich die Tasterabfrage übernommen die so aussieht.

if (!(PINB & (0x01)))

ich habe es bei mir mit while gemacht.

Leider gehen die Leds aber sofort an(ohne dass ich den Taster drücke), 
erst wenn ich das "!" rausnehme geht es wie gewünscht.

Aber das "!" muss doch drin sein, wegen dem Pullup.

Was mache ich falsch?
1
#define F_CPU 3686400
2
#include <avr/io.h>
3
#include <util/delay.h>
4
5
6
7
8
9
int main()
10
{
11
12
DDRB=0x00;      //PortB Eingänge
13
DDRC=0xFF;      //PortC Ausgänge
14
PORTB=0xFF;     //Pullup 
15
16
17
do  
18
{
19
PORTC = 0;
20
}
21
22
while(!(PINB & (0x01)));    //wenn TasterEin gedrückt
23
{
24
PORTC = 0x01;
25
_delay_ms(500);      
26
PORTC = 0x02;
27
_delay_ms(500);  
28
PORTC = 0x04;
29
_delay_ms(500);  
30
}
31
return 0;
32
}

von Timmo H. (masterfx)


Lesenswert?

Vermutlich hast du vorher nur mit Arduino gearbeitet. Dort gibt es eine 
"setup"-Funktion welche nur einmal beim starten aufgerufen wird und eine 
"loop"-Funktion welche danach immer wieder aufgerufen wird.

Nun arbeitest du aber mit der Main-Funktion als wäre sie die loop 
funktion. Die while-Schleife wird jedoch nur dann durchlaufen wenn der 
Taster beim Reset des Controllers gedrückt war. Und da das vermutlich 
nie bei dir der Fall ist wird die main-Funktion direkt mit "return 0" 
"verlassen" und der Controller macht quasi ein Reset und fängt in der 
Main wieder von vorne an, inkl. der Initialisierung.
Was du aber willst ist eher sowas:
1
main(){
2
  DDRB=0x00;      //PortB Eingänge
3
  DDRC=0xFF;      //PortC Ausgänge
4
  PORTB=0xFF;     //Pullup 
5
6
  PORTC = 0;
7
8
  while(1){
9
10
    if (!(PINB & (0x01))){
11
     //mach was
12
    }
13
14
  }
15
16
17
}

von VIA (Gast)


Lesenswert?

Moin,

O. A. schrieb:
> while(!(PINB & (0x01)));


Gehe die Schleifenbedingung noch einmal genau durch und veranschauliche 
dir dabei, welcher Zustand bei gedrücktem und nicht-gedrücktem Taster 
anliegt.


Das Beispiel in deinem Buch fragt den Taster ab, ob er gedrückt ist
 -> wenn(Taster gedrückt): mache sonstwas

Du möchtest in deiner while-Schleife aber warten, solange der Taster 
nicht gedrückt ist
 -> solange(Taster nicht gedrückt): mache nichts.

Dadurch wird dann auch deine Bedingung negiert.


Noch ein paar Anmerkungen:
- Du solltest dir von vornherein einen "sauberen" Programmierstil 
angewöhnen, was schon beim Einrücken der Zeilen beginnt
- Deine kopfgesteuerte "do-Schleife" ist überflüssig
- Die geschweiften Klammern nach der while-Schleife sind überflüssig
- Du solltest dich demnächst mit dem Thema "Taster-Entprellung" 
auseinandersetzen
- Da du scheinbar auf einem (AVR-) µC programmierst, beachte, dass ein 
Mikrocontroller niemals "ins leere" laufen sollte. Nachdem deine 
while-Schleife verlassen und PORTC mit den _delay's gesetzt wurde, 
sollte das Programm von vorne beginnen (Endlosschleife)

1
#define F_CPU 3686400
2
#include <avr/io.h>
3
#include <util/delay.h>
4
5
6
int main(){
7
8
   DDRB = 0x00;      //PortB Eingänge
9
   DDRC = 0xFF;      //PortC Ausgänge
10
   PORTB = 0xFF;     //Pullup 
11
   PORTC = 0;
12
13
   while(1){                   // oder: for(;;){
14
15
      while(PINB & (0x01));    //*solange* TasterEin *NICHT* gedrückt
16
17
      PORTC = 0x01;
18
      _delay_ms(500);      
19
      PORTC = 0x02;
20
      _delay_ms(500);  
21
      PORTC = 0x04;
22
      _delay_ms(500);  
23
   }
24
   return 0;
25
}

von O. A. (sokrates1989)


Lesenswert?

DAnke ...!

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

> Deine kopfgesteuerte "do-Schleife" ist überflüssig
> Die geschweiften Klammern nach der while-Schleife

Ich wette, der TO wollte da zwei Schleifen programmieren. Ihm ist wohl 
gar nicht bewusst, dass der Block hinter while gar nicht Bestandteil der 
Schleife ist.

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Anders ausgedrückt:

Die geschweifte Klammer muß IN DER SELBEN ZEILE wie die Abfrage stehen, 
damit der Rest auch zur Abfrage gehört.

MfG

von STK500-Besitzer (Gast)


Lesenswert?

Patrick J. schrieb:
> Die geschweifte Klammer muß IN DER SELBEN ZEILE wie die Abfrage stehen,
> damit der Rest auch zur Abfrage gehört.

Sieht vielleicht gut aus, ist aber Quatsch.

von O.a (Gast)


Lesenswert?

Stefan U. schrieb:
> Ich wette, der TO wollte da zwei Schleifen programmieren. Ihm ist wohl
> gar nicht bewusst, dass der Block hinter while gar nicht Bestandteil der
> Schleife ist.

Ich dachte ...der Port soll ausgeschaltet werden ...
Durch do
Und solange die Bedingung im while erfüllt ist
Wird das lauflicht ausgeführt
Das hat auch geklappt..aber nur einmal ..
Also sobald der Taster gedrückt war ging alle Lampen einmal an und dann 
leuchtete nur noch die lerzte...

von Joachim B. (jar)


Lesenswert?

O.a schrieb:
> Das hat auch geklappt..aber nur einmal ..
> Also sobald der Taster gedrückt war ging alle Lampen einmal an und dann
> leuchtete nur noch die lerzte...

das ist ja kein Wunder

O. A. schrieb:
> while(!(PINB & (0x01)));    //wenn TasterEin gedrückt
> {
> PORTC = 0x01;
hier dritte LED aus und
erste  LED an

> _delay_ms(500);
> PORTC = 0x02;
hier erste LED aus und
zweite LED an

> _delay_ms(500);
> PORTC = 0x04;
> _delay_ms(500);
hier erste & zweite LED aus und
dritte LED an

und so bleibt es doch, was sollte sich ändern?

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Du hast nicht verstanden, wie man Schleifen mit do und while 
programmiert.

Die einzige Schleife in deinem Programm ist dies:
1
do  
2
{
3
    PORTC = 0;
4
}
5
while(!(PINB & (0x01)));

Hier wird der ganze Port C auf Low geschaltet. Und wenn danach PB1=Low 
ist, dann wird das solange wiederholt, bis PB1 nicht mehr Low ist.

Danach (also wenn PB1 nicht mehr Low ist) wir der nächste Block 
ausgeführt:
1
{
2
    PORTC = 0x01;
3
    _delay_ms(500);      
4
    PORTC = 0x02;
5
    _delay_ms(500);  
6
    PORTC = 0x04;
7
    _delay_ms(500);  
8
}
9
return 0;

Das ist der Teil, wo die Klammern nichts Sinnvolles bewirken. Also 
nachdem der Taster losgelassen wird, gehen deine drei LED's nacheinander 
an. Zum Schluss bleibt PC2 an und das Programm ende mit return 0.

Prinzipiell dürfen Mikrocontroller Programme niemals enden, denn dies 
würde Frage aufwerfen, was er denn danach machen soll. Beim AVR fügt der 
Compiler automatisch eine leere Endlosschleife am Ende des Programmes 
sein, damit es bei solchen Fehlern wenigstens nicht abstürzt.

In deinem Fall scheint es sinnvoll, dass das Programm sich nach dem LED 
geblinke wiederholt, so dass es auf den nächsten Tastendruck wartet. 
Also brauchst du zwei verschachtelte Schleifen. VIA hat es schon korrekt 
geschrieben:
1
int main()
2
{
3
   initialisierung;
4
5
   while (1)  // Wiederhole endlos oft
6
   {
7
8
      while (taste nicht gedrückt)
9
      {
10
         ; // tu nichts, leere Warteschleife
11
      }
12
13
      lasse die LED's blinken
14
15
   }
16
}

Gewöhne Dir ganz schnell an, den Code richtig einzurücken und Leerzeilen 
sinngemäß zu setzen. Dann ist der fehler nämlich offensichtich.

Hier nochmal dein ganzes fehlerhaftes Programm, jedoch mit korrekter 
Formatierung:
1
#define F_CPU 3686400
2
#include <avr/io.h>
3
#include <util/delay.h>
4
5
int main()
6
{
7
8
    DDRB=0x00;      //PortB Eingänge
9
    DDRC=0xFF;      //PortC Ausgänge
10
    PORTB=0xFF;     //Pullup 
11
12
    do  
13
    {
14
        PORTC = 0;
15
    }
16
    while(!(PINB & (0x01)));    // Wiederhole, solange TasterEin gedrückt
17
18
    // Wenn Taster nicht mehr gedrückt:
19
    {
20
        PORTC = 0x01;
21
        _delay_ms(500);      
22
        PORTC = 0x02;
23
        _delay_ms(500);  
24
        PORTC = 0x04;
25
        _delay_ms(500);  
26
    }
27
28
    return 0;
29
}

Ist der Fehler jetzt klar?

von O. A. (sokrates1989)


Lesenswert?

VIA schrieb:

>
>
1
> #define F_CPU 3686400
2
> #include <avr/io.h>
3
> #include <util/delay.h>
4
> 
5
> 
6
> int main(){
7
> 
8
>    DDRB = 0x00;      //PortB Eingänge
9
>    DDRC = 0xFF;      //PortC Ausgänge
10
>    PORTB = 0xFF;     //Pullup
11
>    PORTC = 0;
12
> 
13
>    while(1){                   // oder: for(;;){
14
> 
15
>       while(PINB & (0x01));    //*solange* TasterEin *NICHT* gedrückt
16
> 
17
>       PORTC = 0x01;
18
>       _delay_ms(500);
19
>       PORTC = 0x02;
20
>       _delay_ms(500);
21
>       PORTC = 0x04;
22
>       _delay_ms(500);
23
>    }
24
>    return 0;
25
> }
26
>

Hey Via,

wenn ich den Taster loslasse, dann leuchtet die letzte Led 
(0x04)..warum?
Es müsste doch alles ausgehen wegen PORTC = 0;

von Jonas B. (jibi)


Lesenswert?

>Hey Via,

>wenn ich den Taster loslasse, dann leuchtet die letzte Led
>(0x04)..warum?

Schaltet der Taster vielleicht gegen Masse?

Gruß

von Stefan F. (Gast)


Lesenswert?

> wenn ich den Taster loslasse, dann leuchtet die letzte Led
> (0x04)..warum?

Weil der vorletzte Befehl in deiner While Schleife sie eingeschaltet hat 
und da ist niemad, der sie danach wieder aus schaltet.

Du schaltest die LED's nur ein einziges mal während der 
Initialisierungs-Sequenz aus.

Wann genau möchtest du denn, dass die LED's alle aus gehen?

von Joachim B. (jar)


Lesenswert?

O. A. schrieb:
> Es müsste doch alles ausgehen wegen PORTC = 0;

da kommt er doch nach dem Init NIE WIEDER hin, wegen

Endlosschleife nach dem power on:
O. A. schrieb:
>>    while(1){                   // oder: for(;;){
>>
>>       while(PINB & (0x01));    //*solange* TasterEin NICHT gedrückt
>>
>>       PORTC = 0x01;
>>       _delay_ms(500);
>>       PORTC = 0x02;
>>       _delay_ms(500);
>>       PORTC = 0x04;
>>       _delay_ms(500);
>>    }


es gilt weiterhin
Beitrag "Re: Taster auf "1" abfragen in AVR-C"

: Bearbeitet durch User
von VIA (Gast)


Lesenswert?

O. A. schrieb:
> ..warum?

Joachim B. hat es schon erklärt:

Die Zeile
1
    PORTC = 0;

wird in diesem Fall genau ein einziges Mal ausgeführt.
Und zwar nach dem POR (Power-On-Reset) und danach nie wieder,
bis der µC neu gestartet wird (Reset).

Nachdem er dies ein Mal gemacht hat, wird er in der ersten 
"while-Schleife" gefangen,
da die Schleifenbedingung niemals FALSE sein wird (Endlosschleife):
1
    while(1){                   // oder: for(;;){


Wenn die LED nach dem Loslassen des Tasters ausgehen soll,
musst du ihm das auch so beibringen:
1
#define F_CPU 3686400
2
#include <avr/io.h>
3
#include <util/delay.h>
4
5
6
int main(){
7
8
   DDRB = 0x00;      //PortB Eingänge
9
   DDRC = 0xFF;      //PortC Ausgänge
10
   PORTB = 0xFF;     //Pullup 
11
   PORTC = 0;
12
13
   while(1){                      // oder: for(;;){
14
15
      while(PINB & (0x01));       //*solange* TasterEin *NICHT* gedrückt
16
17
      PORTC = 0x01;
18
      _delay_ms(500);      
19
      PORTC = 0x02;
20
      _delay_ms(500);  
21
      PORTC = 0x04;
22
//      _delay_ms(500);           // ueberflüssig
23
24
      while(!(PINB & (0x01)));    //*solange* TasterEin *gedrückt*
25
      PORTC = 0x00;               // alle Lampen aus
26
  
27
   }
28
   return 0;
29
}

von VIA (Gast)


Lesenswert?

VIA schrieb:
> //      _delay_ms(500);           // ueberflüssig


Das _delay ist nicht überflüssig, wenn du sicher stellen willst,
dass die letzte LED mindestens 500ms lang leuchtet.

von VIA (Gast)


Lesenswert?

Sorry, nochmal Korrektur:

am besten du schreibst das letzte _delay nach dem Warten auf den Taster.
Damit blendest du das Prellen des Tasters zumindest provisorisch erst 
einmal aus.

Also:

1
#define F_CPU 3686400
2
#include <avr/io.h>
3
#include <util/delay.h>
4
5
6
int main(){
7
8
   DDRB = 0x00;      //PortB Eingänge
9
   DDRC = 0xFF;      //PortC Ausgänge
10
   PORTB = 0xFF;     //Pullup 
11
   PORTC = 0;
12
13
   while(1){                      // oder: for(;;){
14
15
      while(PINB & (0x01));       //*solange* TasterEin *NICHT* gedrückt
16
17
      PORTC = 0x01;
18
      _delay_ms(500);      
19
      PORTC = 0x02;
20
      _delay_ms(500);  
21
      PORTC = 0x04;
22
23
      while(!(PINB & (0x01)));    //*solange* TasterEin *gedrückt*
24
      _delay_ms(500);
25
      PORTC = 0x00;               // alle Lampen aus
26
  
27
   }
28
   return 0;
29
}

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.