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.
:
Bearbeitet durch Moderator
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:
1 | int main() |
2 | {
|
3 | DDRB |= _BV(PB5); |
4 | for(;;) PINB =_BV(PB5); |
5 | return 0; |
6 | }
|
Generierter Code (Ausschnitt)
1 | 82: 80 e2 ldi r24, 0x20 ; 32 |
2 | 84: 83 b9 out 0x03, r24 ; 3 |
3 | 86: fe cf rjmp .-4 ; 0x84 <main+0x4> |
an ufuf: Stimmt. "Daran habe ich allerdings nicht gedacht." "Sie sollen aber denken! Dafür sind Sie ein gebildeter Mensch."
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
S. Landolt schrieb: > an ufuf: > > Stimmt. > Hier noch mal eine Variante im OOP Mäntelchen. Es wird exakt der gleiche Code generiert
1 | Combie::Pin::OutputPin<13> pin; |
2 | |
3 | int main(void) |
4 | {
|
5 | pin.init(); |
6 | for(;;) pin.toggle(); |
7 | }
|
- die Lib kann ich gerne zeigen, wenn gewünscht.
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.
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.