Hallo,
ich brauche eure Hilfe,
bekomme das Dispay mit dem Chip ST7789V nicht ans laufen.
Den u.g. Initialisierungscode kommt vom Displayliefgerant.
Ich möchte zum Anfang einfach mal nur ein paart Pixels setzen.....
Ja ich weiß, es gibt libs, aber ich möchte erst mal sehen, ob das
Display überhaut etwas macht.
Die Hintergrundbeleuchtung geht an. Sonst passiert aber nichts auf dem
Display. Hier der Code:
1 | void lcdput (unsigned char reg, unsigned char dat)
| 2 | {
| 3 | LCD_RS_Clear(); // RS Pin Low = Command
| 4 | SPI2Put (reg); // 1 Byte Command schreiben
| 5 | LCD_RS_Set(); // RS Pin High = Data
| 6 | SPI2Put (dat); // 1 Byte Daten schreiben
| 7 | }
| 8 |
| 9 | void lcddata (unsigned char dat)
| 10 | {
| 11 | LCD_RS_Set(); // RS Pin High = Data
| 12 | SPI2Put (dat); // 1 Byte Daten schreiben
| 13 | }
| 14 |
| 15 | void display_initialize (void) // TFT Touch SPI2 display Init
| 16 | {
| 17 |
| 18 | LCT_LIGHT_Set(); // Beleuchtung ein
| 19 |
| 20 | LCD_RST_Clear(); // Reset LCD
| 21 | delay_ms (120);
| 22 | LCD_RST_Set();
| 23 | delay_ms (120);
| 24 |
| 25 | SS_LCD_Clear(); // Slave select Display on (low)
| 26 |
| 27 | lcdput (0x36,0x00); // Resister, Data
| 28 |
| 29 | //------------------------------------------------------------------------------
| 30 | // Data 03= RGB-4-4-4-bit input)
| 31 | // Data 05= RGB-5-6-5-bit input)
| 32 | // Data 06= RGB-6-6-6-bit input)
| 33 | lcdput (0x3a,0x05); // Resister, Data,
| 34 |
| 35 |
| 36 | lcdput (0xb2,0x0c); // Resister, Data
| 37 | lcdput (0xb2,0x0c); // Resister, Data
| 38 | lcdput (0xb2,0x00); // Resister, Data
| 39 | lcdput (0xb2,0x33); // Resister, Data
| 40 | lcdput (0xb2,0x33); // Resister, Data
| 41 |
| 42 | lcdput (0xb7 ,0x35 ); // Resister, Data
| 43 | lcdput (0xbb ,0x2b ); // Resister, Data
| 44 | lcdput (0xc0 ,0x2c ); // Resister, Data
| 45 | lcdput (0xc2 ,0x01 ); // Resister, Data
| 46 | lcdput (0xc3 ,0x11 ); // Resister, Data
| 47 | lcdput (0x04 ,0x20 ); // Resister, Data
| 48 | lcdput (0xc6 ,0x0f ); // Resister, Data
| 49 | lcdput (0xd0 ,0xa4 ); // Resister, Data
| 50 | lcdput (0xd0 ,0xa1);
| 51 |
| 52 | lcdput (0xe0 ,0xd0); // Resister, Data
| 53 | lcdput (0xe0 ,0x00); // Resister, Data
| 54 | lcdput (0xe0 ,0x05); // Resister, Data
| 55 | lcdput (0xe0 ,0x0e); // Resister, Data
| 56 | lcdput (0xe0 ,0x0d); // Resister, Data
| 57 | lcdput (0xe0 ,0x37); // Resister, Data
| 58 | lcdput (0xe0 ,0x43); // Resister, Data
| 59 | lcdput (0xe0 ,0x47); // Resister, Data
| 60 | lcdput (0xe0 ,0x09); // Resister, Data
| 61 | lcdput (0xe0 ,0x15); // Resister, Data
| 62 | lcdput (0xe0 ,0x12); // Resister, Data
| 63 | lcdput (0xe0 ,0x16); // Resister, Data
| 64 | lcdput (0xe0 ,0x19); // Resister, Data
| 65 | lcdput (0xe0 ,0x11); // Resister, Data
| 66 |
| 67 | lcdput (0xe1 ,0xd0); // Resister, Data
| 68 | lcdput (0xe1 ,0x00); // Resister, Data
| 69 | lcdput (0xe1 ,0x05); // Resister, Data
| 70 | lcdput (0xe1 ,0x0d); // Resister, Data
| 71 | lcdput (0xe1 ,0x0c); // Resister, Data
| 72 | lcdput (0xe1 ,0x2d); // Resister, Data
| 73 | lcdput (0xe1 ,0x44); // Resister, Data
| 74 | lcdput (0xe1 ,0x40); // Resister, Data
| 75 | lcdput (0xe1 ,0x0e); // Resister, Data
| 76 | lcdput (0xe1 ,0x1c); // Resister, Data
| 77 | lcdput (0xe1 ,0x18); // Resister, Data
| 78 | lcdput (0xe1 ,0x16); // Resister, Data
| 79 | lcdput (0xe1 ,0x19); // Resister, Data
| 80 |
| 81 | lcdput (0xe7 ,0x10); // Resister, Data
| 82 | //lcdput (0x11 ,0x00); // ? Resister, Data
| 83 | lcddata (0x11); // ? Resister, Data
| 84 |
| 85 | delay_ms (120);
| 86 | // lcdput (0x29 ,0x00); // Display on command 29
| 87 | lcddata (0x29 ); // Display on command 29
| 88 | delay_ms (120);
| 89 |
| 90 | //----------------Test-----------------------------------
| 91 |
| 92 | int i;
| 93 | lcddata (0x2c); // Memory write
| 94 | for (i=0;i<100;i++)
| 95 | {
| 96 | lcddata (0xf9); // irgendwelche daten
| 97 | }
| 98 |
| 99 |
| 100 |
| 101 | SS_LCD_Set(); // SLave select Display off (high)
| 102 | }
|
Dein Display braucht innerhalb einer Befehlssequenz einen Start, CS low,
und einen Stop, CS high. Das kann ich in deinen Ausführungen nicht
finden.
Werde ich morgen mal testen. Danke
Anschließen musst du den CS natürlich auch.
Warum schickst du das Display on command mit der display data Funktion?
Mal abgesehen von deiner - hmmm, sagen wir ambitionierten -
Initialisierung fällt zunächst Folgendes auf:
Dirk F. schrieb:
> lcddata (0x2c); // Memory write
RAMWR ist ein Command und sollte nicht als Data gesendet werden.
Ich bin gerade fertig mit dem Schreiben eines Treibers für einen ST7735,
der braucht als Initialisierung nur: SLPOUT, INVOFF, DISPON, NORON.
Deine 1 | lcdput (0xe0 ,0xd0); // Resister, Data
| 2 | …
|
Orgie ist höchstwahrscheinlich sowohl unnötig, als auch fehlerhaft.
Nach GMCTRP1 und GMCTRP2 Command kommen normalerweise ganze Säcke voller
Daten. Am Stück! Nicht Byteweise.
Wastl schrieb:
> Anschließen musst du den CS natürlich auch.
Ja , hab ich.
Die Frage ist, wann muss ein _CS kommen ?
Für jedes Byte oder für eine Sequenz Command,Data1,Data2....
Dirk F. schrieb:
> Die Frage ist, wann muss ein _CS kommen ?
Vor einer Sequenz, und nach der Sequenz zurücksetzen.
Das steht übrigens alles im Datenblatt des Controllers.
Harry L. schrieb:
> Vor einer Sequenz, und nach der Sequenz zurücksetzen.
Als Anlage ein Teil der Anleitung vom Displaylieferanten, der aber
leider keinen Support macht.
Als Beispiel der dritte Befehl 0xb2.
Ist das dann so richtig:
_CS auf low
0xb2 als command senden
5 Byte (0x0c....0x33) als Data senden
_CS auf high
Dirk F. schrieb:
> Ist das dann so richtig:
> _CS auf low
> 0xb2 als command senden
> 5 Byte (0x0c....0x33) als Data senden
> _CS auf high
Ja.
Dirk F. schrieb:
> Ist das dann so richtig:
> _CS auf low
> 0xb2 als command senden
> 5 Byte (0x0c....0x33) als Data senden
> _CS auf high
Zumindest beim ST7735 muss CS nicht dynamisch gesetzt werden.
Es reicht wenn man CS zum Anfang des Jahres auf Low setzt und im
Anschluss kontinuierlich CMDs und DATA sendet. Gegen Silvester dann CS
wieder auf High.
Norbert schrieb:
> Zumindest beim ST7735 muss CS nicht dynamisch gesetzt werden.
> Es reicht wenn man CS zum Anfang des Jahres auf Low setzt und im
> Anschluss kontinuierlich CMDs und DATA sendet. Gegen Silvester dann CS
> wieder auf High.
Das kann man zwar so machen, spart aber kaum Zeit und erschwert im
Fehlerfall die Fehlersuche ganz erheblich, da CS nicht nur als
ChipSelect dient, sondern auch die Controller-interne State-Machine in
einen definierten Zustand zwingt.
Daher ist es best-Practice CS lt. Datenblatt zu verwenden.
Harry L. schrieb:
> spart aber kaum Zeit
Jaaaaa, ich weiß worauf du hinaus willst. Das Problem ist aber das man
erst den kompletten SPI FIFO bzw. dessen Entleerung abwarten muss bevor
man CS hoch hebt. Das kann sich als durchaus lästig erweisen wenn man
etwas wirklich Schnelles bauen möchte.
Deshalb lohnt es sich über einen Zwischenweg nachzudenken, eine
komplette Zeichenoperation bestehend aus einem Sack voller CMDs und DATA
zusammen zu fassen und diese Sequenz in /CS und CS zu kapseln.
Das Beste aus beiden Welten sozusagen.
Hallo,
vielen Dank für die bisherigen Hinweise, die ich versucht habe
umzusetzen.
Hier ist der neue Code. Leider immer noch keine Reaktion im Display....
Bitte um weitere Hinweise.
1 | void lcdcmd (unsigned char cmd)
| 2 | {
| 3 | LCD_RS_Clear(); // RS Pin High = Data
| 4 | SPI2Put (cmd); // 1 Byte Daten schreiben
| 5 | }
| 6 |
| 7 | void lcddat (unsigned char dat)
| 8 | {
| 9 | LCD_RS_Set(); // RS Pin High = Data
| 10 | SPI2Put (dat); // 1 Byte Daten schreiben
| 11 | }
| 12 |
| 13 | void lcd_cs_hi (void)
| 14 | {
| 15 | SS_LCD_Set();
| 16 | delay_ms (1);
| 17 | }
| 18 |
| 19 | void lcd_cs_lo (void)
| 20 | {
| 21 | SS_LCD_Clear();
| 22 | delay_ms (1);
| 23 | }
| 24 |
| 25 |
| 26 | void display_initialize (void) // TFT Touch SPI2 display Init
| 27 | {
| 28 | LCT_LIGHT_Set(); // Beleuchtung ein
| 29 | LCD_RST_Clear(); // Reset LCD
| 30 | delay_ms (120);
| 31 | LCD_RST_Set();
| 32 | delay_ms (120);
| 33 |
| 34 | lcd_cs_lo ();
| 35 | lcdcmd (0x36); lcddat(0x00);
| 36 | lcd_cs_hi ();
| 37 |
| 38 | //------------------------------------------------------------------------------
| 39 | // Data 03= RGB-4-4-4-bit input)
| 40 | // Data 05= RGB-5-6-5-bit input)
| 41 | // Data 06= RGB-6-6-6-bit input)
| 42 | lcd_cs_lo ();
| 43 | lcdcmd (0x3a);lcddat(0x05);
| 44 | lcd_cs_hi ();
| 45 |
| 46 | lcd_cs_lo ();
| 47 | lcdcmd (0xb2);
| 48 | lcddat(0x0c);
| 49 | lcddat(0x0c);
| 50 | lcddat(0x00);
| 51 | lcddat(0x33);
| 52 | lcddat(0x33);
| 53 | lcd_cs_hi ();
| 54 |
| 55 | lcd_cs_lo (); lcdcmd (0xb7 ); lcddat(0x35 ); lcd_cs_hi ();
| 56 | lcd_cs_lo (); lcdcmd (0xbb ); lcddat(0x2b ); lcd_cs_hi ();
| 57 | lcd_cs_lo (); lcdcmd (0xc0 ); lcddat(0x2c ); lcd_cs_hi ();
| 58 | lcd_cs_lo (); lcdcmd (0xc2 ); lcddat(0x01 ); lcd_cs_hi ();
| 59 | lcd_cs_lo (); lcdcmd (0xc3 ); lcddat(0x11 ); lcd_cs_hi ();
| 60 | lcd_cs_lo (); lcdcmd (0x04 ); lcddat(0x20 ); lcd_cs_hi ();
| 61 | lcd_cs_lo (); lcdcmd (0xc6 ); lcddat(0x0f ); lcd_cs_hi ();
| 62 | lcd_cs_lo (); lcdcmd (0xd0 ); lcddat(0xa4 ); lcd_cs_hi ();
| 63 | lcd_cs_lo (); lcdcmd (0xd0 ); lcddat(0xa1); lcd_cs_hi ();
| 64 |
| 65 | lcd_cs_lo ();
| 66 | lcdcmd (0xe0);
| 67 | lcddat(0xd0);
| 68 | lcddat(0x00);
| 69 | lcddat(0x05);
| 70 | lcddat(0x0e);
| 71 | lcddat(0x0d);
| 72 | lcddat(0x37);
| 73 | lcddat(0x43);
| 74 | lcddat(0x47);
| 75 | lcddat(0x09);
| 76 | lcddat(0x15);
| 77 | lcddat(0x12);
| 78 | lcddat(0x16);
| 79 | lcddat(0x19);
| 80 | lcddat(0x11);
| 81 | lcd_cs_hi ();
| 82 |
| 83 | lcd_cs_lo ();
| 84 | lcdcmd (0xe1 );
| 85 | lcddat(0xd0);
| 86 | lcddat(0x00);
| 87 | lcddat(0x05);
| 88 | lcddat(0x0d);
| 89 | lcddat(0x0c);
| 90 | lcddat(0x2d);
| 91 | lcddat(0x44);
| 92 | lcddat(0x40);
| 93 | lcddat(0x0e);
| 94 | lcddat(0x1c);
| 95 | lcddat(0x18);
| 96 | lcddat(0x16);
| 97 | lcddat(0x19);
| 98 | lcd_cs_hi ();
| 99 |
| 100 | lcd_cs_lo (); lcdcmd (0xe7 ); lcddat(0x10);lcd_cs_hi ();
| 101 | lcd_cs_lo (); lcdcmd (0x11 ); lcd_cs_hi ();
| 102 |
| 103 | delay_ms (120);
| 104 | lcd_cs_lo (); lcdcmd (0x29 ); lcd_cs_hi (); // Display on command 29
| 105 | delay_ms (120);
| 106 |
| 107 | //----------------Test-----------------------------------
| 108 | lcd_cs_lo ();
| 109 | lcdcmd(0x2a); // 0x2a = Spaltenadresse
| 110 | lcddat(0x00); // XS8..15
| 111 | lcddat(0x10); // XS0..7
| 112 | lcddat(0x00); // XE8..15
| 113 | lcddat(0x20); // XE0..7
| 114 | lcd_cs_hi ();
| 115 |
| 116 | lcd_cs_lo ();
| 117 | lcdcmd(0x2b); // 0x2b = Zeilenadresse
| 118 | lcddat(0x00); // YS8..15
| 119 | lcddat(0x10); // YS0..7
| 120 | lcddat(0x00); // YE8..15
| 121 | lcddat(0x20); // YE0..7
| 122 | lcd_cs_hi ();
| 123 |
| 124 | lcd_cs_lo ();
| 125 | lcdcmd (0x2c ); // 2c = Memory write
| 126 | int i;
| 127 | for (i=0;i<100;i++)
| 128 | {
| 129 | lcddat (0xf9); // irgendwelche daten
| 130 | }
| 131 | lcd_cs_hi ();
| 132 | }
|
Oh sorry. Das war noch falsch. Jetzt gehts....
1 | void lcd_cs_hi (void)
| 2 | {
| 3 | delay_ms (1);
| 4 | SS_LCD_Set();
| 5 | }
|
Norbert schrieb:
> Das Problem ist aber das man
> erst den kompletten SPI FIFO bzw. dessen Entleerung abwarten muss bevor
> man CS hoch hebt.
Hättest du das nicht schon letzte Woche schreiben können? ;-)
Hab gerade angefangen einen Textausgabe für ILI9341 auf einem STM32 zu
schreiben (gibts schon, aber ich will es selber lernen).
Dabei bin ich über 2h genau an diesem Problem festgehangen: Auf dem DSO
habe ich mir den Anfang der SPI-Kommunikation angesehen - passt
eigentlich alles.
Bis irgendwann mal zum Ende der Sequenz gescrollt habe und dort auf CS
gestoßen bin.
Norbert schrieb:
> eine komplette Zeichenoperation bestehend aus einem Sack voller CMDs
> und DATA zusammen zu fassen und diese Sequenz in /CS und CS zu kapseln.
Mal sehen wie ich das noch einbauen kann.
Das wird dann wohl auf unterschiedliche Senderoutinen rauslaufen...
Dirk F. schrieb:
> Bitte um weitere Hinweise.
1 | lcd_cs_lo (); lcdcmd (0xb7 ); lcddat(0x35 ); lcd_cs_hi ();
|
Die Klammern gehören bei Funktionen immer direkt ohne Leerzeichen an den
Namen.
Wirst du nach Zeichanzahl bezahlt? Schon mal was von einem Array gehört?
Das löst zwar nicht dein Problem, verbessert deinen Programmierstil aber
sichtbar. Eher so.
1 | uint8_t sequence1[] = {0xe1, 0xd0, 0x00, 0x05, 0x0d, 0x0c, 0x2d, 0x44, 0x40, 0x0e, 0x1c, 0x18, 0x16, 0x19};
| 2 |
| 3 | lcdcmd_long(sequence1, sizeof(sequence1));
| 4 |
| 5 | void lcdcmd_long(const uint8_t *data, uint16_t cnt) {
| 6 |
| 7 | lcd_cs_lo();
| 8 | lcdcmd(*data++);
| 9 | for(cnt--; cnt > 0; cnt--) {
| 10 | lcddat(*data++);
| 11 | }
| 12 | lcd_cs_hi();
| 13 | }
|
Danke Falk.
Ja stimmt. Wollte nur erst mal "quick and dirty" sehen, ob das Display
überhaupt etwas macht, um Layoutfehler auszuschließen.
Jetzt ist es ansprechbar, dann kommt Optiomierung.
Noch was:
geht nicht: 1 | for (i=0;i<62000;i++)
| 2 | {
| 3 | lcddat(0xff); // Bildschirm löschen
| 4 | }
|
funktioniert: 1 | for (i=0;i<62000;i++)
| 2 | {
| 3 | lcd_cs_lo(); // Slave select low
| 4 | lcddat(0xff); // Bildschirm löschen
| 5 | lcd_cs_hi(); // Slave select high
| 6 | }
|
Also nochmal großes Danke an das Forum hier.
Mir wurde mal wieder geholfen :-)
Dirk F. schrieb:
> funktioniert:for (i=0;i<62000;i++)
> {
> lcd_cs_lo(); // Slave select low
> lcddat(0xff); // Bildschirm löschen
> lcd_cs_hi(); // Slave select high
> }
Ist aber Unfug! Man muss nicht für jedes Byte einen komplette CS-Zyklus
durchlaufen, schon gar nicht, wenn du bei jedem CS Wechsel 1ms wartest!
Warum machst du nicht das Offensichtliche?
1 | lcd_cs_lo();
| 2 | for (i=0; i<62000; i++) lcddat(0xff);
| 3 | lcd_cs_hi();
|
Falk B. schrieb:
> Warum machst du nicht das Offensichtliche?
Stimmt. Läuft auch so-
Habe auch die Wartezeit von 1 ms auf 20us reduziert.
Dirk F. schrieb:
> Habe auch die Wartezeit von 1 ms auf 20us reduziert.
Naja, ein Workaround. Was für einen Controller hast du? Und an welcher
Schnittstelle hängt dein LCD? SPI oder UART im SPI Modus? Man kann das
Ende der Übertragung in einem Register abfragen, dann ist die Wartezeit
immer minimal.
Falk B. schrieb:
> Was für einen Controller hast du?
PIC32MZ. Display am SPI mit 1 MHz
Falk B. schrieb:
> Ende der Übertragung in einem Register abfragen, dann ist die Wartezeit
> immer minimal.
Ja kommt später noch....
Dirk F. schrieb:
>> Ende der Übertragung in einem Register abfragen, dann ist die Wartezeit
>> immer minimal.
> Ja kommt später noch....
Wieso? Das ist elementar und einfach und man baut das gleich ein.
Falk B. schrieb:
> Wieso? Das ist elementar und einfach und man baut das gleich ein.
Habs jetzt so gemacht. Funktioniert:
1 | void lcdput (unsigned char dat)
| 2 | {
| 3 | int dummy;
| 4 | SS_LCD_Clear(); // Slave select low
| 5 | SPI2Put (dat); // 1 Byte Daten schreiben
| 6 | while(SPI2STATbits.SPIRBE); // Warten so lange wie Empfangspuffer leer ist
| 7 | dummy = SPI2BUF; // Puffer leeren
| 8 | dummy = dummy; // sonst meckert compiler
| 9 | SS_LCD_Set(); // Slave select high
| 10 | }
|
Komisch ist, dass die Sache mit dem !SPI2STATbits.SPITBE (Transmit
Puffer nicht Empty) nicht funktioniert..
Dirk F. schrieb:
> Komisch ist, dass die Sache mit dem !SPI2STATbits.SPITBE (Transmit
> Puffer nicht Empty) nicht funktioniert..
Der Puffer/FIFO kann durchaus schon leer sein während die Hardware noch
die letzten Bits raus schiebt. Beim RP2040 nehme ich gerne das SPI busy
flag um das zu erkennen.
Norbert schrieb:
> Der Puffer/FIFO kann durchaus schon leer sein während die Hardware noch
> die letzten Bits raus schiebt.
Ja das ist genau so eine Falle, in die man reintappen kann.
Und wenn man dann wie beim Display kaum Diagnosemöglichkeiten hat und
LCD einfach keine Reaktion von sich gibt, kann das schnell frustrierend
werden.....
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|