Hallo Leute,
ich bin noch ziemlicher Anfänger in Sachen Mikrocontroller und mir ist
gerade beim Testen etwas aufgefallen, bei dem ich gern den Hintergrund
kennen würde.
Habe hier einen AVR328 mit ext. 16 MhZ am laufen (Steckbrett). In der
Loop lasse ich einen Portpin toggeln um die Durchlaufgeschwindigkeit
mittels Oszi zu ermitteln. Die Frequenz beträgt dabei 314 kHz. Dies
entspräche 628.000 Schleifendurchläufen pro Sekunde.
Ich lasse ich mir nun eine 1 auf dem seriellen Monitor ausgeben
(programmiere mit Arduino IDE). Nun fällt die Frequenz schlagartig auf
160 Hz ab (320 Schleifendurchläufe pro Sekunde)!!! Die Baudrate wurde
mit 9600 initialisiert.
Würde doch heißen, die serielle Übertragung (Debugging) muss bei
zeitkritischen Berechnungen permanent mit berücksichtigt werden?
Klärt mich doch mal bitte auf.
Hanswurst schrieb:> Würde doch heißen, die serielle Übertragung (Debugging) muss bei> zeitkritischen Berechnungen permanent mit berücksichtigt werden?
Würde für mich lediglich heißen, dass die Programmierung nicht gut ist.
Zeig doch mal dein Programm...
> Würde doch heißen, die serielle Übertragung (Debugging) muss bei> zeitkritischen Berechnungen permanent mit berücksichtigt werden?
Richtig, denn jede eingefügte Programzeile muss evtl. ausgeführt werden.
Und diese Zeit geht dir flöten. Wenn dein Ablauf darauf angewiesen ist,
dann musst du entweder 1. besser programmieren oder 2. einen schnelleren
Rechner nehmen.
Hallo,
Hanswurst schrieb:> Würde doch heißen, die serielle Übertragung (Debugging) muss bei> zeitkritischen Berechnungen permanent mit berücksichtigt werden?
erstmal generell ja.
Bei mir steht die serielle auch auf AVR meist auf 115200.
Dazu kommt die Zeit für evtl. nötige Aufbereitung der Debug-Daten.
Debugausgaben gibt es bei mir nur an notwendigen Stellen, meist in ein
#define DEBUG_xxx
oben am Anfang des Sketches und
#ifdef DEBUG_xxx
... Ausgabe
#endif
an den möglichen Stellen. Das läßt sich dann recht geziehlt ein- und
ausschalten.
Wie der Name schon sagt, sollten zeitkritischen Berechnungen auch
schnell ausführbar sein. Damit sollten sie überschaubar bleiben und gut
testbar sein.
Testen geht ja meist mit Aufrufen mit passenden Dummy- und Grenzwerten
und da spielt die Rechenzeit ja dann erstmal keine Rolle.
Letztlich kann man dann ja, wie Du jetzt auch, einen Pin beim Aufruf
setzen und am Ende zurück. Dann kann man die Laufzeit mit dem Oszi
kontrollieren.
Und wenn man den Pin statt mit digitalWrite() noch mit direktem
Portzugriff und z.B. Pin toggle (1 nach PINx schreiben) stark abkürzen.
Und ganz praktisch noch: es gibt recht wenige wirklich zeitkritische
Situationen wenn das Programmkonzept stimmt...
Gruß aus Berlin
Michael
Hanswurst schrieb:> Ich lasse ich mir nun eine 1 auf dem seriellen Monitor ausgeben> (programmiere mit Arduino IDE). Nun fällt die Frequenz schlagartig auf> 160 Hz ab (320 Schleifendurchläufe pro Sekunde)!!! Die Baudrate wurde> mit 9600 initialisiert.
Wenn du das Senden ohne TX-Interrupts machst, wirst du warten müssen,
bis das Byte verschickt ist, erst dann geht's im Programm weiter. Bei
9600 Bit/s und 10 Bit (Start - 8 Bit Stop) sind das ~1ms, damit bist du
bei 960Hz maximum. Plus noch ein bischen Code (Arduino :-) ) kommst du
bei 160Hz raus.
Hanswurst schrieb:> Habe hier einen AVR328 mit ext. 16 MhZ am laufen (Steckbrett). In der> Loop lasse ich einen Portpin toggeln um die Durchlaufgeschwindigkeit> mittels Oszi zu ermitteln. Die Frequenz beträgt dabei 314 kHz. Dies> entspräche 628.000 Schleifendurchläufen pro Sekunde.
Eigentlich müsste das reine PinToggle noch schneller sein. Falls du
Arduino-Biblitheken verwendest, werden wahrscheinlich dadurch einige
unnötige CPU-Takte anfallen. In deinem Beispiel brauchst du 50 Takte für
das Toggeln. Das geht auch deutlich kürzer, wenn man direkt den Pin über
Registerzugriffe toggelt, statt Arduino-Funktionen zu verwendet.
Hanswurst schrieb:> Habe hier einen AVR328 mit ext. 16 MhZ am laufen (Steckbrett). In der> Loop lasse ich einen Portpin toggeln um die Durchlaufgeschwindigkeit> mittels Oszi zu ermitteln. Die Frequenz beträgt dabei 314 kHz. Dies> entspräche 628.000 Schleifendurchläufen pro Sekunde.
Respektive 25 Taktzyklen pro Schleifendurchlauf. Da muß man sich schon
echt anstrengen, um das so langsam hinzukriegen. Arduino halt.
> Ich lasse ich mir nun eine 1 auf dem seriellen Monitor ausgeben> (programmiere mit Arduino IDE). Nun fällt die Frequenz schlagartig auf> 160 Hz ab (320 Schleifendurchläufe pro Sekunde)!!! Die Baudrate wurde> mit 9600 initialisiert.
Bei 9600 Bd und 8N1 Codierung kannst du maximal 960 Zeichen pro Sekunde
übertragen. Bei 16 Mhz Taktfrequenz sind das knapp 17000 Taktzyklen pro
Zeichen. Da der UART im Arduino nicht gepuffert ist, muß bei der Ausgabe
eines neuen Zeichens gewartet werden, bis der UART das vorhergehende
Zeichen gesendet hat. Dein Arduino verbringt die meiste Zeit bei eben
diesem Warten.
> Würde doch heißen, die serielle Übertragung (Debugging) muss bei> zeitkritischen Berechnungen permanent mit berücksichtigt werden?
Nur wenn man sich dumm anstellt und in der Ausgabefunktion auf den UART
wartet. Clever macht man das so, daß man einen Sendepuffer verwendet.
Dann schreibt die Ausgabefunktion nur noch in den Puffer und die
eigentliche Übertragung macht man per Interrupt-Serviceroutine.
Axel S. schrieb:> und 8N1 Codierung kannst du maximal 960 Zeichen pro Sekunde> übertragen.
Und falls du Serial.println("1") benutzt, wird nach jeder 1 noch ein \r
und ein \n geschickt.
Somit also jeweils 3 Zeichen - und schon bist du bei deinen 320 Hz.
DanVet schrieb:> Eigentlich müsste das reine PinToggle noch schneller sein.
Richtig!
Unter Verzicht auf die Komfort Funktionen sitzen 2,66MHz Ausgabe
Frequenz drin. Auf einem 16MHz Uno.
Ein Toggle braucht auf einem AVR Arduino also 3 Taktzyklen, incl
Endlosschleife.
Axel S. schrieb:> Nur wenn man sich dumm anstellt und in der Ausgabefunktion auf den UART> wartet. Clever macht man das so, daß man einen Sendepuffer verwendet.> Dann schreibt die Ausgabefunktion nur noch in den Puffer und die> eigentliche Übertragung macht man per Interrupt-Serviceroutine.
Das nützt überhaupt nichts, solange eine Schleife 628.000 mal pro
Sekunde durchlaufen wird und bei jedem Durchlauf etwas seriell
ausgegeben werden soll. Der Puffer ist dann ratz-faz voll. Man kann dann
natürlich den Puffer gnadenlos überlaufen lassen oder Ausgaben in den
Puffer unterbinden, bis wieder genug Platz ist ... macht die Sache aber
nicht unbedingt besser.
> 2,66MHz Ausgabe Frequenz> 3 Taktzyklen, incl Endlosschleife
Nanu?
Ich dachte, das wäre ein ATmega328, da käme ich aber mit 'sbi PIN' &
'rjmp' auf 2+2 Takte, also 16.0 MHz / (2*(2+2)) = 2.0 MHz.
DanVet schrieb:> Das geht auch deutlich kürzer, wenn man direkt den Pin über> Registerzugriffe toggelt, statt Arduino-Funktionen zu verwendet.
Suche mal nach digitalWriteFast(). Das hilft schon mal.
Es ist ja nicht so, dass diese Feststellung neu ist.
Wolfgang schrieb:> Das nützt überhaupt nichts, solange eine Schleife 628.000 mal pro> Sekunde durchlaufen wird und bei jedem Durchlauf etwas seriell> ausgegeben werden soll. Der Puffer ist dann ratz-faz voll.
Richtig!
Arduino Serial hat eine 64 Byte FiFo.
Die ist Ruckzuck voll, und danach blockieren die Print Methoden, bis
wieder Platz ist.
> Respektive 25 Taktzyklen pro Schleifendurchlauf. Da muß man sich schon> echt anstrengen, um das so langsam hinzukriegen. Arduino halt.
Das ist schon relativ gut, es waren früher mal rund 70 Taktzyklen.
> Da der UART im Arduino nicht gepuffert ist, muß bei der> Ausgabe eines neuen Zeichens gewartet werden
Nach meinem Kenntnisstand ist er schon gepuffert, aber wenn man
fortlaufend Zeichen in den Puffer legt, ist der ständig voll. Deswegen
muss das Programm warten.
Eine Debug Ausgabe lebt davon, nur die wirklich wichtigen Infos
auszugeben. Wenn deine Ausgaben kleiner als der Puffer sind und bis zur
nächsten Ausgabe genug Zeit verstreicht, muss das Programm nicht warten.
Bei 57600 Baud und einer mittleren Zeilenlänge von 30 Zeichen wären das
immerhin bis zu 185 Zeilen pro Sekunde. So schnell kann kein Mensch
mitlesen.
> So schnell kann kein Mensch mitlesen.
Schon, aber man kann es abspeichern und danach in Ruhe prüfen.
"Meiste Information
steckt in sechs, acht Worten schon,
doch ein Dump auf hundert Seiten
kann Entsetzen nur verbreiten."
in memoriam KLEN
Stefanus F. schrieb:> Bei 57600 Baud und einer mittleren Zeilenlänge von 30 Zeichen wären das> immerhin bis zu 185 Zeilen pro Sekunde. So schnell kann kein Mensch> mitlesen.
Chuck Norris kann das. Chuck Norris fügt noch Zeilenvorschübe ein, um
den Vorgang zu beschleunigen.
S. Landolt schrieb:> Nanu?> Ich dachte, das wäre ein ATmega328, da käme ich aber mit 'sbi PIN' &> 'rjmp' auf 2+2 Takte, also 16.0 MHz / (2*(2+2)) = 2.0 MHz.
Arduino UNO Testcode:
Und da ich nun schon im Zitier-Modus bin, die anderen eh Fußball
schauen, noch ein kleiner Text für Stefanus (sowie geneigte Mitleser):
... he walks into the lab. What greets him there surprises him. He shows
it by smiling wryly. A great heap of paper lies on the floor, a
continuous sheet of computer paper streaming out of the carriage at
Gollum's system console. Stretched out, the sheet would run across the
room and back again several times. You could fit a fairly detailed
description of American history from the Civil War to the present on it.
Veres sits in the midst of this chaos, the picture of the scholar. He's
examined it all. He turns to Holberger. 'I found it,' he says.
Tracy Kidder: The Soul of a New Machine
Danke, schon klar - ich war irgendwie auf das 'sbi' fixiert, das 'out'
mit vorbesetztem Arbeitsregister fiel mir erst in dem Moment wieder ein,
als ich es sah.
Wolfgang schrieb:> Axel S. schrieb:>> Nur wenn man sich dumm anstellt und in der Ausgabefunktion auf den UART>> wartet. Clever macht man das so, daß man einen Sendepuffer verwendet.>> Dann schreibt die Ausgabefunktion nur noch in den Puffer und die>> eigentliche Übertragung macht man per Interrupt-Serviceroutine.>> Das nützt überhaupt nichts, solange eine Schleife 628.000 mal pro> Sekunde durchlaufen wird und bei jedem Durchlauf etwas seriell> ausgegeben werden soll. Der Puffer ist dann ratz-faz voll.
Das ist richtig. Aber die Rede war ja von Debug-Ausgaben. Und wenn man
da mehr (schneller) ausgibt, als die Schnittstelle hergibt, dann macht
man grundsätzlich etwas falsch. Mit einem hinreichend großen Buffer und
der Baudrate am oberen Limit statt am unteren kann man da schon was
reißen.
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