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
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).
> 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.
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!
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)))"
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).
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?
"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
@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))?
deine abfrage ist falsch: if(PORTB |= (1 << PB2)) ---> if(PORTB & (1 << PB2)) pumpkin
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) ;)
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
@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...
### 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
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?
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
Nein, gibt es nicht. Da musst Du schon mit Bitschiebereien arbeiten (also "1 << irgendwas"). Den Datentyp bool gibts übrigens nur bei C++!
>### Frage an dich: was passiert hier (PORTB |= (1 << PB2))? >immer TRUE! Nicht nur das. Am PortB wird auch das PB2-Bit gesetzt... C halt.
Mein Posting von 12:52 bezog sich übrigens auf die Frage von pumpkin von 12:49...
#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.
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.
@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
>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.
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
...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
>rechnet der preprozessor auch schiftnotationen von c um!?
ja. Alles was konstant ist.
@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...
wow ... darüber muss ich jetzt erstmal einen moment in ruhe resignieren... aber die erste antwort ist schonmal super, dann machts ja auch sinn. ...
#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?
tja, damit überschreibst du aber alle anderen Ports mit 0. Um ready/modify/write kommst du eh nicht drumherum.
@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...
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
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))
@johnny.m hmm, ok - also ist der einsatz des schift ops an der stelle gleichwertig zu den von mir geposteten verfahren!? maddin
@inoffizieller WM-Rahul aber genau das ist die essens des weinzigen unterschiedes, so wies scheint :_) übrigens 8.... maddin
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...
@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.
>Und 1 << 3 ist tatsächlich 8 und nicht 16
Stimmt. Deswegen benutze ich auch lieber die hexadezimale Schreibweise:
0x08
>Und 1 << 3 ist tatsächlich 8 und nicht 16
Nicht in meinem Zahlensystem ;-)
@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
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.