LCD: Ist I2C deutlich schneller als 4Bit Ansteuerung?

Hi,

es geht um einen RGB Strip Controller/Lichtsythesizer welcher (zwecks komfortabler Programm- u. Parameteränderung) über 6 Potis gesteuert wird und zusätzlich via LCD zeigen soll, was gerade geht.

Ich habe jetzt zu Testzwecken ein 4*16 LCD über eine 4Bit Library angesteuert - viel Löterei, gähnend langsam. Die alternative Ausgabe der Daten zum Serialmonitor ebenso. Entweder bricht mir die Framerate ein, oder die Aktualisierung des Displays ist sehr verzögert.

Ich habe noch nie etwas über I2C angesteuert - wäre mein Projekt unter Verwendung von z.B. diesem Modul hier http://www.komputer.de/zen/index.php?main_page=product_info&cPath=30&products_id=49 erheblich schneller?

MfG, Helmuth

Helmuth:
Ich habe jetzt zu Testzwecken ein 4*16 LCD über eine 4Bit Library angesteuert - viel Löterei, gähnend langsam. Die Ausgabe der Daten zum Serialmonitor ebenso. Entweder bricht mir die Framerate ein, oder die Aktualisierung des Displays ist sehr verzögert.

Über den Serial-Monitor darfst Du pro Zeiteinheit nicht mehr Zeichen ausgeben als in derselben Zeiteinheit gesendet werden können, sonst verlangsamt es den ganzen Sketch. Solange noch Platz im Serial-Sendepuffer ist, kannst Du Zeichen beliebig schnell reinschieben, aber sobald der Sendepuffer voll ist, gibt es Verzögerungen, weil dann die Serial.print und Serial.write Kommandos Warteschleifen einlegen, bis wieder Platz im Sendepuffer ist. Damit beim Senden keine Zeichen verloren gehen. Dafür geht dann aber ggf. Zeit verloren. Also besser niemals mehr als 63 Zeichen auf einmal in den Sendepuffer schieben und auch im Schnitt nie mehr Daten in den Sendepuffer reinschieben als es der Datenrate entspricht (bei 9600 Baud ca. 960 Zeichen pro Sekunde, bei 115200 Baud ca. 11520 Zeichen pro Sekunde). Darüber hinaus wird die Programmausführung beim Warten auf das Leeren des seriellen Sendepuffers verzögert.

Ansonsten hängt es wohl auch von der ansteuernden Library ab, wie schnell die Ausgabe ist: Normalerweise kann der LCD-Displaycontroller eine Rückmeldung geben, wann ein übermittelter Befehl vollständig ausgeführt ist und der nächste Befehl an den Controller übermittelt werden kann. Die Liquidcrystal-Library geht aber wohl davon aus, dass der dafür notwendige Rückkanal-Pin am LCD gar nicht angeschlossen ist (was wohl auch gängige Anschlußpraxis ist), sonst würden in der Liquidcrystal-Library nicht überall die delayMicroseconds()-Aufrufe stehen. Die warten einfach so lange, bis das lahmste am Markt verfügbare LCD-Modul mit der Kommandoausführung fertig sein müßte, statt abzufragen, wann der Befehl tatsächlich abgearbeitet ist.

Insbesondere wenn Du das Löschkommando beim LCD-Modul verwendest, schlägt das Delay erheblich zu:

void LiquidCrystal::clear()
{
  command(LCD_CLEARDISPLAY);  // clear display, set cursor position to zero
  delayMicroseconds(2000);  // this command takes a long time!
}

Das sind ja jedesmal 2 Millisekunden beim Löschen des Displays. Also im Zweifelsfall mal in die Library schauen, ob Du besonders viele Aufrufe von Kommandos hast, die nur mit delay ausgeführt werden, und dann ggf. solche Aufrufe möglichst vermeiden.

Hey jurs,

jurs:
Ansonsten hängt es wohl auch von der ansteuernden Library ab, wie schnell die Ausgabe ist: Normalerweise kann der LCD-Displaycontroller eine Rückmeldung geben, wann ein übermittelter Befehl vollständig ausgeführt ist und der nächste Befehl an den Controller übermittelt werden kann. Die Liquidcrystal-Library geht aber wohl davon aus, dass der dafür notwendige Rückkanal-Pin am LCD gar nicht angeschlossen ist (was wohl auch gängige Anschlußpraxis ist), sonst würden in der Liquidcrystal-Library nicht überall die delayMicroseconds()-Aufrufe stehen. Die warten einfach so lange, bis das lahmste am Markt verfügbare LCD-Modul mit der Kommandoausführung fertig sein müßte, statt abzufragen, wann der Befehl tatsächlich abgearbeitet ist.

Insbesondere wenn Du das Löschkommando beim LCD-Modul verwendest, schlägt das Delay erheblich zu:

Die Ausgabe über Serial war nur ein Workarround zum Testen. Am Ende will ich ein Standalone Gerät ohne Computer daneben haben.

LCD Löschen verwende ich nicht, ich intitalisiere mit

LiquidCrystal lcd(6,5,4,3,2,7,8);

mache dann einmal

 lcd.setCursor(0, 0);
  lcd.print("Speed     Length");
  lcd.setCursor(-4, 2);
  lcd.print("red  green  blue");

und dann bei jeder festestellten Änderung der AnalogIN Werte das hier

lcd.setCursor(0,1);
  lcd.print("                ");
  lcd.setCursor(0, 1);
  lcd.print(sensor0);
  lcd.setCursor(6, 1);
  lcd.print(sensor1);
  lcd.setCursor(12, 1);
  lcd.print(sensor2);
  lcd.setCursor(-4,3);
  lcd.print("                ");
  lcd.setCursor(-4, 3);
  lcd.print(sensor3);
  lcd.setCursor(2, 3);
  lcd.print(sensor4);
  lcd.setCursor(8, 3);
  lcd.print(sensor5);

Frag mich nicht, warum die unteren 2 Zeilen um 4 Zeichen nach rechts verschoben sind und ich den Cursor auf -4 setzen muss, wenn ich 0 meine...aber so funktioniert es...

Es läuft halt parallel FastSPI und ohne LCD Output ist der Ablauf ca. um den Faktor 10 schneller...

Ist I2C Kommunikation eine Lösung, oder nicht?

Grüße, Helmuth

Helmuth:
Es läuft halt parallel FastSPI und ohne LCD Output ist der Ablauf ca. um den Faktor 10 schneller...

Ist I2C Kommunikation eine Lösung, oder nicht?

Keine Ahnung, LCD und I2C hatte ich hier noch nie am Laufen.
Vielleicht weiß jemand anderes was.

Eine mögliche Geschwindigkeitssteigerung in der Liquidcrystal-Lib um den Faktor 2 sehe ich allerdings hier in der pulseEnable-Funktion:

delayMicroseconds(100); // commands need > 37us to settle

Unter der Annahme, dass da wirklich 37 us ausreichen und dass die delayMicroseconds Funktion bis auf +/- 10 us genau arbeitet (es sei denn Du sperrst irgendwie Interrupts in Deinem Programm), würde es jeder Wert über 47us auch tun, z.B.:

delayMicroseconds(50); // commands need > 37us to settle

Würde ich ggf. mal ausprobieren zum Beschleunigen.
Bringt aber nur allenfalls Faktor 2.

Den Vergleich zu I2C kann ich leider auch nicht liefern, aber das Update des LCD würde ich fallbezogen machen. Also nur dann schreiben, wenn eine Änderung am AnalogIn erfolgt und dann auch nur den Wert aktualisieren.

Die -4 resultieren aus dem Adressbereich des Displays. kann in der Liquid Lib angepasst werden.

Wie oft beschreibts du denn das LCD?

ich hatte ein ähnliches Problem, als mir das beschreiben des LCD den Arduino ziemlich ausbremste.

Ich habe es dann dadurch gelöst, dass ich immer erst prüfe, ob sich der Wert geändert hat und nur dann den Wert neu aufs LCD schreiben.

if (altwert1 != neuwert1){
  lcd.setCursor(x1, y1);
  lcd.print(neuwert1);
  altwert1 = neuwert1;
}

und das für jeden zu schreibenden Wert.

Oder du hängst dein LCD-Schreiben in eine 200ms Zeitscheibe, schneller kannst du das sowieso nicht lesen:

if (millis() -lastwrite > 200){
  lastwrite = millis();
  // beschreibe LCD neu
}

guntherb:
Wie oft beschreibts du denn das LCD?

Hi guntherb,
ich schreibe - so wie Du - nur dann aufs LCD, wenn sich der Wert tatsächlich geändert hat, also wenn am Poti gedreht wird...aber da die Eingänge leicht rauschen (oder meine Potis Sch**** sind?), ist das ziemlich oft der Fall...

Gute Idee, das für jeden Eingang einzeln und nicht für alle 6 auf einmal zu checken. Wenn ich die Auflösung mit map vergröbere, geht dafür viel Zeit drauf.

MfG

Randfrage: Wie beschreibst du denn das Display neu? Löschst du das Display jedesmal (was tatsächlich zum Flackern führt) oder überschreibst du einfach die entsprechenden "Zellen"?

sth77:
Randfrage: Wie beschreibst du denn das Display neu? Löschst du das Display jedesmal (was tatsächlich zum Flackern führt) oder überschreibst du einfach die entsprechenden "Zellen"?

Hi sth77,
da der Sensorwert 1-4stellig sein kann, also Reste vom alten Wert sichtbar bleiben würden, wenn der neue weniger Stellen hat, lösche ich die Zeile im Display durch überschreiben mit " ".
Mein Problem ist nicht das Display selbst, sondern das Ruckeln der Ausgabe zum Strip während des (häufigen, wegen rauschen) LCD Schreibens.

MfG

sth77:
Randfrage: Wie beschreibst du denn das Display neu? Löschst du das Display jedesmal (was tatsächlich zum Flackern führt) oder überschreibst du einfach die entsprechenden "Zellen"?

Im Rahmen der Lernkurve wurde das Löschen sehr schnell eliminiert.....
jetzt läuft die Displayfunkion in einer 500ms Zeitschleife UND ich schreibe nur geänderte Werte. Wobei ich wirklich jeden Wert einzeln abfrage und schreibe (ohen vorher zu löschen) Das mache ich aber hauptsächlich um Rechenzeit zu sparen.

Gunther

Edit: Ähem. ich glaube ich war garnicht gemeint... :blush:

Aber:

Helmuth:
ich schreibe - so wie Du - nur dann aufs LCD, wenn sich der Wert tatsächlich geändert hat, also wenn am Poti gedreht wird...aber da die Eingänge leicht rauschen (oder meine Potis Sch**** sind?), ist das ziemlich oft der Fall...

Dann würde es sich sicherlich anbieten, das LCD-schreiben in eine Zeitscheibe zu stecken. Und vielleicht die AD-Eingänge zu filtern.

Hier meine Filterfunktion als Beispiel:

int Average_Input(int SensorNr){
  #define filtershift  6  // festlegen des Filterfaktors  FF = 2^filtershift; Einfluß aktwert = 1/FF 
  int average = 0;
  static unsigned long Average_HighRes[5];
   // gleitender Durchschnitt über die letzten 2^filtershift Werte
  Average_HighRes[SensorNr] = ((Average_HighRes[SensorNr] << filtershift)  - Average_HighRes[SensorNr] 
                                + (long(TempWerte[SensorNr]) << filtershift)
                               ) >> filtershift;  
  average = Average_HighRes[SensorNr] >> filtershift;
  return average;
 }

Gegen Rauschen hilft filtern. Ich würde auch nicht die ganze Zeile löschen, ist ja nicht nötig. Spart somit doppelt Zeit. Und damit's schöner aussieht bau doch eine Funktion, die abhängig vom Wert Leerzeichen voranstellt. Dann ist's rechtsbündig und gefälliger zu lesen.

Ich war zu langsam :smiley:

Neenee, passt schon, danke für die Inspiration!

Helmuth:
ich schreibe - so wie Du - nur dann aufs LCD, wenn sich der Wert tatsächlich geändert hat, also wenn am Poti gedreht wird...aber da die Eingänge leicht rauschen (oder meine Potis Sch**** sind?), ist das ziemlich oft der Fall...

Gude,
ich bin beim Basteln letztens über das gleiche Problem gestolpert. Meine Lösung in pseudo-Code:

define POTI_JITTER = 2

wenn ( abs(alter_wert - neuer_wert) > POTI_JITTER ) {
lcd updaten
}

Damit wird das LCD erst dann upgedated, wenn sich alter und neuer Wert mindestens um 3 unterscheiden.

Grüße, Mathias