Forum: Mikrocontroller und Digitale Elektronik ILI9341 SPI Pixel auslesen geht teilweise. ID4 auslesen nicht


von Felix N. (felix_n888)


Angehängte Dateien:

Lesenswert?

Guten Abend,

Ich habe an einem Raspberry Pi Zero W ein 2.8 Zoll TFT Display verbaut 
mit ILI9341 als Controller. Ich weiß man könnte auch ein HDMI Display 
dran anschließen etc.. und eigentlich ist der RasPi auch absolut 
Overkill aber egal.

Das Display anzusteuern ist absolut kein Problem zumindest wenn es um 
das beschreiben des Displays geht das funktioniert nämlich super. Ein 
teil der GUI läuft ja auch schon auf dem Display. Verwendet wird der AUX 
SPI1 des Raspberry Pis. Angeschlossen habe ich alle Leitungen wie 
CS,D/C,SCK,MOSI,MISO,RESET. Es ist ein Display mit XPT2046 
Touchcontroller welcher ebenfalls angeschlossen ist und ohne Probleme 
funktioniert. Gelesen wird auch nur mit 8MHz! SPI Frequenz darf ja 
eigentlich auch nicht mehr als 10MHz betragen ...

Kommen wir zum Problem: Ich möchte gerne aus dem internen Grafikspeicher 
des ILI9341 Pixel auslesen um ein bestimmten Bereich wieder herzustellen 
nachdem eine Eingabe vom Nutzer erfolgt ist. Das Problem ist das ich 
zwar den Pixel lesen kann jedoch nur einmal danach kann ich ruhig 
irgendein anderen Pixel versuchen zu lesen es wird jedoch immer der Wert 
des zuerst gelesen Pixel zurückgeben. Also das pixel lesen vom Display 
funktioniert teilweise.

Ich habe auch versucht zB. die ID4 auszulesen(Gibt dann ja 0x9341 
wieder) jedoch bekomm ich da nur 00 00. Nie etwas anderes.

Mit einem STM32 Nucleo Board und der eTFT Library hab ich es auch nicht 
geschafft die ID4 auszulesen andere Befehle(Auch aus der Level1 
Befehlsgruppe) funktionieren ebenfalls nicht.

Ich muss sagen ich verstehe das Datenblatt auch da nicht so ganz. Es 
gibt Serial Interface I und II welche durch die IM Pins fix kodiert 
sind, wie die bei diesem Display beschaltet sind? Keine Ahnung. Da aber 
der SDO(MISO) Pin rausgeführt ist gehe ich von 4-wire Serial Interface 
II aus mit D/C als Pin und nicht als Bit.

Geschrieben ist das Programm in C mit der BCM2835 library um die 
Prozessorfunktionen anzusteuern. Ich hab auch mal aufm Logic analyzer 
geschaut was dort so ankommt jedoch passt das von der Übertragung nur 
der ILI9341 gibt einfach keine Daten zurück.

Hier mal die readPixel funktion:
1
void ili9341_readPixel(uint16_t x, uint16_t y) {
2
    bcm2835_gpio_fsel(RPI_BPLUS_GPIO_J8_36, BCM2835_GPIO_FSEL_OUTP);    //Manual CS control - To keep CS continues low during the operation
3
    bcm2835_gpio_write(RPI_BPLUS_GPIO_J8_36, LOW);                      //CS Low
4
5
    //Set read address
6
    ili9341_writeCommand(0x2A);
7
    ili9341_writeDataDWord((x << 16) + x); //32-Bit
8
    ili9341_writeCommand(0x2B);
9
    ili9341_writeDataDWord((y << 16) + y); //32-Bit
10
11
    ili9341_writeCommand(0x2E);     //Read from GRAM
12
13
    //Write D/C pin High
14
    bcm2835_gpio_write(DC_PIN, HIGH);
15
    dcHigh = true;
16
17
    //Setup SPI clock to 8MHz
18
    bcm2835_aux_spi_setClockDivider(bcm2835_aux_spi_CalcClockDivider(ILI_CLOCK));
19
    bcm2835_aux_spi_setCS(2);
20
21
    char rxBuf[4] = { 0x00, 0x00, 0x00, 0x00 };
22
    bcm2835_aux_spi_transfern(rxBuf, sizeof(rxBuf));
23
24
    bcm2835_gpio_write(RPI_BPLUS_GPIO_J8_36, HIGH);                     //CS High to end transmission
25
    bcm2835_gpio_fsel(RPI_BPLUS_GPIO_J8_36, BCM2835_GPIO_FSEL_ALT4);    //Back to auto CS
26
27
    printf("Read Pixel from X:%d Y:%d - Red: %02X - Green: %02X - Blue: %02X - 565: %X\r\n", x, y, rxBuf[1], rxBuf[2], rxBuf[3], ((rxBuf[1] & 0xF8) << 8) | ((rxBuf[2] & 0xFC) << 3) | (rxBuf[3] >> 3));
28
}

Und auch nochmal hier die readID4() funktion:
1
void ili9341_readID(void) {
2
3
    bcm2835_gpio_fsel(RPI_BPLUS_GPIO_J8_36, BCM2835_GPIO_FSEL_OUTP); //Manual CS control
4
    bcm2835_gpio_write(RPI_BPLUS_GPIO_J8_36, LOW); //CS Low
5
6
    ili9341_writeCommand(0xD3); //Command 0xD3 read ID4 - writeCommand sets DC to low for command.
7
8
    ili9341_readByte(); //Dummy read - readBytes set D/C to high for data
9
    ili9341_readByte(); //First data byte all zeros
10
    uint8_t id1 = ili9341_readByte();   //ID part 0 (should be 0x93)
11
    uint8_t id2 = ili9341_readByte();   //ID part 1 (should be 0x41)
12
13
    bcm2835_gpio_write(RPI_BPLUS_GPIO_J8_36, HIGH); //CS High
14
    bcm2835_gpio_fsel(RPI_BPLUS_GPIO_J8_36, BCM2835_GPIO_FSEL_ALT4); //Back to auto CS
15
16
    printf("ID is: %X %X\r\n", id1, id2);
17
}

Hab mich bereits auch schon schlau gelesen das es scheinbar Probleme mit 
dem auslesen im SPI Modus gibt hab jedoch nichts gefunden war für mich 
hilfreich gewesen ist. Die IM pins scheinen ja wohl richtig zu sein wenn 
man mit readPixel einmalig arbeitet kommen ja auch die richtigen Daten 
von dem Pixel an X,Y zurück so wie es soll nur funktioniert readPixel 
kein zweites mal obwohl die Daten richtig zum Display gesendet werden 
das Display antwortet einfach neuert mit den gleichen Daten.

Ich komm da nicht mehr hinter was da los ist. Hat einer vielleicht von 
euch ne Idee woran es liegen könnte?

Mfg und schönen Abend noch

von Harry L. (mysth)


Lesenswert?

Warum benutzt du keinen Framebuffer-Treiber?
Den hab ich schon mehrfach für ili9341 genutzt, und der funktioniert 
ganz hervorragend, und das mit jeder Software, die Framebuffer 
unterstützt.

https://github.com/notro/fbtft/wiki

von Felix N. (felix_n888)


Lesenswert?

Harry L. schrieb:
> Warum benutzt du keinen Framebuffer-Treiber?

Hallo,

Framebuffer-Treiber? Das heißt das Display wird zum Hauptdisplay/Monitor 
für das Linux Betriebssystem auf dem Raspberry? Falls ja ist das nicht 
gewollt. Zumal ich eh nur eine Raspbian Version ohne Grafische 
Oberfläche verwende und alles andere via SSH mache.

Das Projekt sollte ursprünglich auf einem STM32F446RE umgesetzt werden 
da ich aber auch noch ein RF Signal erzeugen muss im Bereich von 170MHz 
war dieses auf einem Raspberry mit Hilfe des rpitx Programm leichter 
umzusetzten als mit externer Hardware zum STM32 dazu und da der Preis 
unterschied zwischen einem Nucleo 64 Board(16€) und dem Raspberry Pi 
Zero(22€) nicht so groß war hab ich mich dieses mal für den Raspberry 
entschieden.

An sich könnte ich auch auf die lese Funktionen des ILI9341 verzichten 
in dem ich einfach ein 16 Bit Array anlege mit einer Größe von 320x240. 
Dann hätte ich den Grafikspeicher selbst im Arbeitsspeicher des 
Raspberrys(Der Pi hat ja mehr als genug RAM für sowas) und die 
Grafikfunktionen wie drawPixel, drawRect fillRect ... schreiben dann 
halt nicht nur auf das Display via SPI sondern halt auch noch in dieses 
Array mit rein. In Bezug auf Geschwindigkeit beim auslesen der Pixels 
geht dann nicht mehr schneller :) Als alle Daten wieder via SPI 
abzufragen.

Nur es hatte mich halt verwundert das ich halt so gut wie keine Daten 
vom Display via SPI beziehen kann obwohl dieses eigentlich möglich 
seinen müsste. Mit einem ILI9486 den ich mal im 8-Bit Parallel Modus 
angesteuert hatte war das kein Problem alle Register auszulesen. Mit dem 
ILI9341 im SPI Interface hab ich es noch nicht wirklich geschafft es 
auszulesen.

Aber das scheint ja wohl ein Problem zu sein das andere auch haben mit 
dem Auslesen im SPI Modus des Displays. Beiträge gibts dazu ja genug 
teilweise auch mit Codebeispielen mit diesem Ominösen 0xD9 Befehl der ja 
offiziell nicht mal dokumentiert(Bei der Adafruit lib für den ILI9341 
ist er als "Woo sekret command?" kommentiert) ist im Datenblatt(Er 
funktioniert bei mir auch nicht) aber anderen scheint der wohl geholfen 
zu haben Register auszulesen.

Mfg

von Εrnst B. (ernst)


Lesenswert?

Felix N. schrieb:
> Kommen wir zum Problem: Ich möchte gerne aus dem internen Grafikspeicher
> des ILI9341 Pixel auslesen um ein bestimmten Bereich wieder herzustellen
> nachdem eine Eingabe vom Nutzer erfolgt ist.

Wenn das Limitierende die SPI-Geschwindigkeit ist, ist's vermutlich 
schneller einfach den Bereich neu zu Zeichnen, ohne vorher nachzuschauen 
welche Pixel sich geändert haben.

Felix N. schrieb:
> An sich könnte ich auch auf die lese Funktionen des ILI9341 verzichten
> in dem ich einfach ein 16 Bit Array anlege mit einer Größe von 320x240.
> Dann hätte ich den Grafikspeicher selbst im Arbeitsspeicher

Du kannst dir auch anschauen, wie das z.B. lvgl gelöst hat. Das braucht 
keinen kompletten Framebuffer im RAM, aktualisiert nur geänderte 
Bereiche, und organisiert die Updates so, das man sie einfach per DMA 
zum Display schieben kann. Optional mit zwei Buffern, damit der eine 
berechnet werden kann, während der andere gerade rausgeschrieben wird.

von Harald K. (kirnbichler)


Lesenswert?

Felix N. schrieb:
> Framebuffer-Treiber? Das heißt das Display wird zum Hauptdisplay/Monitor
> für das Linux Betriebssystem auf dem Raspberry?

Nein, das bedeutet nur, daß Deine Software ein Abbild des 
Bildschirmspeichers des Displays vorhält, und jeden Schreibvorgang 
sowohl auf das Display als auch auf dieses Abbild durchführt. Bei 
Lesezugriffen aber wird nicht auf das Display, sondern dieses Abbild 
zugegriffen.

Es ist auch oft schneller, den kompletten Puffer an das Display zu 
senden als einzelne Pixel zu adressieren, so kann auch das Beschreiben 
nur über das Abbild erfolgen, während eine wo auch immer laufende 
Funktion zum Aktualisieren des Displays angestoßen wird, die (z.B. via 
DMA) den Puffer an das Display rausschaufelt.

von Walter T. (nicolas)


Lesenswert?

ID4 auslesen geht nicht: Ja, das ist bei manchen Displaymodulen 
"normal". Es hängt vom Leiterplatten-Layout ab (Siehe Datenblatt, 
Stichwort "extended command list").

: Bearbeitet durch User
von J. S. (jojos)


Lesenswert?

meistens machen aber da die Displays mit parallelem Interface Probleme, 
für die Arduino Kompatibilität haben die Levelshifter für 5V drauf und 
die sind meist nicht bidirektional. Bei dem SPI gibt es noch die 
Variante SDA oder SDI/SDO, aber wenn diese Signale am Stecke so heißen 
dann sollte das eigentlich auch so konfiguriert sein (IM3=1).
Beim eTFT ist ein Beispiel ScreenCapture dabei, das soll mit ILI9341 und 
STM32 laufen. Die Vielfalt an ILI9341 Displayboards ist aber recht groß.

Aber da es das Display nur langsam macht und die Ausgaben flackern wenn 
man pixelweise Bereiche überschreibt, mache ich das auch nur noch mit 
Framebuffer. Und auch mit lvgl, für Raspberry gibt es da auch Beispiele:
https://github.com/lvgl/lv_port_linux_frame_buffer

: Bearbeitet durch User
von Walter T. (nicolas)


Lesenswert?

Felix N. schrieb:
> Das Problem ist das ich
> zwar den Pixel lesen kann jedoch nur einmal danach kann ich ruhig
> irgendein anderen Pixel versuchen zu lesen es wird jedoch immer der Wert
> des zuerst gelesen Pixel zurückgeben.

Nachtrag: Ich habe mir Dein Timing noch nicht genau angeschaut, aber was 
mir mal aufgefallen ist: Beim Schreiben vertragen die ILI9341, die nativ 
über SPI angesteuert werden (es gibt auch welche, die Parallel mit zwei 
Schieberegistern am SPI angeschlossen sind :( ), eine deutlich höhere 
Datenrate als beim Lesen.

: Bearbeitet durch User
von Thomas F. (igel)


Lesenswert?

Felix N. schrieb:
> void ili9341_readPixel(uint16_t x, uint16_t y) {

>     //Set read address
>     ili9341_writeCommand(0x2A);
>     ili9341_writeDataDWord((x << 16) + x); //32-Bit
>     ili9341_writeCommand(0x2B);
>     ili9341_writeDataDWord((y << 16) + y); //32-Bit
>     ili9341_writeCommand(0x2E);     //Read from GRAM

Du setzt hier ein 1x1 Pixel großes Fenster und liest dann dieses eine 
Pixel aus.
Ich habe dich so verstanden dass du ganze Bereiche des Displays lesen 
willst. Dann setze doch mal ein größeres Lesefenster und lese alle 
Pixel(Bytes) in einem Rutsch aus. Vielleicht funktioniert das besser?

von Felix N. (felix_n888)


Lesenswert?

Εrnst B. schrieb:
> Wenn das Limitierende die SPI-Geschwindigkeit ist, ist's vermutlich
> schneller einfach den Bereich neu zu Zeichnen, ohne vorher nachzuschauen
> welche Pixel sich geändert haben.

Geschwindigkeit ist tatsächlich nicht das Problem naja das SPI Interface 
ist im Verhältnis zum Parallel betrieb natürlich deutlich langsamer. 
Offiziell sind es laut Datenblatt nur max 10MHz an SPI Takt erlaubt. 
Wobei die Frequenz selbst garnicht angegeben ist sondern nur die 
maximale Periodendauer von 100ns? Das wären ja dann 10MHz.

Da der AUX SPI1 Bus am Raspberry vom APB Bus angetrieben wird welcher 
auf 250MHz laufen sollte und kann man mit dem Prescaler von 2 bis 65536 
fast alles einstellen. Wobei der Entwickler der BCM2835 C Bibliothek zu 
nicht mehr als über 60MHz rät da wohl dann die MISO/MOSI Leitungen 
anfangen zu schwingen. 62.5MHz funktionieren bei mir aber ganz gut

Ich habe die maximale Geschwindigkeit des Displays mal ausgetestet. Bis 
62,5MHz läuft das Display problemlos und ohne Pixelfehler bei 80MHz 
werden teilweise rote Pixel falsch dargestellt. Bei 125MHz(Prescaler 2) 
nun ja es kommt alles an aber alles im bunten Farbenmischmasch :)

Lesen tute ich aber nur mit 8MHz tatsächlich macht es aber kein 
Unterschied ob ich mit 62.5MHz die Lesebefehle ausführe oder mit 8MHz es 
kommt wenn was zurückkommt immer das gleiche zurück.

Harald K. schrieb:
> Bei
> Lesezugriffen aber wird nicht auf das Display, sondern dieses Abbild
> zugegriffen.

Diese Idee hatte ich ja auch schon mal oben erwähnt gehabt hat natürlich 
auch ein gewaltigen Geschwindigkeitsvorteil:

Felix N. schrieb:
> An sich könnte ich auch auf die lese Funktionen des ILI9341 verzichten
> in dem ich einfach ein 16 Bit Array anlege mit einer Größe von 320x240.
> Dann hätte ich den Grafikspeicher selbst im Arbeitsspeicher des
> Raspberrys

Harald K. schrieb:
> Es ist auch oft schneller, den kompletten Puffer an das Display zu
> senden als einzelne Pixel zu adressieren

Ein komplettes löschen bei 62.5MHz dauert ca. 47ms bei diesem Display. 
Es ist schnell man sieht es auch noch auf dem Display es ist aber 
vertretbar und ich habe kein Problem damit das man es kurz sieht das 
sich das Display ändert.

Wenn ich dann bestimmte Bereiche ändern will zB. oben linkes wird die 
Uhrzeit dargestellt dann schreibe ich nur den Text an der entsprechenden 
Stelle neu das geht dann so brutal schnell weil der adressierte Bereich 
relativ klein ist das man das nicht sieht das sich da was geändert hat. 
Also kein flackern zu erkennen.

Walter T. schrieb:
> Es hängt vom Leiterplatten-Layout ab

Ja das hab ich auch schon mehrmals gelesen im Netz. Hat ja zum Teil auch 
was mit diesen IM pin zutun. Da das Display aber ein SDI sowie SDO pin 
hat und aus dem SDO pin die richtigen Daten teilweise rauskommen denke 
ich das IM=1110 sind wird sprich "4-wire 8-bit data serial interface Ⅱ"

Weil dann hätte ich folgende Pins zur Ansteuerung des Displays: 
SCL,SDI,D/CX,SDO, CSX

Und das sind auch die Pins die am Display rausgeführt sind.

Walter T. schrieb:
> Siehe Datenblatt,
> Stichwort "extended command list"

Tatsächlich kann ich den Punkt "Extended command list" nicht im 
Datenblatt finden

https://cdn-shop.adafruit.com/datasheets/ILI9341.pdf

Ich verstehe aber auch nicht was die Unterschied zwischen den 
sogenannten "Level1" und "Level2" Befehlen ist. Also das die genau von 
einander unterscheidet, bzw kann ich im Datenblatt nichts finden oder 
habs überlesen :P. Read ID4 gehört zu den Level2 Befehlen. Aber auch die 
Level1 befehle wie Read ID1(0xDA) oder Read Display Identification
Information(0x04) bekomm ich keine Antwort vom Display mit sinnvollen 
Daten in der regel ist alles 0x00.

Ich bin aktuell noch nicht Zuhause kann später nochmal ein Bild vom 
Logic analyzer hochladen bei den Level1 befehlen.

J. S. schrieb:
> Beim eTFT ist ein Beispiel ScreenCapture dabei, das soll mit ILI9341 und
> STM32 laufen. Die Vielfalt an ILI9341 Displayboards ist aber recht groß.

Ah ok das ist interessant werde ich heute Nachmittag mal ausprobieren, 
dazu berichte ich noch später!

Thomas F. schrieb:
> Du setzt hier ein 1x1 Pixel großes Fenster und liest dann dieses eine
> Pixel aus.

Genau weil die Funktion readPixel() von einen Pixel auslesen soll. 
Später wollte ich noch eine readPixels() Funktion machen.

Das komische ist ja wenn zB. readPixel(0, 0) mache und dort der Pixel 
rot ist dann bekomme ich 0xF800 zurück -> PASST!

Wenn ich dann nochmal später im Programm readPixel(10, 10) mache zB. und 
der Pixel weiß ist bekomme ich trotzdem 0xF800 zurück immer das was 
zuvor mit dem ersten readPixel gelesen wurde. Lasse ich das erste 
readPixel(0, 0) weg und hab somit nur readPixel(10, 10) drin stehen dann 
bekomme ich als Resultat 0xFFFF was korrekt ist wenn ich aber danach 
nochmal readPixel(0, 0) der ja rot ist bekomme ich wieder 0xFFFF.

Absolut komisch. Ich lad nachher nochmal ein Bild vom Logic analyzer 
hoch

Thomas F. schrieb:
> Ich habe dich so verstanden dass du ganze Bereiche des Displays lesen
> willst. Dann setze doch mal ein größeres Lesefenster und lese alle
> Pixel(Bytes) in einem Rutsch aus. Vielleicht funktioniert das besser?

Ja will ich später auch das ist korrekt. Ich werde das nachher mal 
ausprobieren mal schauen was da rauskommt.

Für den Tipp mit lvgl das schaue ich mir mal die Tage an.

Mfg

von Walter T. (nicolas)


Lesenswert?

Felix N. schrieb:
> Tatsächlich kann ich den Punkt "Extended command list" nicht im
> Datenblatt finden

Seiten 11 und 85. Hier heißt es wohl "Extended Command Set", bei einigen 
ILI9xxxx-Schwester-Bausteinen heißt die Tabelle "Extended Command List".

24 MHz SPI-Takt bei bei mir (STM32F446RE) immer deutlich zu schnell beim 
auslesen. Schreiben ging, fürs lesen musste ich heruntertakten, ich 
glaube auf 12 MHz. (Bei den Zahlen bin ich mir nicht 100% sicher - ich 
habe nur noch im Kopf, dass Schreiben doppelt so schnell wie lesen ging. 
Nachgucken kann ich erst heute abend.)

: Bearbeitet durch User
von Felix N. (felix_n888)


Angehängte Dateien:

Lesenswert?

So Hallo nochmal,

Walter T. schrieb:
> Extended Command List

Ah okay ja dann hab ich es wohl einfach übersehen. Achso das wird also 
durch den EXTC pin per Hardware bestimmt ob man die Register auslesen 
kann. Nun ja gut das ich jetzt sagen kann das ich es tatsächlich 
geschafft habe die ID4 auszulesen und zwar mit diesem unbekannten 0xD9 
Command.

Er ist zwar nicht dokumentiert und auch in einem älteren 
Datenblatt(V1.01) ist dieser Befehl nicht aufgelistet. Meine Vermutung 
ist das dieser Befehl aus einen Register Daten extrahiert. Weil man muss 
nachdem 0xD9 Befehl noch ein index mitschicken. Die eTFT Library hat 
diesen Befehl als readCommand8() implementiert. Wenn ich den Befehl 
jetzt auf 0xD3(Read ID4) anwende mit Index 2 und 3(Für Byte 2 und 3) 
dann bekomme ich für Index 2 = 0x93 und für Index 3 = 0x41.

Also die ID4. Bei Level1 Befehlen wie zB. Read Display ID Infos(0x04) 
funktioniert er nicht dort kommt einfach nur 0x00 wieder zurück.

J. S. schrieb:
> Beim eTFT ist ein Beispiel ScreenCapture dabei, das soll mit ILI9341 und
> STM32 laufen.

Das das läuft tatsächlich! Ich habs auf einem STM32F446RET6 gespielt und 
siehe im Anhang die Pixel konnten ausgelesen werden und mittels 
Processing 4.3 dargestellt werden. Also das Display kann schon Daten 
ausgeben!

Thomas F. schrieb:
> Vielleicht funktioniert das besser?

Jein, also ich habe jetzt eine readPixels() Funktion hinzugefügt welche 
ein Block an pixels ließt. Das gute zu erst es funktioniert und die 
Pixeldaten stimmten auch, jetzt das schlechte wird die Funktion über den 
genau gleichen Pixelblock nochmal ausgeführt kommen andere teilweise 
korrekte Daten zurück siehe Bild "Output" von der SSH Konsole.

Das verstehe ich halt nicht. Ich habe per Logic analyzer überprüft ob da 
was falsch gesendet aber wird es nicht die Adresse etc... wird alles 
korrekt wie beim ersten readPixels korrekt übertragen. Nur das Display 
antwortet komplett anderes.

Auch mit der readPixel Funktion wird es nicht besser. Der erste 
readPixel Funktion Aufruf liefert korrekt die richtigen Pixeldaten der 
zweite liefert die gleichen vom ersten readPixel obwohl der Pixel an der 
angegeben X,Y komplett andere Werte hat. Und der dritte Aufruf liefert 
was komplett falsches.

Ich habe das Gefühl das der interne address pointer im ILI9341 selbst 
nicht zurückgesetzt wird wenn man die Adresse neu schreibt mit 0x2A und 
0x2B. Hab aber auch nichts darüber gefunden das man das nach einem Read 
zurücksetzten muss oder so.

Auch die readPixel und rectRectRGB Funktionen aus der TFT_eSPI Library 
sind gleich aufgebaut wie meine machen aber auch nichts anderes.

Was mir noch aufgefallen ist im Logic analyzer, was ich mir auch nicht 
erklären kann, sobald das Display einmal Daten geschickt hat egal welche 
werden diese IMMER bei einem Transfer wieder auf der MISO gesendet siehe 
dazu den Anhang "setAddr MISO Daten" dort wird der Befehl 0x2A also die 
Adresse gesetzt sobald der D/C pin auf Daten schaltet und die 
eigentliche Adresse kommt(0x0A und 0x13) wird auf MISO wieder 0xFC 
ausgegeben und beim nächsten Befehl von 0x2B wieder.

Das kann doch nicht richtig sein, da dürfte der Controller eigentlich 
gar nicht antworten, auch im Datenblatt ist vermerkt das dort keine 
Daten zurückgeben werden.

Ich hatte eben nochmal kurz in den Code für den ILI9486 welcher auf 
einem STM32 läuft reingeschaut. Das Display war im 8-Bit 8080 parallel 
Modus angesteuert. Da hatte ich den Code so geschrieben das bevor das 
Display überhaupt initialisiert wird erstmal die ID4 ausgelesen wird um 
zu bestätigen das es das richtige Display ist. Dort habe ich das aber 
ganz normal gemacht wie es das Datenblatt vorsieht 0xD3 als Befehl 
senden. Einen Dummyread danach 3x reads und dann hatte ich meine ID, hat 
immer funktioniert nie Probleme gemacht das Ding ist beim Kollegen seit 
knapp 1 Jahre als Hardware Monitor im Einsatz.

Hier nochmal der Code der ausgeführt wird in der main und die Ausgabe in 
der Konsole erzeugt:
1
  //Read Level2 command "Read ID4" returns 0x9341
2
  ili9341_readRegister(0xD3, 2); //Returns 0x93 Byte 2 of 0xD3
3
  ili9341_readRegister(0xD3, 3); //Returns 0x41 Byte 3 of 0xD3
4
5
  //Read Level1 command "Read display identification information"
6
  //ili9341_readRegister(0x04, 1); //ID1
7
  //ili9341_readRegister(0x04, 2); //ID2 Funktioniert nicht!
8
  //ili9341_readRegister(0x04, 3); //ID3
9
10
  ili9341_fillRect(10, 10, 10, 1, RED);
11
  ili9341_fillRect(10, 11, 5, 1, GREEN);
12
  ili9341_fillRect(15, 11, 5, 1, BLUE);
13
  ili9341_fillRect(10, 12, 10, 1, WHITE);
14
15
  ili9341_readPixels(10, 10, 10, 3); //Reads from X:10 Y:10 auslesen des zur bearbeiten Bereichs
16
17
  ili9341_readPixel(10, 10); //Should return 0xF800
18
  ili9341_readPixel(16, 11); //Should return 0x001F
19
  ili9341_readPixel(17, 12); //Should return 0xFFFF
20
21
  ili9341_readPixels(10, 10, 10, 3); //Reads from X:10 Y:10

Mfg

von Wastl (hartundweichware)


Lesenswert?

Felix N. schrieb:
> So Hallo nochmal,

Ausdrücklichen Dank an dich für die Hinweise zum Auslesen
der Display-Identifikation. Funktioniert, wäre ich nicht
draufgekommen. Endlich kann ich das auch mal ....

von Felix N. (felix_n888)


Lesenswert?

Guten Abend nochmal an alle,

Ich habe heute nochmal ein bisschen weiter experimentiert mit dem 
Display und vergesst mal fast alles was ich im letzten Beitrag bzgl. 
readPixel und readPixels geschrieben habe.

Ich habe hier 2 gleiche Display(Zumindest dachte ich dieses) vom Typ 
ILI9341 liegen sehen exakt gleich aus wie auf dem Bild was ich 
hochgeladen habe. Beide habe ich 2018 auf Amazon bestellt kamen auch 
beide vom gleichen Verkäufer in einem Paket das weiß ich noch.

Nur hatte ich eins am Raspberry angeschlossen und das andere(War sogar 
noch eingeschweißt in der ESD Verpackung) am STM32 angeschlossen. Weil 
dachte es seien beide gleich. Nun ja das Display welches eingeschweißt 
war funktioniert am STM32 perfekt und auch am Raspberry funktioniert es 
so wie es soll heißt die oben beschreibenden Probleme mit readPixel und 
readPixels treten nicht mehr auf! Das andere Display funktioniert am 
STM32 nicht richtig! Das screenshot capture Skript läuft nicht sauber 
durch teilweise werden gar keine Pixel geladen manchmal nur halb ... Es 
scheint wohl so als wäre das Display defekt zumindest was das lesen 
angeht. Beim darstellen von Pixeln und beschreiben von Registern scheint 
es keine Probleme zu geben die wären sonst in den letzten 2 Wochen 
aufgefallen beim Testen

Toll hat jetzt nur fast 3 Tage gedauert um das herrauszufinden.

Ich habe noch eine Anmerkung zu dem Memory Read(0x2E) Befehl. Es steht 
nicht direkt beim Memory Read Command drin sondern beim NOP 
Command(0x00) wenn man fertig ist mit lesen beendet JEDER Befehl laut 
Memory Read Beschreibung den Lesevorgang man kann ein NOP senden(Vllt. 
soll man das auch?) um das lesen zu stoppen. Das gleich gibt auch für 
den Memory Write(0x2C) Befehl..

Ich habe nachdem ich die Pixeldaten in readPixel und readPixels fertig 
gelesen habe nochmal ein NOP Befehl gesendet, danach bliebt das ominöse 
Verhalten der MISO Leitung(Siehe Anhang letzter Beitrag setAddr MISO 
...) weg. Sie gibt also nicht mehr die selben Daten bei jeden 
Befehl/Daten aus -> TOLL!

Das ID lesen bleibt mir noch immer ein Rätsel vllt. kann ja einer von 
euch das mal für mich ausprobieren mit seinem ILI9341 Display und 
schauen ob er was anderes bekommt. Es geht dabei um den Befehl 0x04. 
0x04 ist "Read display identification information" und sendet in der 
Theorie laut Datenblatt ein Dummy und 3x IDs. Die drei ID's bekommt man 
aber auch mit Read ID1(0xDA), Read ID2(0xDB) und Read ID3(0xDC). Und es 
handelt sich dabei um USER IDs. Der 0x04 Befehl scheint die IDs somit 
nur als ein Befehl auszugeben in der Theorie.

Heißt man muss die selber Programmieren mit dem NV Memory Befehlen! 
Standardmäßig sind ID1 = 0x00 und ID3 = 0x00. Bei ID2 bin ich mir nicht 
sicher es soll sich da um eine Revision ID handeln im Bereich von 0x80 
bis 0xFF. Ob jetzt jedoch der Default Value von ID2 = 0x80 ist bin ich 
mir nicht sicher ich würde sagen laut Datenblatt ja.

Aber ich lese da immer nur 0x00 zurück. Egal ob direkt oder mit diesem 
geheim 0xD9 Befehl ...

Vielleicht kann ja jemand das von euch mal testen was er bei dem 
Read-ID2 Befehl bekommen würde.

Ich habe auch alle anderen Lesebefehle die so direkt mit "Read ..." im 
Datenblatt gestanden ausprobiert. Zb. liefert mit der 0x0C Befehl (Read 
Display Pixel Format) 0x05 zurück. Was 16bit/pixel entspricht und auch 
passt.

Jedoch ist mir da eins aufgefallen laut Datenblatt ist das erste Byte 
ein Dummy-Read und enthält keine Daten erst das zweite Byte enthält die 
Daten. Komischerweise ist es bei mir genau anderes rum. Das "Dummy-Byte" 
laut Datenblatt enthält bei mir die 0x05 und das zweite Byte nix. Ich 
kann auch nur ein Byte lesen(Sprich laut DB das "Dummy Byte") und 
bekomme die richten Daten.

Das gleiche trifft auch auf die Read Display Status/Power/Image Befehle.

Ich bin mir auch nicht sicher ob man jedes Register lesen darf. Zum 
Beispiel bei dem "Positive Gamma Correction(0xE0)" Register dort 
schreibt man ja beim Init die Gamma Werte rein. Wenn ich das jetzt lesen 
würde via SPI muss ich ja ein Byte senden um eins zu empfangen das würde 
ja bedeuten das ich die vorherigen Daten überschreibe also kann ich das 
eigentlich nicht auslesen. Mit dem 0xD9 Befehl geht das dann aber wieder 
doch. Wenn ich den Sende und auf das Gamma Register anwende und den 
Index entsprechend setzte werden mir die Werte wiedergeben welche ich 
zuvor beim Init festgelegt habe ins Register.

Also erstmal danke an euch die mir geholfen haben, das Thread ist 
hiermit eigentlich erledigt. Pixel auslesen geht und die ID4 lesen geht 
auch. Vielleicht hilft dem einen oder anderen dieser Beitrag ja auch 
weiter. Ganz zu 100 % Zufrieden bin ich zwar nicht da ich noch nicht 
alles so ganz zu 100 % nachvollziehen kann. Ist aber so.

Vielleicht kann ja der ein oder andere wenn er Lust dazu mal die Punkte 
die ich angesprochen habe bei sich ausprobieren würde mich echt mal 
interessieren wie gut das Sachen bei euch funktionieren gerne auch mit 
einem 8-Bit 8080 parallel interface :) Aber ist kein muss!

Mfg und schönen Abend noch.

von Wastl (hartundweichware)


Lesenswert?

Felix N. schrieb:
> Ich bin mir auch nicht sicher ob man jedes Register lesen darf.

Darfst du auch nicht. Jedes Kommando ist auf Lesen oder
Schreiben ausgelegt, nicht für beides.

Felix N. schrieb:
> Beispiel bei dem "Positive Gamma Correction(0xE0)" Register dort
> schreibt man ja beim Init die Gamma Werte rein.

Genau das. Schreiben, nicht lesen. Dass es hintenrum anders doch
zu lesen geht ist dann ein anderes Thema.

Felix N. schrieb:
> Wenn ich das jetzt lesen
> würde via SPI muss ich ja ein Byte senden um eins zu empfangen das würde
> ja bedeuten das ich die vorherigen Daten überschreibe also kann ich das
> eigentlich nicht auslesen.

An diesem Punkt hast du SPI nicht verstanden. Ein SPI Schreiben
ist auch immer ein Lesen. Es ist ein Transfer in beide Richtungen.
Erst der Kontext bestimmt ob im Lese- oder im Schreib-Register
etwas sinnvolles drinsteht.

Beispiel: wenn du 0xE0 kommandierst bekommst du im Lese-Register
auch etwas zurück was aber nicht von Bedeutung ist. In C würde
man "undefined behaviour" sagen. Wenn du in die Parameter-Liste
des Kommandos schaust wirst du dort kein einziges Byte finden
das du lesen kannst, nur schreiben. Das virtuelle Read-Signal
(RDX) ist immer inaktiv (statisch 1).

Umgekehrt musst du - um ein Byte zu lesen - immer auch ein Byte
schreiben. In diesem Fall (im richtigen Kontext) ist der Wert
des geschriebenen Bytes ohne Bedeutung.

Dass man die geschriebenen Daten des Kommandos trotzdem
("hintenrum") lesen kann hast du ja offenbar herausgefunden,
nur geht das eben nicht mit dem Kommando 0xE0 da das ein
Schreib-Kommando ist.

von Felix N. (felix_n888)


Lesenswert?

Wastl schrieb:
> Jedes Kommando ist auf Lesen oder
> Schreiben ausgelegt

Guten Morgen,

Macht ja auch irgendwie nur sinn. Ich bin es tatsächlich ein wenig 
gewohnt in Datenblätter eine Angabe direkt unter dem Bit haben ob es 
Read Only(R) oder Read-Write(RW) ist :O. Aber klar hier wird es mit dem 
RDX und WRX angegeben, die sind zwar nur im parallel betrieb 
interessant. Bei SPI braucht man diese zwei Datenleitungen ja nicht bzw. 
stehen ja auch gar nicht zur Verfügung vllt. habe ich deswegen diese 
Angaben(RDX, WRX) im Datenblatt unterbewusst ignoriert.

Wastl schrieb:
> An diesem Punkt hast du SPI nicht verstanden

Ich glaube ich hab mich da etwas falsch ausgedrückt. Ich weiß wie SPI 
funktioniert ich weiß auch das ich ein Byte schreiben muss um ein Byte 
lesen zu können.

Was ich meinte war wenn ich in das Positive Gamma Correction(0xE0) ein 
Byte mittels SPI schreibe ich den ersten Wert in diesem Register 
überschreibe obwohl ich ein Byte lesen wollte. Sprich als erstes steht 
dort zB. 0xFE drin ich will dieses lesen schicke ein 0x00 um die 8 clock 
pulse auf der SCK Leitungen zu generieren und lese dann MISO aus. MISO 
bleibt bei 0x00 weil das Display hier nicht antwortet wegen Write Only 
Register. Nun aber an erster Stelle, im Register, nicht mehr 0xFE steht 
sondern mein 0x00 welches aber ein Dummy-Byte ist um eigentlich nur die 
Clock-Pulse zu generieren damit das Display was ausgeben könnte wenn es 
sich um ein Leseregister handeln würde.

Das war damit gemeint.

Wastl schrieb:
> Dass man die geschriebenen Daten des Kommandos trotzdem
> ("hintenrum") lesen kann hast du ja offenbar herausgefunden,
> nur geht das eben nicht mit dem Kommando 0xE0 da

Doch es geht auch bei dem 0xE0 Befehl. Es gibt ja keine Offizielle 
Dokumentation zu dem 0xD9 Befehl mit dem man auch solche Register 
auslesen kann welche eigentlich nicht zum Auslesen gedacht sind. Ich 
habe gestern Abend in einem anderen Forum ein Beitrag von 2006 gefunden. 
Es soll wohl ein Inoffizielles Datenblatt des ILI9341 geben mit der V0.X 
da sei der noch dokumentiert. Allerdings fängt mein Datenblatt erst bei 
V1.0 im Jahre 2010 an.

Dort wird aber das zu auslesende Register als Parameter übergeben zB:
Befehl 0xD9 senden
Parameter index senden (Also welches Byte man vom Register lesen möchte)
Parameter cmd senden (Hier wird das zu auslesende Register genannt)
-> Das Byte kommt zurück

Sprich für 0xE0 wäre das so:
send Command 0xD9
send Index 0x11 (Entspricht Byte0)
send Register 0xE0
-> Liefert bei mir zB. 0x0F dann zurück

Das könnt ihr sogar gegen prüfen in der ili9341.c weiter oben in dem 
"ili9341InitCodes" gibts den Befehl "0x01E0" dahinter steht direkt als 
erstes Byte im Gamma register 0x0F.

Aber mit einem Befehl zu arbeiten welches offiziell nicht dokumentiert 
ist fühlt sich auch irgendwie nicht so richtig an. Wird ja wohl ein 
Grund haben das dieser nicht mit aufgeführt ist im Datenblatt.

Warum ich aber die ID4 nicht auf normalen Weg auslesen kann (Ist ja ein 
Read-Only Register) sondern nur mit dem geheimen 0xD9 Befehl. Keine 
Ahnung.

Mfg

: Bearbeitet durch User
von Wastl (hartundweichware)


Lesenswert?

Felix N. schrieb:
> Warum ich aber die ID4 nicht auf normalen Weg auslesen kann (Ist ja ein
> Read-Only Register) sondern nur mit dem geheimen 0xD9 Befehl. Keine
> Ahnung.

Das hast du dir ja schon selbst beantwortet.

Felix N. schrieb:
> Achso das wird also
> durch den EXTC pin per Hardware bestimmt ob man die Register auslesen
> kann.

Vermutlich haben unsere Displays die Leitung hart codiert sodass
das nicht funktioniert. Ich konnte beim näheren Hinschauen auf die
Leiterplatte (unter dem Glas, dem eigentlichen Display) mir
keinen Reim daraus machen.

von Felix N. (felix_n888)


Lesenswert?

Wastl schrieb:
> Vermutlich haben unsere Displays die Leitung hart codiert sodass
> das nicht funktioniert.

Das wird so sein. Nur ob jetzt das Pin auf VSS oder VDD liegt wäre mal 
nett zu wissen :)

Wobei dieser EXTC alle Level2 Befehle beeinflusst. Bei jedem Level2 
Befehl steht ja immer bei den Restriction bei:

"EXTC should be high to enable this command"

Und auch unter der Pinbeschreibung von EXTC steht ja drin wenn er Low 
ist:
Low: extended command set is discarded".

Das die Befehle dann keine Nutzung haben, es gibt neben dem Read ID4 
noch den NV Memory Read Status Befehl welcher auch ein Read-Only Befehl 
ist. Bei mir liefert dieser 0x00 zurück ist glaubig auch richtig weil 
ich bisher noch nie den NV Memory beschreiben habe erst dann würde sich 
im Status Register was tun.

Ich glaub ich probiere das später mal aus den NV Memory zu beschreiben. 
Es gibt ein Flow-chart wie man das machen kann(Bei mir auf Seite 234 im 
Datenblatt) dann sollte NV Memory Status was anderes zurückgeben und 
wenn das beschreiben des NV Memory erfolgreich war müsste auch Read 
ID1/2/3 einen von mir festlegten Wert zurück liefern. Da bin ich mal 
gespannt ob ich geht.

Andererseits bin ich mir sicher das EXTC auf VDD liegt und somit die 
Befehle aktiviert sind weil zB. der Positive/Negative Gamma Correction 
Register auch dazu gehören und die werden in der Init Sequenz des 
ILI9341 mit Werten gefüttert und mit dem 0xD9 Befehl kann man die 
Register ja trotzdem auslesen und die Werte die zurückkommen passen ja 
mit den die während der Init Sequenz reingeschrieben wurden. Wäre EXTC 
ja nicht VDD sondern VSS sprich Low dann dürften keine Daten in den 
beiden Gamma Registern stehen. So zumindest meine Theorie :)

Mfg

von Wastl (hartundweichware)


Lesenswert?

Felix N. schrieb:
> So zumindest meine Theorie :)

Ich interpretiere das so: das EXTC Bit bestimmt einfach dass die
erweiterten Kommandos im regulären Fall nicht erreichbar sind,
jedoch durch die (von dir herausgefundene) Hintertür trotzdem
verfügbar.

Da nicht offiziell dokumentiert - auch "nicht verfügbar".

von Felix N. (felix_n888)


Angehängte Dateien:

Lesenswert?

Wastl schrieb:
> jedoch durch die (von dir herausgefundene) Hintertür trotzdem
> verfügbar.

Naa also von mir herausgefunden ist jetzt ein bisschen weit gegriffen :) 
Hab das ganze selbst auch aus einem anderen Beitrag erfahren.

Muss aber auch mal sagen ich habe mir jetzt mehrere ILI9341 librarys 
angeschaut wie TFT_eSPI, Adafruit ILI9341, ESP8266_ILI9341 und ein paar 
custom Libs auf github mit wenigen Funktionen.

Also wenn dort ein readID() Funktion drin ist, bezieht sie sich immer 
eigentlich auf 0xD3 und ausgelesen wurde bisher in allen Library mit dem 
undokumentierten 0xD9 Befehl. Meisten gab es auch eine 
readCommand8/16/32 oder readRegister8/16/32 Funktion welche auf dem 0xD9 
Befehl basiert.

Das mache ich jetzt bei mir auch so geht ja scheinbar nicht anderes. 
Muss ich wohl akzeptieren, auch wenn ich es gerne so hätte wie es im 
Datenblatt eigentlich angegeben ist.

Wastl schrieb:
> das EXTC Bit bestimmt einfach dass die
> erweiterten Kommandos im regulären Fall nicht erreichbar sind

Das interessante ist ja die Beschreibung vom EXTC Pin:

"Please connect EXTC to VDDI to read/write extended registers 
(RB0h~RCFh, RE0h~RFFh)"

Heißt für mich wenn EXTC nicht auf VDDI liegt also VSS(0V)=Low das ich 
die extended registers nicht lesen und auch nicht beschreiben kann. 
Betrifft alle Register von 0xB0 bis 0xCF. Das sind alle Register 
angefangen von den Level2 Befehlen bis Power Control B. Heißt aber auch 
die NV Memory Register sind vom EXTC Pin beeinflusst und die 
Funktionieren! Naja fast.

Ich habe das ganze mal mit der ID1 und dem NV Memory ausprobiert. Und 
ich konnte den Wert 0x7E als ID1 abspeichern. Wenn ich jetzt read 
ID1(0xDA) ausführe bekomme ich 0x7E als Wert zurück. ID2 und ID3 hab ich 
jetzt nicht programmiert.

Lese ich das NV Memory Status Read Register auf normalen Weg aus bekomme 
ich in allen Bytes wieder nur 0x00. Lese ich das mit dem 0xD9 Befehl aus 
bekomme ich Byte1 ein 0x01 das bedeutet das ich die ID1 1 mal 
programmiert habe(Bis zu 3x darf man).

Also bin ich mir nicht sicher ob EXTC High ist xD. Normalerweise müsste 
das lesen des Registers auf normalen Wege auch gehen. Normalerweise 
reflektiert der 0x04 Befehl(Read display ID infos) die readID1/2/3 
Befehle. Da kommt aber auch nur 0x00 obwohl für ID1 in der Theorie 0x7E 
zurückkommen müsste, also den Wert den ich vorher einprogrammiert hatte. 
Keine Ahnung.

Aber die Displayplatinen gibt es ja auch wie Sand am Meer hab hier 3x 
2.8 Zoll Module. 2x davon sind gleich(Wie oben beschreiben) das andere 
hat noch Levelshifter und ein F_CS pin mit drauf(Wofür auch immer der 
ist).

Aber ich lasse es jetzt mal gut sein sonst komme ich nie weiter mit 
meinem eigentlich Projekt. Wollte ja nur Pixel auslesen können :D

Im Ahnung nochmal zwei Bilder vom NV Memory Write und auslesen der ID1

Mfg

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.