mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik LED über Taster schalten


Autor: Markus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Detlev (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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!

Autor: Markus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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)))"

Autor: inoffizieller WM-Rahul (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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).

Autor: Markus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: inoffizieller WM-Rahul (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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))?

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
deine abfrage ist falsch:

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

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

pumpkin

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ist das hier ein livechat?  ^^

pumpkin

Autor: MatrixMan (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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) ;)

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: pumpkin (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Markus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: johnny.m (Gast)
Datum:

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

Autor: inoffizieller WM-Rahul (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: johnny.m (Gast)
Datum:

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

Autor: inoffizieller WM-Rahul (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Sonic (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: inoffizieller WM-Rahul (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: inoffizieller WM-Rahul (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
das hat sich einfach eingebürgert. (denke ich)

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: inoffizieller WM-Rahul (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>rechnet der preprozessor auch schiftnotationen von c um!?

ja. Alles was konstant ist.

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

...

Autor: inoffizieller WM-Rahul (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: crazy horse (Gast)
Datum:

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

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: inoffizieller WM-Rahul (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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))

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@johnny.m


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

maddin

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@inoffizieller WM-Rahul


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

übrigens 8....

maddin

Autor: inoffizieller WM-Rahul (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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...

Autor: johnny.m (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: inoffizieller WM-Rahul (Gast)
Datum:

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

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

Autor: inoffizieller WM-Rahul (Gast)
Datum:

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

Nicht in meinem Zahlensystem ;-)

Autor: Maddin (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Unbekannter (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.