Forum: Mikrocontroller und Digitale Elektronik LCD funktioniert nur nach Programmierung


von Felix H. (fluppy)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe ein Problem mit meinem LCD (4x20 Zeichen, DEM20485-SYH-LY), das 
ich an einem ATmega8 angeschlossen habe.

Mein Testprogramm mit Initalisierung des LCDs und einer Zeichenausgabe 
funktioniert. Jedoch funktioniert es nur direkt nach dem Programmieren, 
oder nach mehrmaligem resetten des ATmega8 (wenn der programmer nicht 
mehr angeschlossen ist).

Die Atmega-Beschaltung entspricht der aus dem AVR-Tutorial. Ich verwende 
jedoch einen 4MHz Quarzoszillator (SUT_CKSEL fuse gesetzt auf 
EXTCLK_6CK_64MS). Im Anhang noch ein Bild zur Beschaltung der Port-Pins.


Hatte jemand von euch schon einmal dieses Problem und könnte mir helfen? 
Ich komme leider nicht weiter.

Bin für jeden Tipp dankbar.


Viele Grüße
fluppy


Hier ist noch das Programm:
1
#define F_CPU 4000000UL //definiere Takt-Konstante für delay.h
2
3
//header files
4
#include <avr/io.h>
5
#include <util/delay.h>
6
7
//function prototyping
8
void enable(void);
9
void send_upper_4bit(uint8_t data);
10
void send_instruction(uint8_t instruction);
11
void send_data(uint8_t data);
12
void init(void);
13
void print_character(uint8_t character);
14
void clear_display(void);
15
void set_DDRAM_address(uint8_t address);
16
17
int main(void)
18
{  
19
  init(); //Initialisierung des LCDs
20
  
21
  while(1)
22
  {
23
    set_DDRAM_address(0x40);
24
    print_character('T');
25
    print_character('E');
26
    print_character('S');
27
    print_character('T');
28
    clear_display();
29
  }
30
  
31
  return 0;
32
}
33
34
void enable(void)
35
{
36
  PORTD |= (1 << PD5);
37
  _delay_ms(100);
38
  PORTD &= ~(1 << PD5);
39
}
40
41
void send_upper_4bit(uint8_t data) 
42
{
43
  PORTD &= 0xF0; //Datenleitungen löschen, Signalleitungen erhalten
44
  PORTD |= (data >> 4); //schreibe die oberen 4 bit auf die Datenleitungen
45
  enable(); //enable-Puls
46
  _delay_ms(100);
47
}
48
49
void send_instruction(uint8_t instruction)
50
{
51
  PORTD &= ~(1 << PD4); //RS = 0 -> Instruction Register
52
  send_upper_4bit(instruction);
53
  send_upper_4bit(instruction << 4);
54
  _delay_ms(100);
55
}
56
57
void send_data(uint8_t data)
58
{
59
  PORTD |= (1 << PD4); //RS = 1 -> Data Register
60
  send_upper_4bit(data);
61
  send_upper_4bit(data << 4);
62
  _delay_ms(100);
63
}
64
65
void init(void) //Initialisiere LCD
66
{
67
68
  PORTD = 0x00;
69
  DDRD = 0xFF; //PORTD Ausgang
70
71
  uint8_t i = 1;
72
  while(i++ < 10)
73
    _delay_ms(100);
74
  
75
  send_upper_4bit(0x30); //Function set 0x0011xxxx
76
  
77
  _delay_ms(100);
78
  
79
  send_upper_4bit(0x20); //Function set 0x0010xxxx
80
  send_upper_4bit(0x80); // 0x(N)(F)xxxxxx N=1, F=0
81
  
82
  _delay_ms(100);
83
  
84
  send_upper_4bit(0x20); //Function set 0x0010xxxx
85
  send_upper_4bit(0x80); // 0x(N)(F)xxxxxx N=1, F=0
86
  
87
  _delay_ms(100);
88
  
89
  send_upper_4bit(0x00); //Display control 0x0000xxxx
90
  send_upper_4bit(0xF0); // 0x1(D)(C)(B)xxxx D=1,C=1,B=1
91
  
92
  _delay_ms(100);
93
  
94
  send_upper_4bit(0x00); //Display clear 0x0000xxxx
95
  send_upper_4bit(0x10); // 0x0001xxxx
96
  
97
  _delay_ms(100);
98
  
99
  send_upper_4bit(0x00); //entry mode set 0x0000xxxx
100
  send_upper_4bit(0x60); //0x01(I/D)(S)xxxx I/D=1, S=0
101
  
102
  _delay_ms(100);
103
  
104
  clear_display();
105
  
106
}
107
108
void print_character(uint8_t character)
109
{
110
  send_data(character);
111
}
112
113
void clear_display(void)
114
{
115
  send_instruction(0x01);
116
}
117
118
void set_DDRAM_address(uint8_t address)
119
{
120
  send_instruction(0x80 | address); //DB7 = 1
121
}

von frosch (Gast)


Lesenswert?

Beschaltung des Reset-Pin am Atmega8 ?

von Karl H. (kbuchegg)


Lesenswert?

Was'n das für Konstrukt
1
  uint8_t i = 1;
2
  while(i++ < 10)
3
    _delay_ms(100);

schreib doch einfach
1
  _delay_ms( 1000 );
und gut ists.

Wie auch immer
1
  send_upper_4bit(0x30); //Function set 0x0011xxxx
2
  
3
  _delay_ms(100);
4
  
5
  send_upper_4bit(0x20); //Function set 0x0010xxxx
6
  send_upper_4bit(0x80); // 0x(N)(F)xxxxxx N=1, F=0

Nein.
Du musst 3 mal hintereinander die 0x30 anlegen. Direkt hintereinander 
und mit Wartezeiten dazwischen. Und erst danach wird einmalig die 0x20 
an den 4 oberen Bits angelegt und erst dann kommen die 0x28 für den 
Modus, dann allerdings bereits mit der ganz normalen Ausgabefunktion für 
8 Bit

Also
1
  send_upper_4bit(0x30); //Function set 0x0011xxxx
2
  _delay_ms(100);
3
  send_upper_4bit(0x30); //Function set 0x0010xxxx
4
  _delay_ms( 100 );
5
  send_upper_4bit(0x30); //Function set 0x0010xxxx
6
  _delay_ms( 100 );
7
8
  send_upper_4bit(0x20); //Function set 0x0010xxxx
9
  _delay_ms( 100 );
10
11
 ... und erst jetzt ist das LCD zuverlässig im 4 Bit Modus


....
1
  send_upper_4bit(0x00); //Display control 0x0000xxxx
2
  send_upper_4bit(0xF0); // 0x1(D)(C)(B)xxxx D=1,C=1,B=1
Mann der Berge! Du hast doch Ausgabefunktionen für 8 Bit Werte 
(send_instruction und send_data)! Warum benutzt du die denn nicht? Du 
wirst doch im Hirn beklopft, wenn du selbst im Code die Codezahlen 
entsprechend aufteilen musst! Das kann doch der Computer viel 
zuverlässiger als du.

von spess53 (Gast)


Lesenswert?

Hi

>Mein Testprogramm mit Initalisierung des LCDs und einer Zeichenausgabe
>funktioniert. Jedoch funktioniert es nur direkt nach dem Programmieren,
>oder nach mehrmaligem resetten des ATmega8 (wenn der programmer nicht
>mehr angeschlossen ist).

Typisch, wenn die Wartezeit vor der LCD-Initialisierung zu kurz ist.

MfG Spess

von Karl H. (kbuchegg)


Lesenswert?

spess53 schrieb:
> Hi
>
>>Mein Testprogramm mit Initalisierung des LCDs und einer Zeichenausgabe
>>funktioniert. Jedoch funktioniert es nur direkt nach dem Programmieren,
>>oder nach mehrmaligem resetten des ATmega8 (wenn der programmer nicht
>>mehr angeschlossen ist).
>
> Typisch, wenn die Wartezeit vor der LCD-Initialisierung zu kurz ist.

Hatte ich auch gedacht. Aber 1 Sekunde sollte auf jeden Fall reichen.

von Felix H. (fluppy)


Lesenswert?

@Karl Heinz: Vielen Dank für deine Hinweise. Ich werde sie beachten.

Karl Heinz schrieb:

> [...]

> Nein.
> Du musst 3 mal hintereinander die 0x30 anlegen. Direkt hintereinander
> und mit Wartezeiten dazwischen. Und erst danach wird einmalig die 0x20
> an den 4 oberen Bits angelegt und erst dann kommen die 0x28 für den
> Modus, dann allerdings bereits mit der ganz normalen Ausgabefunktion für
> 8 Bit
>
> [...]

Bei der Initialisierung habe ich mich jedoch am Datenblatt des 
LCD-Controllers (ST7066U) orientiert. Im Anhang die Initialisierung im 
4-Bit Modus (Quelle: Datenblatt ST7066U)

Soll ich die init-Funktion trotzdem so umschreiben, wie du beschrieben 
hast?

von BattMan (Gast)


Lesenswert?

Er fragt das LCD-Busy nicht ab (DB7)
"DB7 can be used as a busy flag. The address counter is updated 4us 
after the busy flag is cleared"

In Schreibroutinen sollte man dies immer abfragen,
das war bei mir das Problem.

von Felix H. (fluppy)


Lesenswert?

frosch schrieb:
> Beschaltung des Reset-Pin am Atmega8 ?

10 kOhm Widerstand auf 5V und 100nF Keramikkondensator auf GND.

von Karl H. (kbuchegg)


Lesenswert?

BattMan schrieb:
> Er fragt das LCD-Busy nicht ab (DB7)
> "DB7 can be used as a busy flag. The address counter is updated 4us
> after the busy flag is cleared"

Bei seinen Zeiten ist das ziemlich egal.
Nach 100ms ist auf einem LCD alles fertig.

von Felix H. (fluppy)


Angehängte Dateien:

Lesenswert?

Hier noch die Initialisierung aus dem Datenblatt (ST7066U)

von Karl H. (kbuchegg)


Lesenswert?

Felix H. schrieb:

> Bei der Initialisierung habe ich mich jedoch am Datenblatt des
> LCD-Controllers (ST7066U) orientiert. Im Anhang die Initialisierung im
> 4-Bit Modus (Quelle: Datenblatt ST7066U)

Du hast recht, die ist tatsächlich dort so angegeben.
Funktional sollte es eigentlich aufs gleiche rauslaufen. Da hab ich mich 
wohl davon leiten lassen, dass der Anfang der Initialisierung nicht so 
aussieht, wie er normalerweise aussieht.

Die Initialisierung mit 3 mal hintereinander 0x30 ist ja nur dazu da, 
dass man das LCD aus einem gerade begonnenen Kommando rausholt, sollte 
der Prozessorreset just zu einem Zeitpunkt erfolgen, an dem das eine 
Nibble eines Befehls schon zum LCD übertragen wurde und das andere noch 
nicht. Und wenn man das für alle Kommandos analysiert, dann kommt man 
drauf, dass der schlimmste Fall dann eingetreten ist, wenn das LCD noch 
auf 1 Nibble wartet. D.h. der ersten der 3 0x30 ist dann dann dafür da, 
dass das LCD eine erst mal begonnene Aktion auch noch abschliesst und 
erst die nächsten beden Nibbles mit jeweils 0x30 schalten das LCD dann 
erst mal zurück auf 8 Bit Interface, von wo aus dann der erste gezielte 
0x20 wieder in den 4 Bit Modus zurückführt.

> Soll ich die init-Funktion trotzdem so umschreiben, wie du beschrieben
> hast?

Ich würds so probieren. Mehr als nicht funktionieren kann es nicht.

von Felix H. (fluppy)


Lesenswert?

Ok, dann werde ich es so probieren und melde mich dann wieder hier.

Merkwürdig ist aber, dass es ja funktioniert, jedoch nur nach einem oder 
mehreren (immer unterschiedlich) resets.

von Karl H. (kbuchegg)


Lesenswert?

Felix H. schrieb:
> Ok, dann werde ich es so probieren und melde ich dann wieder hier.
>
> Merkwürdig ist aber, dass es ja funktioniert, jedoch nur nach einem oder
> mehreren (immer unterschiedlich) resets.

Reset oder Ein/AUsschalten?

Das ist nicht dasselbe! Denn nach dem Ein/AUsschalten ist das LCD 
gesichert in einem ganz bestimmten Modus.

Wenn du aber nur den Prozessor resettest, dann ist nicht sicher 
gestellt, wie die interne Logik des LCD zu diesem Zeitpunkt gerade 
steht, wenn der Mega erneut mit der Intialisierungssequenz beginnt.

von Pete K. (pete77)


Lesenswert?

Das F_CPU gehört in das Makefile und nicht in die main.c. Du kannst 
nicht sicher sein, dass delay.c auch mit diesem Wert übersetzt wird.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> Du hast recht, die ist tatsächlich dort so angegeben.
> Funktional sollte es eigentlich aufs gleiche rauslaufen. Da hab ich mich
> wohl davon leiten lassen, dass der Anfang der Initialisierung nicht so
> aussieht, wie er normalerweise aussieht.

Wenn ich mir das nochmal durch den Kopf gehen lasse und das in den 
Zusammenhang damit bringe, warum man am Anfang üblicherweise 3-mal 0x30 
ausgibt, dann komme ich zum Schluss, dass die dort angegebene Sequenz 
falsch ist und nicht immer funktioniert.

von Felix H. (fluppy)


Lesenswert?

Karl Heinz schrieb:
> Reset oder Ein/AUsschalten?

Reset.


Ich beschreibe mal was passiert:

Ich schalte die gesamte Schaltung ein -> 2 schwarze Balken auf dem LCD 
-> Balken verschwinden, aber keine Zeichen erscheinen

Dann resette ich den ATmega ein paar Mal -> Balken auf LCD verschwinden, 
Zeichen erscheinen auf dem Display


Direkt nach dem Einschalten funktioniert es eigentlich nie.

von Karl H. (kbuchegg)


Lesenswert?

Felix H. schrieb:

> Direkt nach dem Einschalten funktioniert es eigentlich nie.


Probiers mal mit der Sequenz
1
void init()
2
  _delay_ms( 1000 );
3
4
  send_upper_4bit(0x30); //Function set: 8 Bit Mode
5
  _delay_ms(100);
6
  send_upper_4bit(0x30); //Function set: 8 Bit Mode
7
  _delay_ms( 100 );
8
  send_upper_4bit(0x30); //Function set: 8 Bit Mode
9
  _delay_ms( 100 );
10
11
  send_upper_4bit(0x20); //Function set: 4 Bit Mode am '8 Bit Bus'
12
  _delay_ms( 100 );
13
14
  send_instruction( 0x28 );    //   4Bit, 0x(N)(F)xxxxxx N=1, F=0
15
  _delay_ms(100);
16
  
17
  send_instruction( 0x0F );    //Display control (D)(C)(B) D=1,C=1,B=1
18
  _delay_ms(100);
19
  
20
  send_instruction( 0x06 );    //entry mode set 0x0000xxxx
21
  _delay_ms(100);
22
  
23
  clear_display();
24
}

von Felix H. (fluppy)


Lesenswert?

Karl Heinz schrieb:
> Karl Heinz schrieb:
>
>> Du hast recht, die ist tatsächlich dort so angegeben.
>> Funktional sollte es eigentlich aufs gleiche rauslaufen. Da hab ich mich
>> wohl davon leiten lassen, dass der Anfang der Initialisierung nicht so
>> aussieht, wie er normalerweise aussieht.
>
> Wenn ich mir das nochmal durch den Kopf gehen lasse und das in den
> Zusammenhang damit bringe, warum man am Anfang üblicherweise 3-mal 0x30
> ausgibt, dann komme ich zum Schluss, dass die dort angegebene Sequenz
> falsch ist und nicht immer funktioniert.

Ok, ich probiere deine beschriebenen Vorgehensweise und melde mich dann 
wieder.

Vielen Dank.

von Felix H. (fluppy)


Lesenswert?

Karl Heinz schrieb:
> Probiers mal mit der Sequenz
>
>
1
> void init()
2
>   _delay_ms( 1000 );
3
> 
4
>   send_upper_4bit(0x30); //Function set: 8 Bit Mode
5
>   _delay_ms(100);
6
>   send_upper_4bit(0x30); //Function set: 8 Bit Mode
7
>   _delay_ms( 100 );
8
>   send_upper_4bit(0x30); //Function set: 8 Bit Mode
9
>   _delay_ms( 100 );
10
> 
11
>   send_upper_4bit(0x20); //Function set: 4 Bit Mode am '8 Bit Bus'
12
>   _delay_ms( 100 );
13
> 
14
>   send_instruction( 0x28 );    //   4Bit, 0x(N)(F)xxxxxx N=1, F=0
15
>   _delay_ms(100);
16
> 
17
>   send_instruction( 0x0F );    //Display control (D)(C)(B) D=1,C=1,B=1
18
>   _delay_ms(100);
19
> 
20
>   send_instruction( 0x06 );    //entry mode set 0x0000xxxx
21
>   _delay_ms(100);
22
> 
23
>   clear_display();
24
> }
25
>


Ganz großes Dankeschön Karl Heinz! Mit dieser Initialisierung 
funktioniert es endlich.

Super! Ich freue mich.

von Karl H. (kbuchegg)


Lesenswert?

Felix H. schrieb:

> Mit dieser Initialisierung
> funktioniert es endlich.

Die ist auch logisch.
Die im Datenblatt angegeben ist es nicht.
1
erste Ausgabe   0x30      das LCD ist nach dem Power Up im 8 Bit Modus
2
3
nächste Ausgabe 0x20      da das LCD im 8 Bit Modus ist, wird das als kompletter 
4
                          Befehl angesehen. Das LCD schaltet auf 4 Bit
5
6
nächste Ausgabe 0x80      Die 0x80 sind eigentlich die 2.te Hälfte des voorhergehenden
7
                          Befehls (0x20). Da das LCD zu diesem Zeitpunkt aber noch
8
                          im 8 Bit Modus war, sind die 0x20 schon als Befehl angesehen
9
                          worden und jetzt beginnt der nächste.
10
                          Müsste man jetzt ergründen, was das LCD mit 0x80 macht.
11
                          Auf jeden Fall nicht das, was beabsichtigt wurde
12
13
nächste AUsgabe 0x20      diese 0x20 werden zu den 0x80 von vorher dazugestoppelt,
14
                          da ja das LCD nach der ersten 0x20 bereits in den 4 Bit
15
                          Modus geschaltet hat und mit der vorhergehenden 0x80
16
                          ein Nibble des vermeintlich nächsten Befehls,
17
                          zumindest aus Sicht des LCD, übertragen wurde
und so gehts dann immer weiter: High-Nibble und Low-Nibble sind komplett 
vertauscht.
Nach ein paar Resets erwischt du dann zufällig mit dem Reset mal das 
Prgogramm in der Situation, dass es das High-Nibble schon ausgegeben hat 
und das LCD noch auf das Low-Nibble noch wartet. Der Prozessorreset 
führt dann dazu, dass erst mal wieder die 0x30 ausgegeben werden, welche 
das LCD als das Low-Nibble des noch ausstehenden Daten/Kommando Bytes 
auffasst. Wenn dann der Prozessor mit der 0x20, 0x80 Sequenz loslegt, 
dann ist er tatsächlich mit dem LCD synchron, welches Nibble gerade 
übertragen wird. Und erst dann funktioniert alles so wie es soll.

Die ganze Sequenz aus dem Datenblatt macht wenig Sinn. Der Fehler ist, 
dass die erste Umschaltung in den 4 Bit Modus noch als ein 8 Bit Befehl 
angesehen werden muss, der in einem Rutsch (also nicht auf 2 mal) 
übertragen wird. Und erst dann kann der Eiertanz mit jeweils 2 
Übertragungen für 1 Byte losgehen.

von Karl H. (kbuchegg)


Lesenswert?

Felix H. schrieb:

> Ganz großes Dankeschön Karl Heinz! Mit dieser Initialisierung
> funktioniert es endlich.
>
> Super! Ich freue mich.

Die Delays kannst du jetzt sukzessive verkürzen, wenn du willst.
Ist zwar sicher kein Beinbruch, wenn sie so lange bleiben, aber muss ja 
nicht sein.

von Felix H. (fluppy)


Lesenswert?

Ja, das werde ich alles nach und nach noch anpassen.

Danke für die Tipps und auch für die ausführliche Erklärung der 
Initialisierung!

Viele Grüße
Felix

von Paul Baumann (Gast)


Lesenswert?

Da sieht man, daß auch in Datenblättern manchmal grober Unfug steht.

MfG Paul

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.