Schönen Sonntag Abend!
Ich versuche gerade ein 40x4 Zeichen LCD - Modul (NHD-0440WH-ATMI-JT#,
Datenblatt:
https://www.newhavendisplay.com/specs/NHD-0440WH-ATMI-JT.pdf) an einem
AtMega 2560 zum Laufen zu bringen.
Im Datenblatt des LCDs ist ein Beispiel angegeben wie man das LCD Modul
initialisiert.
Das verwende ich, aber irgendwie funktioniert es nicht wirklich.
In der ersten Zeile des Displays sind immer alle Pixel aktiv, trotz
löschen des Displays in der initLCD Funktion.
Einen Verdrahtungsfehler schließe ich aus, wenn ich nämlich den Cursor
auf das erste Zeichen in der ersten Zeile setzt und blinken lasse, dann
sieht man dass das Kästchen auf dem Display flackert ("blinkender
Cursor" aber nicht wirklich).
Wenn ich einen Verdrahtungsfehler gemacht hätte (E1 Leitung nicht
angeschlossen) dann würde das nich passieren, die obere Hälfte würde
dann einfch "nichts tun".
Ja, der Kontrast ist korrekt eingestellt, wenn ich den Cursor in Zeile 3
blinken lasse dann sehe ich das ohne Probleme.
Hier ist der Code:
1
/*
2
3
Ports:
4
######
5
6
7
PB1 SCK for programmer
8
PB2 MOSI for programmer
9
PB3 MISO for programmer
10
11
PC0 LCD D0
12
PC1 LCD D1
13
PC2 LCD D2
14
PC3 LCD D3
15
PC4 LCD D4
16
PC5 LCD D5
17
PC6 LCD D6
18
PC7 LCD D7
19
20
PG0 LCD E2
21
PG1 LCD E1
22
PG2 LCD RS
23
24
*/
25
26
#define F_CPU 16000000UL //Clock is 16MHz
27
28
#include <avr/io.h>
29
#include <util/delay.h>
30
31
void LCD_Command_TOP(char i) //Top half of the display
32
{
33
PORTC = i;
34
PORTG = PORTG & 0b11111011; //Set RS to 0
35
PORTG = PORTG | 0b00000010; //set E1 to 1
36
_delay_ms(2);
37
PORTG = PORTG & 0b11111101; //set E1 to 0
38
}
39
void LCD_Command_BOT(char i) //Bottom half of the display
Pieter schrieb:> ist das richtig?>> 76 _delay_ms(5);>> 77 _delay_ms(0x38); ???>> 78 //Function Set = 8bit mode; 2‐line; 5x8>> 79 LCD_Command_BOT(0x38); und TOP???
Das "delay 0x38" finde ich auch etwas seltsam, aber so steht es im
Datenblatt.
Das mit dem fehlenden "LCD_Command_TOP(0x38)" steht ebenso im
Datenblatt, deshalb habe ich es ohne nachzudenken übernommen.
(Ich dachte "vielleicht muss man ja nur dem 'unteren' LCD controller
sagen dass 8 bits verwendet werden und der gibt das dann an den oberen
controller weiter")
Wenn ich "LCD_Command_TOP(0x38)" dazuschreibe geht es perfekt!
Was lerne ich daraus:
Datenblätter sind nicht perfekt und enthalten auch Fehler!
Danke!
Ich würde ja sagen die E Signale sind falsch. 2 ms High und 0 ms Lo
passt irgendwie nicht zusammen.
LCD_Data bzw LCD_Comnand Funktionen machen nichts mit dem Parameter. Das
Ding dürfte nicht ohne Warnings compilieren.
Thomas Z. schrieb:> Ich würde ja sagen die E Signale sind falsch. 2 ms High und 0 ms Lo> passt irgendwie nicht zusammen.> LCD_Data bzw LCD_Comnand Funktionen machen nichts mit dem Parameter. Das> Ding dürfte nicht ohne Warnings compilieren.
Was?
Erste Zeile in den Funktionen:
PORTC = i;
Das ist nicht "nichts damit machen" ...
Thomas Z. schrieb:> Andre G. schrieb:>> Das ist nicht "nichts damit machen" ...>> stimmt hatte ich überlesen, das E Signal ist trotzdem komisch.
Naja wenn ich noch mehr Verzögerungen einfüge, dann würde ein kompletter
Schreibzyklus dauert eh schon ungefähr 2 Sekunden. (Ein Zeichen in jedes
"Kästchen" schreiben)
Und es funktioniert ja ...
Ein kompletter E Cycle soll 1200ns dauern, dabei ist die minimale
Pulslänge für E high minimal 140ns. 1ms ist also deutlich übertrieben.
Selbst wenn man alle Zeiten gegenüber dem DB verdoppelt, kommt man mit
2µs aus, davon 1µs high und min. 1µs low.
Siehe das DB des LCD, Seite 7.
Matthias S. schrieb:> Ein kompletter E Cycle soll 1200ns dauern, dabei ist die minimale> Pulslänge für E high minimal 140ns. 1ms ist also deutlich übertrieben.> Selbst wenn man alle Zeiten gegenüber dem DB verdoppelt, kommt man mit> 2µs aus, davon 1µs high und min. 1µs low.> Siehe das DB des LCD, Seite 7.
Wieso hat der Hersteller in dem Beispielcode dann so ewig lange
Wartezeiten?
Um "copy paste" - Programmierer zu ärgern?
Andre G. schrieb:> Wieso hat der Hersteller in dem Beispielcode dann so ewig lange> Wartezeiten?
Woher soll ich das wissen? :-P
Vllt. kannten die Autoren den '_delay_us()' Aufruf noch nicht, der kommt
erst im Kurs für Fortgeschrittene?
Ernst nehmen sollte man die langen Zeiten in der Init Phase. Da kann man
ruhig etwas länger stillhalten. Ob man da 15ms wartet oder 50ms, ist
auch unerheblich.
Matthias S. schrieb:> Andre G. schrieb:>> Wieso hat der Hersteller in dem Beispielcode dann so ewig lange>> Wartezeiten?>> Woher soll ich das wissen? :-P> Vllt. kannten die Autoren den '_delay_us()' Aufruf noch nicht, der kommt> erst im Kurs für Fortgeschrittene?> Ernst nehmen sollte man die langen Zeiten in der Init Phase. Da kann man> ruhig etwas länger stillhalten. Ob man da 15ms wartet oder 50ms, ist> auch unerheblich.
Ja, das ist mir schon klar.
Die Initialisierung kann ruhig lange dauern, das stört niemanden (oder
mich nicht).
Ich habe ein wenig herumprobiert, den Code so gelassen wie er ist, und
einfach mal die delays in den Funktionen LCD_Data_xxx und
LCD_Command_xxx geändert.
Bei 100 µS delay aktualisiert das Display vernünftig schnell und alles
wird immer noch vom Display korrekt angezeigt (keine falsch
interpretierten bytes, ...).
Andre G. schrieb:> Wieso hat der Hersteller in dem Beispielcode dann so ewig lange> Wartezeiten?
Im Datenblatt steht übrigens nichts von delay_ms(2) dort steht delay(2)
also vermutlich 2 µs.
Thomas Z. schrieb:> Andre G. schrieb:>> Wieso hat der Hersteller in dem Beispielcode dann so ewig lange>> Wartezeiten?>> Im Datenblatt steht übrigens nichts von delay_ms(2) dort steht delay(2)> also vermutlich 2 µs.
Oh, das erklärt einiges ...
Zu den Wartezeiten. Das Booten nach dem Powerup dauert immer sehr lange.
Aber eigentlich nur das. Sonst sollte man 5 Refreshes des Inhaltes pro
Sekunde hinbekommen. Die Aenderung sollte aber Minimal sein, sonst wirkt
es nervoes.
>> Denn so hast Du TOP nicht richtig initialisiert (es sind ja 2> Controller).
Stimmt.
Wenn ich das jetzt richtig verstanden habe dann sind das eigentlich zwei
2x40 Displays die "parallelgeschaltet" sind was die Datenleitungen
angeht.
Über die E1 und E2 Leitungen kann man auswählen welches Display man
ansprechen will, das obere oder das untere.
Dann könnte man ja eigentlich zum Löschen des ganzen Displays den
Lösch-Code senden und beide E Leitungen aktivieren, oder?
Andre G. schrieb:> Dann könnte man ja eigentlich zum Löschen des ganzen Displays den> Lösch-Code senden und beide E Leitungen aktivieren, oder?
Ja, könntest Du... NUR ist es meist ratsam entweder nur die betreffenden
Stellen zu überschreiben oder gleich das komplette Display neu zu
befüllen. "Clear Display" führt meist nur zu unschönem Flimmern.
Teo D. schrieb:> Andre G. schrieb:>> Dann könnte man ja eigentlich zum Löschen des ganzen Displays den>> Lösch-Code senden und beide E Leitungen aktivieren, oder?>> Ja, könntest Du... NUR ist es meist ratsam entweder nur die betreffenden> Stellen zu überschreiben oder gleich das komplette Display neu zu> befüllen. "Clear Display" führt meist nur zu unschönem Flimmern.
Ja, aktuelle mache ich das so dass ich im µC einen String habe der den
aktuell anzuzeigenden Text speichert.
Dieser wird ein paar mal pro Sekunde auf das Display geschrieben.
Wenn etwas geändert werden soll, dann wird das in dem String geändert.
(und dann automatisch beim nächsten "Frame update" auf den LCD
übernommen)
(Ich habe das "Cursor auf Position xy setzen" irgendwie noch nicht
hinbekommen ...)
Thomas Z. schrieb:> Andre G. schrieb:>> Wieso hat der Hersteller in dem Beispielcode dann so ewig lange>> Wartezeiten?>> Im Datenblatt steht übrigens nichts von delay_ms(2) dort steht delay(2)> also vermutlich 2 µs.
Im Datenblatt stehen in der Tabelle der Kommandos (Seite 6) die nötigen
Wartezeiten. Entweder 37us oder 1,52ms. Das heißt nicht, dass der
Zugriff so lange dauern muss, sondern dass man NACH JEDEM Befehl die
genannte Zeit abwarten muss, da das Display in dieser Zeit beschäftigt
ist. Bei einem Clear Display oder Return Home sind das eben 1,52ms,
sonst immer 37us. Statt stumpfsinnig zu warten kann man auch das Busy
Flag auslesen. Wird aber nicht viel bringen, da das Display trotzdem
seine Zeit braucht.
Es ist auch noch darauf zu achten, dass die Zeitangaben für eine
Oszillatorfrequenz von 270kHz gelten. Bei diesem Display ist über die
Genauigkeit der internen Taktfrequenz keine Angabe gemacht. Oft sind da
aber 20% zu erwarten, da billige Oszillatoren verwendet werden. Ich
persönlich würde 2ms und 50us als Wartezeit verwenden.
LCD schrieb:> Thomas Z. schrieb:>> Andre G. schrieb:>>> Wieso hat der Hersteller in dem Beispielcode dann so ewig lange>>> Wartezeiten?>>>> Im Datenblatt steht übrigens nichts von delay_ms(2) dort steht delay(2)>> also vermutlich 2 µs.>> Im Datenblatt stehen in der Tabelle der Kommandos (Seite 6) die nötigen> Wartezeiten. Entweder 37us oder 1,52ms. Das heißt nicht, dass der> Zugriff so lange dauern muss, sondern dass man NACH JEDEM Befehl die> genannte Zeit abwarten muss, da das Display in dieser Zeit beschäftigt> ist. Bei einem Clear Display oder Return Home sind das eben 1,52ms,> sonst immer 37us. Statt stumpfsinnig zu warten kann man auch das Busy> Flag auslesen. Wird aber nicht viel bringen, da das Display trotzdem> seine Zeit braucht.> Es ist auch noch darauf zu achten, dass die Zeitangaben für eine> Oszillatorfrequenz von 270kHz gelten. Bei diesem Display ist über die> Genauigkeit der internen Taktfrequenz keine Angabe gemacht. Oft sind da> aber 20% zu erwarten, da billige Oszillatoren verwendet werden. Ich> persönlich würde 2ms und 50us als Wartezeit verwenden.
Danke1
Nein, Busy-Flag auslesen geht nicht, ich habe den R/!W Pin des Displays
auf GND gelötet.
(Immer auf WRITE, Pins sparen ...)
Eigentlich könnte man sich ja E2 auch sparen und einfach E1 invertieren.
Wenn man nicht die obere Hälfte anspricht, dann spricht man ja
zwangsweise mit der unteren Hälfte ...
(Spart einen µC Pin, aber man braucht einen Inverter ...)
Andre G. schrieb:> Nein, Busy-Flag auslesen geht nicht, ich habe den R/!W Pin des Displays> auf GND gelötet.> (Immer auf WRITE, Pins sparen ...)
Lohnt eh nur wenn große Temperaturschwankungen zu erwarten sind. Da
ändern sich die Taktfrequenzen der Display recht stark.
(LCD schrieb:> Es ist auch noch darauf zu achten, dass die Zeitangaben für eine> Oszillatorfrequenz von 270kHz gelten. Bei diesem Display ist über die> Genauigkeit der internen Taktfrequenz keine Angabe gemacht. Oft sind da> aber 20% zu erwarten, da billige Oszillatoren verwendet werden. Ich> persönlich würde 2ms und 50us als Wartezeit verwenden.)Andre G. schrieb:> Eigentlich könnte man sich ja E2 auch sparen und einfach E1 invertieren.
Wenn Du aber mehrfach auf das selbe Display schreiben willst, brauchst
Du jedes mal ein Dummybefehl (Cur.Pos. oä.) für das Andere!
Teo D. schrieb:> Lohnt eh nur wenn große Temperaturschwankungen zu erwarten sind. Da> ändern sich die Taktfrequenzen der Display recht stark.
Nein, das Ganze soll bei "Raumtemperatur" betrieben werden.
Teo D. schrieb:> Andre G. schrieb:>> Eigentlich könnte man sich ja E2 auch sparen und einfach E1 invertieren.>> Wenn Du aber mehrfach auf das selbe Display schreiben willst, brauchst> Du jedes mal ein Dummybefehl (Cur.Pos. oä.) für das Andere!
Ja, aber ich schreibe eh immer ein ganzes "Frame" also wäre das kein
Problem.
Andre G. schrieb:> //Function Set = 8bit mode; 2‐line; 5x8
Hi, das ist für zwei Zeilen initialisiert.
Nur zur Erinnerung:
Das Display hat zwei Controller.
Einen für die oberen zwei Zeilen,
einen weiteren für die unteren zwei Zeilen.
Manchmal ist die Reihenfolge auch verschachtelt.
Also z.B. Controller 1 für Zeilen 1 und 3, Controller 2 für Zeilen 2 und
4.
Die Controller werden nur über den jeweils zugehörigen Enable-Impuls
angesprochen.
Das Pinout sieht dafür doch schon zwei Enable-Eingänge vor.
"...9 E1 MPU Operation Enable signal. Falling edge triggered for top 2
line
...
15 E2 MPU Operation enable signal. Falling edge triggered for bottom 2
lines..."
Die Initialisierung muss für beide Controller erfolgen.
Also zweimal dasselbe hintereinander. Nur mit dem richtigen
Enable-Impuls und auf dem richtigen Pin.
Die Ausgaberoutine muss dementsprechend angepasst werden.
Wird nur momentan ein Zeichen auf Zeile 1 ausgegeben, dann nur ein
Enable-Impuls, gültig für diese Zeile.
Dann geht die Ausgabe zügig, ohne immer alles durchzurattern.
Probleme mit der Adressierung der Spalten dürften kaum auftreten,
da der HD44780 standardmäßig von 40-zeiligen LCDisplays ausgeht.
Zum Beispiel für ein 16 zeiliges:
Da auch LCDs mit "nur" 16 Spalten Controller verwenden, die ursprünglich
für
40 Spalten ausgelegt sind, gibt es für die Darstellung von "Fließtexten"
die
weitere Schwierigkeit, daß der Text nicht automatisch nach Terminalart
am Ende der
jeweiligen Zeile umgebrochen wird. Es wird vielmehr auch der nicht
darstellbare
Adressbereich zunächst vollgeschrieben, erst dann erscheint der weitere
Text auf
Anfang Zeile 2.
Würden keine Positionierungsangaben gesetzt, erschiene die Ziffernfolge
fortlaufend hintereinander auf dem Display.
Bei Adressenincrement also:
0 1 2 3 4 5 6 7 8 9
und bei Adressendecrement:
0
?
Ja, richtig, denn hierbei wird erst der "nicht darstellbare"
Adressbereich
von "hinten" vollgeschrieben.
Wollte man nun die Ziffernfolge 9 8 7 6 5 4 3 2 1 0 darstellen im
Adressendecrement, müßte nämlich ganz am Anfang eine
Positionierungsangabe gesetzt werden (hex C9).
(Bei Adressendecrement erscheinen also Texte "verkehrt".)
Mit sowas brauchte man sich bei einem 40-zeiligen nicht herumzuärgern.
Zur Sicherheit doch noch einmal ausprobieren.
ciao
gustav
Andre G. schrieb:> Eigentlich könnte man sich ja E2 auch sparen und einfach E1 invertieren.> Wenn man nicht die obere Hälfte anspricht, dann spricht man ja> zwangsweise mit der unteren Hälfte ...> (Spart einen µC Pin, aber man braucht einen Inverter ...)
würde ich nicht machen.
Es gibt befehle die du an beide "logischen" Displays senden musst z.B.
Clear oder das ganze initialisieren am Anfang. Das kannst dann jeweils
doppelt machen oder eben beide E Pins gemeinsam pulsen.
noiasca schrieb:> Das kannst dann jeweils> doppelt machen oder eben beide E Pins gemeinsam pulsen.
Hi,
gerade der Enable-Impuls bzw. die Erkennung desselben ist etwas
kritischer. Auf die Flanke kommt es an.
Schaltungstechnisch wird das meistens im Chip so gelöst, dass durch
Verschaltung mehrerer Gatter eine Gatterverzögerung erzeugt wird. Am
Ausgang werden die beiden Zweige wieder zusammengeführt. Dadurch ist die
Verzögerung gerade so groß, dass nur kurze Flanke übrigbleibt, auf die
dann getriggert wird.
Also, die E-Pins sollten vom Hardwarestandpunkt aus gesehen nicht direkt
parallelgeschaltet werden.
Abgesehen davon wird mit jedem Enableimpuls der Adresszähler
weitergezählt, (je nach Initsequenzauswahl "shift" etc.) auch auf der
"anderen" Hälfte, wenn beide E-Pins parallelgeschaltet würden.
Welche Folgen das hat? Die Anzeige wandert unten auch. Möchtest Du das
so?
Was hindert einen eigentlich daran, das Ganze softwaremäßig zu lösen?
Eine Kommandozeile mehr oder weniger macht den Kohl auch nicht fett.
Und an Ports am µC mangelt es doch hier sicherlich auch nicht.
Zur Not kann man ja auch den Vierbit-Modus noch einführen.
ciao
gustav
dauert ein wenig. 1,52 ms nach der fallenden Flanke des
Enable-Impulses kann es dann weitergehen. DB Seite 6 ganz oben rechts.
Enable selbst muss nur min. 140 ns auf high sein - die fallende Flanke
ist der "entscheidende Impuls / Trigger".
Wenn Du das Cusrsor-Kommando gleich "Hinterherschiesst" (ohne die
Wartezeit von 1,52 ms) dann mag er das nicht ...
Und das
Display ON/
OFF control
0 0 0 0 0 0 1 D C B
D=1: Entire display on
C=1: Cursor on
B=1: Blinking cursor on
hast Du mit 0b0000 1 0 1 1 D = 0 -> Display aus und Blinke-Cursor an
nicht ganz glücklich umgesetzt ...