Taktgeschwindigkeit erhöhen Arduino

Hallo,

ich habe einen Arduino Nano ESP32 welchen ich für ein Leistungsmessgerät verwende.

Ich möchte die gemessenen Werte auf einem Display anzeigen lassen, gleichzeitig über Serial ausgeben und auf einer SD Karte Speichern. (Auf das Display kann ggf. verzichtet werden)

Ich möchte eine Aktualisierungsrate der Messwerte am liebsten alle 20 ms, noch besser wären alle 10 ms erhalten.

Mit meinem Aktuellen Programm komme ich, wenn ich es wie oben überall anzeigen lasse auf eine minimale aktualisierungsrate von 180ms... Was ziemlich schlecht ist.

Dann habe ich es nirgendswo anzeigen lassen, sondern die Messwerte nur auf der SD Karte gespeichert und komme dann auch eine minimale Aktualisierungsrate von 20 - 22 ms.

Kurze Erklärung:

Ardunio erhält über SPI Messdaten vom Mess-IC ADE9153A.
Dann zeigt er sie über Serial an.
Dann zeigt er sie über ein Display über I2C an.
Dann schreibt er per SPI die Daten auf die SD Karte.

Wie kann ich mein Programm auf Geschwindigkeit optimieren.
Mir würde es genügen wenn ich die Daten über Serial ausgebe und auf der SD Karte speichern kann und das mit einer Rate von 10ms bis max 20ms.

Hier mein Programm:

(Nur der Wichtige teil:

      Serial.println("Die Messung wird gestartet!");
      Serial.println("Neue Datei wird Erstellt...");
      messdatei();
      digitalWrite(messungLED, HIGH);
      file_counter++;
      speicherzeit = millis();

/////////////////////////////////////////////Aktualisierungsrate//////////////////////////////////////////////////////
      while(true){
      
        // Erfasse die aktuelle Zeit seit dem Start des Mikrocontrollers in Millisekunden
        unsigned long aktuellerReport = millis();

        // Überprüfe, ob genügend Zeit seit dem letzten Bericht vergangen ist
        if ((aktuellerReport - letzterReport) >= abtastrate_int) {
        // Aktualisiere die Zeit des letzten Berichts auf die aktuelle Zeit
        akt_speicherzeit = millis();
        verg_speicherzeit = akt_speicherzeit - speicherzeit;        
        letzterReport = aktuellerReport;
       // Lese die Messdaten vom IC
        SPI.begin();
        SPI.beginTransaction(SPISettings(SPI_SPEED,MSBFIRST,SPI_MODE0));
          ade9153A.ReadPowerRegs(&powerVals);   
          ade9153A.ReadRMSRegs(&rmsVals);
          ade9153A.ReadPQRegs(&pqVals);
        SPI.endTransaction();
       //Speicher die Daten auf der SD Karte.
        speichern();
        }
     }

void speichern(){
  SPI.begin();
  if (SD.exists(dateiname)) {
    File datenfile = SD.open(dateiname, FILE_APPEND);
    if (datenfile) {

    datenfile.println("");
    datenfile.print("Zeit: ");
    datenfile.println(verg_speicherzeit);
    datenfile.print("Spannung: ");
    datenfile.print((float)rmsVals.VoltageRMSValue/1000, 3);
    datenfile.println(" V");
    datenfile.print("Strom: ");
    datenfile.print((float)rmsVals.CurrentRMSValue/1000, 3); 
    datenfile.println(" A"); 
    datenfile.print("Frequenz: ");
    datenfile.print(pqVals.FrequencyValue);
    datenfile.println(" Hz");
    datenfile.print("Wirkleistung: ");
    datenfile.print((float)powerVals.ActivePowerValue/1000, 2);  
    datenfile.println(" W");
    datenfile.print("Blindleistung: ");
    datenfile.print((float)powerVals.FundReactivePowerValue/1000, 2);  
    datenfile.println(" var");
    datenfile.print("Scheinleistung: ");
    datenfile.print((float)powerVals.ApparentPowerValue/1000, 2);  
    datenfile.println(" VA");   
    datenfile.close();
    }
    } else {
    Serial.println("Fehler beim Öffnen der Datei!");
    delay(1000);
    } 
    SPI.end();

  }

Das Programm so zu betreiben, ermöglicht wie schon erwähnt eine minimale Aktuallisierung von 20 - 22 ms.

Wie kann ich mein Programm auf höhere Geschwindigkeiten trimmen.

Beste Grüße auf Bayern

Serial auf maximalen wert setzen, algemein ist Serial langsam.

Danke für die Antwort:

Ich habe in meinem Oben gegebenen Programm allerdings nirgendswo eine Serielle Ausgabe und trotzdem habe ich nur eine Rate von 20 - 22 ms.

Alle vergleiche weg lassen, + delay und wo zu SPI END?

SPI.begin() in der Endlosschleife ist unnötig. Das muss nur einmal aufgerufen werden (void setup()).

Warum überhaupt eine while(1) ? Im „Arduino Kosmos“ ist void loop() doch schon so etwas wie eine Endlosschleife.

Hat dieser nicht 2 Cores, die beide mit 240MHz laufen können....?

Angeblich ja. Merken tuhe ich davon noch nicht so viel.

Das mit dem SPI mache ich mal

meine Loop ist schon voll. Das Programm hat über 1000 Zeilen Code. Habe alle Programmteile in Funktionen ausgelegt.

Da ich über SPI einmal auf die SD und auf das IC zugreife und anfangs Probleme mit dem SPI Bus hatte. Habe ich jeweils immer den Bus freigegeben für den anderen Slave.

LG

In deinem Programm nutzt du den 2ten Core gar nicht.
Zumindest sehe ich davon nix.
Also kannst du die "Verdoppelung der Arbeitsgeschwindigkeit" auch nicht spüren.
Noch nicht mal im Ansatz.

Okey interessant.

Kannst du mir erklären wie ich den 2ten Kern nutzen kann? Muss man das dem Code sagen?

xTaskCreateUniversal()
xTaskCreateStaticPinnedToCore()

Könnten dich auf die richtige Spur bringen.

Das Problem:
Ist dein Kopf rund genug, dass sich die Richtung des Denken soweit ändern kann?

Dein Sensor ist SPI.
Deine SD Karte ist SPI.

Beide nutzen also SPI. Da macht ein Parallelisieren imho wenig Sinn wenn du die gleiche HW Resource (SPI) dafür verwendest.

Das I2C auf den zweiten Kern schieben - ja vieleicht. Um welches Display / welche Lib handelt es sich?

Mach lieber mal Messungen ob du die Ausgabe auf die SD Karte beschleunigen kannst.
Streaming.h nutzen.
SPI.begin/.end nur dort wo notwendig (im Setup).
EIN Print genauer ... streamen mit <<
Weniger Overhead Schreiben (die Bezeichner müssen ja nicht wirklich jede Zeile sein!)
File offenlassen und nur öffnen wenn es nicht offen ist.

1 Like

Hallo,

allein die ständige Wiederholung der "Namen" kostet Zeit.

    datenfile.println("");
    datenfile.print("Zeit: ");
    datenfile.println(verg_speicherzeit);
    datenfile.print("Spannung: ");
    datenfile.print((float)rmsVals.VoltageRMSValue/1000, 3);
    datenfile.println(" V");
    datenfile.print("Strom: ");
    datenfile.print((float)rmsVals.CurrentRMSValue/1000, 3); 
    datenfile.println(" A"); 
    datenfile.print("Frequenz: ");
    datenfile.print(pqVals.FrequencyValue);
    datenfile.println(" Hz");
    datenfile.print("Wirkleistung: ");
    datenfile.print((float)powerVals.ActivePowerValue/1000, 2);  
    datenfile.println(" W");
    datenfile.print("Blindleistung: ");
    datenfile.print((float)powerVals.FundReactivePowerValue/1000, 2);  
    datenfile.println(" var");
    datenfile.print("Scheinleistung: ");
    datenfile.print((float)powerVals.ApparentPowerValue/1000, 2);  
    datenfile.println(" VA");   

Die Daten landen doch am Ende bestimmt in einer Tabelle? Excel o.ä.?
Dann schreibe bei Datei Ersterstellung einmal eine Kopfzeile mit diesen und danach immer nur die Daten mit bspw. Komma getrennt. Dann kannste hinterher alles bequem in Excel einlesen und formatieren. Würde hier über die Hälfte der Übertragungszeit sparen.

Edit: sorry, hat noiasca schon angesprochen

Es gibt 2 Möglichkeiten. Kurzfristig viele Daten hintereinander erfassen. Das braucht Speicher der dann weggeschrieben werden muss. Das heißt schnelle Datenerfassung und Pause wechseln sich ab. Bei kontinuierlicher Datenerfassung bestimmt die Übertragung die Erfassungsgeschwindigkeit.

Dein Display wird am langsamten sein. Hier gilt auch nur notwendige Daten übertragen. Möglichst mit jedem loop Durchlauf ein Wert. Du musst also die Daten für das Display in Happen aufteilen. Für ein Display reicht sowieso aller 500ms eine Aktualisierung oder größer.

Und dann musste eben schauen was deine externe Hardware so an Bustakt verträgt. Dann gehts weiter mit kurzen Kabeln usw.

2 Likes

Man könnte die SD Karte per SD_MMC anbinden und der Sensor kommt an den anderen SPI.

Dann 3 Tasks:

  • Sensor lesen und Daten in eine Queue schieben
  • Daten aus der Queue lesen, formatieren und auf SD schreiben
  • Aktuelle werte auf LCD ausgeben

Beim schreiben auf die SD nicht so kleine Prints machen sondern per snprintf() was größeres zusammenstellen.

1 Like

Wozu das?

natürlich soll man nicht für jeden Buchstaben open und close machen, aber sonst macht das schon die SD oder sdfat library für dich, und transferiert nur bei Bedarf geänderte 512Byte Blöcke komplett auf die SD-Karte.

1 Like

Open()/Close() sind natürlich tötlich für die Geschwindigkeit.

Und ich habe die Erfahrung gemacht, das die Geschwindigkeit unter vielen kleinen Schreibzugriffen leidet. KA was da passiert. Vielleicht wird jedesmal die Größe der Datei aktualisert.

1 Like

Ich bin mir nicht sicher, aber evt. wird bei close() jedesmal der dateipuffer noch geschrieben, falls noch daten drin sind. und bei open() wird evt. der Dateipuffer vorbereitet, da ja, evt immer ein kompletter block geschrieben werden muss, könnte es sein, dass bei jedem open() der puffer entsprechend vorbereitet wird. Wenn es so ist, dann wäe das der grund, warum die beiden Funktionen zeitintensiv sind

Richtig, wenn das nicht so wäre, hättest Du Datenverluste/-chaos.

Gruß Tommy

Da kannst du sicher sein. Mindestens wenn vorher was im Puffer verändert wurde.
Der zweite Puffer (vom Dateiverzeichnis, wo die Dateigröße, die Adresse der Daten usw. steht) sollte mMn optimalerweise auch erst bei close aktualisiert werden und nicht bei jedem write.

Ohne close() oder flush() steht es ja noch nicht auf dem Datenträger, also kann auch dessen Metainfo nicht aktualisiert werden.

Gruß Tommy