Forum: Mikrocontroller und Digitale Elektronik LED über Taster schalten


von Markus (Gast)


Lesenswert?

Hallo,

versuch gerade mal die ersten schritte in der µC Programmierung mit dem
STK500. Controller Atmega8515 in C.

Möchte über einen Taster eine LED ein und ausschalten.
Nur klappt das nicht.

Das habe ich bis jetzt

#include <avr/io.h>

unsigned char i;

void init_io(void)
{
    DDRD=(1<<PD3);                  //PortD.2 wird als Ausgang
definiert
    PORTD=(1<<PD3);
  DDRB= 0xff;                 //interner PullUp Widerstand von PortD.3
wird aktiviert
}

int main(void)
{
    init_io();                      //Ruft die Funktion init_io() auf

    while(1)                        //endlose Schleife
    {
        if ((PIND & (1<<PD3)) > 0)  //Ist der Taster gedrückt?
        {

    PORTB|=(1<<PD3);        //Wenn ja dann LED einschalten
        }
        else
        {
            PORTB&=(!(1<<PD2));     //Wenn nicht dann LED wieder
ausschalten
        }
    }

}

Kann mir einer sagen warum das nicht klappt

von Detlev (Gast)


Lesenswert?

Erst einmal ist der Taster nicht entprellt.

Fehler ist aber, dass du für die LED einmal PD2 und dann PD3
benutzt(PB2 und PB3 gibt es übrigens auch).

Und: Ein Bit löschen geht nicht mit ! (logisches not) sondern ~
(bitweises not).

von johnny.m (Gast)


Lesenswert?

> PORTB&=(!(1<<PD2));
Das klappt so nicht. Du darfst nicht den logischen Negierungs-Operator
"!" benutzen, sondern musst den Bitkomplement-Operator "~" nehmen.


> if ((PIND & (1<<PD3)) > 0)
Da kannste das "> 0" weglassen.

Allerdings sind die Taster beim STK500 in meiner Erinnerung Low-aktiv,
so dass der Pin "0" ist, wenn der Taster gedrückt ist. Also müsste
die Abfrage lauten
if (!(PIND & (1<<PD3))) {}

Außerdem schaltest Du PD3 ein und PD2 aus. Da müsstest Du Dich schon
entscheiden, an welchem Pin die LED wirklich hängt. Die LEDs sind afaik
übrigens auch Low-aktiv, also muss eine "0" ins Portregister, um sie
einzuschalten und eine "1" zum Ausschalten.

von johnny.m (Gast)


Lesenswert?

Ach ja, noch was: Du hast einen Taster an Port B, aber Port B mit "DDRB
= 0xff;" als Ausgang geschaltet. Der Port B muss als Eingang
programmiert werden! Pull-Ups werden mit "PORTB = 0xff;" aktiviert!

von Markus (Gast)


Lesenswert?

Hab es jetzt so

#include <avr/io.h>
#include <stdio.h>
#include <inttypes.h>

int main(void)
{
  DDRB = 0xff;
  PORTB = 0xff;

  DDRD = 0x00;
  PORTD = 0x00;

  while(1)
  {
  if (!(PIND & (1<<PIND2)))
    {

    PORTB &= ~(1 << PB2);

    }
  }
}

Klappt!

Nur was genau bedeutet diese Zeile  "if (!(PIND & (1<<PIND2)))"

von inoffizieller WM-Rahul (Gast)


Lesenswert?

if (!(PIND & (1<<PIND2)))

if: Einleitung einer Kontollstruktur

(1<<PD2): verschiebe eine 1 (binär sieht das Ding so aus: 00000001) um
PD2 Stellen (in diesem Fall um 2 Stellen) nach links (00000100)

PIND liest den Zustand des Eingangspins ein (funktioniert auch, wenn
der Pin als Ausgang geschaltet ist. Dann wird aber der Ausgangszustand
eingelesen)

PIND & (1<<PD2) führt eine bitweise Und-Verknüpfung durch. In (1<<PD2)
ist nur ein Bit gesetzt. Die Verknüpfung ist dann ungleich 0, wenn im
PIND das gleiche Bit gesetzt ist (es können in PIND auch mehrere
gesetzt sein. Die interessieren aber nicht 0 & 1 = 0...)

Alle Zahlen, die ungleich 0 sind, gelten in C als wahr.
Wenn also PIND & (1<<PD2) ungleich 0 ist, ist das Ergebnis wahr.
! invertiert das Ergebnis logisch (war es wahr, ist es jetzt falsch und
anders herum).

von Markus (Gast)


Lesenswert?

Aha, so ungefähr verstehe ich.

Jetzt soll beim ersten Tastendruck die LED angehen, beim zweiten wieder
aus.

Bei mir geht sie nur an

#include <avr/io.h>
#include <stdio.h>
#include <inttypes.h>

int main(void)
{
  DDRB = 0xff;
  PORTB = 0xff;

  DDRD = 0x00;
  PORTD = 0x00;

  while(1)
  {
  if(!(PIND & (1<<PIND2)))
    {

  if(PORTB |= (1 << PB2))

  PORTB &= ~(1 << PB2);

  else
  PORTB |= (1 << PB2);

    }
  }
}

Warum?

von pumpkin (Gast)


Lesenswert?

"Die Funktionen bit_is_clear bzw. bit_is_set sind nicht erforderlich,
man kann auch "einfache" C-Syntax verwenden, die universell
verwendbar ist. bit_is_set entspricht dabei z.B. (Registername & (1 <<
Bitnummer)). Das Ergebnis ist <>0 ("wahr") wenn das Bit gesetzt und 0
("falsch") wenn es nicht gesetzt ist. Die Negierung des Ausdrucks,
also !(Registername & (1 << Bitnummer)), entspricht bit_is_clear und
gibt einen Wert <>0 ("wahr") zurück, falls das Bit nicht gesetzt
ist,"

avr-gcc tutorial

pumpkin

von inoffizieller WM-Rahul (Gast)


Lesenswert?

@pumpkin:
Da kann man naürlich auch nochgucken.

@Markus: Du mußt die Tasten entprellen.
UND:
mach das:
 if(PORTB |= (1 << PB2))
so:
if(PORTB & (1 << PB2))


Frage an dich: was passiert hier (PORTB |= (1 << PB2))?

von pumpkin (Gast)


Lesenswert?

deine abfrage ist falsch:

if(PORTB |= (1 << PB2))

---> if(PORTB & (1 << PB2))

pumpkin

von pumpkin (Gast)


Lesenswert?

ist das hier ein livechat?  ^^

pumpkin

von MatrixMan (Gast)


Lesenswert?

du hast bei deinem zweiten if die {} klammern vergessen!

so hast dus:

  if(PORTB |= (1 << PB2))

  PORTB &= ~(1 << PB2);

  else
  PORTB |= (1 << PB2);

so sollte es sein:

  if(PORTB |= (1 << PB2))
   {
  PORTB &= ~(1 << PB2);

  else
  PORTB |= (1 << PB2);
   }


(ich denke mal das daran liegt! bin auch kein c profi bin aber grad
auch dabei c zu lernen (zumindest versuch ichs) ;)

von pumpkin (Gast)


Lesenswert?

naja, sehr gewagt.

eher so:

if(PORTB & (1 << PB2))    //!!!!
{
 PORTB &= ~(1 << PB2);
}
else
{
 PORTB |= (1 << PB2);
}

aber da würde imho der compiler rumzicken wenns nich sauber getrennt
werden kann.

pumpkin

von johnny.m (Gast)


Lesenswert?

@MatrixMan:
Nein, so wie es oben stand, war es schon richtig. So wie Du die
Klammern setzt, ist es definitiv falsch (und gibt mit Sicherheit eine
Fehlermeldung vom Compiler!). Wenn nur eine Anweisung nach dem if
steht, kann man die Klammern weglassen. Nur wenn es mehrere sind, muss
man Klammern, aber nicht so wie bei Dir!

@pumpkin:
Nicht gewagt, sondern falsch...

von pumpkin (Gast)


Lesenswert?

### Frage an dich: was passiert hier (PORTB |= (1 << PB2))?

immer TRUE!

dazu hab ich mal ne frage. gibt es unter gcc irgendwie nen datentyp der
tatsächlich zur ein bit im speicher belegt? die boolean typen aus dem
'normalen' sind ja 8 bit (wenn ich mich jetz nicht irre, es können
auch 16 sein)...aber so ein 1-bit-type wäre echt genial. sowas wie
true-bool  ^^

pumpkin

von Markus (Gast)


Lesenswert?

Was passiert hier (PORTB |= (1 << PB2))?

Zuerst wird durch die '<<'-Ausdrücke eine "2" n-mal nach links
geschoben. Dies ergibt somit (in Binärschreibweise) 0b00000010 für (1
<< PB2. Das Ergebnis wird bitweise ODER-verknüpft. Diese Maske wird mit
dem aktuellen Inhalt von PORTB bitweise ODER-verknüpft und das Ergebnis
PORTB mittels '|=' wieder zugewiesen.

Das hab ich nun kapiert.

Was hat das mit dem Tasten entprellen auf sich, bzw. wie funktioniert
das?

von Maddin (Gast)


Lesenswert?

Au man,

jetzt habe ich endlich mal einen thread in dem das diskutiert wird!

meine frage:

warum nutzt ihr:

PORTB &= ~(1 << PB2);

warum nicht

#define    TASTE     0x04

PORTB &= ~(TASTE);

hat das was mit dem späteren compilieren zu tun!? oder warum
schifte´n!?

gibts eigentlich ein define:+

PORTB_P2 für pin 2 an PORTB???

warum nicht so:
#define    LED     0x04
#define    SETLED  PORTB|=LED
#define    RESLED  PORTB&=~(LED)

maddin

von johnny.m (Gast)


Lesenswert?

Nein, gibt es nicht. Da musst Du schon mit Bitschiebereien arbeiten
(also "1 << irgendwas"). Den Datentyp bool gibts übrigens nur bei
C++!

von inoffizieller WM-Rahul (Gast)


Lesenswert?

>### Frage an dich: was passiert hier (PORTB |= (1 << PB2))?

>immer TRUE!
Nicht nur das. Am PortB wird auch das PB2-Bit gesetzt... C halt.

von johnny.m (Gast)


Lesenswert?

Mein Posting von 12:52 bezog sich übrigens auf die Frage von pumpkin von
 12:49...

von inoffizieller WM-Rahul (Gast)


Lesenswert?

#define    LED     0x04
Das genau das, was der Preprozessor aus (1<<2) macht (man betrachte den
Assembler-Code)

#define    SETLED  PORTB|=LED
#define    RESLED  PORTB&=~(LED)

ist Standard, wenn man der LED oder dem Portpin einen lesbareren Namen
gibt.

#define    RS485TXon PORTB|=(1<<4)
#define    RS485TXon PORTB&=~(1<<4)

Beim #define handelt es sich ja eh nur um eine Textersetzung. Und
Konstanten rechnet der Preprozessor aus. Weswegen auch
Baudratenberechnung (und diverse Peter Dannegger Berechnungen) im
Programmcode stehen. Die Werte werden zur Compile- und nicht zur
Laufzeit berechnet.

von Sonic (Gast)


Lesenswert?

Wolltest Du weiter oben nicht die LED zuerst ein und dann ausschalten?
Dann frag den Taster zuerst auf 'gedrückt' ab und warte dann auf's
loslassen (primitive entprellung).
Dann mit exclusiv-ODER
PORTB ^= (1 << PB2);
das Bit umschalten.

von Maddin (Gast)


Lesenswert?

@inoffizieller WM-Rahul

>Das genau das, was der Preprozessor aus (1<<2) macht (man betrachte
den Assembler-Code)

warum dann nicht auch gleich hinschreiben!?


#define    RS485TXon PORTB|=(1<<4)
#define    RS485TXon PORTB&=~(1<<4)

du meintest:
RS485TXoff oder!? naja ich weiß ja wies gemeint ist.

aber trotzdem geht der grund für den einsatz der shift notation für
mich nicht hervor.

das der preprozessor konstanten ausrechnen kann ist klar - und das es
sich bei einem define um eine textersetzung handelt war auch klar.

was ich mich fragte war:

der einsatz des schift ops veruracht doch nur das jetzt der zu
toggelnde port pin über die op noch berechnet werden muss - warum nicht
sofort so wie ich dargestellt habe - angeben!?

wenn man den portpin wechselt kann man doch auch hier das define
ändern, und die sw erneut kompilieren!?

#define    LED     0x04
#define    LED     0x02

#define    SETLED  PORTB|=LED
#define    RESLED  PORTB&=~(LED)

sorry, vielleicht hab ich die sache falsch formuliert!

maddin

von inoffizieller WM-Rahul (Gast)


Lesenswert?

>RS485TXoff oder!? naja ich weiß ja wies gemeint ist.
Copy'n'paste...

Warum man shiftet, weiß ich auch nicht; man könnte auch einfach
Konstanten Namen geben.

von Maddin (Gast)


Lesenswert?

hmmm,

ich habe das hier halt schon in einigen codesegmenten entdekt und mich
gewundert.

...mich interessiert nur - warum man an dieser spezifischen stelle
schiftet - nicht das das falsch verstanden wird...

trotzdem vielen dank,

maddin

von inoffizieller WM-Rahul (Gast)


Lesenswert?

das hat sich einfach eingebürgert. (denke ich)

von Maddin (Gast)


Lesenswert?

...naja,

aber so muss der prozessor ja erstmal 4 schift ops ausführen, an jeder
stelle an dem das define eingesetzt wird, werden doch auch 4schift
befehle im avr assembler erzeugt, oder!? rechnet der preprozessor auch
schiftnotationen von c um!?

maddin

von inoffizieller WM-Rahul (Gast)


Lesenswert?

>rechnet der preprozessor auch schiftnotationen von c um!?

ja. Alles was konstant ist.

von johnny.m (Gast)


Lesenswert?

@Maddin:
> der einsatz des schift ops veruracht doch nur das jetzt der zu
> toggelnde port pin über die op noch berechnet werden muss

Nein, muss er nicht. "1" ist eine Konstante und z.B. "PIND2" auch.
Der Ausdruck "1 << PIND2" ist also konstant und zur Compiler-Laufzeit
bekannt und wird demzufolge auch zur Compiler-Laufzeit berechnet. An den
Assembler wird im obigen Beispiel ein "0x04" übergeben.

Ob man jetzt für die jeweiligen Bitmasken eigene Makros #definiert
(z.B. "#define LED1 (1 << PIND2)), bleibt jedem selbst überlassen.
Vorteil der "Shift"-Darstellung ist, man sieht sofort, was da
eigentlich gemacht wird. Außerdem kann man die Codegröße reduzieren,
weil man mit der Schreibweise leicht mehrere Bitmasken verodern kann
und so mehrere Bits in einem Register mit einem einzigen Zugriff
manipulieren kann (allerdings unabhängig davon, ob man direkt "1 <<
irgendwas" schreibt oder ein "#define IRGENDEINEBITMASKE (1 <<
irgendwas)"). Die Methode von Dir mit SETLED... hat eben den Nachteil,
dass, wenn Du mehrere LEDs am Port hast, die Du unabhängig schalten
willst, ist für jede LED ein Zugriff auf das Portregister fällig...

von Maddin (Gast)


Lesenswert?

wow ...  darüber muss ich jetzt erstmal einen moment in ruhe
resignieren...

aber die erste antwort ist schonmal super, dann machts ja auch sinn.

...

von inoffizieller WM-Rahul (Gast)


Lesenswert?

#define LED1 0x01
#define LED2 0x02
#define LED3 0x04
#define LED4 0x08

PORTB = LED1 | LED2 | LED4;

Ist doch eigentlich übersichtlicher als die Shift-Schreibweise, oder
habe ich da eine Denkblockade?

von crazy horse (Gast)


Lesenswert?

tja, damit überschreibst du aber alle anderen Ports mit 0.
Um ready/modify/write kommst du eh nicht drumherum.

von johnny.m (Gast)


Lesenswert?

@crazy horse:
Er hat wahrscheinlich nur das | vergessen...
Also "PORTB |= LED1 | LED2 | LED4;"

Aber wie schon oben gesagt: Ob man die Shifts direkt in den Code
schreibt oder ob man alles mit #define erschlägt, bleibt jedem selbst
überlassen...

von Maddin (Gast)


Lesenswert?

hmm...
#define    LED     0x04

#define    SETLED  PORTB|=LED
#define    RESLED  PORTB&=~(LED)

oder
#define    LED     0x02

#define    SETLED PORTB|=(1<<LED)
#define    RESLED PORTB&=~(1<<LED)

SETLED ist ja eine spezifische weitere abstraktion die auf den
basierenden defines beruht!

....vielleicht ein ganz kurzes beispiel, mir ist das immer noch nicht
ganz klar...

@Rahul,

ich finde das auch sehr übersichtlich, und es geht...

maddin

von inoffizieller WM-Rahul (Gast)


Lesenswert?

Dann halt so:
PORTB |= LED1 | LED2 | LED4;

Mir geht es nicht um den Portzugriff, sondern um das Shiften.

Es macht doch eigentlich keinen Unterschied, ob ein Makro ein
Platzhalter für 3 oder für 16 ist, oder? ((1<<3) == (16))

von Maddin (Gast)


Lesenswert?

@johnny.m


hmm, ok - also ist der einsatz des schift ops an der stelle
gleichwertig zu den von mir geposteten verfahren!?

maddin

von Maddin (Gast)


Lesenswert?

@inoffizieller WM-Rahul


aber genau das ist die essens des weinzigen unterschiedes, so wies
scheint :_)

übrigens 8....

maddin

von inoffizieller WM-Rahul (Gast)


Lesenswert?

Es hat sich eingebügert und ist irgendwie historisch bedingt. Irgendwer
dachte wohl, dass es übersichtlicher sei, den Exponenten zu benutzen;
da braucht man nur die Bits abzählen...

von johnny.m (Gast)


Lesenswert?

@Maddin:
In diesem Fall ja. Wenn es nur eine LED gibt, ist es Jacke wie Hose.
Und 1 << 3 ist tatsächlich 8 und nicht 16. Und genau da liegt ein
(kleiner) Vorteil der Schieberei: Es gibt eben auch Leute, die nicht
mal eben schnell aus dem Kopf von Binär nach Hex (geschweige denn nach
Dezimal) umschreiben können. Man kann es hinschreiben, ohne Bits Zählen
zu müssen und es bleibt trotzdem anschaulich, was da gemacht wird. Bei
der Fehlersuche ist das u.U. auch hilfreich, wenn man sofort sieht, was
passiert.

von inoffizieller WM-Rahul (Gast)


Lesenswert?

>Und 1 << 3 ist tatsächlich 8 und nicht 16

Stimmt. Deswegen benutze ich auch lieber die hexadezimale Schreibweise:
0x08

von inoffizieller WM-Rahul (Gast)


Lesenswert?

>Und 1 << 3 ist tatsächlich 8 und nicht 16

Nicht in meinem Zahlensystem ;-)

von Maddin (Gast)


Lesenswert?

@johnny.m

...das wollte ich schon lange mal hier anbringen!? konnte mir den grund
nicht erklären...

@alle

damit sind meine wissendürste diesbezüglich zu 100% gedeckt.

dank euch für die vielen posts und die hilfe!

Maddin

von Unbekannter (Gast)


Lesenswert?

es wird nur gefährlich wenn man die Ausdrucksweisen vermischt, deshalb
sollte man tunlichst bei einer bleiben:

#define LED1  0x04 /* Led an Bit2 */

PORTB |= (1<<LED1);

schaltet dann natürlich Bit4 ein. Das wäre ein typischer Cut&Paste
Fehler wenn im Programm schon mit der Shift Notation gearbeitet wird
(z.B. weil es von hier oder Atmel AppNotes übernommen wurde) und
Konstanten jetzt anders benannt werden.

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.