Loop Durchgang sehr langsam durch Schleifen?! (Graph mit SPI OLED Display)

Hallo zusammen. Ich hoffe jemand kann mir helfen.

Ich habe folgendes vor bzw gemacht.

Ich verwende ein Spannungsmess-Modul (INA219) und ein 128x64Pixel Oled (S/W) Display (SSD1309) welches via SPI angeschlossen ist.
Für das Display und die Grafik verwende ich die U8g2lib

  • U8g2: Library for monochrome displays

> Die Grafik Bibliothek von Adafruit habe ich noch nicht getestet weil ich nicht wusste ob das 1309 Display damit läuft <

Ich will die Spannungswerte als fortlaufenden Graphen anzeigen lassen also nicht, dass der Graph von links nach rechts läuft und wenn er angekommen ist wird alles gelöscht sondern dass das Bild immer weiter läuft und man alle Werte nach links verschoben werden.

Das funktioniert auch alles doch das große Problem ist die Geschwindigkeit. Ich habe es mal "gemessen" und ein Durchlauf (bis das Bild aktualisiert wird) dauert ca. 630ms.

Ich habe mit 2 verschiedenen Bibliotheken (INA226.h und INA226_WE.h) mal einen Durchlauf gemessen, wenn 125 Werte eingelesen werden. Mehr war im Code nicht drin. Da dauerte ein Durchlauf 160ms bzw 159ms.

#include <Wire.h>
#include <INA226_WE.h>
#define I2C_ADDRESS 0x40

INA226_WE ina226(I2C_ADDRESS);

unsigned long lastMillis;

float shuntVoltage_mV = 0.0;
float busVoltage_V = 0.0;



void setup() {


  Wire.begin();
  ina226.init();

ina226.setAverage(AVERAGE_4);
ina226.setConversionTime(CONV_TIME_1100);
ina226.setMeasureMode(CONTINUOUS);
ina226.setCurrentRange(MA_400);

ina226.setCorrectionFactor(0.95);

ina226.waitUntilConversionCompleted();

lastMillis = millis();
Serial.begin(9600);

}

void loop() {


for(int g = 125; g >= 1; g--)
{

  Serial.print("Bus voltage:   ");
  Serial.println(ina226.getBusVoltage_V(), 3);
  Serial.print("Shunt voltage: ");
  Serial.println(ina226.getShuntVoltage_mV()/1000, 3);

}

 Serial.println(millis() - lastMillis);
 lastMillis = millis();
}

Da ich für die X-Achse 125 float Werte benötige, habe ich einen Teensy LC (48 MHz) verwendet.
Ein Arduino Nano oder Micro hatte leider zu wenig Speicher für die vielen Werte.

Der Ablauf im geplanten Programm (je Durchlauf) ist wie folgt.

Variablen:

float Spannung[126];
float messwert = 0;

im loop:

Als erstes lese ich den Wert ein und dann in das Array gepackt:

messwert = loadVoltage_V;

Spannung[2] = messwert;

loadVoltage_V ist das Ergebnis aus busVoltage_V + (shuntVoltage_mV/1000) und kommt von der INA226_WE.h Bibliothek.

Dann wird neu sortiert so dass der neue Wert links ist >> Graph läuft von links nach rechts

for(int g = 124; g >= 2; g--)
{
  Spannung[g+1] = Spannung[g];
  //Serial.println(Spannung[g]);
}

Und dann zeichne ich den Graphen

for (int i = 125; i >= 2; i--) {

 
    if ((20 + ((mittelwert_neu + differenz)*1500 - (Spannung[i]  + differenz)*1500)) >= 63) {
      y_draw = 63;
      }
    else {
      if ((20 + ((mittelwert_neu + differenz)*1500 - (Spannung[i]  + differenz)*1500)) <= 8) {
    
      y_draw = 8;
      }
      else {
        y_draw = (20 + ((mittelwert_neu + differenz)*1500 - (Spannung[i]  + differenz)*1500));
      }}
      
  u8g2.drawPixel(i,y_draw);
  
}

Bezogen auf den Mittelwert: Nach einer bestimmten Zeit wird ein neuer Mittelwert berechnet um den Graphen immer auf einer Höhe zu halten wenn die Spannung mal sinkt. Das soll ggf alle 60 Sekunden mal passieren.

Die Differenz ist zum Ausgleich bezogen auf den ersten Mittelwert der beim Start gebildet wird und dem aktuellen. Damit wird auch der Graph auf der Höhe gehalten.

Die *1500 ist für eine feinere Anzeige von Abweichungen bzw. damit werden Nachkommastellen zu ganzen Zahlen und dann kann ich das besser als Pixel anzeigen lassen.

Mit dem y_draw schränke ich den Ausschlag der Höhe ein, so das immer auf der ganzen X-Achse ein Wert zu sehen ist.

Also in jedem Durchgang wird ein Wert eingelesen, alle Graph-Werte neu Sortiert und dann der ganze Graph gezeichnet also alle Werte in die Liste als Punkte eingetragen.

Das ganze läuft aber sehr langsam - 0,63s (630ms) pro Durchgang und ich weiß nicht warum das so lange dauert.

Ich hoffe jemand hat da für mich einen Tipp oder eine Idee.

Danke vorab und viele Grüße

Ich habe jetzt mal nur den Graphen zeichnen lassen und die Werte kommen über eine Zufallszahl + 30.

Hier auf www.arduino.cc wird mir für das SSD1309 U8g2 Bibliothek angegeben.
U8g2: Monochrome LCD, OLED and eInk Library. Display controller: SSD1305, SSD1306, SSD1309, SSD1316, SSD1322,

#include <Arduino.h>
#include <U8g2lib.h>

#define U8X8_HAVE_HW_SPI
#include <SPI.h>

U8G2_SSD1309_128X64_NONAME0_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);  


float Spannung[126];
float messwert = 0;
unsigned long lastMillis;

void setup() {

  Serial.begin(9600);
  u8g2.begin();
  lastMillis = millis();

}

void loop() {

  u8g2.clearBuffer();
  messwert = random(-6, 6);

Spannung[2] = messwert;

for(int g = 124; g >= 2; g--)       // Sortieren
{
  Spannung[g+1] = Spannung[g];
}


for (int i = 125; i >= 2; i--) {    // Pixel Zeichnen
  u8g2.drawPixel(i,30 + Spannung[i]);
}

u8g2.sendBuffer();

Serial.println(millis() - lastMillis);
lastMillis = millis();
}

Nach einem Durchgang wird mir ca. 108ms als Zeit angezeigt.
Sieht realtiv schnell aus bzw. viel schneller oder flüssiger als im Haupt-Code.

Irgendwie komisch dass es nicht richtig richtig schnell durchläuft, so dass man fast nichts sehen kann. Habe damit gerechnet irgendwo eine geschwindigkeitsbegrenzung einzubauen damit es langsamer läuft aber jetzt wäre ein boost toll

Ich sehe da evtl. einen Widerspruch in Deinem Code:

#define U8X8_HAVE_HW_SPI <--- Hardware SPI

U8G2_SSD1309_128X64_NONAME0_F_4W_SW_SPI <-- Software SPI - Warum?

Gruß Tommy

Hi Tommy,

danke dass du dich gemeldet hast. Also ich meine, dass ich das so aus einem Beispiel übernommen habe.
Du meinst wegen HW und SW?
Denke es steht für Hardware und Software? Was kann dadurch passieren?

Ich muss das gleich mal testen bzw prüfen, warum das so ist, wie es ist und ob ich da falsch abgeschrieben habe.

WAMBO:
Denke es steht für Hardware und Software? Was kann dadurch passieren?

Die Software-Variante ist wesentlich langsamer.

WOOOOOOOOOW

Danke!
Habe grad geguckt und es gibt neben der SW Variante auch die HW Variante und das hilft!

Komme beim Test fürs zeichnen des Graphen auf 5ms pro Durchlauf, also gut 20x schneller!

Super, toll dass ihr bzw Tommy das gesehen hat.

U8G2_SSD1309_128X64_NONAME0_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);  
U8G2_SSD1309_128X64_NONAME0_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);

Dazu eine Frage: Wieso ist es bei HW nicht nötig, den clock und data Pin anzugeben?

WAMBO:
Dazu eine Frage: Wieso ist es bei HW nicht nötig, den clock und data Pin anzugeben?

Weil die bei der HW Variante nicht frei gewählt werden können, sondern durch eben die Hardware festgelegt sind.

Habe das ganze jetzt in meinen ursprünglichen Quellcode integriert und da ist leider die Geschwindigkeit nicht soo viel schneller geworden. :o

Bin von 630ms auf 535ms gekommen.

KOMISCH oder?

Werde jetzt mal die einfachen und einzelnen Teile zusammenbringen also Graphen zeichnen und Werte einlesen und gucken, ob es dann schneller ist als im eigentlichen Programm.

WAMBO:
Habe das ganze jetzt in meinen ursprünglichen Quellcode integriert und da ist leider die Geschwindigkeit nicht soo viel schneller geworden. :o

Bin von 630ms auf 535ms gekommen.

KOMISCH oder?

Komisch sicher nicht.
Da ist in deinem Sketch sicher noch ein Fehler, den du übersiehst.

WAMBO:
KOMISCH oder?

Ist doch nicht komisch. Die Übertagung der Daten ist doch - so wie ich das verstehe - in beiden Fällen die gleiche. Also gewinnst Du in beiden Fällen etwa gleich viel Zeit - das sind eben jene 100ms.
Der Rest der Zeit geht offensichtlich durch das Aufbereiten der Daten drauf. Da ändert sich durch die schnellere Übetragung zum Oled nichts.

P.S. Du arbeitest sehr viel mit float. Float ist langsam. Für die Daten des Oled brauchst Du letztendlich aber eh immer integer-Werte. Schau mal, ob Du beim Berechnen so früh wie möglich mit integer Werten arbeiten kannst.

ALSO ich habe jetzt das einlesen der float Spannungswerte und das sortieren der 125 float Werte mit dem Graphen kombiniert und ich komme auf 6ms pro Durchlauf.

Irgendwas muss somit in meinem Hauptcode mist sein.

Wegen den float Werten könnte ich zusehen, dass ich die Komma-werte beim einlesen direkt mit 1000 Multipliziere und als Int speicher (z.B. 1,43263 * 1000 = 1433 > Int) und hinterher wieder durch 1000 teile oder fürs rechnen irgendwie so aufbereite usw.
Für das Zeichnen brauche ich eh ganze Zahlen.
So wollte ich 3 Nachkommastellen für die Anzeige haben. Speichern kann ich es als Int und dann diese 1000er Sache machen

WAMBO:
Irgendwas muss somit in meinem Hauptcode mist sein.

Und warum postest du den nicht nach deiner Änderung ?
Wir können nicht auf deinen PC sehen.

Wenn ich das richtig gelesen habe, arbeitest Du mit einem Teensy LC. Das ist doch ein 32-Bit Rechner. Damit rechnet der also am liebsten. Da könntest Du deine Spannungen sogar in µV speichern, und hättest noch keine Probleme mit den Zahlengrößen. Da brauchst Du definitv kein float. Und in 32Bit Integer rechnet der Teensy dann auch recht flott ( nicht float :wink: ).

Das stimmt!
Also dass ist der aktuelle Test-Code, wo beides zusammengekommen ist und ich 6ms pro Durchlauf habe:

#include <Arduino.h>
#include <U8g2lib.h>



#include <Wire.h>
#include <INA226_WE.h>
#define I2C_ADDRESS 0x40

INA226_WE ina226(I2C_ADDRESS);

  float shuntVoltage_mV = 0.0;
  float busVoltage_V = 0.0;


#define U8X8_HAVE_HW_SPI
#include <SPI.h>

//U8G2_SSD1309_128X64_NONAME0_F_4W_SW_SPI u8g2(U8G2_R0, /* clock=*/ 13, /* data=*/ 11, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);  
U8G2_SSD1309_128X64_NONAME0_F_4W_HW_SPI u8g2(U8G2_R0, /* cs=*/ 10, /* dc=*/ 9, /* reset=*/ 8);  

float Spannung[126];
float messwert = 0;
float y_draw = 0;
unsigned long lastMillis;



void setup() {

  Serial.begin(9600);
 
    Wire.begin();
  ina226.init();

  
  u8g2.begin();
  lastMillis = millis();

ina226.setAverage(AVERAGE_4);
ina226.setConversionTime(CONV_TIME_1100);
ina226.setMeasureMode(CONTINUOUS);
ina226.setCurrentRange(MA_400);

//ina226.setCorrectionFactor(0.95);

ina226.waitUntilConversionCompleted();


}

void loop() {
  // put your main code here, to run repeatedly:

  u8g2.clearBuffer();          // clear the internal memory
  
 //messwert = random(-6, 6);
messwert = 100 * ina226.getBusVoltage_V() - 100;
Serial.println(ina226.getBusVoltage_V());

Spannung[2] = messwert;

for(int g = 124; g >= 2; g--)
{
  Spannung[g+1] = Spannung[g];
  //Serial.println(Spannung[g]);
}



for (int i = 125; i >= 2; i--) {

  u8g2.drawPixel(i, Spannung[i]);
  
}



  u8g2.sendBuffer();          // transfer internal memory to the display
Serial.println(millis() - lastMillis);
 lastMillis = millis();
}

Das "100 * ina226.getBusVoltage_V() - 100" bezieht sich auf ca. 1,5V (1,48243...) so das ich 148 als Wert habe und dann mit -100 einen Wert für den Graphen von ca. 48 bekomme. Wie gesagt, das ist nur der Test-Code, um beides zu vereinen.

MicroBahner:
Wenn ich das richtig gelesen habe, arbeitest Du mit einem Teensy LC. Das ist doch ein 32-Bit Rechner. Damit rechnet der also am liebsten. Da könntest Du deine Spannungen sogar in µV speichern, und hättest noch keine Probleme mit den Zahlengrößen. Da brauchst Du definitv kein float. Und in 32Bit Integer rechnet der Teensy dann auch recht flott ( nicht float :wink: ).

recht float :smiley: , der ist gut.
Also den Teensy LC habe ich erstmal wegen dem höheren Speicher / RAM genommen - wegen den vielen Variablen / 125er Array / Float-Werten. Was der noch besser kann (außer höhere Takt-Frequenz), weiß ich nicht so recht. 3 Nachkommastellen sollten sicher reichen da ich darüber hinaus bestimmt einen zu unruhigen Graphen bekomme.
Momentan mache ich es so, dass ich einen "starken" Ausschlag bekommen will (am Graphen), wenn es eine Abweichung von 0,01 gibt. Habe es mit 4 noch nicht getestet aber weil die 3. Nachkommastelle schon so am eiern war (1,432 - 1,431 - 1,433 usw), dachte ich mir - besser nicht.

Naja, da rechnest Du ja auch praktisch nichts in float. Von einer Variable in die andere umkopieren geht bei float genauso schnell wie bei int.

Stell mal alles auf int um ( Hauptsketch, wo Du viel berechnest ) und schau dann wie schnell es ist.

WAMBO:
Was der noch besser kann (außer höhere Takt-Frequenz), weiß ich nicht so recht.

Das Wesentliche ist: er ist ein 32-Bit Prozessor. Damit geht der Zahlenbereich eins 'int' von -2.147.483.648 bis 2.147.483.647. Das reicht doch locker um deine Spannungen in der kleinsten sinnvollen Auflösung darzustellen. Selbst für die Summen bei der Mittelwertbildung ist da genügend Luft. Float ist unnütz ( und ungenauer )

Gut, dann werde ich das mal testen, also dass ich die Werte als int speicher und damit arbeite.

Mal sehen wie es damit klappt.

BESTEN DANK

Bin gespannt ...

Du könntest noch Zeit sparen beim umkopieren der Meßwerte (memcpy statt Schleife) und wenn du die fertig gerechneten Werte auch in Array packst und verschiebst.
So oft ändert sich der mittelwert ja nicht.