Forum: Mikrocontroller und Digitale Elektronik Portumschaltung mit AVR


von Peter (Gast)


Lesenswert?

Hallo, hier etwas was mich viel Zeit gekostet hat. Vieleicht hilft es
jemand in der Zukunft.
Ich brauche für eine Anwendung 4 serielle Schnittstellen. Ich habe
einen ATMega32 und einen TL16C554 genommen. Lief auf Anhieb, den 554
benutze ich schon länger an einem ATMega128 wo aber der Bus benutzt
wird.
PortB vom ATMega32 ist der Datenport vom 554 und muss dementsprechend
von Input auf Output und zurück umgeschaltet werden. Alles lief
interruptgesteuert super bis ich ein 0xFF empfangen habe.
Um die Sache abzukürzen, laut Datenblatt kann der Port nicht einfach
von Input auf Output und umgekehrt geschaltet werden (RTFM ich
weiss...), es muss ein Zwischenzustand programmiert werden.
Bitte beachten, ich habe das vorher nirgendwo gelesen oder gehört.

Peter

von thomas (Gast)


Lesenswert?

und wie wird das dann programmiert? kannst du mal den auszug aus dem
code hier rein schreiben? und was für einen zwischenzustand meinst du?

von Peter (Gast)


Lesenswert?

Umschalten auf Output:
    //Transition
    DDRB = 0x00;
    PORTB = 0xff;
    //Output
    DDRB = 0xff;
    //Byte output
    PORTB = TL_byte;
    .....

Umschalten auf Input:
    //Input
    DDRB = 0x00;
    PORTB = 0x00;
    //Byte input
    TL_byte = PINB;

Peter

von Peter Dannegger (Gast)


Lesenswert?

"Um die Sache abzukürzen, laut Datenblatt kann der Port nicht einfach
von Input auf Output und umgekehrt geschaltet werden (RTFM ich
weiss...), es muss ein Zwischenzustand programmiert werden."


Das wäre mir aber das allerneueste.

Wo steht denn das im M32-Datenblatt ?

Zum Umschalten muß nur das DDRx-Register geschrieben werden, sonst
nix.


Deine Leseroutine ist fehlerhaft, so muß es richtig sein:

1. DDRx auf Eingang schalten
2. /IOR = 0
3. NOP, damit die Daten stabil anliegen
4. PINx einlesen
5. /IOR = 1
6. DDRx auf Ausgang

Setzt Du /IOR = 0 vor der Umschaltung als Eingang, gibt es
Datenkämpfe.


Peter

von Harry (Gast)


Lesenswert?

Hier ein Auszug beim ATmega8:


When switching between tri-state ({DDxn, PORTxn} = 0b00) and output
high ({DDxn,
PORTxn} = 0b11), an intermediate state with either pull-up enabled
({DDxn, PORTxn} =
0b01) or output low ({DDxn, PORTxn} = 0b10) must occur. Normally, the
pull-up
enabled state is fully acceptable, as a high-impedant environment will
not notice the difference
between a strong high driver and a pull-up. If this is not the case,
the PUD bit in
the SFIOR Register can be set to disable all pull-ups in all ports.
Switching between input with pull-up and output low generates the same
problem. The
user must use either the tri-state ({DDxn, PORTxn} = 0b00) or the
output high state
({DDxn, PORTxn} = 0b11) as an intermediate step.


Gut möglich, dass es beim Mega32 genauso ist.

Viele Grüsse
Harry

von Stevko (Gast)


Lesenswert?

Hallo Harry,

habe fix mal beim Mega32 nachgesehen es ist auch so.
Aber wo ich nicht durchblicke: Was meinen die mit Tri-State, bzw. was
soll da für ein Zustand am Port herrschen?

Gruß
  Stevko

von Michael Wilhelm (Gast)


Lesenswert?

Der Tristate-Zustand ist folgender:
DDRX auf 0
PORTX auf 0

interne Pull-Ups enabled
DDRX auf 0
PORTX auf 1

MW

von Peter Dannegger (Gast)


Lesenswert?

Die Betonung liegt aber auf:

"Normally, the pull-up enabled state is fully acceptable"

D.h. nur wenn es dem angeschlossenen IC nicht wurscht ist, muß man
Maßnahmen ergreifen !


Üblicher Weise ist Peripherie-ICs der Datenbus aber wurscht, wenn /IOW
und /IOR = 1.

Und in Deinem Fall ist es auch so und Du darfst beliebig auf DDRx
schreiben, solange /IOW = /IOR = 1 ist.

Hauptsache DDRx = 0x00 während der gesamten Zeit, wo /IOR = 0 ist,
sonst sind ja beide ICs Ausgänge und kämpfen gegeneinander.


Peter

von Peter (Gast)


Lesenswert?

Bei normaler Portrichtungsumschaltung mit DIR = 0x00 bzw 0xFF konnte
nach Empfang von 0xFF nicht mehr auf Output umgeschaltet werden.
Das Problem trat ja nicht bei allen anderen Bitkombinationen, sondern
nur bei 0xFF auch. Steht ja auch eigentlich so im Datenblatt.

Ich will auch keine Diskussion hier entfachen, es ging erst nachdem der
Port wie das Programmbeispiel zeigt, programmiert wurde.
Wers nicht glaubt solls einfach überlesen.

Peter

von Stevko (Gast)


Lesenswert?

@Michael Wilhelm und Peter Dannegger
ok, das war verständlich

@Peter
Habe ich Deine Seitenangabe überlesen?
Ist das nur bei PortB oder bei allen Ports?
Interessiert mich, da bei meinem Mega32 alle Ports voll sind und
teilweise auch zwischen Out/In geschaltet wird.

Gruß
  Stevko

von Peter (Gast)


Lesenswert?

Hallo Stevko,
das Problem tritt laut Datenblatt bei allen Ports auf aber nur wenn du
den ganzen Port umschaltest und den Mode änderst Wenn du einzelne Bits
umschaltest macht das keine Probleme.
Ich wollte in diesem Forum auf diese Besonderheit hinweisen, es hat
mich eine Menge Zeit gekostet diesen Fehler zu finden.
Im meinem anfänglichen Programm habe ich einfach den Port zwischen
Input ohne Pullup und Output mit Pullup umgeschaltet. Wenn du dir das
Datenblatt anschaust ist das nicht erlaubt. Ich konnte dann nichts mehr
ausgeben.

Peter

von Peter Dannegger (Gast)


Lesenswert?

@Peter

"Ich will auch keine Diskussion hier entfachen, es ging erst nachdem
der
Port wie das Programmbeispiel zeigt, programmiert wurde."


wenn es hilft, den echten Fehler zu finden, warum nicht ?

Poste dochmal den kompletten Code zum Lesen und Schreiben eines Bytes
inclusive der wichtigen /IOR und /IOW Signale und nicht nur die
Fragmente, mit denen keiner was anfangen kann.

Jedenfalls, wenn Du ohne /IOR = 0 zu setzen sofort nach der
Richtungsumschaltung auslesen kannst, dann must Du es ja davor gesetzt
haben und damit haben 2 Ausgänge gegeneinander gekämpft.
Völlig klar, daß man damit Fehlfunktionen provoziert.

Figure 4 und 5 im TL16C554 Datenblatt zeigen ja sehr schön das richtige
Signalspiel.


Peter

von Peter Dannegger (Gast)


Lesenswert?

P.S.:

Poste bitte auch den vorherigen Code, der nicht ging, damit man den
Unterschied erkennen kann.


Peter

von Peter (Gast)


Lesenswert?

BTW, es hat auch nicht funktioniert mit Input mit Pullup und Output mit
Pullup. Laut Datenblatt muss auch hier ein Zwischenstatus programmiert
werden.

Peter

von Peter (Gast)


Lesenswert?

Hier auf vielfachen Wunsch, allerdings hier mit PortA

//----------------------------------------------------------
void TL_WR_A(char TL_reg, char TL_byte)
{
    //Transition
    DDRA = 0x00;
    PORTA = 0xff;
    //Output
    DDRA = 0xff;
    //Set the register
    PORTB = ((PINB & 0b11111000)| TL_reg);

    //Byte output
    PORTA = TL_byte;
    TL_CSA_Low;                 //CS active
    TL_WR_Low;
    #asm("nop");
    TL_WR_High;
    TL_CSA_High;                //CS inactive

}

//----------------------------------------------------------
char TL_RD_A(char TL_reg)
{
    char TL_byte;

    //Input
    DDRA = 0x00;
    PORTA = 0x00;
    //Set the register
    PORTB = ((PINB & 0b11111000) | TL_reg);
    TL_CSA_Low;                 //CS active
    TL_RD_Low;
    #asm("nop");
    #asm("nop");
    TL_byte = PINA;
    TL_RD_High;
    TL_CSA_High;                //CS inactive

    return TL_byte;
}

Das folgende hat nicht funktioniert:
//----------------------------------------------------------
void TL_WR_A(char TL_reg, char TL_byte)
{
    //Set the register
    PORTB = ((PINB & 0b11111000) | TL_reg);

    //Data dir output
    DDRA = DIR_OUTPUT;
    //Byte output
    PORTA = TL_byte;
    TL_CSA_Low;                 //CS active
    TL_WR_Low;
    #asm("nop");
    TL_WR_High;
    TL_CSA_High;                //CS inactive

}

//----------------------------------------------------------
char TL_RD_A(char TL_reg)
{
    char TL_byte;

    //Set the register
    PORTB = ((PINB & 0b11111000) | TL_reg);
    //Data dir input
    DDRA = DIR_INPUT;
    TL_CSA_Low;                 //CS active
    TL_RD_Low;
    #asm("nop");
    #asm("nop");
    TL_byte = PINA;
    TL_RD_High;
    TL_CSA_High;                //CS inactive

    return TL_byte;
}

Der Unterschied liegt nur in der Art der Programmierung der
Richtungsumkehr.

Peter

von Willi (Gast)


Lesenswert?

Hallo,
hier wird das Datenblatt falsch interpretiert.
Es muss kein Zwischenzustand programmiert werden,
sondern es muss zwangsläufig ein Zwischenzustand auftreten,
da man zum Umschalten gewisser Portzustände den Inhalt
zweier Register (DDRx und PORTx) ändern muss.
Und das geht halt nur hintereinander.

Wenn man z.B. ein Port von
Input mit Pullup (DDRx=0, PORTx=1) auf
Output von Null (DDRx=1, PORTx=9) umschalten will,
dann muss zwischendurch entweder der Zustand
Input ohne Pullup (DDRx=0, PORTx=0) oder der Zustand
Output von 1 (DDRx=1, PORTx=1) auftreten,
je nachdem in welcher Reihenfolge man die Register umsetzt.

Das muss man wissen und bedenken.
- Mehr will uns das Datenblatt nicht sagen -

Die Funktion des Ports hat da nix mit zu tun.
Man kann die Reihenfolge der Umschaltung wählen wie mann will,
oder besser, wie es je nach angeschlossener Hardware sinnvoll ist.

Peters Probleme haben ihre Ursache ausserhalb des AVRs.

MfG Willi

von Peter D. (peda)


Lesenswert?

Nun da sind ja doch weitere erhebliche Unterschiede:

Einmal wird 0x00 / 0xFF benutzt, das andere mal DIR_INPUT / DIR_OUTPUT

Dann wird PORTB maskiert vor der Richtungsumschaltung, das andere mal
danach.

Für eine Maskierung sollte man aber immer das PORTX Register nehmen, da
Eingänge ja einen anderen Pegel haben können.


Für eine richtige Überprüfung müßte man nun sämtliche unbekannten
Ausdrücke kennen:

DIR_INPUT, DIR_OUTPUT, TL_reg, TL_CSA_Low usw.

DDRB ist auch noch wichtig.


Peter

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.