Forum: Mikrocontroller und Digitale Elektronik Atmega32 16x2 LCD


von G4st (Gast)


Lesenswert?

Hallo,
ich habe ein 16x2 LCD an einen Atmega32 angeschlossen. Allerdings will 
das Ganze nicht so richtig funktionieren...

Display : Gleichmann GE-C1602B-TMI-JT/R
Datenblatt : 
http://www.produktinfo.conrad.com/datenblaetter/175000-199999/183045-da-01-en-LCD_MODUL_16X2_LED_GE_C1602B_TMI_JT_R.pdf

Display Controller : ST7066U
Datenblatt : http://www.crystalfontz.com/controllers/ST7066U.pdf

Pinbelegung:
Vcc und GND sind natürlich entsprechend angeschlossen
VO steht als optional also hab ich es mal weggelassen
RS : PB0
R/W : PB1
E : PB2
DB0-7 : PD0-7
A : Vcc
K : GND

Code :
1
#define LCDSETTINGS PORTB
2
#define LCDDATA PORTD
3
4
#define F_CPU 16000000UL
5
#include <avr/io.h>
6
#include <util/delay.h>
7
8
void E_DELAY(void);
9
10
void E_DELAY()
11
{
12
  LCDSETTINGS = 0b00000100;
13
  _delay_ms(40);
14
  LCDSETTINGS = 0b00000000;
15
}
16
17
int main(void)
18
{  
19
  
20
  DDRB = 0x03;
21
  DDRD = 0xFF;
22
  
23
  _delay_ms(50);
24
  LCDDATA = 0b00110000;
25
  E_DELAY();
26
  _delay_us(45);
27
  LCDDATA = 0b00110000;
28
  E_DELAY();
29
  _delay_us(45);
30
  LCDDATA = 0b00001001;
31
  E_DELAY();
32
  _delay_us(45);
33
  LCDDATA = 0b00000001;
34
  E_DELAY();
35
  _delay_ms(2);
36
  LCDDATA = 0b00000100;
37
  E_DELAY();
38
  LCDSETTINGS = 0b00000001;
39
  E_DELAY();
40
  _delay_us(37);
41
  LCDDATA = 0b00110000;
42
  E_DELAY();
43
  
44
  
45
    while(1)
46
    {
47
         
48
    }
49
}

Mit diesem Code sollte das LCD im Normalfall initialisiert werden und 
dann eine "0" auf das LCD ausgegeben werden.

Auf Seite 13 vom Datenblatt des LCDs findet man eine Tabelle nach der 
zufolge,sofern ich das richtig verstanden habe, sollte, wenn RS = 1 ist, 
die Binäradresse um eine "0" zu schreiben 0b00110000 sein.

Ich bekomme allerdings nichts auf dem Display angezeigt.
Die Initialisierung steht auf Seite 18 des Datenblatts.
Ich weiß momentan auch nicht wo mein Fehler liegen könnte.
Hoffe mir kann jemand helfen.

von holger (Gast)


Lesenswert?

>VO steht als optional also hab ich es mal weggelassen

Dann ist es kein Wunder das du gar nichts siehst.

von OldMan (Gast)


Lesenswert?

holger schrieb:
>>VO steht als optional also hab ich es mal weggelassen
>
> Dann ist es kein Wunder das du gar nichts siehst.

Wenn Du das geklärt hast, was holger korrekter Weise angemerkt hat,
dann suche hier einmal nach LCD. Es gibt hier einiges an fertigen 
Routinen für diese Displays. Die sind (fast) alle gleich.
Bevor Du das Rad neu erfindest.

von Karl H. (kbuchegg)


Lesenswert?

G4st schrieb:

> RS : PB0
> R/W : PB1
> E : PB2

So so

> void E_DELAY()
> {
>   LCDSETTINGS = 0b00000100;

keine gute Idee.
Du schaltest damit zwar E auf 1.
Aber alle anderen Pins auf 0!

>   DDRB = 0x03;

Und was ist mit PB2. Soll das nicht auf Ausgang geschaltet werden?



> Ich weiß momentan auch nicht wo mein Fehler liegen könnte.

* Deine Schreibweisen sind beschissen
* Du hast die Portpins nicht korrekt initialisiert (auf Ausgang 
gestellt)
* wenn du E schaltest, veränderst du auch die anderen Steuerpins
* der R/S Pin, der zwischen Kommando Byte und Daten Byte umschaltet, 
scheint dir auch ziemlich gleichgültig zu sein.

> Hoffe mir kann jemand helfen.

Hilf dir selbst und lerne erst mal wie man eine LED korrekt ein und aus 
schaltet. Und zwar so, dass man nicht den ganzen Port beeinflusst.
Es hilft nichts. Ein LCD anzusteuern ist nicht der Punkt an dem man 
anfängt. Um ein LCD anzusteuern darf es keine Ungewissheiten mehr geben, 
wie man einzelne Portpins manipuliert. Und das beginnt schon damit, dass 
eine Zuweisung einer Binärzahl fast die dümmste Art und Weise ist, wie 
man das bei den Steuerbits machen kann. Binärzahlen direkt 
hinzuschreiben hat manchmal seine Berechtigung. Meistens ist es aber nur 
etwas mit dem man seine Freundin beeindrucken kann - Blumentopf gewinnt 
man damit keinen.

: Bearbeitet durch User
von M. K. (sylaina)


Lesenswert?

Karl H. schrieb:
> Meistens ist es aber nur
> etwas mit dem man seine Freundin beeindrucken kann

Bei meiner Freundin klappt selbst das nicht :D

von Blumentopf (Gast)


Lesenswert?

Michael K. schrieb:
> Karl H. schrieb:
>> Meistens ist es aber nur
>> etwas mit dem man seine Freundin beeindrucken kann
>
> Bei meiner Freundin klappt selbst das nicht :D

Falsche Freundin...

... meine bewundert alles, was sie an mir nicht versteht. ;-)

von G4st (Gast)


Lesenswert?

Erstmal danke für die Antworten. Hab jetzt an den VO Pin ein 10kOhm 
Potentiometer angeschlossen. Jetzt kann ich auch schonmal was erkennen.
Allerdings bekomme ich trotzdem keine 0 angezeigt.
Da niemand etwas zu der Initialisierung gesagt hat nehme ich mal an, 
dass die richtig ist.

@Karl Heinz
Ich denke mal nicht, dass mir der RS Pin gleichgültig ist. Schließlich 
ist er vor dem Senden der 0 auf 1 gesetzt worden(LCDSETTINGS = 
0b00000001).
Aber Danke für den Hinweis mit der Enable Funktion. Hab leider nicht 
daran gedacht, dass sich die anderen Pins dann auch umschalten.

Aber :

Wieso sollte ich ein LCD ansteuern wollen ohne eine LED richtig 
ansteuern zu können?
Wieso sollte ich die Binäradressierung schreiben um jemanden zu 
beeindrucken?
Und wieso sollte ich ein LCD ansteuern wollen ohne die Grundlagen der 
Microcontrollerprogrammierung zu kennen?

So jetzt nochmal zurück zu meinem Problem.
Ich habe jetzt den Code mit den mir genannten Verbesserungsvoschlägen 
geändert
1
#define LCDSETTINGS PORTB
2
#define LCDDATA PORTD
3
4
#define F_CPU 16000000UL
5
#include <avr/io.h>
6
#include <util/delay.h>
7
8
void E_DELAY(void);
9
10
void E_DELAY()
11
{
12
  LCDSETTINGS |= (1<<PB2);
13
  _delay_ms(40);
14
  LCDSETTINGS &= ~(1<<PB2);
15
}
16
17
int main(void)
18
{  
19
  
20
  DDRB = 0x07;
21
  DDRD = 0xFF;
22
  
23
  _delay_ms(50);
24
  LCDDATA = 0b00110000;
25
  E_DELAY();
26
  _delay_us(45);
27
  LCDDATA = 0b00110000;
28
  E_DELAY();
29
  _delay_us(45);
30
  LCDDATA = 0b00001001;
31
  E_DELAY();
32
  _delay_us(45);
33
  LCDDATA = 0b00000001;
34
  E_DELAY();
35
  _delay_ms(2);
36
  LCDDATA = 0b00000100;
37
  E_DELAY();
38
  LCDSETTINGS = 0b00000001;
39
  E_DELAY();
40
  _delay_us(37);
41
  LCDSETTINGS = 0b00000001;
42
  E_DELAY();
43
  LCDDATA = 0b00110000;
44
  E_DELAY();
45
  
46
  
47
    while(1)
48
    {
49
         
50
    }
51
}

Das Ergbnis bleibt leider das Gleiche.

von Dieter F. (Gast)


Lesenswert?

Den Hinweis von Holger hast Du nicht beachtet.

Der Unterschied zwischen optional und variabel ist Dir geläufig?

"3 VO (Variable) Contrast Adjustment"

von G4st (Gast)


Lesenswert?

Zitat von mir selbst : "Hab jetzt an den VO Pin ein 10kOhm
Potentiometer angeschlossen."

von Dieter F. (Gast)


Lesenswert?

G4st schrieb:
> Zitat von mir selbst : "Hab jetzt an den VO Pin ein 10kOhm
> Potentiometer angeschlossen."

Habe ich übersehen ...

von Lcd (Gast)


Lesenswert?

Offensichtlich fehlen dir sämtliche Grundlagen.
Vielleicht doch mal die SF versuchen?
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung

von Arduinoquäler (Gast)


Lesenswert?

G4st schrieb:
> So jetzt nochmal zurück zu meinem Problem.
> Ich habe jetzt den Code mit den mir genannten Verbesserungsvoschlägen
> geändert

Versuche dir doch mal Gedanken zu machen warum du ein
Signal RS angeschlossen hast aber nicht betätigst/benutzt.
Das Datenblatt deines LCDs gibt Aufschluss darüber ....
... wann/wie es zu betätigen ist ....

von M. K. (sylaina)


Lesenswert?

G4st schrieb:
> @Karl Heinz
> Ich denke mal nicht, dass mir der RS Pin gleichgültig ist. Schließlich
> ist er vor dem Senden der 0 auf 1 gesetzt worden(LCDSETTINGS =
> 0b00000001).

Dazu sage ich nur

Karl H. schrieb:
> * wenn du E schaltest, veränderst du auch die anderen Steuerpins

Gleiches gilt hier auch für den RS-Pin: Du schaltest mit der 
Bit-Zuweisung den kompletten Port und damit alle anderen Steuerbits. Das 
ist, gelinde gesagt, eine ganz dumme Angwohnheit. Bits in einem 
Register/Port setzt man so:
1
Register |= (1 << myBitToSet);

und löschen macht man so:
1
Register &= ~(1 << myBitToClear);

Damit verändert man im Register nur das eine Bit und ändert kein 
anderes. Das ist aber auch, wie Karl Heinz schon schrieb, Grundlagen. 
Das muss sitzen bevor man sich an ein Display ran macht.

von G4st (Gast)


Lesenswert?

Das Einzige was man ändern kann,was ich auch getan habe, ist bei der 
vorletzten Zeile schreiben LCDSETTINGS |= (1<<PBO);
Und bevor die Initialisierung beginnz LCDSETTINGS &= ~(1<<PBO);
Das Ergebnis bleibt allerdings weiterhin das Selbe.

von G4st (Gast)


Lesenswert?

Ich habe jetzt auch mal die LCD-Routines.h aus dem Link ausprobiert. Das 
Ergebnis bleibt gleich.

von Karoly (Gast)


Lesenswert?

...wozu brauchst du einen 40ms breiten Enable Impuls? Zweitens, schau in 
DB nach wie viel Zeit man bei der Initialisierung braucht, die 1. 
Verzögerung ist bei dir 50ms, die zweite aber nur mehr 45µs, ich glaube 
das ist zu wenig.

von G4st (Gast)


Lesenswert?

Stimmt so.
Dort steht zuerst mehr als 40ms und dann mehr als 39µs

von Georg G. (df2au)


Lesenswert?

Du solltest ganz am Anfang nach dem Setzen der DDR deinen LCD Steuerport 
definiert setzen, also E inaktiv. Dann empfiehlt es sich der Übersicht 
wegen, ein lcd_cmd_write() und ein lcd_data_write() zu haben, das die RS 
und RW Leitungen dann passend setzt und E betätigt. Es genügt 1us als 
E-Pulsbreite. 40ms sind nicht tödlich, machen das Programm aber unnötig 
langsam.

Noch ein Hinweis: Dein Controller möchte recht steile Flanken am 
E-Signal sehen. Ich kenne deinen Aufbau nicht. Auf auf dem Steckbrett 
mit 30cm Klingeldraht könnte es knapp werden.

: Bearbeitet durch User
von G4st (Gast)


Lesenswert?

Danke für die Antwort werde das mal ausprobieren

von G4st (Gast)


Lesenswert?

Es hat zwar etwas gedauert aber jetzt funktioniert alles.
Vielen Dank für eure Antworten.

@Georg G.

Hat mir sehr geholfen habe den Code jetzt neu geschrieben mit den von 
dir vorgeschlagenen Funktionen.
1
#define LCDSETTINGS PORTB
2
#define LCDDATA PORTD
3
#define RS PB0
4
#define RW PB1
5
#define E PB2
6
7
#define F_CPU 16000000UL
8
#include <avr/io.h>
9
#include <util/delay.h>
10
11
void E_DELAY(void);
12
void Send_CMD(uint8_t CMD);
13
void Send_Data(uint8_t Data);
14
void LCD_init(void);
15
16
void LCD_init()
17
{
18
  _delay_ms(45);
19
  LCDSETTINGS &= ~((1<<RS) | (1<<RW) | (1<<E));
20
  Send_CMD(0b00111000);
21
  _delay_us(40);
22
  Send_CMD(0b00111000);
23
  _delay_us(40);
24
  Send_CMD(0b00001111);
25
  _delay_us(40);
26
  Send_CMD(0b00000001);
27
  _delay_ms(2);
28
  Send_CMD(0b00000110);
29
}
30
31
void E_DELAY()
32
{
33
  LCDSETTINGS |= (1<<E);
34
  _delay_us(1);
35
  LCDSETTINGS &= ~(1<<E);
36
}
37
38
void Send_CMD(uint8_t CMD)
39
{
40
  LCDSETTINGS &= ~(1<<RS);
41
  LCDDATA = CMD;
42
  E_DELAY();
43
  _delay_ms(2);
44
}
45
46
void Send_Data(uint8_t Data)
47
{
48
  LCDSETTINGS |= (1<<RS);
49
  LCDDATA = Data;
50
  E_DELAY();
51
  _delay_us(45);
52
}
53
54
int main(void)
55
{  
56
  
57
  DDRB = 0x07;
58
  DDRD = 0xFF;
59
  
60
  LCD_init();
61
  Send_Data('T');
62
  
63
    while(1)
64
    {
65
         
66
    }
67
}

von Karl H. (kbuchegg)


Lesenswert?

G4st schrieb:

> Wieso sollte ich die Binäradressierung schreiben um jemanden zu
> beeindrucken?

Genau das frage ich DICH!

Warum benutzt du Binärschreibweise, wenn es nicht angebracht ist?

Wenn du den Datenport auf das Binärmuster für das Zeichen '0' setzen 
willst, warum schreibst du dann nicht einfach
1
  LCDDATA = '0';

ist dir das zu einfach? Ist dir das zu lesbar? Nicht kryptisch genug?

> Und wieso sollte ich ein LCD ansteuern wollen ohne die Grundlagen der
> Microcontrollerprogrammierung zu kennen?

Weil du
* ständig einfach nur Binärschreibweise benutzt
* weil du dir keine Funktionen für
a) Kommando ausgeben
b) Daten ausgeben
machst
* weil du für die R/S Leitung schon wieder den kompletten Port über den 
Haufen wirfst
* weil du scheinbar immer noch nicht gelernt hast, dass man sich für 
Pins Makros macht, die der Funktion des Pins entsprechen und nicht 
seiner Position am Port. Was ist der Unterschied zwischen
1
  LCDSETTINGS |= (1<<PB2);
und
1
  LCDSETTINGS |= (1 << LCD_ENABLE);

richtig: beim 2.ten sieht jeder Halbblinde sofort, dass du hier den 
Enable Pin auf 1 schalten willst. Beim ersten muss man erst mal in 
deinem Prosatext rausfinden, dass an PB2 wohl (hoffentlich) der Enable 
Pin vom LCD angeschlossen ist.

Selbiges für die anderen Steuerleitungen. Dafür definiert man sich 
Makros, die die Pinbezeichnungen am Port mit der Funktion des Pins 
verknüpfen. Dann braucht man dann auch hier
1
  DDRB = 0x07;
nicht mehr hoffen, dass die richtigen Pins auf Ausgang geschaltet 
werden.
1
#define LCD_ENABLE   PB2
2
#define LCD_RS       PB0
3
#define LCD_RW       PB1
4
5
....
6
7
8
   DDRB |= ( 1 << LCD_ENABLE ) | ( 1 << LCD_RS ) | ( 1 << LCD_RW );

und sinngemaess natürlich auch an all den anderen Stellen. Insbesondere 
hier
1
  LCDSETTINGS = 0b00000001;
wo eigentlich nur der RS Pin auf 1 gesetzt werden sollte und nichts 
weiter.

> Das Ergbnis bleibt leider das Gleiche.

Stufenweise vorgehen.
Erst mal das LCD ohne Initialisierung nur unter Spannung setzen. Mit dem 
Poti an VO stellst du dann erst mal den Kontrast so ein, dass du die 
initiale Blockzeile gut sehen kannst.
Und erst dann gehts mit der Ansteuerung los. Wenn deine Initialisierung 
durchgeht (was sollen die ganzen Binärzahlen bewirken, die du als 
Kommando ausgibst? Muss man das als Binärzahl schreiben, oder kann man 
das auch so formulieren, dass man im Code sehen kann, was du eigentlich 
haben willst?), dann muss die Blockzeile verschwinden. Solange das nicht 
der Fall ist, ist deine Initialisierung nicht korrekt (wahrscheinlich 
sind dann die Zeiten zwischen den Kommandos zu kurz).

Erst dann wenn die Blockzeile verschwindet, erst dann können wir uns 
über Zeichenausgabe unterhalten.

Du willst zu viel auf einmal, und fällst dabei ständig auf die Nase. 
Eben weil du zu viel auf einmal willst und dir keine Gedanken darüber 
machst, wie du dein Programm vernünftig so strukturieren kannst, so dass 
man auch erkennen kann, was passieren wird. Es fehlen eben die 
Grundlagen, die du beim Spielen mit LED eigentlich hättest erwerben 
sollen.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

G4st schrieb:
> Es hat zwar etwas gedauert aber jetzt funktioniert alles.
> Vielen Dank für eure Antworten.

Da haben sich unsere Antworten überschnitten.

Ja, jetzt ist das meiste davon umgesetzt.
Warum nicht gleich so? Das hätte dir viel Arbeit und Kopfzerbrechen 
erspart.


Edit:
Das hier
1
  DDRB = 0x07;

kann man besser formulieren. Du willst die Pins für Enable, RS und RW 
auf Ausgang setzten. Für die Pinbezeichnungen gibt es Makros. Die kannst 
du gerne auch hier benutzen und dir damit beim nächsten LCD, das an 
andere Pins angeschlossen ist, eine Menge Kopfzerbrechen ersparen. Auch 
könnte man das 'auf Ausgang setzen' in die Funktion LCD_init() mit 
reinziehen. Denn da gehört es hin. Wenn LCD_init() alles zur Verwendung 
des LCD vorbereiten soll, dann gehört da auch das auf Ausgang setzen mit 
dazu.

Aber bitte nicht wieder mit der Rundumschlagmethode! Du willst die 3 
Pins auf Ausgang setzen. Die 3 und nur die 3. Die restlichen Bits im 
DDRB bleiben unangetastet!

: Bearbeitet durch User
von G4st (Gast)


Lesenswert?

Danke für deine Antwor Karl Heinz.
Ich fand es auch recht lustig deine Antwort zu lesen die abgeschickt 
wurde als mein Code schon fertig war. Ich werde mich jetzt an dem 4Bit 
Modus versuchen(Was jetzt wohl nicht so schwer sein wird nachdem ich das 
Prinzip verstanden habe). Ich werde dann die Binär- und 
Hexadezimaladressierung bei DDRB auch ablegen und das Ganze auch in die 
init Funktion mit rein holen.
Danach kann ich noch versuche das Ganze über 4Bit Modus und PCF8574 
anzusteuern(Keine Sorge ich habe schon eine erfolgreiche Übertragung mit 
dem PCF8574 geschafft und konnte auch LEDs darauf steuern und einzelne 
Taster auslesen :)).

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.