Forum: Mikrocontroller und Digitale Elektronik 44780 initialisierung klappt nicht (8051, Keil)


von ZuDummFürLCD (Gast)


Lesenswert?

Hallo,

ich habe hier ein LCD mit einem 44780 und mit 2*40 Reihen, was ich nicht 
initalisiert bekomme.
Ein Handbuch hab ich dazu nicht.
Das Display funktioniert einwandfrei in einer anderen Schaltung, die ich 
so übernommen habe und auch past.
Der µ-Controller wird 1-2min nach dem Display eingeschaltet, dort gibt 
es also keine Timing Probleme.

Der µ-Controller ist ein 89C51RE2 von Atmel und ich verwende die freie 
Version von Keil uVison3 bis 2kb Code.

Hab versucht mich an den Sachen von folgenden links zu orientieren:
Beitrag "LCD - Ansteuerung"
http://www.sprut.de/electronic/lcd/index.htm#strom

So nun zu meinem Code:

//hier beginnt die Initalierung des LCD:
  disp_RS=0;

  disp_out(0x38,45);
  disp_out(0x38,15);
  disp_out(0x38,15);

  disp_out(0x0e,15);
  disp_out(0x01,15);
  disp_out(0x06,15);

//hier die Funktion disp_out:
void disp_out(unsigned char p, unsigned char t)
{
  disp_EN=0;
  disp_port=p;
  disp_EN=1;
  disp_EN=0;
  zeit=t;
  while(zeit)
  {
  }
}

//Die variable zeit wird im timer0 decrementiert.
//Hier das Init vom Timer:

  //timer0 16bit zähler
  TMOD=0x021;

  //timer0 für interrupt alle 0,333ms
  TH0 =0x0E8;
  TL0 =0x006;
  TR0  =1;

ET0 und EA werden später gesetzt.
Nach meiner Rechnung müßte der alle 0,33ms kommen.
Ich verwende einen 18.432Mhz Quarz und der µ-Controller braucht 6takte 
pro Befehl.
R/W vom Display liegt an GND. Geht auch leider nicht anders.
Am Kontrast hab ich auch schon gedreht und ich kann mit einem 
Oszilloskop sehen wie die Befehle an den Pins des 44870 ankommen. 
Jedenfalls son zucken :)
Die variable zeit hab ich auch schonmal höher und kleiner gemacht, hat 
nicht viel gebracht.

Jetzt weiß ich echt nicht mehr weiter. Wo liegt mein Fehler?
Sehe mittlerweile auch den Wald vor lauter Bäumen nicht mehr...

von Ralf (Gast)


Lesenswert?

1. bitte kompletten (relevanten) Code posten
2. bitte kompletten (relevanten) Code mit Kommentaren posten

Siehst du denn bei der Kontrastregelung einen Effekt? Stell den Kontrast 
zu Beginn ziemlich hart ein. Nach einem Reset ist normalerweise immer 
eine Zeile dunkel. Nach den Initialisierungsbefehlen verschwindet die 
dunkle Zeile.
Wenn du also den Kontrast sichtbar einstellen kannst, die dunkle Zeile 
nach dem Einschalten erscheint und nach der Initialisierung 
verschwindet, bist du fast fertig. Also wie weit kommst du?

Ralf

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

> Der µ-Controller wird 1-2min nach dem Display eingeschaltet, dort gibt
> es also keine Timing Probleme.
Schon das könnte ein Timingproblem sein.

> Sehe mittlerweile auch den Wald vor lauter Bäumen nicht mehr...
Teile und herrsche.
Ja, wie wäre es denn, wenn du erst mal deinen Timer kontrollierst?
Einfach im Interrupt einen Pin toggeln und kontrollieren, ob die Zeit zu 
deinen erwarteten 330us passt.


BTW:
Dir ist klar, dass du hier ein Semaphorenproblem bekommen kannst:
  disp_EN=0;
  disp_port=p;
  disp_EN=1;
  disp_EN=0;
  zeit=t;
  while(zeit)
  {
  }
Während der Zuweisung von zeit solltest du den Interrupt sperren...

von Matthias (Gast)


Angehängte Dateien:

Lesenswert?

Versuch erstmal das Display ganz ohne Timer mit reichlich 
dimensionierten Warteschleifen zum laufen zu bringen.

Als Anlage ein C-Programm, was bei mir auf AT89C51ED2 läuft. Zwar für 
SDCC, aber leicht an Keil anzupassen.

von ZuDummFürLCD (Gast)


Lesenswert?

@Ralf
Das ist (fast) mein kompleter C-Code. Alles andere hab ich mittlerweile 
rausgeworfen.
Und was soll ich da noch mehr Kommentieren?

Denn Kontrast kann ich so einstellen das nur die untere Reihe da ist. 
Die bleibt aber auch. Da passiert nix.

@Lothar
Wiso könnte da ein Problem sein wenn der µ-Controller so lange nach dem 
LCD angeht?
Das mit dem PIN ist eine gute Idee. Werde ich mal ausprobieren.

Und stimmt, das mit zeit hab ich so noch gar nicht gesehen - danke :)

@Matthias
Das mit den Schleifen hab ich auch schon ausprobiert. Hab ich damit auch 
nicht so hinbekommen und da ich nicht so richtig wußte wie lange so eine 
Schleife dauert, hab ichs jetzt so probiert.
Werde mir das Stück C-Code morgen mal genauer angucken.

Danke erstmal für die Hilfe

von Anja (Gast)


Lesenswert?

Hallo,

folgende Fragen:
a)
wie wird disp_EN initialisiert (mindestens 15 ms vor dem ersten Aufruf 
von disp_out) üblicherweise ist nach dem Reset alles auf High-pegel beim 
8031. Dies würde also eine Timing-Verletzung bedeuten.

b)
wird im Interrupt das Interrupt-Flag des Timers zurückgesetzt? (oder 
wird der Timer-Interrupt als Endlosschleife durchlaufen).

c)
Bist Du dir sicher ob der Enable-Impuls lang genug ist? Ich kann mich 
dunkel erinnern daß die Maximalfrequenz für einen sichern Betrieb eines 
LCD bei ca 11 MHz Prozessortakt liegt. (Ich weiß allerdings nicht mehr 
ob dies nur für den Betrieb am Datenbus gilt oder auch am I/O-Port).

von ZuDummFürLCD (Gast)


Angehängte Dateien:

Lesenswert?

Hallo nochmal...

hab gerade mal den Tip von Lothar probiert und den Timer-Takt auf einen 
Pin gelegt.
Da hab ich festgestellt das mein Timmer alle 0,4s kommt. Egal was ich in 
TH0 und TL0 einstelle.
Ist also ein Timer Problem. Es ist egal ob in TH0 0xFF oder 0x00 steht 
immer sind es 0,4s, dann kann es ja auch nicht funktioneren...

Hat jemand eine idee warum? Eigentlich müßte Timer0 doch richtig 
eingestellt sein? Hab mal meinen ganzen Code angehängt.

von R. W. (quakeman)


Lesenswert?

Du kannst es alternativ mal mit den fertigen Codebeispielen von Meba 
probieren. Ich benutze die schon seit Jahren und sie funktionieren 
problemlos. Der Code ist in ASM und C geschrieben für 8051 kompatible 
Controller und Keil-uVision Umgebung.

Sämtlichen LCD-Code bekommst du auf seiner Homepage unter:
http://www.c51.de/c51.de/Dateien/c51_buecher.php?Korb=1&UIN=
Dort dann den Link "Buch_PraxisTeil2_Kap7.ZIP" herunterladen. Dort ist 
Code für 8Bit und 4Bit Ansteuerung drin.

Und du bekommst auf seiner Homepage auch ein Plugin für uVision, welches 
genau diese LCDs simuliert, womit du deinen Code rein am PC schon testen 
kannst.

Demoversion Erhältlich unter:
http://www.c51.de/c51.de/Dateien/LCD.php?UIN=
Dort dann den Link "lcd.zip" herunterladen.
Die Demoversion ist schon ausreichend um das LCD im 4Bit Modus komplett 
zu simulieren. Funktioniert ebenfalls wunderbar bei mir. :)

Ciao,
     Rainer

von Matthias (Gast)


Lesenswert?

>Da hab ich festgestellt das mein Timmer alle 0,4s kommt. Egal was ich in
>TH0 und TL0 einstelle.

Wenn Du Dir mein Bsp. weiter oben mal genauer angesehen hättest, 
besonderes die TIMER0ISR, dann wär Dir aufgefallen, dass TH0 und TL0 in 
der ISR jedesmal nachgeladen wird um den Zählumfang des Timers zu 
verkürzen. Du machst das nicht, somit läuft Dein Zähler immer von 0x0000 
bis 0xFFFF. Damit dürften die 400ms zu erklären sein.

Insgesamt sollte sich das LCD nicht an so langen Pausen stören, die 
Ausgabe würde nur entsprechend in Zeitlupe erfolgen.

von R. W. (quakeman)


Lesenswert?

Also du hast in deinem Code schonmal einen grundlegenden Denkfehler 
drin.
Der Timer0 arbeitet als 16Bit Timer, besitzt aber keine auto-reload 
Funktionn in diesem Modus. Du initialisierst zu Beginn in deiner init() 
Methode die Timer Register mit TH0=0x0FF und TL0=0x000 (wieso überhaupt 
dreistellige Hex Zahlen, es sind ja nur 8Bit Register).
Das heißt, der Timer läuft das erste mal auch vom richtigen Startwert 
los. Aber sobald er einmal übergelaufen ist startet er wieder bei 
0x0000. Du mußt also in der ISR des Timer0 die Register TH0 und TL0 
immer wieder neu mit deinen Reload Werten befüllen, wenn du den 
gewünschten Effekt erreichen willst.

Mit deinem jetzigen Code wird bei 18.432MHz und Standardeinstellungen 
alle ~42,6ms der Portpin umgeschaltet (wegen dem fehlenden Reload). 
Deine 0,4s sind für mich nicht nachvollziehbar.

Um ehrlich zu sein, finde ich dein Herangehensweise im Code um das LCD 
anzusteuern etwas merkwürdig. Ich würde deshalb erst mal versuchen 
funktionierenden LCD Code zu benutzen um Softwarefehler auszuschließen. 
Und falls das LCD damit funktioniert kannst du dich an eine eigene 
Implementierung wagen. :)

Ciao,
     Rainer

von ZuDummFürLCD (Gast)


Lesenswert?

Ahh jetzt verstehe ich das mit dem 16Bit timer. Dachte das wäre wie mit 
dem 8Bit timer.

@MAtthias
Ja hab ich jetzt auch gesehen. Muß aber gestehen das ich deinen Code 
nicht verstehe. Bin noch C anfänger.

@Fox Mulder
ich wußte gar nicht das es sowas schon so komplet fertig gibt. Dacht bis 
jetzt immer das man sich sowas immer selber schreibt.
Mach das ganze hier auch komplet autodidaktisch. Hab mit µ-Controllern 
noch nie richtig was zu tun gehabt - studier Maschinbau.

Wie binde ich das denn jetzt richtig ein? Hab die LCD_Lib_8BitPort.h 
eingebunden. Beim Aufruf der Funktion uc_LCDIni(_8BIT | _2LINE) bekomme 
ich vom Compiler zwar keine fehlermeldung im Debugger sehe ich aber auch 
keine Änderung des Ausgangsport.
Die Pin belegung hab ich in der .a51 angepast.
Ist das so alles richtig? Was macht so eine .a51?

von R. W. (quakeman)


Angehängte Dateien:

Lesenswert?

A51 steht für assembler Dateien. Denn die eigentliche Ansteuerung ist 
aus Effizienzgründen in Assembler geschrieben. Du bruchst zum Einbinden 
in dein Projekt nur die zwei Dateien LCD_Lib_4BitPort.h und 
LCD_Lib_4BitPort.a51. In der Header Datei (*.h) stehen die gesamten 
Definitionen für die LCD Bibliothek drin. Im Normalfall brauchst du dort 
nur den Eintrag "#define LCD_PORT P1" auf den von dir verwendeten Port 
abzuändern (z.B. P0, P1, P2, ...). In der .a51 Datei brauchst du dann 
keine Änderungen vorzunehmen. Standardmäßig wird der Port dann wie folgt 
mit dem LCD verbunden:

LCD_EN = LCD_PORT.0;
LCD_RW = LCD_PORT.1;
LCD_RS = LCD_PORT.2;
LCD_D0 = LCD_PORT.4;
LCD_D1 = LCD_PORT.5;
LCD_D2 = LCD_PORT.6;
LCD_D3 = LCD_PORT.7;

Falls dir diese Anordnung nicht passen sollte, dann kannst du in der 
LCD_Lib_4BitPort.a51 die sbit Definitionen von Hand anpassen.

Um nun die Bibliothek zu verwenden musst du zu Beginn die Header Datei 
in dein Projekt einbinden mit

#include <LCD_Lib_4BitPort.h>

Dadurch stehen dann automatisch alle LCD Funktionen zur Verfügung. Nun 
musst du das LCD initialisieren in deinem Programm. Das machst du z.B. 
mit

// 4Bit Modus, 2 oder mehr Zeilen
uc_LCDIni(_4BIT | _2LINE);
// Cursor ausgeschaltet und nicht blinken, LCD eingeschaltet
v_SETLCDIns(CONTROL | CURSOR_OFF | NO_BLINK | DISPLAY_ON);

Nun kannst du den nicht mehr sichtbaren Cursor auf dem Display an eine 
gewünschte Position verschieben mit

// Cursor in Zeile 1 Spalte 10
v_SETLCDIns(SET_DD_RAM | (_1_LINE + 10));

Und zu guter letzt kannst du dann ein Zeichen ausgeben an dieser Stelle 
mit

// Zeichen 'x' auf dem LCD ausgeben
putchar('x');

Es stehen auch noch weitere Konfigurationsparameter zur Verfügung. Z.B. 
kannst du einstellen, daß die Zeichen von rechts nach links ausgegeben 
werden sollen mit

// Automatische Adressweiterschaltung dekrementieren
v_SETLCDIns(ENTRY | DECREMENT);

Das LCD hat intern eine automatische Adressweiterschaltung, weshalb du 
nicht für jedes Zeichen die Position angeben musst. Von der einmal 
angegebenen Position aus kannst du fortlaufend Text ausgeben und das LCD 
wechselt nach jedem Zeichen an die nächste Stelle (default eins nach 
rechts).

Ich habe dir mal ein komplettes Beispielprojekt für uVision angehängt 
mit einem 4x20 LCD. Ich habe in dem Projekt auch das Plugin eingebungen, 
mit welchem man LCDs im Debug Modus direkt simulieren kann. Falls du das 
Plugin von der Homepage nicht installiert haben solltest kann ich dir 
das nur empfehlen. Damit kannst du deine Software für das LCD komplett 
in uVision debuggen.
Ich habe das Programm gut dokumentiert, also solltes du schnell die 
Zusammenhänge erkennen können. :)

Ciao,
     Rainer

von Klaus (Gast)


Lesenswert?

@ZuDummFürLCD

Sorry! Ich vermisse die Information ob du im 4- Bit oder 8-Bit modus, 
das LCD ansteuerst.

So wie ich das sehe hälst du das Timing bei der Initialiserung nicht 
ein.

Und lass für den Anfang den quatsch mit dem Timerinterrupt. So lange 
Timer nicht beherscht.
Versuchs erstmal das Timing lt. Datenblatt mit Warteschleifen zu 
realisieren.

Wenn der Cursor auf der 1 Zeile Blinkt und ein Zeichen ausgeben kannst 
kannste weitermachen.

In deiner disp_warte routine scheinen die Zeiten, die da übergibts, zu 
kurz zu sein. Das Timing zum setzen und Rücksetzen ist hier ne 
entscheidende sache.

Machst das zu schnell hat der LCD-Controller keine Zeit die Daten 
abzuholen. Lieber etwas langsamer am Anfang.
Kannst ja mal versuchen das mit BUSY-Flag abfrage zu realisieren.

Gruß Klaus

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.