Wartezeiten passen nicht zusammen

Hallo,

[ hier stand Unsinn ]

Mein eigentliches Problem ist folgendes. Die Conversion Time vom MCP3550-50 beträgt typisch 80ms +/- 2%.
Nun habe ich ja mit while die Möglichkeit die Wartezeit auf das Readysignal zuverlängern. Gleichzeitig frage ich darin einen Counter ab, der bei überschreiten eines Wertes die gesamte Funktion abbricht um eine Endlosschleife zu verhindern, falls der SPI Bus gestört ist oder der MCP defekt oder sonstwas.
Der Witz ist folgender wo ich nicht ganz mitkomme.
Die Addition der festen delay Zeit von 80ms und die zusätzliche Wartezeit in der while in µs passen nicht zusammen mit der Gesamtzeit die ich mir zusätzlich über Serial … micros direkt davor/danach ausgeben lasse.

delay    Counter    wait      Wartezeit gesamt
70ms       682      2728µs        84,04ms
75ms       402      1608µs        83,89ms
80ms       121       484µs        84,05ms
82ms         8        32µs        83,88ms  
83ms         0         0µs        84,90ms

Mit 82ms festem delay, müßte zwar das Readysignal auch sofort da sein laut Datenblatt, weil 2% von 80 sind 1,6ms, also sollte nach 81,6ms alles ready sein. Aber nun gut. Mein Verständnisproblem ist das 70ms + 2,728ms nicht 84,04ms ergeben. Oder 75ms + 1,608ms nicht 83,89 ergeben. Die letzte Spalte mit um die 84ms sehe ich als konstant an. Die zusätzliche Wartezeit in while müßte doch in etwa die Differenz aus 84ms und der delay Zeit ergeben. Hab ich einen Denkfehler? :roll_eyes:
Bin am austesten ob ich jeweils eine ms einfüge oder doch µs um die Wartezeit nicht künstlich stark zu erhöhen.

float MCP3550_Reading (float UB_AREF)  
{
  static long Messwert = 0;                   // vom MCP3550-50 
  static float Spannung = 0;                  // Endergebnis in [mV]
  byte OVL, OVH, SIGN, OV_Bits = 0;           // Overflow Bits und Vorzeichen Bit
  unsigned int readyWaitCounter = 0;          // while Endlosschleifenüberwachung für Ready Signal
  // das ist zugleich die 50ns Zwangspause CS High to Low

  //  80ms typische Conversion Time (MCP3550-50) 
  Serial.print(F("warten gestartet: ")); Serial.println(micros() );
  digitalWrite(CS, LOW);             // starte AD Convertion
  delay(80);                         // 80ms typische Convertion Time (MCP3550-50)
  while(digitalRead(MISO) == HIGH)   // warten bis SDO/RDY mit LOW Ready signalisiert
    { 
     digitalWrite(CS, HIGH);         // wenn nicht Ready, toggle CS erneut High zu Low
     delayMicroseconds(2);           // warte extra 2µs
     digitalWrite(CS, LOW);          
     delayMicroseconds(2);           // warte extra 2µs
     readyWaitCounter++;             // inkrementiere den Sicherheits Counter (Endlosschleife)
     if (readyWaitCounter > 1000)    // wenn zusätzliche Wartezeit > 4ms (1000x4µs)
       {
        digitalWrite(CS, HIGH);   
        return INFINITY;             // dann Abbruch mit Fehlercode "unendlich"
       }
    }
  Serial.print(F("warten beendet: ")); Serial.println(micros() );  
  Serial.print(F("readyWaitCounter: ")); Serial.println(readyWaitCounter);  // zum testen  
  Serial.print(F("additional readyWaitTime: ")); Serial.println(readyWaitCounter*4);  // zum testen

 ... usw.

Der gesamte Code im Anhang.

MCP3550_50_010.ino (8.33 KB)

Wenn Du das hier machst:

Serial.print(F("warten beendet: ")); Serial.println(micros() );

ist das Laufzeitverhalten ganz stark vom Füllzustand des seriellen Sendepuffers abhängig.

Das "Serial.print(F("warten beendet: "));" läuft nämlich in weit weniger als einer Millisekunde durch, falls der serielle Sendepuffer leer ist und die Zeichen nur in den Sendepuffer übertragen werden müssen.

Oder derselbe Befehl dauert abhängig von der Baudrate bei 9600 Baud zum Beispiel 16 Millisekunden für das Senden von 16 Zeichen aus dem Sendepuffer. Wenn nämlich der serielle Sendepuffer voll ist, blockiert der Aufruf den Sketch so lange, bis der serielle Sendepuffer das letzte zu sendende Zeichen aufnehmen kann. Und dann wird die Zeit mit micros() danach eben 16 Millisekunden später aufgerufen.

Hallo,

aha. Danke. Also müßte ich das serielle an der Stelle komplett weglassen und die micros in einer Variablen zwischenspeichern und in loop ausgeben wenn alles vorbei ist und Zeit ist. Dann sollte das doch ziemlich genau passen?

Edit: Ich hab die an Ort und Stelle einer Variablen zugewiesen, dass sollte schnell gehen und lasse danach gleich die Differenz ausgeben. Effekt ist, die Gesamtzeit von ca. 84ms ist um 2ms auf konstante 82,1ms gesunken, alles andere bleibt seltsamerweise wie vorher. Die Zeiten passen noch nicht zusammen. Warum? Wartezeit_1 und Wartezeit_2 sind global als unsigned long definiert.

Edit2: Irgendwas stimmt nicht mit dem delayMicroseconds oder dem Counter in der while schleife. Denn wenn die Gesamtzeit von 82,1ms jetzt konstant bleibt, dann bedeutet das, dass es funktioniert alles so wie es soll. Alles was ich dem festen delay wegnehme an Zeit muß de whileschleife ersetzen. Da die Gesamtzeit konstant bleibt, muß die das demzufolge korrekt ausgleichen. Nur irgendwie bekomme ich das falsch zu Gesicht. So meine Logik derzeit. Ist meine Rechnung falsch? Counter * 4µs ?

float MCP3550_Reading (float UB_AREF)  
{
  static long Messwert = 0;                   // vom MCP3550-50 
  static float Spannung = 0;                  // Endergebnis in [mV]
  byte OVL, OVH, SIGN, OV_Bits = 0;           // Overflow Bits und Vorzeichen Bit
  unsigned int readyWaitCounter = 0;          // while Endlosschleifenüberwachung für Ready Signal
  // das ist zugleich die 50ns Zwangspause CS High to Low

  //  80ms typische Conversion Time (MCP3550-50)
  Wartezeit_1 = micros();
  digitalWrite(CS, LOW);             // starte AD Convertion
  delay(70);                         // 80ms typische Convertion Time (MCP3550-50)
  while(digitalRead(MISO) == HIGH)   // warten bis SDO/RDY mit LOW Ready signalisiert
    { 
     digitalWrite(CS, HIGH);         // wenn nicht Ready, toggle CS erneut High zu Low
     delayMicroseconds(2);           // warte extra 2µs
     digitalWrite(CS, LOW);          
     delayMicroseconds(2);           // warte extra 2µs
     readyWaitCounter++;             // inkrementiere den Sicherheits Counter (Endlosschleife)
     if (readyWaitCounter > 1000)    // wenn zusätzliche Wartezeit > 4ms (1000x4µs)
       {
        digitalWrite(CS, HIGH);   
        return INFINITY;             // dann Abbruch mit Fehlercode "unendlich"
       }
    }
  Wartezeit_2 = micros();  
  Serial.print(F("Wartezeit: ")); Serial.println(Wartezeit_2 - Wartezeit_1);  
  Serial.print(F("readyWaitCounter: ")); Serial.println(readyWaitCounter);  // zum testen  
  Serial.print(F("additional readyWaitTime: ")); Serial.println(readyWaitCounter*4);  // zum testen

Hallo,

ich muß wohl nochmal das Datenblatt vom Chip zur Hand nehmen. Wenn ich die delayMicroseconds in der while drastisch erhöhe, von 2 auf 50 oder gar noch höher, umso näher komme ich an die eigentlich richtig erwartete Differenz ran. Mit 2x50µs erhalte ich schon 6,3ms extra bei festen 75ms und konstanten 82,1ms. Ist verdammt nah dran. Warum das so ist erschließt sich mir aber noch nicht. Sinn der Übung ist, dass ich laut Datenblatt eigentlich unter 10µs bleibt wollte für CS High, damit der IC nicht zwischendurch in Shutdown geht. Ich lese das Datenblatt nochmal. Bestimmt irgendein Timingfehler, wenn ihr sonst im Code keinen Fehler findet?

while(digitalRead(MISO) == HIGH)   // warten bis SDO/RDY mit LOW Ready signalisiert
    { 
     digitalWrite(CS, HIGH);         // wenn nicht Ready, toggle CS erneut High zu Low
     delayMicroseconds(50);           // warte extra
     digitalWrite(CS, LOW);          
     delayMicroseconds(50);           // warte extra 
    ... usw.

Doc_Arduino: Ist meine Rechnung falsch? Counter * 4µs ?

Ja, total.

Die Funktion digitalWrite ist auf dem Arduino langsam und benötigt über 60 Takte, das sind bei 16 MHz Taktung ca. 4µs.

Mal die ungefähre Dauer der Ausführung bei einigen Programmzeilen eingetragen:

     digitalWrite(CS, HIGH);         // ==> braucht ca. 4 µs
     delayMicroseconds(2);           // warte extra 2µs
     digitalWrite(CS, LOW);         // ==> braucht ca. 4 µs 
     delayMicroseconds(2);           // warte extra 2µs

jurs:
Die Funktion digitalWrite ist auf dem Arduino langsam und benötigt über 60 Takte, das sind bei 16 MHz Taktung ca. 4µs.

Hallo,

ich habe aus Interesse mal die Zeit gemessen, die digitalWrite() braucht. Dazu habe ich folgenden Sketch verwendet:

const int triggerPin = 9;
const int messPin = 10;

void setup() {
  pinMode(triggerPin, OUTPUT);
  pinMode(messPin, OUTPUT);
}


void loop() { 
  digitalWrite(triggerPin, LOW);
  digitalWrite(messPin,LOW);
  delay(1);
  digitalWrite(triggerPin, HIGH);
  digitalWrite(messPin, HIGH);
  delay(1);
  
}

Also TriggerPin geht hoch und dann die Zeit gemessen bis MessPin auch HIGH ist. Angezeigter Zeitverbrauch 5µs - siehe Bild.

Gemessen wurde dabei mit 16MSa/s, der korrekte Zeitverbrauch kann also irgendwo zwischen 4,875µs und 5,125µs liegen.

Gruß,
Ralf

PS: Ganz vergessen zu erwähnen: Arduino Uno mit 16MHz

Hier gibt es einen netten Code, der die Geschwindigkeit von vielen der Standard Funktionen mit dem Serial Monitor testet: http://playground.arduino.cc/Main/ShowInfo

Hallo,

Danke Euch allen. :) Diese Informationen helfen ungemein weiter. Dadurch kam ich dann darauf, dass ich das Datenblatt des MCP... noch nicht richtig verstanden hatte. :roll_eyes: Der hat 2 Modi. Einen Single- und einen Continuous Conversion Mode. Durch diese Fehlersuche wegen Codeoptimierung kam nun raus das ich das durcheinander gewurfen hatte. Mit festen Delay fiel das nicht weiter auf. Deshalb ist mein ganz oben getipptes auch völlliger Unsinn, dass man den MCP nur mit festen delay verwenden kann mit Hardware SPI. (Das lösche ich gleich wenn das noch möglich ist.)

Edit: noch eine Nachfrage für mein Verständnis. Wenn der digitalWrite Befehl 4µs dauert. Dann wäre sowas hier Unsinn? Weil die 50ns NOP schon in dem folgenden Befehl CS auf High zu legen drin sind?

digitalWrite(CS, LOW);             // 
NOP;                                              // 50ns erforderlich CS auf Low 
digitalWrite(CS, HIGH);            //

Schachmann: Gemessen wurde dabei mit 16MSa/s, der korrekte Zeitverbrauch kann also irgendwo zwischen 4,875µs und 5,125µs liegen. ... PS: Ganz vergessen zu erwähnen: Arduino Uno mit 16MHz

Hast Du auch mal an verschiedenen Pins bei Deinem UNO gemessen?

Die Dauer von digitalWrite ist je nach Pin mehr oder weniger verschieden, soweit ich das rein per Software ermittele.

Teste z.B. an einem UNO mal Pin-10 und vergleiche dann mal mit Pin-12! Siehst Du dann auch auf Deinem Oszilloskop einen Unterschied in der Zeitdauer?

Und wenn Du hast, schließe mal zum Vergleich einen MEGA an und teste! Beim MEGA dauert digitalWrite auf fast allen Pins deutlich länger als auf einem UNO!

Dann wäre sowas hier Unsinn? Weil die 50ns NOP schon in dem folgenden Befehl CS auf High zu legen drin sind?

Ja. Jeder Befehl dauert mindestens 1 Takt (= 50ns bei 20 MHz, 62,5ns bei 16MHz ) Allein der Aufruf einer Unterfunktion sind mehrere Befehle...

Nachtrag:

#define CS_PORT PORTB
#define CS_BIT   0x04  // Arduino Uno Pin 10
void loop()
{
  CS_PORT &=~ CS_BIT;
  CS_PORT |= CS_BIT; 
}

Dies sollte den Pin 10 auf einem Uno für ca. 60 ns auf LOW ziehen. Kürzer geht nicht. Um sicher zu sein, dass es mindestens 50 .. 100 ns sind, macht hier ein assembler-nop Sinn.

Aber siehe auch: http://www.arduino.cc/en/Reference/PortManipulation :

Generally speaking, doing this sort of thing is not a good idea.

Das sind mehr als ein Takt. Du hast mindestens 2 Takte, weil du das Register einliest und wieder zurückschreibst

Das gibt es auch als Lib: https://code.google.com/p/digitalwritefast/downloads/list Wenn die Pins zur Compile Zeit feststehen hat man damit die digitalWrite() Syntax, mit der Geschwindigkeit von direktem Port-Zugriff.

Dass es generell eine schlechte Idee ist, ist aber Unsinn. Ohne die Arduino Abstraktionsebene würde man es genauso machen. Und nicht nur auf AVR Prozessoren. Andere Systeme machen es genauso. Es ist halt schwerer lesbar und man muss genau wissen was man tut. Aber die Ausnahme wie da dargestellt ist es nicht. Die Ausnahme ist eher der Arduino Weg, auch wenn der in vielen Standard Situationen ausreicht und einfacher ist.

Hallo,

man staunt immer wieder was eine Frage an neuen Informationen ans Tageslicht spült. :slight_smile:

Für meine Funktion benötige ich nicht unbedingt die niedrigsten Timings. Da geht es eher drum gewisse Mindestwartezeiten einzuhalten. Aber gut zu wissen wie es andere machen und was so möglich ist.

Meine Funktion zum MCP3550 funktioniert nun ohne festes delay und die loop bleibt unter 1ms. Muß sie nur noch vernünftig kommentieren und mit anderen SPI Devices quer testen. Dann stell ich das Update zur Verfügung.

Danke.

@ Schachmann
Was hast Du für einen Logikanalyzer genau? Ich hatte mal meinen Mega2560 dafür verwendet. Nur an ihn selber zu gucken geht schlecht wenn man nur einen hat. :wink:
http://www.pighixxx.com/arduino-logic-analyzer/

Ich hoffe ja immer noch das es bald ein ähnliches Projekt für einen mbed FRDM-K64F gibt. Mit 256kB RAM und 120MHz könnte man günstig sehr viel logic analysieren … :wink:

Doc_Arduino:
@ Schachmann
Was hast Du für einen Logikanalyzer genau? Ich hatte mal meinen Mega2560 dafür verwendet. Nur an ihn selber zu gucken geht schlecht wenn man nur einen hat. :wink:

Einen USB Saleae Logic Analyzer 24M 8 Channel, bestellt bei Ama**n für knapp über 20,- Euro. Für den Preis absolut genial.

Gruß,
Ralf

[Edit:] Da ich mittlerweile auf der Website, die ich hier als Download-Link angegeben hatte, einen Hinweis entdeckt habe, dass die Software nicht mit Clones verwendet werden soll, habe ich den Download-Link hier entfernt. In dem License.txt, der der Software beiliegt, habe ich keinen Hinweis auf Clones entdeckt.

Hallo,

Danke, klingt gut. Ist zwar bestimmt kein Original, aber für 20,- kann man nichts falsch machen. Habs eins bestellt.

Doc_Arduino: Danke, klingt gut. Ist zwar bestimmt kein Original, aber für 20,- kann man nichts falsch machen. Habs eins bestellt.

Nee, ist kein Original. Aber schreib mir doch mal was Du davon hälst, wenn Du es mal ausprobiert hast.

Gruß, Ralf

mach ich

jurs: Hast Du auch mal an verschiedenen Pins bei Deinem UNO gemessen? Die Dauer von digitalWrite ist je nach Pin mehr oder weniger verschieden, soweit ich das rein per Software ermittele.

Ja, mich hat das selbst gewundert, dass ich 5µs gemessen habe, obwohl "alle Welt" von 4µs ausgeht. Deshalb jetzt nochmal mit Pin 13 (mit der LED und Vorwiderstand): Sowohl mit dem Logic Analyzer als auch mit dem Oszilloskop genau 4µs.

Jetzt frage ich mich, ob das mit dem Vorwiderstand der LED zusammenhängt, dann fließt ja deutlich mehr Strom, als wenn nur ein Messgerät mit einer "Riesen-Impedanz" am Pin hängt. Wenn ich mal Zeit dafür habe, probiere ich da mal die verschiedenen Varianten (gerne auch mit Uno und Mega im Vergleich) aus.

Gruß, Ralf

Schachmann: Jetzt frage ich mich, ob das mit dem Vorwiderstand der LED zusammenhängt, dann fließt ja deutlich mehr Strom, als wenn nur ein Messgerät mit einer "Riesen-Impedanz" am Pin hängt. Wenn ich mal Zeit dafür habe, probiere ich da mal die verschiedenen Varianten (gerne auch mit Uno und Mega im Vergleich) aus.

Nein, ganz andere Richtung. Wenn man sich mal den Quelltext von digitalWrite ansieht, dann erkennt man, dass die Funktion mehr macht als es den Anschein hat.

Die Programmlogik von DigitalWrite ist nämlich wie folgt:

- Wenn es ein PWM-Pin ist ==> Führe Code aus zum Abschalten von PWM
- Setze den Pin

Das führt dazu, dass digitalWrite auf allen PWM-Pins länger dauert als auf den Nicht-PWM Pins. Der Code zum Abschalten von PWM wird nämlich jedesmal beim Aufrufen von digitalWrite ausgeführt, wenn es ein PWM-Pin ist. Und zwar unabhängig davon, ob PWM an diesem Pin aktiv ist oder nicht.

D.h. an einem UNO sind die langsamen Pins identisch mit den PWM-Pins: 3, 5, 6, 9, 10 und 11 An den PWM-Pins dauert digitalWrite ca. 4,65 bis 5,03 µs. Alle anderen Pins sind mit digitalWrite schneller und liegen bei 3,84µs, einschließlich der Analog-Pins.

Diese Werte für den Zeitbedarf von digitalWrite gelten aber nur für den UNO. Beim MEGA ist der Zeitbedarf generell noch höher, und das hängt mit der höheren Pin-Zahl zusammen.

Wenn Interesse besteht, könnte ich mal einen Profiler-Sketch posten, der das Timing an den Pins softwaretechnisch erfaßt und über Serial ausgibt.

jurs: Wenn Interesse besteht, könnte ich mal einen Profiler-Sketch posten, der das Timing an den Pins softwaretechnisch erfaßt und über Serial ausgibt.

Auf jeden Fall, würde ich gerne mal ausprobieren.

Gruß, Ralf

Schachmann:
Auf jeden Fall, würde ich gerne mal ausprobieren.

OK, ich habe da mal etwas vorbereitet.

Falls andere Controller verwendet werden sollen, kann man das über andere #elif-Bedingungen leicht nachrüsten, so wie gepostet werden nur Atmega328 und Atmega2560 direkt unterstützt. Da die Serial-Schnittstelle an den Pins 0/1 vom Sketch genutzt wird, teste ich Pin-0 und Pin-1 nicht mit.

Das Timing wird ermittelt für digitalWrite, und zwar getrennt für das Setzen von “HIGH” und “LOW” (es gibt einen Unterschied!) und damit das Programm nicht gar so wenig leistet, ermittelt es gleich auch noch die Zeiten für digitalRead an den verschiedenen Pins.

volatile int dummy;
#if defined (__AVR_ATmega328__)
  byte testpins[]={2,3,4,5,6,7,8,9,10,11,12,13,A0,A1,A2,A3,A4,A5};
#elif defined (__AVR_ATmega328P__) 
  byte testpins[]={2,3,4,5,6,7,8,9,10,11,12,13,A0,A1,A2,A3,A4,A5};
#elif defined (__AVR_ATmega2560__)
  byte testpins[]={2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
                   21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,
                   41,42,43,44,45,46,47,48,49,50,51,52,53,A0,A1,A2,A3,A4,A5,A6,
                   A7,A8,A9,A10,A11,A12,A13,A14,A15};
#else
  Error: Controller not defined;
#endif


byte numpins=sizeof(testpins);




unsigned long testRead1(byte pin, int runcount)
{
  unsigned long time=micros();
  for (int i=0;i<runcount;i++)
  {
    dummy=digitalRead(pin);
  }
  return micros()-time;
}

unsigned long testRead2(byte pin, int runcount)
{
  unsigned long time=micros();
  for (int i=0;i<runcount;i++)
  {
    dummy=digitalRead(pin);
    dummy=digitalRead(pin);  // ==> diese Zeile ist zusätzlich und wird gemessen
  }
  return micros()-time;
}



unsigned long testWrite1(byte pin, byte value, int runcount)
{
  unsigned long time=micros();
  for (int i=0;i<runcount;i++)
  {
    digitalWrite(pin, value);
  }
  return micros()-time;
}

unsigned long testWrite2(byte pin, byte value, int runcount)
{
  unsigned long time=micros();
  for (int i=0;i<runcount;i++)
  {
    digitalWrite(pin, value);
    digitalWrite(pin, value);    // ==> diese Zeile ist zusätzlich und wird gemessen
  }
  return micros()-time;
}


void setup() {
  Serial.begin(9600);
  Serial.println();
  Serial.println("Arduino Performance Test digitalRead/digitalWrite");
  Serial.println("Function Profiling by 'jurs' for German Arduino Forum");
  Serial.println();
  delay(100);
  performanceTest();
}


void performanceTest() {
  unsigned long t1,t2,t3,t4;
  float f;
  Serial.println();

  Serial.println("Testing digitalWrite, results in microseconds");
  Serial.println("Pin\tLOW\tHIGH");
  delay(100);
  for (int i=0;i<numpins;i++)
  {
    t1=testWrite1(testpins[i],LOW,10000); // test1 digitalWrite LOW
    t2=testWrite2(testpins[i],LOW,10000); // test2 digitalWrite LOW
    t3=testWrite1(testpins[i],HIGH,10000);// test1 digitalWrite HIGH
    t4=testWrite2(testpins[i],HIGH,10000);// test2 digitalWrite HIGH
    Serial.print("Pin-");Serial.print(testpins[i]);Serial.print('\t');
    f=(t2-t1)/10000.0;
    Serial.print(f,3);
    Serial.print('\t');
    f=(t4-t3)/10000.0;
    Serial.println(f,3);
    delay(100);
  }
  Serial.println();


  Serial.println("Testing digitalRead, results in microseconds");
  Serial.println("Pin\tRead");
  delay(100);
  for (int i=0;i<numpins;i++)
  {
    t1=testRead1(testpins[i],10000); // test1 digitalRead
    t2=testRead2(testpins[i],10000); // test2 digitalRead
    Serial.print("Pin-");Serial.print(testpins[i]);Serial.print('\t');
    f=(t2-t1)/10000.0;
    Serial.println(f,3);
    delay(100);
  }
  
  Serial.println();
  Serial.println("Ready!");
}

void loop() {
}

Zur Erklärung der Funktionsweise des Sketches:
Zum Profiling schreibe ich zwei Testfunktionen, die nahezu identisch sind und eine Schleife sehr oft ausführen: Einmal eine Standardschleife, und einmal genau dieselbe Schleife, aber mit dem einen zusätzlichen Befehl, dessen Ausführungszeit ich ermitteln möchte.

Wenn ich den Laufzeitunterschied beider Funktionen durch die Anzahl der Schleifendurchläufe teile, erhalte ich so recht genau die Ausführungsdauer eines einzelnen Befehls.

Falls jemand auf ähnliche Weise andere Ausführungsgeschwindigkeiten von Code ermitteln möchte, so gilt es, drei Klippen zu umschiffen:

  • der Compiler darf die Test1-Funktion nicht wegoptimieren
  • der Compiler darf auch die Schleife innerhalb der Test1-Funktion nicht wegoptimieren
  • der Compiler darf den zu testenden Code in der Test2-Funktion nicht wegoptimieren

Im gegebenen Fall ist das einfach: digitalWrite und digitalRead werden nie wegoptimiert, sondern immer ausgeführt.

Falls sich jemand wundert, wozu das “delay(100);” dienen soll: Mit diesem delay sorge ich dafür, dass vorangegangene Ausgaben auf Serial komplett aus dem seriellen Sendepuffer gesendet wurden, bevor ein neues Timing gestartet wird, so dass Serial-Sendeinterrupts nicht mehr auftreten und das timing Verfälschen können, solange das Timing läuft.

Bei der Auswertung von digitalWrite sieht man übrigens sehr schön, dass das Setzen von LOW stets zwei Takte (ca. 2*62,5ns = ca. 0,125µs) länger dauert als das Setzen von HIGH. Offenbar gibt es für beide Pegel unterschiedliche Logikpfade (if…else) innerhalb der Funktion digitalWrite.