Forum: Mikrocontroller und Digitale Elektronik Problme mil LCD Routine


von Max H. (hartl192)


Lesenswert?

Ich bin gerade dabei mir eine Routine für ein HD44780 kompatibles LCD zu 
schreiben. Die Initialisierung funktioniert, jetzt scheitere ich aber am 
ausgeben des Textes.

Ich Programmiere mit MPLAB IDE 8.84
LCD: 
http://www.unisystem.pl/download/produkty/2011/wh2004a-tmi-ct_gCuyl/WH2004A-TMI-CT%23.pdf

Wenn ich das Schreibe:
1
  LCD_RS=1;
2
3
  LCD_D7=0; 
4
  LCD_D6=1;
5
  LCD_D5=1;
6
  LCD_D4=0; 
7
  Enable
8
  LCD_D7=0;
9
  LCD_D6=0;
10
  LCD_D5=0;
11
  LCD_D4=1;
12
  Enable
13
  Delay10TCYx(19);  //Delay ca. 38us
Wir auf dem LCD ein 'a' angezeigt.

Wenn ich aber das Schreibe:
1
  chr=0x61;
2
3
  LCD_RS=1;
4
5
  LCD_D7=(chr>>7)&0x01; 
6
  LCD_D6=(chr>>6)&0x01;  
7
  LCD_D5=(chr>>5)&0x01; 
8
  LCD_D4=(chr>>4)&0x01;   
9
  Enable
10
  LCD_D7=(chr>>3)&0x01; 
11
  LCD_D6=(chr>>2)&0x01; 
12
  LCD_D5=(chr>>1)&0x01; 
13
  LCD_D4=chr&0x01; 
14
  Enable
15
  Delay10TCYx(19);  //Delay ca. 38us
Wir auf dem LCD ein '!' angezeigt.

von Max H. (hartl192)


Lesenswert?

Wenn ich beim 2. Code chr=0x30; schreibe, wird auf dem LCD wie gewollt 
'0' angezeigt.

von Max H. (hartl192)


Lesenswert?

Hat keiner eine Idee wo das Problem liegen könnte?
Kennt jemand eine alternative lösung?

von Ottmar K. (wil1)


Lesenswert?

Hallo,
Geduld, Geduld, hier wartet doch keiner in ner Schleife ob Du mal ne 
Frage stellst!

Das Nachfolgende musst Du in Deinen eigenen Code umsetzen.

Grundsätzlich läuft das beim HD44780 zum Beispiel so:

1. Instruction "Set DDRAM Adresse" + DDRAM-Adresse
    movlw b'01000000'   ;40h Ausgabeadresse Zeile 2, Digit 0
    iorlw B'10000000'   ;HD44780-Befehl "Set DDRAM-Adresse"
    movwf LcdControl    ;Instruction und DDRAM-Adresse in Variable
    RS=0                ;register select: Instruction
    RW=0                ;schreiben
                        ;->B'11000000'
2.Jetzt DDRAM-Adresse über den 4Bit-Bus ausgeben:
    Busyflagabfrage oder Delay
    ANDLW  b'11110000' ;unteres Nibble ausmaskieren
    movwf    LCDPORT   ;Oberes Nibble an dass LCD übertragen
    Enable =1
    nop                ;1 Takt warten
    Enable= 0
    Busyflagabfrage oder Delay
    swapf  LcdControl,w ;Nibbletausch -> WREG
    ANDLW   b'11110000' ;Bitmaske anwenden
    movwf   LCDPORT     ;Unteres Nibble an das LCD übertragen

3. Jetzt Zeichen festlegen
    movlw "X"
    movwf LcdData
    RS=1                ;Register select Daten
    RW=0                ;schreiben
4. Jetzt Zeichen an LCD senden
    sinngemmäß weiter wie mit Variable LcdControl gezeicht
    Busyflagabfrage ...

für die weiteren unmittelbar aufeinander folgenden Zeichen in der Zeile 
braucht keine neue DDRAM-Adresse gesendet zu werden.

mfg Ottmar

von Max H. (hartl192)


Lesenswert?

Ottmar K. schrieb:
> 4. Jetzt Zeichen an LCD senden
>     sinngemmäß weiter wie mit Variable LcdControl gezeicht
>     Busyflagabfrage ...

Und genau da liegt mein Problem. Ich kann ein Zeichen senden (Code1), 
aber mit der Routine in Code2 wird es nicht korrekt gesendet und ich 
habe keine Ahnung warum.
Ich vermute, dass nur das 6. bit des Zeichens falsch gesendet wird:
'a'=01100001
'!'=00100001

von Markus B. (mbp-bayern)


Lesenswert?

Hallo;

Dein gezeigter Code sieht nicht unbedingt falsch aus;
Aber vielleicht ist Dein Enable zu schnell,
oder der Compiler optimiert was fehlerhaft.

Aber all das sind nur spekulationen,
da du nur einen zu winzigen Ausschnitt vom Programm zeigst.


Vielleicht kannst Du den folgenden Code verstehen,
um ihn auf den PIC umzusetzten:

http://elm-chan.org/fsw/ezlcd/00index_e.html
http://elm-chan.org/docs/lcd/hd44780_e.html

MFG:Markus

von Janik L. (crazygecko)


Lesenswert?

Hallo
Versuch mal die oberen 4-Bit zu senden, während das Enable-Bit gesetzt 
ist. Zwischen denen schaltest du das Enable-Bit kurz aus, dann wieder 
ein. Dann sendest du die 4-Bit, dann schaltest du das Enable-Bit aus und 
machst dein delay von 38us. Ich habs immer so gemacht. Ich weiss nicht 
ob das etwas ändert, aber vielleicht erkennt er dann die negative Flanke 
besser. Dies würde dann aber auch nicht erklären, warum nur 1 Bit falsch 
ist. Aber versuchen kannst du's ja. Habe sonst auch keine andere Idee.

An deinem Beispiel erklärt:
  chr=0x61;

  LCD_RS=1;

  hier Enable auf 1 setzen

  LCD_D7=(chr>>7)&0x01;
  LCD_D6=(chr>>6)&0x01;
  LCD_D5=(chr>>5)&0x01;
  LCD_D4=(chr>>4)&0x01;

  hier Enable auf 0 setzen
  hier Enable auf 1 setzen

  LCD_D7=(chr>>3)&0x01;
  LCD_D6=(chr>>2)&0x01;
  LCD_D5=(chr>>1)&0x01;
  LCD_D4=chr&0x01;

  hier Enable auf 0 setzen

  Delay10TCYx(19);  //Delay ca. 38us

von Max H. (hartl192)


Angehängte Dateien:

Lesenswert?

Hier ist der gesamte Code.
@Janik L. (crazygecko) Ich lege die Daten an, und habe dann sowohl eine 
steigende als auch eine Fallende flanke beim EN-Signal.
1
 #define Enable LCD_EN=1; Delay10TCYx(1); LCD_EN=0; Delay10TCYx(1);
Der Quarz hat 20MHz --> 1 TCY= 200ns

von Markus B. (mbp-bayern)


Lesenswert?

Hallo nochmal;

Dein Programm scheint in der Tat an manchen Stellen ziemlich flott zu 
sein;
Die Routinen zu "Bus Width and Initialization" von Elm warten länger
und senden teils auch andere Initialisierungen.

(Figure 4. Software Reset and Initialization, 2. Link zu Elm von oben,
so wie der C-Quellcode auf der Seite vom 1. Link ganz unten.)

Code von Elm - Auszug:
1
/*-----------------------------------------------------------------------*/
2
/* Initialize LCD module                                                 */
3
/*-----------------------------------------------------------------------*/
4
5
void lcd_init (void)
6
{
7
  uint8_t d;
8
9
10
  IF_INIT();
11
  DELAY_US(40000);
12
  lcd_write(8, 0x30);
13
  DELAY_US(4100);
14
  lcd_write(8, 0x30);
15
  DELAY_US(100);
16
  lcd_write(8, 0x30);
17
18
  d = (IF_BUS == 4 ? 0x20 : 0x30) + LCD_IF_2ROW;
19
  lcd_write(8, d);
20
#if IF_BUS == 4
21
  lcd_write(0, d);
22
#endif
23
  lcd_write(0, 0x08);
24
  lcd_write(0, 0x01);
25
  DELAY_US(LCD_DT1);
26
  lcd_write(0, 0x06);
27
  lcd_write(0, 0x0C);
28
29
  Row = Column = 0;
30
#if _USE_CURSOR
31
  Csr = 0;
32
#endif
33
}


Vor einiger Zeit habe ich auf diesen Routinen für 8051 und SDCC 
entwickelt.
Früher habe ich LCDs unter AVR und assembler verwendet.


Meine Erfahrung aus dieser Zeit ist,
dass die Displays jeden Mist anzeigen,
sofern sie überhaupt was machen,
wenn man ihnen mit dem Takt zu sehr "einheizt".

Zu langsam kann man hingegen eigentlich fast nicht takten.
Auch ganze (m)Sec für den Enable etc. waren noch nie zu lang,
für ein LCD, nur für den Anwender davor...

Auch ganz wichtig zu beachten ist,
dass die LCD beim/nach dem Einschalten Zeit benötigen!
Siehe das Delay von 40.000 uSec aus obigem Code!



Ich würde noch versuchen den Zustand des Byte-Portes
über den Debuger oder externe Leds / RS232 anzusehen,
ehe das Enable getaktet wird, um sicherzugehen,
dass der Compiler auch wirklich das an den Port aufbereitet,
nach was es korrekt aussehen sollte.

Da hatte ich mit unter auch schon Fälle,
wo nur ein Umstellen des Codes half,
so dass Zwischenschritte einzeln angeschreiben waren,
samt eigenen temporären Variabelen für Zwischenscritte.

Manchmal hilft auch ein Typ-Cast wie (char)((x >> y) & z) Wunder,
was mich aber hier eher wundern würde.


MFG:Markus

von Janik L. (crazygecko)


Angehängte Dateien:

Lesenswert?

Ich habe deine Initialisierung mal nach 
http://www.sprut.de/electronic/lcd/ umgeschrieben, da ich dachte, dass 
sie vielleicht so funktioniert. Bei mir ist sie leider nicht gegangen. 
Zudem habe ich fesgestellt, dass wenn ich das Display mithilfe eines 
anderen Programms initialisiert habe, und dann dein Programm drauf 
geladen habe das Display ging. Ich bekam aber am Ende immer noch ein "_" 
angezeigt.

von Max H. (hartl192)


Lesenswert?

WEnn ich die Funktion so schreibe und meine Initialisierung verwende 
funktioniert alles
1
void LCD_Send(char chr)
2
{
3
  char chr2;
4
  LCD_D7=(chr>>7)&0x01;
5
  chr2=chr>>5;
6
  LCD_D6=(chr2>>1)&0x01;
7
  LCD_D5=(chr>>5)&0x01; 
8
  LCD_D4=(chr>>4)&0x01;   
9
  Enable
10
  LCD_D7=(chr>>3)&0x01; 
11
  LCD_D6=(chr>>2)&0x01; 
12
  LCD_D5=(chr>>1)&0x01; 
13
  LCD_D4=chr&0x01; 
14
  Enable
15
  Delay10TCYx(19);  //Delay ca. 38us
16
}

von Max H. (hartl192)


Lesenswert?

Ich habe die Funktionen mehr geschrieben um zu verstehen wie das LCD 
funktioniert und etwas zu lernen. Da im Prinzip alles funktioniert bin 
ich auch zufrieden.

Weiß jemand wo ich eine LCD Bibliothek downloaden kann bei der ich alle 
Ausgänge individuell definieren kann, und nicht an bei den Daten-Pins an 
einen Port gebunden bin? Ich brauche das LCD für ein größeres Projekt, 
und hätte deshalb gerne eine LCD Bibliothek die sicher funktioniert, um 
mir nicht auch noch darüber Gedanken machen zu müssen. Die Bibliothek 
sollte auf einen PIC18F45k22 funktionieren.

von L.P. (Gast)


Lesenswert?

M. H. schrieb:
> Weiß jemand wo ich eine LCD Bibliothek downloaden kann bei der ich alle
> Ausgänge individuell definieren kann, und nicht an bei den Daten-Pins an
> einen Port gebunden bin?

Überleg die eher, wie du 4-Bit für das LCD hardwaremäßig auf einen Port 
bekommst. Dafür gibt es Ports mit mehreren Bits.

von Max H. (hartl192)


Lesenswert?

L.P. schrieb:
> Überleg die eher, wie du 4-Bit für das LCD hardwaremäßig auf einen Port
> bekommst.

Ich habe aber schon die Platine, wo das leider nicht so beschalten ist.

von Max H. (hartl192)


Lesenswert?

Jetzt habe ich die Init Routine wie auf dieser Website beschreiben ist 
geschrieben, jetzt funktioniert sie besser...

http://elm-chan.org/docs/lcd/hd44780_e.html

Ich frage mich aber immer noch, was beim Shiften das Problem ist.

von Markus B. (mbp-bayern)


Lesenswert?

Hallo  M. H.,

Weshalb das eine nicht funktioniert,
das andere mit Zwischenschritt aber dann schon,
und dann nur an dieser einen Stelle ein Problem,
das verwundert mich auch ein wenig.

Ggf. steht es im Zusammenhang dem Datentyp.
Also mit Vorzeichen (signed) oder ohne (unsigned char)
0b11111111 kann 255 sein, oder auch -1, je nach Datentyp!

Weiter lösen Compiler diverse 8 Bit Aufgaben
über einen 16 Bit Datentyp,
und vielleicht ist hier das Problem zu suchen.

Das von mir angedeutete Typ-Casting wäre damit der Compiler
Deine 8 Bit-Zahl nicht auf 16 bit aufbohrt,
bzw. dass er dies gewaltsam wieder auf 8 Bit kürzt.


(char)(0b 0001 0001 0011 0011)

liefert:

0b 0011 0011

Es schneidet ohne Warnung die 8 MSB einfach ab.
Man sollte das Casting nur vorsichtig einsetzen!
Es könnte aber des Rätsels lösung sein.



LCD_D6=(chr>>6)&0x01;  --> LCD_D6 = ((char)(chr >> 6) & 0x01);


Daher hätte ich versucht irgendwie an die Werte zu kommen,
welche man wirklich am Port findet, und wenn es mit dem Voltmeter ist.

Aber gerade bei PIC sollte man mit ICP/ICD einfach debugen können,
was bei Atmel über die ISP nur mit moderner DebugWire geht
und ansonsten Jtag voraussetzt.

Also z.B. ein "while(1);" statt dem 1. "Enable" und mal messen,
Das LCD geht dann zwar natürlich nicht aber Du hast gewissheit,
was dort an den Pins wirklich los ist.

Danach dann das 2. Enable auf selbe Weise ersetzten und messen.



Wenn dort nach Deiner Methode 1 die falschen Bits ankommen
dann geht wirklich beim schieben was schief.

Die Tests sind natürlich mit verschiedenen Zeichen zu widerholen,
z.B. mit 0b1111 1111 und 0b0111 1111 und 0b1010 1010, 0b0101 0101



Ansonsten siehe auch:
Beitrag "Bitshift und Ansi C"
http://www.imb-jena.de/~gmueller/kurse/c_c++/c_cast.html




MFG:Markus

von Max H. (hartl192)


Lesenswert?

Markus B. p. schrieb:
> LCD_D6=(chr>>6)&0x01;  --> LCD_D6 = ((char)(chr >> 6) & 0x01);
Das Funktioniert auch nicht.

Markus B. p. schrieb:
> Die Tests sind natürlich mit verschiedenen Zeichen zu widerholen,
> z.B. mit 0b1111 1111 und 0b0111 1111 und 0b1010 1010, 0b0101 0101
Mit diesem Code werden alle 4 Zeichen Richtig ausgegeben:
1
  chr2=chr>>5;
2
  LCD_D6=(chr2>>1)&0x01;

Den Datentyp in unsigned char zu ändern hat auch nichts gebracht.

Wenn ich das Enable mit einem while(1); ersetz messe ich an LCD_D6 den 
falschen Pegel bei diesem Code:
1
LCD_D6=(chr>>6)&0x01;

Das bringt auch keine Verbesserung:
1
  chr2=chr&0b01000000;
2
  LCD_D6=chr2>>6;

Diese Befehl funktioniert auch nicht:
1
  LATC=0x01;
2
  LATC=LATC>>6;

von Max H. (hartl192)


Lesenswert?

LATC shiften funktioniert schon, ich habe es oben nur in die falsche 
Richtung geschoben.
1
LATC=0x01;
2
LATC=LATC<<6;
1
LATC=0x80;
2
LATC=LATC>>6;
Funktionieren beide problemlos.

von Karl H. (kbuchegg)


Lesenswert?

Die Frage ist, ob shiften hier überhaupt sinnvoll ist. Dein µC shiftet 
sich hier zu Tode. Zähl doch mal in deiner Funktion ab, wieviele Shifts 
du in Summe machst!

1
...
2
   LCD_D7 =  ( chr & ( 1 << 7 ) ) ? 1 : 0;
3
   LCD_D6 =  ( chr & ( 1 << 6 ) ) ? 1 : 0;
4
...

das dürfte pro Bit um einiges effizienter sein.



Allerdings:
für das von dir beobachtete Verhalten hab ich auch keine Erklärung.

von Markus B. (mbp-bayern)


Lesenswert?

Hallo M. H.,

In meiner alten MPLAB 8.30 version habe ich umgestellt auf p18f45k20,
da ich Deinen nicht fand und das mit dem Simulator simuliert.

Ich habe mehrfach "Run to cursor" zu den beiden Enables gemacht,
und die SFR Ausgabe des Simulator beobachtet.

Leider kann ich Deine Probleme im Pic Simulator nicht nachvollziehen;
Alle 3 Bytes werden nach meiner Ansicht korrekt an den 4 Bit ausgegeben,
mit Deiner geposteten ur main.c,
nur an 3 Stellen zum compilieren geändert:

- Include gewechselt
- Config auskommentiert
- ANSELD=0x00 auskommentiert


MFG:MBP
Markus.

von Max H. (hartl192)


Lesenswert?

? 1 : 0; Was macht dieser Teil?

von Markus B. (mbp-bayern)


Lesenswert?

M. H. schrieb:
> ? 1 : 0; Was macht dieser Teil?

Hehe.. dieses Frage wundert mich nicht.
? ist aber wohl nicht auf die schnelle zu finden...

Hier ist also die Lösung:
http://de.wikibooks.org/wiki/C-Programmierung:_Ausdr%C3%BCcke_und_Operatoren#Bedingung_.3F:


MFG:MBP

von Max H. (hartl192)


Lesenswert?

Karl Heinz Buchegger schrieb:
> ...
>    LCD_D7 =  ( chr & ( 1 << 7 ) ) ? 1 : 0;
>    LCD_D6 =  ( chr & ( 1 << 6 ) ) ? 1 : 0;
> ...
Do funktionier es super. Danke.

Das 1 << 7 könnte ich ja auch durch 0b10000000 ersetzen oder gibt es da 
irgendeinen Verstecken unterschied?

von Markus B. (mbp-bayern)


Lesenswert?

M. H. schrieb:
> Karl Heinz Buchegger schrieb:
>> ...
>>    LCD_D7 =  ( chr & ( 1 << 7 ) ) ? 1 : 0;
>>    LCD_D6 =  ( chr & ( 1 << 6 ) ) ? 1 : 0;
>> ...
> Do funktionier es super. Danke.
>
> Das 1 << 7 könnte ich ja auch durch 0b10000000 ersetzen oder gibt es da
> irgendeinen Verstecken unterschied?

Der Versteckte Unterschied ist die einfachere Lesbarkeit;
Übertrage das mal gedanklich auf 32 Bit...
Viel Spass beim Zählen der Nullen ;-)

MFG:MBP

von Karl H. (kbuchegg)


Lesenswert?

M. H. schrieb:

> Das 1 << 7 könnte ich ja auch durch 0b10000000 ersetzen

warum willst du das tun?

bei 1<<7 seh ich auf einen Blick, dass hier das 7. Bit gesetzt ist. 
Warum? Na, weils dort steht! Das steht 7. Für jeden groß und deutlich zu 
lesen.
Bei 0b10000000 muss ich 0-er zählen.

> oder gibt es da
> irgendeinen Verstecken unterschied?

dadurch das 1<<7 vom Compiler ausgerechnet wird gibt es keinen 
Unterschied. Sprich der Compiler ersetzt 1<<7 durch 0b10000000 und 
erspart mir so das Bit zählen.

von Michael A. (Gast)


Lesenswert?

M. H. schrieb:
> Das 1 << 7 könnte ich ja auch durch 0b10000000 ersetzen oder gibt es da
> irgendeinen Verstecken unterschied?

Solange der Compiler das umsetzt, ist das doch zur Laufzeit völlig 
gleichwertig.

von Max H. (hartl192)


Lesenswert?

Jetzt funktioniert alles und ich habe auch noch was gelernt (? 1 : 0;) 
:-)
Danke an Alle für eure Hilfe!

von Karl H. (kbuchegg)


Lesenswert?

M. H. schrieb:
> Jetzt funktioniert alles und ich habe auch noch was gelernt (? 1 : 0;)
> :-)


Du brauchst dringend ein C-Buch, wenn dich der ?: Operator schon in 
Erstaunen versetzt. Da wartet noch jede Menge anderes Zeugs, welches in 
jedem noch so grindigem C-Buch erläutert wird.

von Max H. (hartl192)


Angehängte Dateien:

Lesenswert?

Jetzt sind noch 2 neue Probleme aufgetaucht:

Wenn ich txt[]="Hallo Welt" in der main definiere gibt es mir nur "Ha" 
auf dem LCD aus, wenn ich txt hingegen als globale Variable definiere 
gibt es mit den kompletten Text aus.

Wenn ich schreibe LCD_Text(3,1,"Hallo"); gibt es mir auch das Falsche 
aus (ein Zeichen das aus 2 senkrechten Strichen besteht) und es kommt 
die Warnung:
Warning [2054] suspicious pointer conversion

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.