Forum: Mikrocontroller und Digitale Elektronik LCD im 4bit Modus initialisieren, funktioniert nicht, C, Atmega8


von ChefKoch (Gast)


Lesenswert?

Hallo Leute!

Ich bin neu hier im Forum und sitze seit ein paar Tagen an einem Problem 
mit dem ich nicht weiter komme.

In Sachen Elektronik habe ich wenig Erfahrung, aber C/C++ ist mir nicht 
völlig fremd.

Ziel ist es, ein LCD mit KS0066 kompatiblen Treiber im 4bit Modus mit 
einem Atmel Atmega8 anzusteuern. Der Betrieb im 8bit Modus ist mir 
bereits gelungen, aber warum die 4Pins opfern, wenns auch ohne geht.

Zur Hardware:
μC: Wie oben erwähnt, Atmel Atmega8-16PU
LCD: ANAG VISION AV1624 (Datenblatt hier: 
http://www.produktinfo.conrad.com/datenblaetter/175000-199999/183342-da-01-ml-LCD_Modul_16x2_Zeichen_de_en.pdf)
Konfiguration: Die Pins E, RS und RW werden von PortC, Pins 5, 3 und 4 
angesteuert. Die Datenleitungen 7, 6, 5, 4 liegen in gleicher 
Reihenfolge an den Pins 7, 6, 5, 4 des PortD. Ich habe keine 
Veränderungen zum 8bit Betrieb vorgenommen. Außer die überflüssigen 
Datenleitungen zu entfernen.

Ich gehe davon aus, dass von der Hardware-Seite her alles in Ordnung 
ist, da es ja im 8bit Modus schon funktionierte.

Zur Software:
IDE: Atmel Studio 6.1
C-Code: Siehe unten.

In der "lcd_init()"-Methode habe ich versucht das 
Initialisierungsprotokoll aus dem Datenblatt (Seite 4, Figure 2) 
umzusetzen. Leider finde ich meinen Fehler / meine Fehler nicht. 
Vielleicht hat jemand einen Lösungsvorschlag?

Mir ist klar, dass ich einfach schon vorhandenen Code verwenden könnte. 
(Sicher der leichte Weg.)Ich möchte es aber selbst umsetzten.

Vielen Dank für Eure Hilfe!

Grüße.
1
#include <avr/io.h>
2
#include <util/delay.h>
3
4
#define LCD_Control PORTC
5
#define LCD_DDR_Control DDRC
6
#define LCD_Enable 5
7
#define LCD_ReadWrite 4
8
#define LCD_RegisterSelect 3
9
10
#define LCD_Data PORTD
11
#define LCD_DDR_Data DDRD
12
13
/* 
14
*  send enable signal 
15
*/
16
void LCD_EnableSignal(){
17
  LCD_Control |= 1<<LCD_Enable;
18
  asm volatile ("nop");
19
  asm volatile ("nop");  
20
  LCD_Control &= ~1<<LCD_Enable;
21
}
22
23
/*
24
*  enable / disable command mode
25
*/
26
void LCD_EnableCommandMode(){
27
  LCD_Control &= ~(1<<LCD_RegisterSelect);
28
}
29
void LCD_DisableCommandMode(){
30
  LCD_Control |= 1<<LCD_RegisterSelect;
31
}
32
33
/*
34
*  enable / disable writemode
35
*/
36
void LCD_EnableWriteMode(){
37
  LCD_Control &= ~ (1<<LCD_ReadWrite);  
38
}
39
void LCD_DisableWriteMode(){
40
  LCD_Control |= (1<<LCD_ReadWrite);
41
}
42
43
/*
44
*  check busy flag
45
*/
46
void LCD_CheckBusy(){
47
  LCD_DDR_Data = 0x00;  // make dataport input 
48
  LCD_DisableWriteMode(); 
49
  LCD_EnableCommandMode(); 
50
51
  while (LCD_Data >= 0x80)
52
  {
53
    LCD_EnableSignal();
54
  }
55
  LCD_DDR_Data = 0xFF; // make dataport output
56
}
57
58
void lcd_init(void){
59
  
60
  /* set output pins */
61
  LCD_DDR_Control |= 1<<LCD_Enable | 1<<LCD_ReadWrite | 1<<LCD_RegisterSelect;
62
  LCD_DDR_Data = 0xFF;
63
  
64
  /* send 3 times reset command */
65
  LCD_Data = 0x30;
66
  LCD_EnableSignal();
67
  _delay_ms(10);
68
  LCD_EnableSignal();
69
  _delay_ms(10);
70
  LCD_EnableSignal();
71
  _delay_ms(10);
72
  
73
  /* enable 4bit mode */
74
  LCD_Data = 0x20;
75
  LCD_EnableSignal();
76
  _delay_ms(10);
77
  
78
  /* function set */
79
  LCD_EnableSignal();
80
  //_delay_ms(1);
81
  LCD_Data = 0x00;
82
  LCD_EnableSignal();
83
  //_delay_ms(1);
84
  
85
  /* display off */
86
  LCD_EnableSignal();
87
  //_delay_ms(1);
88
  LCD_Data = 0x80;
89
  LCD_EnableSignal();
90
  //_delay_ms(1);
91
  
92
  /* display clear */
93
  LCD_Data = 0x00;
94
  LCD_EnableSignal();
95
  //_delay_ms(1);
96
  LCD_Data = 0x10;
97
  LCD_EnableSignal();
98
  //_delay_ms(1);
99
  
100
  /* entry mode */
101
  LCD_Data = 0x00;
102
  LCD_EnableSignal();
103
  //_delay_ms(1);
104
  LCD_Data = 0x60;
105
  LCD_EnableSignal();
106
  //_delay_ms(1);
107
  
108
  /* initialization finished */
109
     
110
}
111
112
int main(void)
113
{
114
  _delay_ms(100);    
115
  lcd_init();      // init LCD in 4bit mode  
116
  
117
  /* print character 0x88 on LCD to verify correct init */
118
  LCD_Data = 0x88;
119
  LCD_DisableCommandMode();
120
  LCD_CheckBusy();
121
  LCD_EnableSignal();
122
  LCD_CheckBusy();
123
  LCD_EnableSignal();
124
  while(1);
125
}

: Verschoben durch User
von leluno (Gast)


Lesenswert?

LCD_Data = 0x88; => das ist 8bit, es fehlt die Umsetzung auf 4bit

von ChefKoch (Gast)


Lesenswert?

Das ist mir schon klar, aber das untere Nibble am Datenport hängt in der 
Luft. (Es ist nichts am μC angeschlossen.) Deshalb kann ich ja einfach 
mal alle Pins auf 1 setzen und 2x das Enable Signal senden.

An der Stelle war allerdings trotzdem ein Fehler. Ich habe zuerst die 
Daten in den Datenport geschrieben und dann die "LCD_CheckBusy()" 
Methode aufgerufen. Diese schaltet den Datenport aber von output auf 
input und wieder zurück => Es steht irgendwas in diesem Register. Dieser 
Fehler ist nun behoben. Die neue main() Methode siehe unten. Leider 
funktioniert es immer noch nicht. Der Rest des Codes blieb unverändert.
1
int main(void)
2
{
3
  _delay_ms(100);    
4
  lcd_init();      // init LCD in 4bit mode  
5
  
6
  /* print character 0x88 on LCD to verify correct init */
7
  
8
  LCD_DisableCommandMode();
9
  
10
  /* send higher nibble */
11
  LCD_CheckBusy();
12
  LCD_Data = 0x88;  
13
  LCD_EnableSignal();  
14
  
15
  /* send lower nibble */
16
  LCD_CheckBusy();
17
  LCD_Data = 0x88;
18
  LCD_EnableSignal();
19
20
  while(1);
21
}

von Felix P. (fixxl)


Lesenswert?

Du fragst in deiner Busy-Flag-Check-Funktion (schönes Wort ;-) ) das 
falsche Register ab, nämlich PORTD (Zustand der Pullup-Widerstände) und 
nicht PIND (High oder Low liegt am Eingang an).

Außerdem muss zur Abfrage des Busy-Flags immer am Enable-Anschluss 
"gewackelt" werden und die Abfrage während des High-Pegels erfolgen: 
http://www.ece.uidaho.edu/ee/classes/ECE341/datasheets/SamsungKS0066U.pdf 
(Seite 19, Figure 5).

von Bastler (Gast)


Lesenswert?

>// make dataport input
...schöner Kommentar.

Das erinnert mich an einen Kollegen.
> // unguilty parameters

Aber verstanden hat es jeder!

von ChefKoch (Gast)


Lesenswert?

Ok, das mit dem Busy Flag Check leuchtet ein :-)

So sollte es nach dem Samsung-Datenblatt für den KS0066U korrekt 
implementiert sein:
1
/*
2
*  check busy flag
3
*/
4
void LCD_CheckBusy(){
5
  LCD_DDR_Data = 0x00;  // make dataport input 
6
  LCD_DisableWriteMode(); 
7
  LCD_EnableCommandMode(); 
8
  
9
  uint8_t temp;
10
  do
11
  {
12
    LCD_Control |= 1<<LCD_Enable;  // enable on
13
    asm volatile ("nop");      // some time to think
14
    asm volatile ("nop");
15
    temp = PIND;
16
    LCD_Control &= ~1<<LCD_Enable;  // enable off
17
    
18
    LCD_EnableSignal();        // read and discard D3 (AC3) just to complete 8bit transmission 
19
    
20
  }while(temp >= 0x80);
21
  
22
  LCD_DDR_Data = 0xFF; // make dataport output
23
}

Leider bleibt mein Display immer noch leer. Es erscheint auch kein 
Cursor.

Der Rest des Codes blieb wieder unverändert.

von Felix P. (fixxl)


Lesenswert?

Die fehlenden Wartezeiten bzw. fehlenden Busy-Abfragen in der lcd_init() 
nach dem Umschalten auf den 4-bit-Modus könnten noch ein Problem sein.

Auf Seite 2 des im Eingangspost verlinkten Datenblatts bzw. auf Seite 18 
in der Samsung-Anleitung steht, wie lange die einzelnen Kommandos zur 
Ausführung benötigen (Execution Time), und auf S. 4 des Datenblatts gibt 
es den Hinweis, dass die Wartezeit länger als die Execution Time sein 
oder man das Busy-Flag abfragen muss.

Spendiere doch zunächst in der lcd_init() mal testweise ein bisschen 
Wartezeit (10ms), nachdem ein Kommando komplett übertragen worden ist, 
damit der Controller die Befehle verarbeiten kann. Wenn damit alles 
funktioniert, kannst du den Delay durch eine Busy-Flag-Abfrage ersetzen 
und schauen, ob deine Routine richtig ist.

von Felix P. (fixxl)


Lesenswert?

Es kann natürlich auch sein, dass nichts angezeigt wird, weil der Befehl 
"Display off" (0x08) bzw. bei dir 0x00 gefolgt von 0x80 sowohl das 
Display als auch den Cursor deaktiviert.

Was mir auch noch auffällt: Zwischen der Übertragung des oberen und 
unteren Nibbles wird das Busy-Flag nicht abgefragt. Ein sehr 
übersichtliches Schema für den 4-bit-Transfer gibt es in diesem PDF auf 
Seite 22: https://www.sparkfun.com/datasheets/LCD/HD44780.pdf.

von ChefKoch (Gast)


Lesenswert?

Vielen Dank für deine Hinweise Felix!

Mit der Abfrage des Busy-Flags zwischen den Nibbles und dem exakten 
Timing aus dem Datenblatt läuft es jetzt. :-)

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.