Display ILI9341 "delay" problem

Hallo Leute,

leider habe ich wieder ein total blödes Problem.

Da ich Daten in einer sehr geringen Latenz (unter 1ms) kontinuierlich versenden möchte und gleichzeitig einige Daten auf ein Display anzeigen möchte, habe ich durch das Display mit ein paar formen schon über 1ms.

Das NRF24L01 hat mit meinem Code eine Latenz von etwa 400 µS. Das wär soweit im grünen Bereich.

Da das Display grundsätzlich nicht bei jedem loop aktualisiert werden muss, kam mir im Sinn das quasi wie "Blink without delay" aufzubauen. Problem dabei ist das sobald die if Anfrage ausgeführt wird dort die 1ms Latenz zusätzlich anfallen.

Somit hätte ich dann etwa:

400
400
400
1400
400
400
...

Das ist aber für mein Projekt nicht gut.

Danach dachte ich mir das ich den code auf 2 CPUs aufteile.

Einmal mit Serial.Read die Zeit gemessen.. dauert für 10% Text schon über 3 ms

Dann dachte ich mir probier ichs mal mit der Wire Bibliothek.. Dauert genauso lang.

Also kann ich das ganze auch schlecht über 2 CPUs laufen lassen..

Nun meine Frage. Hat eventuell irgend wer eine Idee wie ich diese Latenzen von dem Display weg bekomme?

Aktuell nutze ich einen Teensy 3.2 mit 120 Mhz Takt.

Hier nochmal der Code der schon über 1ms generiert.

#include "SPI.h"
#include "ILI9341_t3.h"

#define TFT_DC  9
#define TFT_CS 15

ILI9341_t3 tft = ILI9341_t3(TFT_CS, TFT_DC);


long previousMillis = 0; 
long interval = 1000;    
float Latenz;

void setup() {
  Serial.begin(9600);
  analogWrite(6,0);
  
  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(ILI9341_BLACK);
}

void loop() {
  unsigned long start = micros();

  unsigned long currentMillis = millis();
  int x = tft.width();
  int y = tft.height();

  int Width = 200;
  int Height = 2;


 int asdf = constrain(map(analogRead(A11), 150, 858,200,0), 0, 200);
 int asdf1 = constrain(map(analogRead(A10), 87, 800,200,0), 0, 200);


tft.fillRect(x/2 - (Width/2), y/2 - (Height/2), asdf, Height, ILI9341_WHITE);

tft.fillRect(x/2 - (Width/2) +asdf, y/2 - (Height/2), Width-asdf, Height, ILI9341_BLUE);



tft.fillRect(x/2 - (Width/2), y/2 - (Height/2) + 50, asdf1, Height, ILI9341_WHITE);

tft.fillRect(x/2 - (Width/2) +asdf1, y/2 - (Height/2) +50, Width-asdf1, Height, ILI9341_RED);

 
 
if(currentMillis - previousMillis > interval) {
int val = random(40);
tft.fillRect(20, 10, val, 8, ILI9341_WHITE);

tft.fillRect(20 +val, 10, 40-val, 8, ILI9341_BLACK);

tft.setCursor(60, 5);
tft.setTextColor(ILI9341_WHITE);
tft.fillRect(60,5, 50, 8, ILI9341_BLACK);
tft.print(micros() - start);

Serial.println(micros() - start);

previousMillis = currentMillis; 
 }

}

Möglicherweise ist meine Idee mit switch/case etwas gagga, aber was in eine Schleife nicht paßt, packt man in viele:

#include <Adafruit_GFX.h>    // Core graphics library
#include "SPI.h"
#include "Adafruit_ILI9341.h"

#define TFT_DC  9
#define TFT_CS 15

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

unsigned long previousMillis = 0;
unsigned long interval = 1000;
float Latenz;
unsigned int x, y, zustand, val;
const unsigned int Width = 200;
const unsigned int Height = 2;

void setup() {
  Serial.begin(9600);
  analogWrite(6, 0);

  tft.begin();
  tft.setRotation(3);
  tft.fillScreen(ILI9341_BLACK);
  x = tft.width();
  y = tft.height();
}

void loop() {
  unsigned long start = micros();
  unsigned long currentMillis = millis();

  int asdf = constrain(map(analogRead(A10), 150, 858, 200, 0), 0, 200);
  int asdf1 = constrain(map(analogRead(A11), 87, 800, 200, 0), 0, 200);
  if (currentMillis - previousMillis > interval) {
    switch (zustand) {
      case 0:
        tft.fillRect(x / 2 - (Width / 2), y / 2 - (Height / 2), asdf, Height, ILI9341_WHITE);
        tft.fillRect(x / 2 - (Width / 2) + asdf, y / 2 - (Height / 2), Width - asdf, Height, ILI9341_BLUE);
        break;
      case 1:
        tft.fillRect(x / 2 - (Width / 2), y / 2 - (Height / 2) + 50, asdf1, Height, ILI9341_WHITE);
        tft.fillRect(x / 2 - (Width / 2) + asdf1, y / 2 - (Height / 2) + 50, Width - asdf1, Height, ILI9341_RED);
        break;
      case 2:
        val = random(40);
        tft.fillRect(20, 10, val, 8, ILI9341_WHITE);
        break;
      case 3:
        tft.fillRect(20 + val, 10, 40 - val, 8, ILI9341_BLACK);
        break;
      case 4:
        tft.setCursor(60, 5);
        tft.setTextColor(ILI9341_WHITE);
        tft.fillRect(60, 5, 50, 8, ILI9341_BLACK);
        break;
      case 5:
        tft.print(micros() - start);
        break;
      case 6:
        Serial.println(micros() - start);
        previousMillis = currentMillis;
        break;
    }
    zustand++;
    if (zustand > 6) zustand = 0;
  }
}

Ich habe ein paar kosmetische Änderungen zur Beschleunigung vorgenommen, so muß eine Konstante nicht immer neu gebildet werden. Ausschlaggebend ist das aber eher nicht, vermute ich.

Dankeschön! Das ist mir heute auch am Abend eingefallen nur wusste ich nicht wie ich das schnell und einfach realisiere und unendlich erweitern kann. DANKE

Damit ist wenigstens die Latenz etwas gemindert. Falls es noch bessere Lösungen gibt würde ich mich sehr darüber freuen! :slight_smile:

AndiIsl:
Falls es noch bessere Lösungen gibt würde ich mich sehr darüber freuen! :slight_smile:

Ob besser weiß ich nicht, aber regelmäßige Messungen kann man auch mittels Timer und Interrupt vornehmen. Das Hauptprogramm muß die erhaltenen Daten natürlich trotzdem schnell genug wegschaufeln.

Klar das wäre natürlich möglich, doch dann habe ich wie oben beschrieben immer verschiedene Latenzen.

Das Problem liegt leider an den SPI Takt der bei dem Teensy bei exakt 30 Mhz liegt. Wenn dieser irgendwie erhöht werden könnte, wären die Latenzen viel geringer.

#define TFT_DC  9
#define TFT_CS 15

Hast Du schon mal Hardware-SPI probiert? Was verträgt ILI9341?

Danke für die Antwort.

Soweit ich weiß sind das schon die Hardware pins bis auf CS das eigentlich pin 10 ist.

Was meinst du mit verträgt?

falls du den Takt meinst, kann ich von einigen anderen quellen raus lesen das 40 mhz kein problem sei.

AndiIsl:
Soweit ich weiß sind das schon die Hardware pins bis auf CS das eigentlich pin 10 ist.

Das sind meine Kandidaten für SPI:
09 DC (data/command select)
10 CS (wählt die Hardware am Bus)
11 DOUT (SPI Daten zur Hardware)
12 DIN (SPI Daten von der Hardware, beim Display nicht immer genutzt)
13 SCK (SPI Bus-Takt)

Du gibst zwei Pins an, die aber mit SPI nur mittelbar etwas zu tun haben. Da Du die anderen Pins nicht angibst, nutzt Du bereits Hardware-SPI!
(SPI Wiring and Test)

// Use hardware SPI (on Uno, #13, #12, #11) and the above for CS/DC
Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC);

Sorry für die Gedankenschleife meinerseits. :-[

Ich meinte die 40 MHz.

:smiley: kein problem!

Weißt du denn wie ich bei dem Teensy 3.2 den SPI Takt auf 40 Mhz bringe? Hab schon einige Nächte damit verbracht das raus zu finden.

Hier ein Beispiel das es anscheinend möglich ist.

ich denke, das dir das:Serial.begin(9600);Probleme bereitet, setze das mal auf 115200

Danke für die Info.

Den seriellen Monitor brauch ich eh nicht wirklich.

Deshalb hat mich das nicht gestört.

Den code macht es trotzdem nicht schneller

  tft.fillRect(x +2,y +18, asdf1, 2, ILI9341_RED);
    tft.fillRect(x +2 + asdf1,y +18, 301 - asdf1, 2, ILI9341_WHITE);

Diese 2 Zeilen brauchen 365 µS.

Das möchte ich eben schneller ablaufen lassen.

AndiIsl:
Weißt du denn wie ich bei dem Teensy 3.2 den SPI Takt auf 40 Mhz bringe?

Leider nicht, aber ich hätte einen Ansatzpunkt in der Bibliothek für Dich:

static inline void spi_begin(void) {
  // max speed!
  SPI.beginTransaction(SPISettings(24000000, MSBFIRST, SPI_MODE0));
}

Also in meiner lib. steht da eh schon 30000000.

Angeblich das Maximum. Wenn ich auf 40000000 stell bleiben die Latenzen auch absolut gleich. nur wenn ich unter 30000000 geh werden die Latenzen höher...

Aber wie in einigen Videos zu sehen kann man irgendetwas machen damit es dann mit 40mhz läuft.

Dann mußt Du etwas tiefer graben, ungefähr dort: c:\Program Files (x86)\Arduino165\hardware\teensy\avr\libraries\SPI\SPI.h:

	SPISettings() {
		init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0);
	}

Probier ich gleich mal aus!

edit:

Alles geändert auf 40000000 (auch in der ili9341_t3.cpp) doch leider keine Veränderung.

Ich finde folgenden Text:

// We find the fastest clock that is less than or equal to the
// given clock rate. The clock divider that results in clock_setting
// is 2 ^^ (clock_div + 1).

Ich spekuliere mal, 30 MHz ist die größte mittels Teiler erreichbare Frequenz. Also 120/2>40 → 120/4<40 der Teiler von 4 wird genommen. Wenn das Video nicht lügt, arbeitet es möglicherweise mit einem kleineren CPU-Takt. 80/2<=40

Na da liegst du denk ich leider falsch.

der SPI takt wird nicht aus dem CPU Takt geteilt, sondern aus dem F_BUS takt der bei einer CPU Taktrate von 120 Mhz bei 60 Mhz liegt. Wie du schon richtig geraten hast ist der kleinste Teiler 2. Also 60/2 = 30.

Somit müsste ich irgendwie den F_BUS Takt auf 80 Mhz bringen. 80/2=40. Nur leider hab ich keine ahnung wie man das macht.

AndiIsl:
Na da liegst du denk ich leider falsch.

Nicht ohne Grund habe ich “Ich spekuliere mal …” formuliert. Mein teensy wird normal mit 96 MHz betrieben, dann eine Wunschfrequenz von 50 MHz, 96/2<50 würde 48 MHz möglich machen, wäre es F_CPU. So würde ich es mal probieren, entgegen aller Theorie.

Nur zur Sicherheit: Du verwendest die Bibliotheken für den teensy? Bei mir hat der Compiler gestern die für den UNO erwischt, drum komme ich drauf.

Schönes WE und fröhliches Grübeln :slight_smile:

Ich verwende die Teensy Bibliotheken ja.

Dankeschön agmue :smiley: dir auch ein schönes Wochenende.