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
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.
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..
@Matthias: Stimmt, genau das sollte der Compiler aus meinem C-Beispiel machen, wenn er gescheit ist...
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.
"die ist nach wie vor atomar" "volatile" sorgt nicht für atomare Zugriffe!
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?
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:
1 | unsigned char sreg = SREG; |
2 | cli(); |
3 | /* Atomare Aktion ausführen */
|
4 | |
5 | SREG = sreg; /* Altes I-Flag wieder herstellen */ |
@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....
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.