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.
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
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.