Forum: Mikrocontroller und Digitale Elektronik Programmdurchlauf auf Atmega - Frequenz


von Hanswurst (Gast)


Lesenswert?

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.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

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
von Michael U. (amiga)


Lesenswert?

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

von DanVet (Gast)


Lesenswert?

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.

von DanVet (Gast)


Lesenswert?

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.

von Axel S. (a-za-z0-9)


Lesenswert?

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.

von Hanswurst (Gast)


Lesenswert?

Ok, danke an alle für die Informationen.

von chris (Gast)


Lesenswert?

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.

von Einer K. (Gast)


Lesenswert?

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.

von Wolfgang (Gast)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

> 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.

von Wolfgang (Gast)


Lesenswert?

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.

von Einer K. (Gast)


Lesenswert?

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.

von Stefan F. (Gast)


Lesenswert?

> 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.

von S. Landolt (Gast)


Lesenswert?

> 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

von Defensiver Mittelfeldakteur (Gast)


Lesenswert?

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.

von Einer K. (Gast)


Lesenswert?

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>

von S. Landolt (Gast)


Lesenswert?

an ufuf:

Stimmt.


"Daran habe ich allerdings nicht gedacht."
"Sie sollen aber denken! Dafür sind Sie ein gebildeter Mensch."

von S. Landolt (Gast)


Lesenswert?

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

von Einer K. (Gast)


Lesenswert?

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.

von S. Landolt (Gast)


Lesenswert?

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.

von Axel S. (a-za-z0-9)


Lesenswert?

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
Noch kein Account? Hier anmelden.