mikrocontroller.net

Forum: Compiler & IDEs USART-ISR mit mega8515


Autor: Hendrik Hölscher (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Hendrik Hölscher (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
(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.

Autor: Michael Wilhelm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Hendrik Hölscher (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Michael Wilhelm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Du mußt dir mal den Assemblercode anschauen, aber ich meine eine if then
else Konstruktion geht schneller.
MW

Autor: Jörg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> 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.

Autor: Michael Wilhelm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Hendrik Hölscher (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Hendrik Hölscher (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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

Autor: Hendrik Hölscher (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Hendrik Hölscher (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Hendrik Hölscher (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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...)

Autor: Jörg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: OldBug (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@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!

Autor: Michael Wilhelm (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für die Erklärung, lasse ich wirken und werde im nächsten Projekt
mal entsprechenden Ansatz vergleichen.
MW

Autor: Hendrik Hölscher (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Jörg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Hendrik Hölscher (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Hendrik Hölscher (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
hier die Source

Autor: Hendrik Hölscher (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Jörg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
(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?

Autor: Jörg Wunsch (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Irgendwie kann der w3m keine Dateien anhängen, hier nochmal mit
Galeon...

Autor: Jörg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Hendrik Hölscher (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Jörg Wunsch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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).

Autor: Benjamin Munske (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Leute,

gibt es schon einen DMX-Empfangscode für Basic? Ich habe versucht in
FastAVR DMX zu empfangen, bin jedoch bisher gescheitert!

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.