Forum: Mikrocontroller und Digitale Elektronik "PORT-Output" verzögern


von Roadrunner (Gast)


Lesenswert?

Mahlzeit!
Habe eine interessante Frage. Aber zuerst mal das Problem und meine 
Lösung hierzu.
Programmiere mit CodeVision und STK500 und verwende einen ATTiny26.
Ich möchte für den PORTA für eine bestimmte Funktion je ein Bit setzen 
und eines Löschen. Für einen anderen Zustand der Funktion eben gerade 
andersherum. Z.b.

#define prog_enable   PORTA &= 0xF9; PORTA |= 0x04
#define prog_disable  PORTA &= 0xF9; PORTA |= 0x02

So nun kann ich mit prog_enable; bzw. disable jeweils das eine Bit 
setzen.
Nun tritt aber folgender unschöner Effekt auf. Für eine kurze Zeit sind 
beide Bits nicht gesetzt. (Genauer gesagt nach dem PORTA & Befehl bis 
zur Asuführung des PORT | Befehls). Nun habe ich das Problem mit Hilfe 
einer Hilfsvariable gelöst. Ist ja auch kein großer Akt. Also z.B. 
unsigned char output und dann

#define prog_enable   output &= 0xF9; output |= 0x04; PORTA=output
#define prog_disable  output &= 0xF9; output |= 0x02; PORTA=output

Meine Frage ist nun, ob es eine elegantere Lösung gibt? Dass man z.B. 
Die Ausgabe des Ports verzögert und der Port erst nach dem PORTA | 
Befehl den "endgültigen" Zustand übernimmt.

Ansonsten noch einen schönen Nachmittag!
Gruß Roadrunner

von Falk (Gast)


Lesenswert?

@ Roadrunner

> #define prog_enable   output &= 0xF9; output |= 0x04; PORTA=output
> #define prog_disable  output &= 0xF9; output |= 0x02; PORTA=output

Fehlt da nicht vorher ein output=PORTA;  ?

> Meine Frage ist nun, ob es eine elegantere Lösung gibt? Dass man z.B.
> Die Ausgabe des Ports verzögert und der Port erst nach dem PORTA |
> Befehl den "endgültigen" Zustand übernimmt.

AFAIK nicht. Aber wo ist das Problem? Ist alles im Macro verteckt und 
gut.

MFG
Falk

von johnny.m (Gast)


Lesenswert?

Du musst nur den Zugriff atomar machen
1
PORTA = (PORTA & 0xF9) | 0x04;
Dabei wird PORTA gelesen, verUNDet und verODERt und anschließend 
zurückgeschrieben. Den Rest erledigt der Compiler für Dich. Du brauchst 
keine zusätzliche Variable.

von Matthias (Gast)


Lesenswert?

Das verzögern geht wohl nicht.
Allerdings glaube ich, dass beim Codeoptimieren deine zwei Befehle zu 
einem "verschmelzen". Zumindest ist das im AVR-C-Compiler so. Wie das 
bei Codevision ist weiß ich nicht.
Sonst shreibst du halt deine Befehle als ASM-Makro:

#define prog_enable
 IN r16, PORTA
 ANDI r16, 0xF9
 ORI  r16, 0x04
 OUT PORTA, r16

(weiß nicht ob die Syntax hier so stimmt)
aber das Prinzip sollte klar sein. Aber wie gesagt, nach einer 
Optimierung sollte der Code so etwa aussehen..

von johnny.m (Gast)


Lesenswert?

@Matthias:
Stimmt, genau das sollte der Compiler aus meinem C-Beispiel machen, wenn 
er gescheit ist...

von Matthias (Gast)


Lesenswert?

Ja, das machen die aber nur, wenn sie optimieren.
Und das muss irgendwo eingestellt werden, ob er das soll und wie gut er 
das soll. Und ob der CodeVision das kann weiß ich nicht. bin irgendwann 
mal auf den Comipler von dieser Seite hier umgestiegen...
Oder die Lösung von jonny sollte auch ohne Optimieren gehen

von johnny.m (Gast)


Lesenswert?

@Matthias:
Immer noch joHnny!

Die Lösung sollte eigentlich immer funktionieren, da nur ein 
Schreibvorgang (eine Zuweisung) drin ist. Afaik darf ein Compiler zwar 
mehrere Zuweisungen zu einer zusammenfassen, aber nicht eine Zuweisung 
in mehrere Zerlegen. Korrigiert mich bitte, wenn ich da falsch liegen 
sollte...

von Matthias (Gast)


Lesenswert?

Nein. ich glaub dir das.
und sorry wegen dem fehlenden H !
;-)

von A.K. (Gast)


Lesenswert?

"Afaik darf ein Compiler zwar mehrere Zuweisungen zu einer 
zusammenfassen

Sofern das Ziel der Zuweisungen nicht "volatile" ist.

"aber nicht eine Zuweisung in mehrere Zerlegen"

Aus
  a = b * c + d
wird bei 3-Adress-Maschinen intern
  temp = b * c
  a = temp + d
womit klar ist, das jeder Compiler nicht-triviale Ausdrücke in mehrere 
Zuweisungen zerlegt.

von johnny.m (Gast)


Lesenswert?

@Matt_ias:
> ...ich glaub dir das.
Musst Du aber nicht. Wie gesagt, ich lasse mich da gerne eines besseren 
belehren. Ist nur so, wie ichs im Kopf hab, und das muss ja nicht 
zwingend auch stimmen...

von johnny.m (Gast)


Lesenswert?

@A.K.:
Da spricht der Profi... Aber ich meinte eigentlich die Zuweisung an 
PORTA (bzw. an a in Deinem Beispiel), und die ist nach wie vor atomar 
(OK, PORTA ist ja auch volatile...)

von A.K. (Gast)


Lesenswert?

PS: auch
  a = b * c + d;
darf der Compiler als
  a = b * c;
  a += d;
implementieren, sofern "a" nicht "volatile" ist und kein Aliasing von 
"d" und "a" auftreten kann. Und mit "volatile" als Ziel darf er eine 
Zuweisung im Quelltext nur in exakt eine Zuweisung im Maschinencode 
übersetzen. Nicht 0, nicht 2.

Wäre das anders, wüsste man nie was bei
  USART-Dataregister = expression
rauskommt.

von A.K. (Gast)


Lesenswert?

"die ist nach wie vor atomar"

"volatile" sorgt nicht für atomare Zugriffe!

von johnny.m (Gast)


Lesenswert?

OK, das "volatile" ist der Knackpunkt. Da aber alle I/O-Register des µC 
volatile sind, dürfte es da im obigen Fall keine Probleme geben, gell?

von johnny.m (Gast)


Lesenswert?

Upps, Überschneidung...
> "volatile" sorgt nicht für atomare Zugriffe!
Im Allgemeinen nicht, OK, aber in diesem speziellen Fall tut es doch 
eigentlich genau das... oder liege ich da wieder falsch?

von A.K. (Gast)


Lesenswert?

Der Begriff "atomarer Zugriff" hat in der Branche eine bestimmte 
Bedeutung. Und die deckt sich nun einmal nicht mit der Bedeutung von 
"volatile".

Dass im Zusammenhang mit Interrupts beides verwendet werde muss, heisst 
nicht, dass es sich um das gleiche Konzept handelt.

"volatile" sorgt nicht hier und nicht sonstwo für atomare Zugriffe, 
sondern nur dafür, dass die Zugriffe dort stattfinden, wo der korrekt 
denkende Programmierer sie erwartet. Ob die Zugriffe dann atomar sind 
oder nicht, ist mit und ohne "volatile" gleich.

von Heinz B. (heinz)


Lesenswert?

Roadrunner wrote:
. . .
> #define prog_enable   PORTA &= 0xF9; PORTA |= 0x04
> #define prog_disable  PORTA &= 0xF9; PORTA |= 0x02
>
> So nun kann ich mit prog_enable; bzw. disable jeweils das eine Bit
> setzen.
> Nun tritt aber folgender unschöner Effekt auf. Für eine kurze Zeit sind
> beide Bits nicht gesetzt. (Genauer gesagt nach dem PORTA & Befehl bis
> zur Asuführung des PORT | Befehls). Nun habe ich das Problem mit Hilfe
> einer Hilfsvariable gelöst. Ist ja auch kein großer Akt. Also z.B.
> unsigned char output und dann
>
Hmm die variable PORTA ist ja vermutlich als volatile declariert
daher wird der comiler sich daran halten was Du hinschreibst und nix 
optimieren.
Solange Du die MACROS nicht innerhalb und auserhalb von interrupts 
benutzt
ist das macro unten ok.
Es wird:
- einmal gelesen
- ausmaskiert
- neue bits rein
- zurückgeschrieben

#define prog_enable   PORTA = ((PORTA & 0xF9) | 0x04)
#define prog_disable  PORTA = ((PORTA & 0xF9) | 0x02)

Wenn das im interrupt context benutzt wird müsste man noch die interrups 
während der aktion sperren.

br   heinz

von Peter D. (peda)


Lesenswert?

Stimmt, atomar ist daran garnichts.

Wenn der Port in das Register eingelesen wird und nun haut ein Interrupt 
dazwischen und ändert auch einen Pin des selben Ports, dann geht diese 
Änderung verloren.

Damits atomar ist, muß zusätzlich noch mit cli() und sei() geklammert 
werden !


Peter

von Werner B. (Gast)


Lesenswert?

Wobei, einfach mit cli(); sei(); klammmern ist auch nicht unkritisch. 
Was ist wenn das in einer Funktion passiert die aus einem Codeabschnitt 
aufgerufen wird in dem die Interrupts gesperrt sind?
Anderer Vorschlag:
1
    unsigned char sreg = SREG;
2
    cli();
3
    /* Atomare Aktion ausführen */
4
5
   SREG = sreg;   /* Altes I-Flag wieder herstellen */

  

von A.K. (Gast)


Lesenswert?

Wie immer sollte man schon ungefähr wissen was man tut.

von Falk (Gast)


Lesenswert?

@A.K.

> Wie immer sollte man schon ungefähr wissen was man tut.

Ja, sollte. Doch wir wissen ja nicht erst seit James Dean.

"Denn sie wissen nicht was sie tun."

;-)

MfG
Falk

von Peter D. (peda)


Lesenswert?

@Werner,

das ist natürlich die lupenreine Lösung.


Aber mal ehrlich, mir ist es bisher noch in keinem einzigen Programm 
passiert, daß ich nicht weiß, ob gerade Interrupts gesperrt sind oder 
nicht, oder das sogar beides abhängig vom Caller auftreten könnte.

Ich versuche immer, Interruptsperren so kurz wie möglich zu machen, d.h. 
Sperren sogar über komplette Funktionscalls hinweg sind für mich tabu.

Schlimmstenfalls disabled der Aufrufer die Interrupts, um den Call zu 
kapseln, aber dann enabled der Aufgerufene wieder so schnell wie 
möglich.
Z.B. bei meinen Tastenwiederholfunktionen ist das der Fall.


Peter

von Werner B. (Gast)


Lesenswert?

Solange man alleine an den Programmen "rumschraubt" mag das gehen, 
sobald auch nur ein Zweiter da mit ran muss/will sehe ich ein Problem. 
Sogar man selbst, wenn man nach einem halben Jahr eine Kleinigkeit 
ändern will...
Und du weisst ja, in Wirklichkeit ist es so: "Murphy war ein Optimist!".

 ;-)
Werner

von A.K. (Gast)


Lesenswert?

Also wat willste nu? Interrupts angeschaltet lassen, damit garantiert 
niemand irgendwann einmal versehentlich eine Funktion an die falsche 
Stelle schreibt. Mit dem Nebeneffekt dass das Programm halt ab und zu 
Mist baut?

Oder doch die Interrupts abschalten. Und wenn eine Funktion mal mit 
abgeschalteten Interrupts aufgerufen werden kann, dann eben dort klar 
reinschreiben was man darin tun darf und was nicht.

Ohne einen speziellen Hardware-Support für atomare Operationen wie ihn 
Zilogs eZ8 bietet (per Befehl die nächsten 3 Befehle ununterbrechbar 
zusammenfassen) bleibt nun einmal nur das Abschalten von Interrupts. Sei 
es global, im entsprechenden Device oder im Interrupt-Controller. Dass 
man dabei Unfug bauen kann liegt auf der Hand. Aber bei µC reicht ein 
falsches Bit im DDR und das Teil geht in Rauch auf. Ist halt so. Wer 
damit nicht leben kann programmiert lieber Windows.

von Falk (Gast)


Lesenswert?

@A.K.

> man dabei Unfug bauen kann liegt auf der Hand. Aber bei µC reicht ein
> falsches Bit im DDR und das Teil geht in Rauch auf. Ist halt so. Wer

Du hast auch einen Hang zum Drama, he? Ein kurzgeschlossener Ausgang ist 
zwar unschön und sollte möglichst vermieden werden, aber sooo schnell 
geht kein Controller in Rauch auf. Schon gar nicht, wenn der Kurzschluss 
nur ein paar us dauert.

MFG
Falk

von Läubi (Gast)


Lesenswert?

Ich finds immer wieder toll wie sich aus völlig anderen Fragen abstruse 
Sachverhalte ergeben.
Der TE wollte doch nur wissen wie er ereichen kann das der Port nicht 2x 
schaltet...
Vin Interupts, Atomaren operatioen oder so war doch garnix gesagt?? Oder 
hab ich das überlesen?

von Matthias (Gast)


Lesenswert?

@Läubi:
nein hast du nicht. habs auch verfolgt. habe in nem anderen Thema selbst 
ein Beitrag geleistet und dort will man erklären, das meine vormir 
liegende Funktionierende Schaltung nicht funktioniert...
Sind alles Leute, die den ganzen NUR in solchen Foren hängen und ihre 
"praktische" Erfahrung posten....

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.