Forum: Mikrocontroller und Digitale Elektronik Display mit Chip ST7789V Startprobleme


von Dirk F. (dirkf)


Lesenswert?

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
}

von Wastl (hartundweichware)


Lesenswert?

Dein Display braucht innerhalb einer Befehlssequenz einen Start, CS low, 
und einen Stop, CS high. Das kann ich in deinen Ausführungen nicht 
finden.

von Dirk F. (dirkf)


Lesenswert?

Werde ich morgen mal testen.  Danke

von Wastl (hartundweichware)


Lesenswert?

Anschließen musst du den CS natürlich auch.

von Wastl (hartundweichware)


Lesenswert?

Warum schickst du das Display on  command mit der display data Funktion?

von Norbert (der_norbert)


Lesenswert?

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.

von Dirk F. (dirkf)


Lesenswert?

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....

von Harry L. (mysth)


Lesenswert?

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.

: Bearbeitet durch User
von Dirk F. (dirkf)


Angehängte Dateien:

Lesenswert?

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

von Harry L. (mysth)


Lesenswert?

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.

von Norbert (der_norbert)


Lesenswert?

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.

von Harry L. (mysth)


Lesenswert?

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.

von Norbert (der_norbert)


Lesenswert?

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.

von Dirk F. (dirkf)


Lesenswert?

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
}

von Dirk F. (dirkf)


Lesenswert?

Oh sorry. Das war noch falsch. Jetzt gehts....

1
void lcd_cs_hi (void)
2
{
3
delay_ms (1);
4
SS_LCD_Set();
5
}

von Thomas F. (igel)


Lesenswert?

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...

von Falk B. (falk)


Lesenswert?

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
}

von Dirk F. (dirkf)


Lesenswert?

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
    }

von Dirk F. (dirkf)


Lesenswert?

Also nochmal großes Danke an das Forum hier.
Mir wurde mal wieder geholfen :-)

von Falk B. (falk)


Lesenswert?

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();

von Dirk F. (dirkf)


Lesenswert?

Falk B. schrieb:
> Warum machst du nicht das Offensichtliche?

Stimmt. Läuft auch so-

Habe auch die Wartezeit von 1 ms auf 20us reduziert.

von Falk B. (falk)


Lesenswert?

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.

von Dirk F. (dirkf)


Lesenswert?

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....

von Falk B. (falk)


Lesenswert?

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.

von Dirk F. (dirkf)


Lesenswert?

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..

von Norbert (der_norbert)


Lesenswert?

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.

von Dirk F. (dirkf)


Lesenswert?

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.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.