Hallo. Entweder steh ich auf dem Schlauch, oder dem AVR-Kern fehlt eine wichtige grundsätzliche Funktion. Ich habe keinen Weg gefunden, ein bestimmtes Bit aus einem Register in den IO-Space zu kopieren. Kann das sein???? Es geht imho nur über Umwege und Sprungbefehle. Das Problem kommt mir öfters unter, nämlich immer dann, wenn eine ISR aufgerufen wird, während ich Bits in einem Zwischenregister (read modify write) verarbeite. Klar kann ich einfach cli sei machen, aber manchmal gibts halt ISRs, die man nicht warten lassen kann und wenn man dann noch nebenbei eine Routine abarbeitet, deren Taktbudget knapp bemessen ist, wirds kompliziert. Ich frag deshalb einfach mal in die Runde: Gibts da nicht vielleicht doch einen Trick?
Nimm LAS, LAC oder LAT (XMega). Und wenn's im lower IO ist, gehen SBI und CBI, wobei die Bitposition spätestens zur Link-Zeit bekannt sei muss. Um ein Bit aus einem Register in den IO-Bereich zu schreiben:
:
Bearbeitet durch User
Hallo, so genau hast Du nicht geschrieben, was du willst. Mit diesen Assembleranweisungen in kombination kommt man ans Ziel: http://www.atmel.com/images/Atmel-0856-AVR-Instruction-Set-Manual.pdf Schön wäre ein Beispiel deines Problem. Danke.
Johann L. schrieb: > Um ein Bit aus einem Register in den IO-Bereich zu schreiben:
1 | SBRC Rn, posA |
2 | SBI sfr, posB |
3 | SBRS Rn, posA |
4 | CBI sfr, posB |
Und das ist auch atomar — zumindest falls Rn als normales Register verwendet wird.
Wenn ich das Byte in r16 seriell ausgeben will, geht es z.B. so in r24,PORTB bst r16,0 bld r24,serialpin out PORTB,r24 bst r16,1 bld r24,serialpin out PORTB,r24 bst r16,2 bld r24,serialpin out PORTB,r24 bst r16,3 bld r24,serialpin out PORTB,r24 bst r16,4 bld r24,serialpin out PORTB,r24 bst r16,5 bld r24,serialpin out PORTB,r24 bst r16,6 bld r24,serialpin out PORTB,r24 bst r16,7 bld r24,serialpin out PORTB,r24 (Ist jetzt nur mal schnell als Beispiel hingeschrieben) Wenn mir da jetzt eine ISR zwischenfunkt und den Port ändert, überschreibe ich hier spätestens beim nächsten out die ISR-Änderung. Entweder arbeite ich mit cli sei oder ich muss mit sprungbefehlen und sbi und cbi arbeiten, was arg auf die Performance geht.
Johann L. schrieb: > Johann L. schrieb: >> Um ein Bit aus einem Register in den IO-Bereich zu schreiben: > >
1 | > SBRC Rn, posA |
2 | > SBI sfr, posB |
3 | > SBRS Rn, posA |
4 | > CBI sfr, posB |
5 | > |
> > Und das ist auch atomar — zumindest falls Rn als normales Register > verwendet wird. Das sieht gut aus und ist nur ein Takt mehr pro Bit. Das probiere ich mal aus. Danke!
Diese Methode verursacht leider einen starken Jitter. Schade....
ASM Superprofi schrieb: > Diese Methode verursacht leider einen starken Jitter. Schade.... Logisch, zwei Takte. Dein Problem ist, dass du dein Problem zu isoliert siehst. Gerade in Assembler ist es nötig (und möglich!), das Gesamtwerk zu optimieren. Wenn man nämlich eine sehr zeitkritische Aufgabe hat, kann man einfach mal ein Register speziell dafür reservieren. Das geht, weil man selbst bei recht komplexen Werken nur selten wirklich alle Register braucht, da der AVR einfach recht viele davon besitzt. Und selbst wenn es knapp mit Registern wird, spart ein reserviertes an hochfrequenter Stelle oft weit mehr Takte, als das Ausweichen auf SRAM an weniger frequenten Stellen kostet. Hat man also so ein exclusives Register, kann man es als sog. Schattenregister für ein SFR benutzen. Sprich: alle Ausgaben auf das SFR gehen nicht direkt auf das SFR, sondern auf das Schattenregister und nur dieses wird tatsächlich auf das SFR ausgegeben. Damit löst sich automatisch das Problem der Atomität und man kann z.B. sowas machen: .DEF PORTASHADOW=R2 bst ctrlreg,ctrlbit bld PORTASHADOW,outbit out PORTA,PORTASHADOW Das ist ein Takt weniger als in dem Konstrukt mit sbrc/s und s/cbi und vor allem passiert die tatsächliche Ausgabe immer (relativ) im gleichen Takt. Und dann ist da noch ein wichtiger Punkt, über den du nachdenken solltest: wie kommt eigentlich das Bit ursprünglich in das Register, welches ich hier symbolisch ctrlreg genannt habe? Ist es wirklich nötig, dieses in genau diesem Register an genau der Position von ctrlbit zu speichern? Täte es nicht auch das outbit in PORTASHADOW genauso? Wenn ja, entfallen nämlich nochmal zwei Takte und es bleibt einzig: out PORTA,PORTASHADOW Effizenter geht es dann wirklich nicht mehr... Zu solchen Lösungen kommt man nur, wenn man das Gesamtproblem betrachtet. Genau diese Möglichkeit ist es, die man einem Compiler voraus hat und nur so kann man eine nennenswert höhere Effizienz erreichen als dieser. Der Nachteil ist natürlich, dass hochoptimierter Code selten bis nie wiederverwendbar ist, weil er ja eben gerade aus der Optimierung des Gesamtproblems entstanden ist. Anderes Problem -> sehr wahrscheinlich anderes Optimierungsergebnis.
c-hater schrieb: > Zu solchen Lösungen kommt man nur, wenn man das Gesamtproblem > betrachtet. Gutes Stichwort. Hast du den Thread überhaupt ganz gelesen, damit meine ich nicht nur die letzten zwei Postings?
ASM Superprofi schrieb: > Wenn mir da jetzt eine ISR zwischenfunkt und den Port ändert, > überschreibe ich hier spätestens beim nächsten out die ISR-Änderung. Wenn du nicht während der gesamten Ausgabe die Interrupts sperrst, zerhaut's dir doch eh das Timing. Musst du also sowieso machen.
Rolf M. schrieb: > Wenn du nicht während der gesamten Ausgabe die Interrupts sperrst, > zerhaut's dir doch eh das Timing. Musst du also sowieso machen. Richtig. Wenn man in der blöden Situation ist, dass zwei Dinge zeitkritisch sind, hat man verloren bzw. muss sich eine andere Lösung überlegen. Aber hier gehts darum dass eine ISR zeitkritisch ist, die nicht warten kann und eine parallel laufende nicht-zeitkritische Routine, die mittels read-modify-write IO-Ports bearbeitet. Bisher funktioniert nur die Lösung von Johann (einigermassen, wenn man mal vom starken Jitter absieht.) Man bräuchte sowas wie bst direkt in den IO-Space. Schade, dass das nicht geht :/
ASM Superprofi schrieb: > Diese Methode verursacht leider einen starken Jitter. Schade.... Den hättest du dann aber doch auch, wenn es durch eine einzige Instruktion machbar wäre: Je nachdem, ob eine (lange) ISR vor oder nach der Instruktion getriggert wird, wird die Instruktion eben später oder oder früher ausgeführt.
ASM Superprofi schrieb: > Ich habe keinen Weg gefunden, ein bestimmtes Bit aus einem Register in > den IO-Space zu kopieren Selbst auf einem 8051 (oder ARM mit bit-addressierbarem IO-Space) geht das nur mit zwei ASM-Befehlen: bit-extract und store-bit
ASM Superprofi schrieb: > Aber hier gehts darum dass eine ISR zeitkritisch ist, die nicht warten > kann und eine parallel laufende nicht-zeitkritische Routine, die mittels > read-modify-write IO-Ports bearbeitet. Dann verstehe ich diese Aussage nicht: ASM Superprofi schrieb: > Diese Methode verursacht leider einen starken Jitter. Schade.... Entweder ist deine serielle Ausgabe zeitkritisch, dann darf sie nicht unterbrochen werden, oder sie ist es nicht. Dann kann dir der Jitter aber egal sein. Wobei ich mir eh nicht vorstellen kann, wie deine serielle Ausgabe nicht zeitkritisch sein kann. Da es ja offenbar keinen Clock gibt, handelt es sich wohl um eine asynchrone Übertragung. Dann darf die aber nicht mitten im Byte plötzlich mal stehen bleiben, um eine ISR abzuarbeiten. > Man bräuchte sowas wie bst direkt in den IO-Space. Schade, dass das > nicht geht :/ Besser wäre, einen µC mit einem USI zu verwenden, das sowas einfach nebenher in Hardware macht.
ASM Superprofi schrieb: > Gutes Stichwort. Hast du den Thread überhaupt ganz gelesen, damit meine > ich nicht nur die letzten zwei Postings? Ja. Gegenfrage: Hast du meinen Vorschlag überhaupt verstanden? Ich beantworte es mir mal selber, nein, hast du definitiv nicht.
Johann L. schrieb: > Und das ist auch atomar Nein. Es kann zum beliebigen Zeitpunkt ein Interrupt reinkommen und das Register oder den IO-Bereich verändern.
ASM Superprofi schrieb: > Aber hier gehts darum dass eine ISR zeitkritisch ist, die nicht warten > kann und eine parallel laufende nicht-zeitkritische Routine, die mittels > read-modify-write IO-Ports bearbeitet. Ja, genau für dieses Szenario stellt mein Vorschlag die bestmögliche Lösung dar...
MaWin schrieb: > Johann L. schrieb: >> Und das ist auch atomar > > Nein. Es kann zum beliebigen Zeitpunkt ein Interrupt reinkommen und das > Register oder den IO-Bereich verändern. Nein, zwischen Lesen und Schreiben des I/O-Ports kann kein Interrupt reinkommen, da SBI/CBI beides innerhalb einer Instruktion tun, anders als oben mit IN und OUT.
MaWin schrieb: > Johann L. schrieb: >> Und das ist auch atomar > > Nein. Es kann zum beliebigen Zeitpunkt ein Interrupt reinkommen und das > Register oder den IO-Bereich verändern. Das ist aber immer der Fall. Zur Laufzeit sieht die Sequenz nämlich so aus (Rn.posA = 0):
1 | SBRC Rn, posA |
2 | SBRS Rn, posA |
3 | CBI sfr, posB |
oder so (Rn.posA = 1):
1 | SBRC Rn, posA |
2 | SBI sfr, posB |
3 | SBRS Rn, posA |
Es gibt also nur 1 Instruktion, die das SFR ändert. Natürlich kann vor oder nach dem SBI / CBI eine ISR laufen, aber wäre auch dann der Fall, wenn man den Block in CLI + SEI einrahmt. Falls Rn also ein normales (nicht asynchron verändertes) Register ist, verhältsich die Sequenz so wie eine atomare Sequenz. Anders ausgedrückt: Die Sequenz in CLI + SEI zu klammern bringt nix außer Vergrößerung der IRQ-Latenz. Falls die Sequenz Teil einer längeren Befehlsabfolge mit mehreren solchen I/O-Zugriggen ist, kann das evtl. anders aussehen. Aber wenn eine solche Sequenz nicht ein monolithischer Block ist und z.B: 8 Bits einzeln und atomar behandelt (z.B. CLI + IN + OUT + SEI) und zwischen solchen Teilsequenzen ISRs laufen, dann hat man auch dann einen Jitter. Dieser Jitter ist dann aber eine eigene Baustelle.
:
Bearbeitet durch User
Beitrag #4985209 wurde vom Autor gelöscht.
Ohne low/high vs high/low Jitter und bezogen auf alle anderen Bits im Zielregister atomar: ;in=regA,bitA, out=portB,bitB bst regA, bitA bld r16, bitB in r17, portB eor r16, r17 and r16, 1<<bitB out pinB, r16 Geht nicht bei der ATmega8/16/32 Generation, muss neuer sein.
:
Bearbeitet durch User
A. K. schrieb: > Geht nicht bei der ATmega8/16/32 Generation, muss neuer sein. Wieso sollte das da nicht gehen? Geht natürlich auch dort. Ist aber trotzdem deutlich schlechter als meine Lösung...
c-hater schrieb: >> Geht nicht bei der ATmega8/16/32 Generation, muss neuer sein. > > Wieso sollte das da nicht gehen? Geht natürlich auch dort. Nö. Bei denen bewirkt ein out pinB, ... exakt garnichts.
c-hater schrieb: > A. K. schrieb: > >> Geht nicht bei der ATmega8/16/32 Generation, muss neuer sein. > > Wieso sollte das da nicht gehen? Geht natürlich auch dort. Ah, ja. PINx. Alles klar. Das macht diese Variante noch schlechter.
c-hater schrieb: > ASM Superprofi schrieb: > >> Gutes Stichwort. Hast du den Thread überhaupt ganz gelesen, damit meine >> ich nicht nur die letzten zwei Postings? > > Ja. > > Gegenfrage: Hast du meinen Vorschlag überhaupt verstanden? Ich > beantworte es mir mal selber, nein, hast du definitiv nicht. Ich habe verstanden, was du geschrieben hast. Du auch? Ich sage mal nein. Wenn du mit Schattenregister nämlich meinst, dass die ISR den selben zum verändern nimmt, hättest du das dazu schreiben müssen. Nur so macht deine Aussage irgendwie wenigstens halbwegs Sinn. Vom Problem der schlechten Portabilität (alle Programmierer müssen den selben Schattenregister verwenden, keiner darf direkt den Port ändern) wollen wir mal gar nicht erst reden. Johanns Methode ist bisher die beste.
ASM Superprofi schrieb: > Wenn du mit Schattenregister nämlich meinst, dass die ISR den selben zum > verändern nimmt, hättest du das dazu schreiben müssen. Das habe ich. EXCLUSIV RESEVIERTES REGISTER. Deutlicher kann man kaum darauf hinweisen. Ich bin sogar noch extra darauf eingegangen, warum das sehr oft die effizenteste Lösung ist... > Nur so macht > deine Aussage irgendwie wenigstens halbwegs Sinn. Nein. Das ist direkt und ganz unmittelbar der Sinn. Dein Problem war nur, dass du ihn nicht verstanden hast... > Vom Problem der > schlechten Portabilität (alle Programmierer müssen den selben > Schattenregister verwenden, keiner darf direkt den Port ändern) wollen > wir mal gar nicht erst reden. Auch dieses Problem habe ich im letzten Absatz meines Postings hinreichend gewürdigt. Wobei hier das Problem eigentlich garkein so großes ist, man kann ja sehr leicht konfigurierbar machen, welches konkrete Register das Schattenregister sein soll. Genau dafür gibt's ja diese .def-Zeile im Code... > Johanns Methode ist bisher die beste. Nein. Maximal ist sie unter deinem arg beschränkten Blickwinkel die beste. Technisch die beste ist sicher meine Lösung. Und zwar sowohl unter dem Aspekt der Gesamtperformance als auch unter dem Aspekt des minimalen Jitter. Mein Gott, ich nenne mich zwar nicht "ASM-Superprofi", aber bin im Gegensatz zu dir wirklich einer. Zumindest auf der AVR8-Plattform. Einfach deshalb, weil ich das seit über zehn Jahren intensiv mache und sogar meine Brötchen damit verdiene. Zumindest zu einem gewissen Prozentsatz...
Dein Ton gefällt mir nicht. Er ist um es deutlich zu sagen unter aller Sau. Ich gehe deshalb auf deine Auswürfe nicht weiter ein. Beruhige dich erstmal, dann reden wir vielleicht weiter.
Der c-hater hat selten mal meine Meinung, aber wer sich als Super-Profi bezeichnet, der sollte die Fragen dieses Threads nicht stellen müssen. Wenn man Anforderungen hat, die die HW nicht erfüllt, dann hat man sie Falsch gewählt. Wenn ich ein Byte jitterfrei morsen muß, dann benutz ich halt eine UART oder zumindest ein Schieberegister wie die USI, das mit festen Takt schiebt. Steve Wozniac hat mit Software Diskettencontroller gebaut, aber der war nach dem Maßstab hier kein SuperProfi, sondern ein Gott.
C-Amateur schrieb: > Steve Wozniac hat mit Software Diskettencontroller > gebaut, aber der war nach dem Maßstab hier kein SuperProfi, sondern ein > Gott. Der hat eben einen vernünftig designeden Microprozessor zur Verfügung gehabt, den 6502 - nicht so einen absolut registerorientierten AVR-Schrott ;D (duck und weg...)
Thomas E. schrieb: > Der hat eben einen vernünftig designeden Microprozessor zur Verfügung > gehabt, den 6502 - nicht so einen absolut registerorientierten > AVR-Schrott ;D Also bitte, wenn schon dann TIs 9900. Der I/O-Space dieses 16-Bitters war 1 Bit breit. Da hat sich diese Problematik völlig in Luft aufgelöst. Wenn ein I/O Register nur 13 Bits brauchte, dann hatte es auch nur 13 Bits und jedes davon war einzeln, atomar und nebenwirkungsfrei ansprechbar.
:
Bearbeitet durch User
A. K. schrieb: > Also bitte, wenn schon dann TIs 9900. Mit dem hatte ich mich nie näher beschäftigt - zu viele Register! (Bei mehr, als einem Arbeitregister, verliere ich beim Programmieren in Assembler immer die Übersicht, deshalb benutze ich ja auch PICs...)
Thomas E. schrieb: >> Also bitte, wenn schon dann TIs 9900. > > Mit dem hatte ich mich nie näher beschäftigt - zu viele Register! Also bitte! Weniger geht kaum, nur 3 echte Register: Program Counter, Statusregister und Workspace Pointer. Das ist weniger als bei 6502. > mehr, als einem Arbeitregister, verliere ich beim Programmieren in > Assembler immer die Übersicht, deshalb benutze ich ja auch PICs...) Laut Microchip hat der olle 16F84 insgesamt 68 General Purpose Register. Die liegen zwar im RAM, aber da liegen die schlappen 16 Workspace Register vom 9900 auch.
ASM Superprofi schrieb: > Wenn ich das Byte in r16 seriell ausgeben will, geht es z.B. so > in r24,PORTB > bst r16,0 > bld r24,serialpin > out PORTB,r24 > ... > > Wenn mir da jetzt eine ISR zwischenfunkt und den Port ändert, > überschreibe ich hier spätestens beim nächsten out die ISR-Änderung. > Entweder arbeite ich mit cli sei oder ich muss mit sprungbefehlen und > sbi und cbi arbeiten, was arg auf die Performance geht. Was soll das heissen "arg auf die Performance geht" ? So wie du das oben geschrieben hast, dauert es 3 Takte pro bit. Für 8 bits sind das ganze 24 Takte. Und du kannst für diese 24 Takte die ISR nicht sperren ? Das ist (für mich) absoluter Unsinn. > Bisher funktioniert nur die Lösung von Johann (einigermassen, wenn man > mal vom starken Jitter absieht.) Das ist keine Lösung, das war nur ein Vorschlag. Die einzige Lösung ist die mit cli/sei. Ohne cli/sei kann es immer Jitter geben, selbst wenn du direkt die bits setzen könntest, da die ISR jederzeit zuschlagen kann. P.S. Wenn es aber wirklich atomar und unabhängig/ungestört von der ISR gehen sollte, hätte ich es ganz einfach mit SPI gemacht. serialpin auf PB.3 setzen und gut ist es. Bei SPI2X = 0 / SPI Clock Vorteiler = 2 oder SPI2X = 1 / SPI Clock Vorteiler = 2 haut das immer hin, nur ist es halt 1 Takt schneller oder langsamer als dein Beispiel.
:
Bearbeitet durch User
A. K. schrieb: > Laut Microchip hat der olle 16F84 insgesamt 68 General Purpose Register. Ja, stimmt schon - die PICs konnte ich überhaupt erst von da ab ohne Kopfschmerzen programmieren, nachdem ich mir als Eselsbrücke überlegt hatte, das man sich die "General Purpose Register" statt als Arbeitsregister auch einfach mal als RAM-Speicher vorstellen kann... Zum eigentlichen Problem unseres TO: bin ja kein AVR-Kenner (zu viele Register - s.o.), habe da aber früher schon mal ins Datenblatt geschaut, um zu prüfen, ob ein AVR vielleicht doch zu irgendwas sinnvollem zu gebrauchen ist (für anspruchslose Aufgaben, evtl. als Slave-Prozessor für einen PIC10F202?) - da ist mir irgendwas im Gedächtnis hängen geblieben, daß man einen IO-Pin durch Schreiben auf's Port-Inputregister togglen kann. Damit sollte sich doch eine atomare und völlig jitterfreie Bit-Ausgabe machen lassen?
Thomas E. schrieb: > daß man einen IO-Pin durch Schreiben auf's Port-Inputregister > togglen kann. Ebendies hatte ich oben präsentiert.
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.