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
@ 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
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.
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..
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
@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...
"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.
@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...
@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...)
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.
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?
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.
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
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
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:
@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
@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
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
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.
@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
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?
@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....