Hallo, ich mache gerade erste experimente mit einem Atmega8 µC, aber kapiere die programmierung von Ein- und Ausgängen einfach nicht. Folgende Ausgangssituation: An Port PD0 ist eine LED + Vorwiderstand gegen Masse angeschlossen. An Port PB2 ist eine Lichtschranke angeschlossen die ein High Signal liefert, solange sich nichts zwischen der Lichtschranke befindet. Das Programm soll folgendes leisten: Wenn die Lichtschranke unterbrochen wird (Low-Signal an PB2) soll die LED leuchten (High-Signal an PD0). Aber ich bekomme die Programmierung des Ein- und Ausgangs einfach nicht hin.
Hallo Florian, bei solchen Fragen kann ich dir nur das AVR-GCC-Tutorial oder http://www.mikrocontroller.net/mc-project/ empfehlen. Sebastian
Auf die Idee bin ich natürlich auch schon gekommen. :-) Das Ergebnis habe ich mal als Anhang eingefügt. Ist aber eine von mehreren Varianten die nicht funktioniert hat. Irgend was mache ich mit der Abfrage des Eingangs falsch, zumindest zeigt dieser nie eine Reaktion. Ausgang auf 1/0 setzten geht...
Du setzt voraus, dass im ATMega8 der PORTB nach dem Power-On Reset bereits als Eingang initialisiert ist. Ich kann jedenfalls keine derartige Initialisierung in deinem Programm entdecken. Ansonsten sieht das OK aus. Ein kleiner Schreibfehler bei der 1. PORTD Zuweisung, aber der ist irrelevant. Wie ist es auf der Hardwareseite? Alles richtig an die richtigen Pins angeschlossen? Hast du die Schaltung schon mit einem Taster plus Pullup-Widerstand statt der Lichtschranke aufgebaut? Also eine "Active Low" Beschaltung wie in http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Tasten_und_Schalter Funktioniert die Lichtschranke am Oszi, hat sie einen sauberen LOW-Pegel im Bereich der Spezifikation des AtMega8?
Mmmh, dann vermute ich das die fehlende initialisiert des Eingangs das Problem ist. Kann mir jemand sagen wie die in diesem Beispiel lauten müsste? Pinnbelegung sollte stimmen. Die Lichtschranke liefert saubere High/Low Pegel (ist über einen Pull-Down Widerstand angeschlossen).
Das steht einen Abschnitt unterhalb des bereits angegebenen Links zum Tutorial. Die Datenrichtung im DDRx Register setzen (0 für Input) und je nach Bedarf der externen Beschaltung PORTx die Pull-Up-Widerstände aktivieren oder deaktivieren. Du schreibst früher die Lichtschranke liefert ein HIGH Signal. Und jetzt schreibst du, sie ist über einen Pull-Down Widerstand angeschlossen. Das verstehe ich nicht. Ist deine Lichtschranke ein simples "Taster" System aus LED und Fotoelement ohne weitere Elektronik oder hat sie eine Elektronik zur Erzeugung der HIGH/LOW Signale? Ein Datenblatt der Lichtschranke bzw. Schaltbild deiner Schaltung wäre jetzt nicht schlecht.
Was soll das
>PORTD=(1<<PB2);
damit schaltest du auf PortD für den 2. Pin den Pullup an (etwas
fehlerhaft formuliert)
kann es sein das die Lichtschranke einen Opencollector Ausgang hat und
du meintest
PORTB=(1<<PB2);
Pullup an PortB2 ?
@Wolfram: > Was soll das >> PORTD=(1<<PB2); Das war nur ein Tippfehler von mir. Außerdem hatte ich fälschlicher Weise angenommen dadurch den Port B2 gleichzeitig als Eingang zu definieren. Aber wenn ich das richtig verstanden habe müsste die korrekte Initalisierung lauten: DDRB=(0<<PB2); Werde das ganze morgen noch mal testen und berichten ob es geklappt hat. @Stefan: Ich glaube da habe ich Pull-up und -down durcheinander bekommen. Bei der Lichtschranke handelt es sich in der Tat um ein einfaches Modell aus LED und Fototransistor (liefert im jetzigen Zustand dauerhaft High und bei Unterbrechung Low). Werde jetzt erst noch einmal testen, wenns nicht klappt liefere ich Schaltpläne nach...
> Das war nur ein Tippfehler von mir.
Es sind immer 'nur' die Tippfehler.
Poste immer das Programm, so wie du es kompilierst.
Dann suchen wir nicht nach Tippfehlern, die dir beim
Posten unterlaufen sind.
Juhu, jetzt funktionierts. Besten Dank! Scheinbar war das Problem tatsächlich der nicht initialisierte Eingang. Falls es jemanden interessiert habe ich das funktionierende Programm nochmal angehängt.
Das hier DDRB=(0<<PB2); macht gar nichts. So kann man ein Bit in einem Byte nicht löschen. Daher kannst du dir das auch sparen und durch DDRB = 0; ersetzen. Allerdings ist das im Grunde auch wieder überflüssig, da die Port-Pins beim Einschalten eines ATMegas automatisch auf 'Input' konfiguriert werden. Ich würds allerdings drinnen lassen. Kostet zur Laufzeit nicht wahnsinnig viel und dokumentiert explizit, dass PortB als Eingang funktionieren soll.
Wenn Du das ganze noch eine Kleinigkeit umstellst, dann hilfst Du dem Compiler das besser zu optimieren: while(1) { uint8_t x = PINB & (1<<PINB2); if (x) { PORTD |= (1<<PD0); } else { PORTD &= ~(1<<PD0); } } Wenn man das Einlesen des Wertes direkt im if() macht, dann erzeugt der Compiler deutlich längeren Code, da er 16-Bit Arithmetik verwendet (hier eigentlich überflüssig). Das explizite Definieren von Variablen mit möglichst kleinem Typ ist also nützlich.
"Wenn man das Einlesen des Wertes direkt im if() macht, dann erzeugt der Compiler deutlich längeren Code, da er 16-Bit Arithmetik verwendet (hier eigentlich überflüssig). Das explizite Definieren von Variablen mit möglichst kleinem Typ ist also nützlich." Ja, das sag ich ja immer. Mir ist vor lanegr Zeit augefallen dass es C-Programmierer offensichtlich lieben, Ausdrücke so kompakt wie möglich zu formulieren - also soviel wie nöglich in eine Zeile reinzustopfen. Das ist zum einen unfreundlich zu lesen und zum anderen macht so mancher Compiler daraus ungenügend optimierten Kode. Ausdrücke wie: if( (*StringPtr[e--][a++] )== *muellptr++ ) nicht nur Augenunfreundlich sind ist nur Kosmetik, was der Compiler daraus macht möchte ich gar nicht erst wissen. Debuggen möchte ich so ein Konstrukt auch nicht. Das Beispiel von Michael Dreher lässt sich im überflug lesen und verstehen. IHMO, so sollte es sein. Gerd
Naja, dass
1 | if ( PINB & (1<<PINB2) ) { |
unfreundlicher zu lesen sein soll als
1 | uint8_t x = PINB & (1<<PINB2); |
2 | if (x) |
halte ich für ein Gerücht.
Beides kompiliert mit GCC4.1.1. Ich kann keinen Unterschied im erzeugten Code erkennen. void mytest1(void) { 68: b2 9b sbis 0x16, 2 ; 22 6a: 02 c0 rjmp .+4 ; 0x70 <mytest1+0x8> while(1) { uint8_t x = PINB & (1<<PINB2); if (x) //if (PINB & (1<<PINB2)) { PORTD |= (1<<PD0); 6c: 90 9a sbi 0x12, 0 ; 18 6e: fc cf rjmp .-8 ; 0x68 <mytest1> } else { PORTD &= ~(1<<PD0); 70: 90 98 cbi 0x12, 0 ; 18 72: fa cf rjmp .-12 ; 0x68 <mytest1> } } } void mytest1(void) { 68: b2 9b sbis 0x16, 2 ; 22 6a: 02 c0 rjmp .+4 ; 0x70 <mytest1+0x8> while(1) { //uint8_t x = PINB & (1<<PINB2); //if (x) if (PINB & (1<<PINB2)) { PORTD |= (1<<PD0); 6c: 90 9a sbi 0x12, 0 ; 18 6e: fc cf rjmp .-8 ; 0x68 <mytest1> } else { PORTD &= ~(1<<PD0); 70: 90 98 cbi 0x12, 0 ; 18 72: fa cf rjmp .-12 ; 0x68 <mytest1> } } }
Meine Beobachtung war doch recht allgemein gültig gehalten und ohne direktem Bezug zu DEINEN KODE. Jedenfalls mögen Compiler auch lieber weiter aufgelöste Formulierungen. Was in PC Umgeben mit viel Speicher und leistungsfähigen CPU's kein Problem ist, führt bei Embedded Systems schnell in die Sackgasse. Das Beispiel von Michael Dreher sieht jedenfalls recht übersichtlich aus - nicht nur für den Compiler.
Mit avr-gcc (GCC) 3.4.6 sieht es mit Optimierung -Os und mit dem if(x) Konstrukt genauso aus, wie von Roland Schmidt beschrieben: 33: if (x) +00000043: 9B82 SBIS 0x10,2 Skip if bit in I/O +00000044: C002 RJMP PC+0x0003 Relative jump 35: PORTB |= (1<<PB0); +00000045: 9AC0 SBI 0x18,0 Set bit in I/O register +00000046: CFFB RJMP PC-0x0004 Relative jump 39: PORTB &= ~(1<<PB0); +00000047: 98C0 CBI 0x18,0 Clear bit in I/O +00000048: CFF9 RJMP PC-0x0006 Relative jump Leider war die Optimierung der alten gcc Version noch nicht so gut, daher kam ohne Verwendung der Hilfsvariablen x folgendes raus: 34: if(PIND & (1<<PIND2)) +00000043: B380 IN R24,0x10 In from I/O location +00000044: 2799 CLR R25 Clear Register +00000045: 9596 LSR R25 Logical shift right +00000046: 9587 ROR R24 Rotate right through carry +00000047: 9596 LSR R25 Logical shift right +00000048: 9587 ROR R24 Rotate right through carry +00000049: 7081 ANDI R24,0x01 Logical AND with immediate +0000004A: 7090 ANDI R25,0x00 Logical AND with immediate +0000004B: 2388 TST R24 Test for Zero or Minus +0000004C: F011 BREQ PC+0x03 Branch if equal 36: PORTB |= (1<<PB0); +0000004D: 9AC0 SBI 0x18,0 Set bit in I/O +0000004E: CFF3 RJMP PC-0x000C Relative jump 40: PORTB &= ~(1<<PB0); +0000004F: 98C0 CBI 0x18,0 Clear bit in I/O +00000050: CFF1 RJMP PC-0x000E Relative jump
Die kleineren Mcontroller Compiler nehmen solche Konstrukte oft übel. Bei schnellen CPU Kernen mag es nicht auffallen und wird heufig vernachlässigt. Wenn jedoch um jedes Byte Code gekämpft werden muss da evtl der Takt nicht so groß ist, der Speicherplatz knapp wird, können solche "wirklich unauffällige Stellen" zum Sparen der Resourcen erheblich beitragen. Nun ist das Beispiel der Debatte nicht gerade eines der jenigen die sich der Kritk beugen müssen unleserlich zu sein, jedoch ist gerade dieses simple Beispiel dafür sehr gut geeignet zu demonstrieren wie unauffällig Konstrukte sein können die ungünstig für den Compiler sein können.
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.