Forum: Mikrocontroller und Digitale Elektronik LCD-Display via AT89C5131 ansteuern


von Sascha H. (sascha_h80)


Angehängte Dateien:

Lesenswert?

Guten Morgen allerseits,

ich versuche schon seit 3 Wochen ein LCD Display (Typ: Dot-matrix LCD 
204B-LED, Datenblatt s. Anhang) mittels eines AT89C5131 anzusteueren.

Ich scheitere jedoch schon an der Initialisierung, obwohl ich das 
AVR-LCD Tutorial von mikrocontroller.net beinahe Auswendig kenne, und 
ich alle Schritte von der Beispielinitialisierung in mein Programm 
übernommen habe.

Ich weiß auch das die Zeit das A und O bei der Initialisierung ist, ich 
habe jedoch ausreichend große Wartefunktionen eingebaut, der Timer an 
sich läuft auch, trotzdem will es mit der Inititalisrung nicht klappen 
:/

Langsam stoße ich an meine Grenzen, ich bitte daher um eure Hilfe!

Vorneweg: Am µC liegt es nicht, dieser funktioniert eiwandfrei bei 
anderen Programmen.

Hier ist der Code:
1
/***********************************************************
2
    
3
    Programm: Wert auf LCD mittels AT89C5131 ausgeben
4
    Datum: 23.07.2014
5
    Rev.: 01
6
  
7
************************************************************/
8
9
#include <At89c5131.H> 
10
11
/********Port Pins für LCD definieren********/
12
#define  RS        P1_0      // Register Select
13
#define RW        P1_1      // RW = 1 -> Read
14
#define EN        P1_2      // Enable Display (1)
15
#define Reg_Port  P1        // Register-Port
16
#define DataPort  P2        // Datenport
17
#define DataLCD0  P2_0      // Daten Bit 0  (LSB)    
18
#define DataLCD1  P2_1      // Daten Bit 1      
19
#define DataLCD2  P2_2      // Daten Bit 2
20
#define DataLCD3  P2_3      // Daten Bit 3
21
#define DataLCD4  P2_4      // Daten Bit 4
22
#define  DataLCD5  P2_5      // Daten Bit 5
23
#define  DataLCD6  P2_6      // Daten Bit 6
24
#define  DataLCD7  P2_7      // Daten Bit 7  (MSB)
25
26
27
/*******Deklaration Funktionen******/
28
void lcd_init ();
29
void lcd_data( int data1 );
30
void lcd_command( char data2 );
31
static void lcd_enable( void );
32
static void lcd_out( char data3 );
33
void lcd_clear( void );
34
void wait (int zeit);  
35
36
37
38
/*********Hauptprogramm*********/
39
40
void main (void)
41
{
42
43
  lcd_init();                    // LCD Initialisierung
44
  lcd_out(01000011);            // ASCII = "C" 
45
  while (1);
46
  
47
48
49
50
  
51
}
52
53
 ///////////////////////////////////////////////////////////////////
54
// Initialisierung: muss ganz am Anfang des Programms aufgerufen werden.
55
void lcd_init( void )
56
{
57
    // Initial alle Ausgänge auf Null
58
    P1 = 0x00;
59
    P2 = 0x00;                                  
60
 
61
    // Warten auf die Bereitschaft des LCD      
62
    wait (150);
63
 
64
    // Soft-Reset muss 3mal hintereinander gesendet werden zur Initialisierung                                
65
    lcd_out(0x30);                    
66
    wait(50);                                    
67
 
68
    lcd_enable();    
69
    lcd_out(0x30);    
70
    wait(10);                                    
71
 
72
    lcd_enable();    
73
    lcd_out(0x30);    
74
    wait(10);
75
    lcd_enable();        
76
  
77
    // 8-bit Modus aktivieren 
78
    lcd_command(0x1C);                          // P2 = 00011100  
79
    wait(50);                                  
80
 
81
    // 8-bit Modus / 2 Zeilen / 5x7
82
    lcd_command(0xF0);                          // P2 = 11110000        
83
 
84
    // Display ein / Cursor aus / Blinken aus
85
    lcd_command(0x80);                           // P2 = 10000000          
86
 
87
    // Cursor inkrement / kein Scrollen
88
    lcd_command(0x60);                          // P2 = 01100000            
89
 
90
    lcd_clear();
91
}
92
 
93
////////////////////////////////////////////////////////////////////////////////
94
// Sendet ein Datenbyte an das LCD
95
void lcd_data( int data1 )
96
{
97
    P1 = 0x00;
98
    wait(100);  
99
    RS = 1;      
100
    wait(100);                  // RS auf 1 setzen
101
    lcd_out( data1 );           //  
102
    wait (100);                  // 1ms Warten
103
}
104
 
105
////////////////////////////////////////////////////////////////////////////////
106
// Sendet einen Befehl an das LCD
107
void lcd_command( char data2 ){
108
109
    P1 = 0x00; 
110
    wait(200);                  // RS auf 0 setzen
111
    lcd_out( data2 );           //  
112
    wait(200);
113
}
114
115
////////////////////////////////////////////////////////////////////////////////
116
// Erzeugt einen Enable-Puls
117
static void lcd_enable( void )
118
{ 
119
    P1_2 = 1;
120
    wait (200);                  // 100µS Warten
121
    P1_2 = 0;
122
    wait (200);
123
}
124
 
125
////////////////////////////////////////////////////////////////////////////////
126
// Sendet eine 4-bit Ausgabeoperation an das LCD
127
static void lcd_out( char data3 )
128
{
129
    P2 = data3 ;  
130
    wait(200);                  // 
131
    lcd_enable();                // fallende Flanke um Daten aus Bus zu übernehmen
132
    wait (200);
133
}
134
135
////////////////////////////////////////////////////////////////////////////////
136
// Sendet den Befehl zur Löschung des Displays
137
void lcd_clear( void )
138
{
139
    lcd_command( 0x80 );
140
    wait(200);
141
}
142
143
////////////////////////////////////////////////////////////////////////////////
144
// Timerfunktion
145
146
void wait (int zeit)      
147
{
148
  
149
  int i;
150
  TR0 = 0;                      //  Timer anhalten 
151
  TMOD = TMOD | 0x01;           //  Modus 1, 16-Bit-Zähler, interner Takt 
152
  for ( i=0 ; i<zeit ; i++)
153
  {
154
     TH0 = 0xFF;                 //High-Byte 0b11111111 
155
    TL0 = 0x9B;                 //Low-Byte  0b11111110 
156
    TR0 = 1;                    //Timer starten 
157
    while(TF0==0);              //Auf Überlauf des Zählregisters warten
158
    TF0 = 0;                    //Überlauf zurücksetzen
159
  }
160
}

von Ronny S. (phoenix-0815)


Lesenswert?

Hallo,
schau Dir das mal an und Teste es dann mal.

http://homepage.hispeed.ch/peterfleury/avr-software.html#libs

LCD library for HD44870 based LCD's

Gruß Ronny

von Sascha H. (Gast)


Lesenswert?

Ronny S. schrieb:
> Hallo,
> schau Dir das mal an und Teste es dann mal.
>
> http://homepage.hispeed.ch/peterfleury/avr-software.html#libs
>
> LCD library for HD44870 based LCD's
>
> Gruß Ronny

Zuerst einmal Vielen Dank für deine Antwort.

Allerdings solltest du dir auch mein Programm anschauen, denn ich gehe 
genau so vor wie in dem Tutorial auf der Seite beschrieben. Das Programm 
dort kann ich nicht verwenden, da ich Keil µVision 4 als Compiler 
benutze und der AT89C5131 auch ganz anders anzusteuern ist als der dort 
verwendete Controller.

Der AT89... ist einer der am einfachsten anzusteuernden Controller, wie 
man an meinem Programm sieht.

Versteht mich nicht falsch, ich suche hier keinen der mir meine Arbeit 
abnimmt und eine fertige Init für meinen Controller schreibt, ich suche 
einfach nur jemanden der Erfahrung mit dem AT89... hat und sich mein 
Programm anschaut und mir einen Denkanstoß gibt, wo der Fehler sein 
könnte.

Ich habe nämlich mittlerweile das halbe Internet nach LCD-Inits in C 
durchforscht und so ziemlich alles ausprobiert, was ich dort gelesen 
habe. Nichts hat geholfen, deshalb bin ich so langsam mit meinem Latein 
am Ende

von Bernd N (Gast)


Lesenswert?

Laut Datenblatt...

cursor_off      0x0C
cursor_blink    0x0D
cursor_on       0x0E
clear_display   0x01
cursor_home     0x02
entry_mode      0x38

Also z.B. Init 3x 0x38, Cursor OFF, Clear Display, Cursor Home

von Ralf (Gast)


Lesenswert?

Was mir so auf die Schnelle(tm) auffällt:
1) du verwendest als Parameter char -> bist sicher dass es automatisch 
ein unsigned char ist (bei char ist das ggf. Compiler-abhängig, bei int 
i.d.R. ohne Angabe = signed) ?
2) du verwendest als Parameter int -> warum 16-bit für eine 8-bit 
Operation?
3) du verwendest als Parameter int -> das dürfte definitiv mit 
Vorzeichen sein.

Ralf

von Peter D. (peda)


Lesenswert?

Sascha H. schrieb:
> lcd_out(01000011);            // ASCII = "C"

Der Kommentar sollte immer zum Code passen!
lcd_out ???
01000011 = 0x40009 = 262153

Sascha H. schrieb:
> TH0 = 0xFF;                 //High-Byte 0b11111111
>     TL0 = 0x9B;                 //Low-Byte  0b11111110

Der Kommentar sollte immer zum Code passen!
Seit wann ist 0x9B == 0b11111110 ?

Man kann es auch leserlich schreiben, dann ist ein Kommentar 
überflüssig:
1
#define hi(x) ((x)>>8)
2
#define lo(x) ((x)&0xFF)
3
4
  TH0 = hi( -100 );
5
  TL0 = lo( -100 );

Dein Delay ist also bei 12MHz: zeit * 100µs

Sascha H. schrieb:
> static void lcd_enable( void )
> {
>     P1_2 = 1;
>     wait (200);                  // 100µS Warten
>     P1_2 = 0;
>     wait (200);
> }

Der Kommentar sollte immer zum Code passen!
Du wartest nämlich 20ms.
Du kannst dem Datenblatt ruhig glauben, wenn da steht, daß 500ns 
reichen.

von Sascha H. (sascha_h80)


Lesenswert?

Bernd N schrieb:
> Laut Datenblatt...
>
> cursor_off      0x0C
> cursor_blink    0x0D
> cursor_on       0x0E
> clear_display   0x01
> cursor_home     0x02
> entry_mode      0x38
>
> Also z.B. Init 3x 0x38, Cursor OFF, Clear Display, Cursor Home

Vielen, vielen Dank, damit hat es geklappt!

Allerdings ist mir nicht ersichtlich wo du diese Werte hernimmst? Im 
Initialisierungsbeispiel im DB steht für 8 Bit folgende Reihenfolge und 
Werte:

1x Function Set 0x1C
1x Disp ON/OFF  0xF0
1x Clear Display 0x80
1x Entry Mode Set 0x60
__________________________________________

@ Ralf

Danke für deinen Tipp mit den Variablen, in der Schule haben wir fast 
nur mit Integer gearbeitet deswegen ist es eine Standard-Variable bei 
mir geworden, da habe ich noch garnicht daran gedacht.
___________________________________________

@ peda

Tut mir leid dass nicht alle Kommentare zum Code passen, durch das viele 
herumprobieren in den letzten Wochen ist da einiges durcheinander 
geraten.




____________________________________________________

Jetzt fehlt mir nur noch ein Zeichen auf dem Display auszugeben...
Ich wollte ein einfaches "C" darstellen, in ASCII "C" -> 0b01000010 = 
0x42
Dieses habe ich dann einfach an Port 2 gesendet und danach die 
Enable-Funktion aufgerufen, trd klappt dies nicht. Gibt es da etwas zu 
beachten?

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

Hi

>Allerdings ist mir nicht ersichtlich wo du diese Werte hernimmst? Im
>Initialisierungsbeispiel im DB steht für 8 Bit folgende Reihenfolge und
>Werte:

>1x Function Set 0x1C
>1x Disp ON/OFF  0xF0
>1x Clear Display 0x80
>1x Entry Mode Set 0x60

Du musst die Binärwerte in der richtigen Reihenfolge lesen:

Function set     0 0 1 1  1 0 0 0
                    3        8

>Dieses habe ich dann einfach an Port 2 gesendet und danach die
>Enable-Funktion aufgerufen, trd klappt dies nicht. Gibt es da etwas zu
>beachten?

Um ein Zeichen zu schreiben muss auch RS auf 1 gesetzt werden.

MfG Spess

von Sascha H. (sascha_h80)


Lesenswert?

spess53 schrieb:
> Du musst die Binärwerte in der richtigen Reihenfolge lesen:
>
> Function set     0 0 1 1  1 0 0 0
>                     3        8
>

Aber ich habe an P2_0 doch DB0 und an P2_7 DB7 anliegen, also ist die 
Reihenfolge doch eig andersherum als im Datenblatt oder?

Zudem sind im Datenblatt als Init.-Beispiel auch andere Funktionen 
angegeben als die, die Bernd N. hier angegeben hat.

Init.-Beispiel aus Datenblatt:

1x Function Set 0x1C
1x Disp ON/OFF  0xF0
1x Clear Display 0x80
1x Entry Mode Set 0x60

> Um ein Zeichen zu schreiben muss auch RS auf 1 gesetzt werden.

Vielen Dank, es klappt nun!

von spess53 (Gast)


Lesenswert?

Hi

>Aber ich habe an P2_0 doch DB0 und an P2_7 DB7 anliegen, also ist die
>Reihenfolge doch eig andersherum als im Datenblatt oder?

Nein. In einer Binärzahl steht das höherwertigste Bit immer links:

 P2_7 P2_6 P2_5 P2_4 P2_3 P2_2 P2_1 P2_0
  DB7  DB6  DB5  DB4  DB3  DB2  DB1 DB0

   0    0    1    1    1    0    0   0
           3                  8

MfG Spess

von Ralf (Gast)


Lesenswert?

Guten Morgen Sascha,

> Allerdings ist mir nicht ersichtlich wo du diese Werte hernimmst? Im
> Initialisierungsbeispiel im DB steht für 8 Bit folgende Reihenfolge und
> Werte:
>
> 1x Function Set 0x1C
> 1x Disp ON/OFF  0xF0
> 1x Clear Display 0x80
> 1x Entry Mode Set 0x60
Falsch, uns ist nämlich nicht ersichtlich woher DU DEINE Werte 
hernimmst. Wenn du dir das DB nochmal anguckst wirst du sehen, dass da
1x Function Set 0x1C (korrekt)
1x Disp ON/OFF  0xF0 (falsch) -> 0x0F
1x Clear Display 0x80 (falsch) -> 0x01
1x Entry Mode Set 0x60 (falsch) -> 0x06
steht.
Allerdings steht im DB ja auch beispielsweise, dass 0x0F nicht nur Disp 
ON (nicht OFF!!!) ist, sondern gleichzeitig auch blinkender Cursor ist - 
du siehst also, es kommt auf die einzelnen Bits an.
Deswegen halte ich das:
> cursor_off      0x0C
> cursor_blink    0x0D
> cursor_on       0x0E
für falsch. M.E. müsste es so sein:
cursor_off      0x00
cursor_on       0x02
cursor_blink    0x01
cursor_static   0x00
display_on      0x04
display_off     0x00
display_ctrl    0x08
Den Aufruf macht man dann durch eine Veroderung der Parameter:
lcdcmd(display_ctrl | cursor_on | display_on);

> Danke für deinen Tipp mit den Variablen, in der Schule haben wir fast
> nur mit Integer gearbeitet deswegen ist es eine Standard-Variable bei
> mir geworden, da habe ich noch garnicht daran gedacht.
Mmmmh.... vergiss bitte den Stuss aus der Schule - vorne am Pult sitzt 
auch nur einer, der das amateurhaft macht und dir vielleicht erzählen 
kann wie man ein MCU-Board und zugehörige IDE in Betrieb nimmt. Und 
falls ihr in der Schule nur Programme für den PC gemacht habt isses 
nochmal anders als für Controller (da ist das mit "immer nur int" nicht 
ganz so schlimm).

Faktisch gelten für's erste(!) folgende Regeln:
1) generell den Variablentyp entsprechend der Architektur wählen -> also 
8-bit/char für z.B. 8051er 8-bit MCU (so wie du einen hast) und 32-bit 
für einen ARM 32-bit MCU. Das macht man, weil der Controller bzw. 
Compiler dann nicht erst Befehle generieren muss, um das passend zu 
behandeln. Kleines Beispiel: ein 8051 (8-bit MCU) soll einen int 
(16-bit) verarbeiten. Als 8-Bitter kann er aber nur 8-bit Rechnungen 
durchführen, für einen int muss er also mehrere 8-Bit-Befehle ausführen.
Umgekehrt kann für einen 32-Bitter eine char-Variable (8-Bit) ein 
kleines Problem sein, wenn aus dem RAM nur 32-Bit am Stück gelesen 
werden können: dann müssen die relevanten acht Bit maskiert werden, dann 
die Berechnung, und dann wieder zurück schreiben, ohne die anderen 24 
Bits zu beeinflussen.
2) immer den Variablentyp entsprechend der Aufgabe wählen -> wenn du nur 
einen Zähler für sagen wir zehn Schleifendurchläufe benötigst, brauchst 
du weder einen int noch einen long - da tut's ein char -> Begründung 
siehe 1).
3) wenn Variablen keinen negativen Bereich benötigen, dann immer 
unsigned anlegen. Die meisten Controller benötigen für Rechnungen mit 
Vorzeichen zusätzlichen Aufwand, d.h. es wirkt sich ggf. auf die 
Laufzeit aus.

So, und wie passt nun 2) zu 1), wenn man "RAM-sparend" programmieren 
muss? Auf nem 32-Bitter wählst du für globale Variablen den nötigen Typ 
(siehe 2). In Funktionen legst du lokale Variablen für Schleifen etc. 
nach 1) an. Der Speicher der lokalen Variablen steht nach der Funktion 
ja wieder zur Verfügung, daher bietet es sich eben an, den Typ zu 
wählen, mit dem am schnellsten gearbeitet werden kann.

> Tut mir leid dass nicht alle Kommentare zum Code passen, durch das viele
> herumprobieren in den letzten Wochen ist da einiges durcheinander
> geraten.
Was Peter sagen wollte ist einfach, dass Kommentare kein notwendiges 
Übel, sondern auch ein Hilfsmittel für den Programmierer sind. Viele 
lassen die Kommentare ganz weg und checken zwei Tage später nicht mehr, 
was sie da gemacht haben.
Richtig angewandt helfen sie dir sogar Fehler zu entdecken. Du hast ja 
bei Peter gesehen, dass er sofort Unterschiede zwischen Kommentar und 
Befehl erkannt hat.

> Jetzt fehlt mir nur noch ein Zeichen auf dem Display auszugeben...
> Ich wollte ein einfaches "C" darstellen, in ASCII "C" -> 0b01000010 =
> 0x42
> Dieses habe ich dann einfach an Port 2 gesendet und danach die
> Enable-Funktion aufgerufen, trd klappt dies nicht. Gibt es da etwas zu
> beachten?
Ein ASCII-"C" ist 0x43, wenn ich mich recht entsinne ;)
 Wie hast du das Zeichen geschickt? Denk dran, dass du in dem Fall nicht 
ein Kommando, sondern Daten schicken willst, das heisst du musst RS 
passend bedienen...

Ralf

von Sascha H. (sascha_h80)


Lesenswert?

Vielen, vielen Dank für eure Antworten, es klappt nun mittlerweile alles 
wie gewollt und dank eurer teils sehr ausführlichen Antworten habe ich 
auch einiges dazu gelernt.

Finde ich gut dass hier einem geholfen wird, obwohl ein Thema wie 
Display-Initialisierung hier schon 100000x durchgekaut wurde und es den 
meisten Usern zum Halse raushängen dürfte.

Genau deshalb ein dickes Dankeschön an alle die mir geholfen haben!

Schönen Tag wünsche ich euch noch ;-)


______________________________________________________________________

CLOSED

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.