Moin! Vorweg: Ich habe die Suchen-Funktion benutzt (USART) und mich durch ein paar Tuts durchgekämpft - leider erfolglos :-( Mein Problem: Ich habe versucht, eine (funktionierende) Assembblerfunktion nach C zu portieren, aber scheinbar greift der Interrupt nicht (trotz sei();) Anbei liegt die Source. Ich bin noch ein ziemlicher Anfänger in C, also wird es wahrscheinlich Kleinkram sein. Falls nicht kann ich auch gerne die asm-Variante nachschicken.
(Ach ja: an PortE hängt 'ne LED - nach der LED habe ich die DMX-Routine deaktiviert...) An PortD hängt der Transceiver. Deswegen muss auch da ein Pin gesetzt werden.
UCSR0C = (1<<URSEL)|(1<<USBS)|(1<<UCSZ1)|(1<<UCSZ0);//USART 8 N 2 UCSRB = (1<<RXCIE)|(1<<RXEN);//USART Interrupt Enable Das ist meine Einstellung für DMX-Empfang, 2 Stoppbits. Wenn das Hauptprogramm fertig ist, würde ich den Switch-Kram aus dem Interrupt rausnehmen. Das kostet zuviel Zeit. Weiter brauchst du SREG nicht zu sichern. Wofür? Die Interruptroutine ist doch nicht zu unterbrechen nach deinen Einstellungen. Weiterhin würde ich mit globalen und nicht mit lokalen Variablen arbeiten. Wo landest du denn in der Routine, im FE oder im OR? Setz da doch mal das LED. Wenn er die Routine gar nicht anspringt, überprüf die Initialisierung. Das define der Baudrate ist auch umsonst, da du das Register mit 1 besetzt und nicht mit dem define. MW
Danke für die Tipps! Allerdings war mein Fehler, dass ich "signal" klein geschrieben habe :-( Was spricht gegen Switch / was ginge schneller? SREG puffer ich, weil ich sonst schon mal ein paar Schwierigkeiten hatte (nach längerer Zeit unkontrolliertes Verhalten...) Die USART-Einstellungen sind bei uns identisch ;-) Ach ja: es gibt auch am Ende kein Hauptprogramm, da alles IRQ-controlled wird... Nochmals DANKE Hendrik
Du mußt dir mal den Assemblercode anschauen, aber ich meine eine if then else Konstruktion geht schneller. MW
> Ach ja: es gibt auch am Ende kein Hauptprogramm, da alles > IRQ-controlled wird... Das ist meistens kein günstiges Design. Sinnvoll ist es, die Interruptroutinen (die ja ihrerseits nicht unterbrechbar sind) so kurz wie möglich zu halten und das Hauptprogramm dann den Hauptteil der Arbeit erledigen zu lassen. Dazu setzt man in der ISR ein Flag (volatile nicht vergessen!) und wertet es im main() aus. Michaels Meinung über lokale vs. globale Variablen schließe ich mich ausdrücklich nicht an. Lokale Variablen können sehr oft in Registern untergebracht werden, was für eine ISR gut ist (auch wenn die Register in der ISR natürlich alle zuvor auf dem Stack gesichert werden müssen). Im Zweifelsfalle aber wirklich den generierten Assemblercode ansehen.
Na ja Jörg, wenn du in der ISR nur mit lokalen Variablen arbeitest, kommt das Main nie in den Genuss der Berechnung, Auswertung oder was auch immer mit den Daten passieren soll. Interrupt-Routinen so klein als Möglich halten, finde ich, ist zwingend, erst recht, wenn sie noch Prioritäten haben. Aber die Pushes und Pops kann man sich sparen. Aber ich schätze, das muß man im Einzelfall entscheiden. Wenn ich mit einem DMX-Datenstrom rummache, muß ich ziemlich flott in der Empfangsroutine sein, da alle 44µs Interrupt. Wenn ich da Pushes und Pops vermeiden kann, bin ich ein Stück weiter. MW
wie gesagt - es ist mein erstes C-Programm... Ich dachte, dass lokale Variablen einfach vorübergehend auf Register verteilt werden - ohne dass Werte im SRAM gepuffert werden. Wenn ich danebenliege, werde ich natürlich ein paar globale RegisterTemps anlegen. (Ich kann mir aber nicht vorstellen, dass der Compiler so dämlich ist, lokale Variablen im SRAM zu mirroren...) Gibt es irgendwo Angaben, wie viele Cycles einzelne Befehle brauchen (wie beim AVR instruction set) - so bräuchte ich nicht jede Variante zu Fuß durchprobieren... zur Verlagerung nach main: Die reine Protokollabarbeitung erledige ich natürlich (möglichst schnell) in der ISR. Das muss halt alle 40ms erledigt werden... Die Aufbereitung der Rohdaten könnte ich in main machen. Allerdings: Wie kann ich ausschließen, dass durch den IRQ nicht die Berechnung korrupt wird? Fehler durch SREG kann ich durch's Puffern ausschließen. Allerdings könnten andere Werte in den Temps stehen, bzw. lokale Variablen völlig futsch... Auf jeden Fall danke für Eure Anregungen! Hendrik
@Jörg: BTW: Gemäß Deiner BeerWare-License bin ich Dir wohl inzwischen eine halbe Kiste schuldig... Danke für Deine zahllosen Tuts und Examples! sorry für OT - aber das musste mal raus Hendrik
So ich habe jetzt mal eine reine if-Abfrage gemacht, dmxStatus wegraionalisiert (Startbyte muss ja das erste sein...) und der Krempel ist jetzt richtig schön unübersichtlich. Das Ergebnis (@4MHz) ist allerdings (für mich) überraschend: status if Switch nix 25µs 21,5µs Break 22µs 21,75µs Start 28µs 23,5µs Data 24µs 23,5µs Mit den lokalen Variablen hat leider Michael Recht: Alles wird gepushed und pepoppt. Das macht in diesem Fall die Hälfte der zeit aus... Also werde ich alle 8bit-Variablen in ISRs mit globalen Registern versorgen. Grüße, Hendrik
Ich habe jetzt den ganzen Tag an der Geschwindigkeit herumoptimiert, ohne aber in wirklich andere Größenordnungen als der Switchvariante vorzustoßen. Zum Vergleich hier mal die Daten meiner Variante in asm: nix 6,5µs Break 6,25µs Start 13,0µs Data 9,25µs Mein Fazit: Mit C ist schöner vielseitiger Code möglich. Ich werde immer bei größeren Methoden (Sortieralgorithmen...) darauf zurückgreifen (und später nach asm portieren). Aber wenn es um Geschwindigkeit geht, liegen Welten zwischen Assembler und C. Ich werde das Resultat bei mir uppen (es kamen auch schon Anfragen deswegen) und hoffe auf Optimierungen von Leuten, die mehr von avr-gcc verstehen... Für meine eigenen DMX-Projekte bin in Assembler effizienter. Ich werde gerne noch diesen Thread und ähnliche weiterverfolgen, da die Idee eines Baukastens aus fertigen Modulen (RS232, DMX, LCD, Servo, Stepper, PWM...) für verschiedenste Projekte ziemlich verlockend wäre - allerdings ist mein Code zZ. einfach zu lahm (auch wenn er funzt)! Grüße, Hendrik
Ach ja... Um den ganzen Kram mal in Zusammenhang zu bringen, sollte ich den Link zu mir angeben: www.hoelscher-hi.de/hendrik/ (Auf diese Weise können auch mal Leute, die das irgendwann interessiert, mit mir in Kontakt treten...)
Warum bist du eigentlich an 4 MHz gebunden? Die neuen Controller können doch alle einiges mehr (16 MHz). Auf Stromverbrauch wird es bei DMX ja wohl auch nicht ankommen. Klar, wenn du dir globale Registervariablen leisten kannst, dann nimm solche in einer ISR. Allerdings entzieht das dem Compiler Register, die er sonst zur Optimierung komplexerer Algorithmen irgendwo nehmen könnte.
@Michael Wilhelm: Was Jörg meint, ist etwas ganz anderes, als Du interpretiert hast :) Ein kurzes Beispiel aus dem Kopf: main.c --8<-- #include <avr/io.h> #include <avr/interrupt.h> #include <avr/signal.h> volatile unsigned char isrUpdated; volatile unsigned char isrExchVal; int main(void) { enablesomeints(); for(;;) { if(isrUpdated) { PORTB = isrExchVal; isrUpdated = FALSE; } } /* NEVEREACHED */ return 0; } SIGNAL(SOMEINTVECTOR) { unsigned char isrLocalVal = 0; isrLocalVal = PINA; dosomedecryptionwith(isrLocalVal); isrExchVal = isrLocalVal; isrUpdated = TRUE; } -->8-- Damit ist es dem Compiler möglich, Zugriffe auf isrLocalVal durchaus zu Optimieren, wogegen Zugriffe auf isrExchVal nicht optimiert werden dürfen!
Danke für die Erklärung, lasse ich wirken und werde im nächsten Projekt mal entsprechenden Ansatz vergleichen. MW
Das mit der festen Registervergabe habe ich zwischendurch auch schon gemacht. Und das hat ganze 1,5µs gebracht ;-) Das AVR-Studio simuliert standardmäßig auf 4MHz. Theoretisch würde ich auf 16 gehen können. Aber das ändert an der Effizienz nichts. Desweiteren scheint das Problem auch nicht an in der ISR genutzten Variablen zu liegen: Es wird alles gepushed, was der Compiler in main an Variablen brauchte (Lustigerweise 8Stck...). Dies ist ja auch sinnvoll, wenn durch einen IRQ keine Daten verloren gehen sollen. Der Compiler merkt ja leider nicht, dass mich main nach dem init gar nicht mehr interessiert. Was mir etwas Angst macht ist eine richtige Verarbeitung in main (nach Euren Vorstellungen). Dort dürften dann bei jeder ISR erst ein Mal gemütlich 10-32Variablen auf den Stack verteilt werden... Wie gesagt: Eigentlich ist alles sinnvoll - allerdings ist die Performance durch solche Aktionen nicht gerade berauschend.
Nö, das wird weniger, es wird nur das gerettet, was in der ISR auch modifiziert wird. Wichtig ist, dass man aus ISRs nach Möglichkeit keine anderen Funktionen aufruft (Ausnahme: inline-Funktionen), da in diesem Moment der Compiler keinen Überblick mehr hat, was die gerufene Funktion tatsächlich modifiziert, sodass er alles retten muss, was vom ABI her der gerufenen Funktion gestattet wäre zu modifizieren. Solange man das nicht macht, behält der Compiler aber ganz gut den Überblick, was er retten muss.
Wenn dem so wäre, Jörg, dann würde er nichts pushen, wnn ich in der ISR nur mit Registern und direkten Zuweisungen arbeite. (Höchstens 1push wegen einer eigenen Zuweisungsvar...) Es bleiben aber >6! Die schönste Theorie bringt leider nix, wenn der Compiler was anderes verzapft. Ich kann die Version ja noch mal rekonstruieren und alles uppen - dann siehst du's selbst... Grüße, hendrik
und hier wird gepushed: +00000036: 921F PUSH R1 Push register on stack +00000037: 920F PUSH R0 Push register on stack +00000038: B60F IN R0,0x3F In from I/O location +00000039: 920F PUSH R0 Push register on stack +0000003A: 2411 CLR R1 Clear Register +0000003B: 932F PUSH R18 Push register on stack +0000003C: 933F PUSH R19 Push register on stack +0000003D: 934F PUSH R20 Push register on stack +0000003E: 935F PUSH R21 Push register on stack +0000003F: 938F PUSH R24 Push register on stack +00000040: 939F PUSH R25 Push register on stack +00000041: B10B IN R16,0x0B In from I/O location +00000042: B11C IN R17,0x0C In from I/O location
(Die Disassembler-Ausgabe von AVR Studio finde ich ziemlich bescheuert, insbesondere die sinnlosen Kommentare wie ``Push register on stack'' und so. Ich sehe mir lieber die Compilerausgabe an.) Naja, die Register r18...r25 benutzt der Compiler aber auch wirklich innerhalb der ISR, folglich muss er sie sichern. Die beiden globalen Registervariablen bringen dir rein gar nichts, sie bremsen bestenfalls den Register-Allokator des Compilers aus. Wenn du stattdessen lokale Variablen benutzt, bleibt die Anzahl der PUSHes gleich -- ein deutliches Zeichen, dass man dafür keine globalen Register verplempern muss. Ich hab' mal eine einfache Optimierung vorgenommen, noch nicht ideal, aber die Größe der ISR geht von 114 auf 91 Bytes zurück, es werden auch weniger Register gepusht. Nur mal so, der case STARTADR Fall hat kein break, war das deine Absicht?
Irgendwie kann der w3m keine Dateien anhängen, hier nochmal mit Galeon...
Es ist bemerkenswert, was für Code der GCC 3.4.x für die beiden Tests des UCSRA-Wertes erzeugt. Der erste Test (DOR) generiert einen Code, den man fast auch in Assembler so schreiben würde (sbrc, ...), der zweite Test schiebt völlig umständlich den auf 16 bits erweiterten Wert von tempA 4 Bits nach links und macht ein AND mit 1. GCC 3.3.x hatte diese Macke nicht, er erzeugt in beiden Fällen das schöne SBRC. Dennoch wird die ISR auch mit ihm nicht kürzer, er verplempert anderswo offenbar. Das Verhalten von GCC 3.4.x wäre eine Diskussion auf der AVR-GCC- Mailingliste wert.
Danke für Deine Optimierung, Jörg. Das AVR sagt bei meinem üblichen Testdurchlauf: nichts 19,25us Break 19,5us Start 20,00us Data 21,00us (asm-Werte siehe oben...) Dabei scheint sich vor allem das "return" positiv auszuwirken (wusste nicht, dass ich das darf...). Die if-Verschachtelung bringt gegenüber der case-Konstruktion scheinbar keine spürbaren Vorteile. Zudem hat sich bei der Optimierung irgendwo scheinbar ein kleiner Fehler im Fluss eingeschlichen... (FE wird nicht quittiert.) Ich werde mal in nächster Zeit versuchen, in ein Assemblerprogramm C einzubetten. Erscheint mir momentan sinnvoller als umgekehrt ;-) Macht's gut Hendrik
Andersrum: schreib deine ISR in Assembler. Den Rest kannst du ja locker in C machen. Nimm aber keinen inline-Assembler (das wird krank), sondern eine separate Assembler-Datei. Die avr-libc-Doku hat ein Beispiel dafür, auf was man achten muss (dort am Beispiel eines AT90S1200, den man nicht direkt in C programmieren kann).
Hallo Leute, gibt es schon einen DMX-Empfangscode für Basic? Ich habe versucht in FastAVR DMX zu empfangen, bin jedoch bisher gescheitert!
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.