Forum: Mikrocontroller und Digitale Elektronik Fehler beim Schreiben einer LCD Library auf ATmega16


von Sven S. (nutellamann)


Lesenswert?

Hallo,

für ein Projekt in der Schule muss ich ein LCD zum laufen bringen. Die 
Software soll keine fertigen Librarys verwenden, die nicht zum AVR-GCC 
gehören.

Ich habe hier das Tutorial zum LCD gelesen und auch das Datenblatt von 
Hitachi 
http://www.alldatasheet.com/datasheet-pdf/pdf/63673/HITACHI/HD44780.html, 
weil das LCD HD44780 kompatibel ist. Aufrgund der Aufschrift hinten 
drauf (1602A) schließe ich darauf, dass der im Datenblatt auf Seite 18 
gemeinte Zeichensatz verwendet wird.

Das LCD funktioniert, ich habe es mit einer Hex Datei aus dem Internet 
getestet, am Kontrast oder der Verbindung wird es also nicht liegen.


Mein Programm sieht so aus:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
/* Connections:
4
 *
5
 *  D4->PORTD0
6
 *  D5->PORTD1
7
 *  D6->PORTD2
8
 *  D7->PORTD3
9
 *  E-> PORTD4
10
 *  RW->PORTD5
11
 *  RS->PORTD6
12
 */
13
#define LCD_DATA_PORT PORTD
14
#define E   PD4
15
#define RW  PD5
16
#define RS  PD6
17
18
void e_state(void)
19
{
20
  _delay_us(20);
21
  LCD_DATA_PORT|=(1<<E);
22
  _delay_us(20);
23
  LCD_DATA_PORT&=~(1<<E);
24
}
25
26
void write_data(int input)
27
{
28
  int high_bit,low_bit;
29
  LCD_DATA_PORT|=(1<<RS); //signal to write chars on display
30
  high_bit=(input&0xF0);   //get the higher 4 bit
31
  low_bit=(input&0x0F);    //get the lower 4 bit
32
  LCD_DATA_PORT=high_bit;  //send the higher 4 bit
33
  e_state();
34
  LCD_DATA_PORT=low_bit;   //send the lower 4 bit
35
  e_state();
36
}
37
38
void write_command(int input)
39
{
40
  int high_bit,low_bit;
41
  LCD_DATA_PORT&=~(1<<RS);  //signal to write command
42
  high_bit=(input&0xF0);   //get the higher 4 bit
43
  low_bit=(input&0x0F);    //get the lower 4 bit
44
  LCD_DATA_PORT=high_bit;  //send the higher 4 bit
45
  e_state();
46
  LCD_DATA_PORT=low_bit;   //send the lower 4 bit
47
  e_state();
48
}
49
50
void send_string(char input)
51
{
52
  int output;
53
  output=(int)input;
54
  write_data(output);
55
}
56
57
void split_string(char *input)
58
{
59
  while(*input!='\0')
60
  {
61
    send_string(*input++);
62
  }
63
}
64
65
void init_LCD(void)
66
{
67
  _delay_us(100);         //startup time
68
  write_command(0b00110000);//8-Bit mode
69
  _delay_us(45);
70
  write_command(0b00110000);//8-Bit mode
71
  _delay_us(45);
72
  write_command(0b00110000);//8-Bit mode
73
  _delay_us(45);
74
  write_command(0b00101000);//4-Bit mode 2 line display 5x7 ont type
75
  _delay_us(45);
76
  write_command(0b00001111);//Display on, curser on, curser blink
77
  _delay_us(45);
78
  write_command(0b00000001);//curser auto increment
79
  _delay_us(45);
80
}
81
82
void clear_lcd(void)
83
{
84
     write_command(0b00000001);//clear
85
  _delay_us(45);
86
}
87
88
void home_lcd(void)
89
{
90
     write_command(0b00000010);//home position
91
  _delay_us(45);
92
}
93
94
void goto_xy(int x,int y)
95
{
96
  if(y==1)
97
  {
98
    write_command(0b10000000+0b00000000+x);
99
  }
100
  if(y==2)
101
  {
102
    write_command(0b10000000+0b01000000+x);
103
  }
104
}
105
106
int main(void)
107
{
108
  DDRD=0xFF;
109
  init_LCD();
110
  while(1)
111
  {
112
    //split_string("TEST");
113
    //_delay_ms(2000);
114
    //split_string("LCD123");
115
    //_delay_ms(2000);
116
    write_data(0b01001000);//write H
117
    _delay_us(50);
118
  }
119
}

Das Display zeigt nichts an.

Kann mir jemand sagen, wo da der Fehler im Programm liegt?

Hier noch der Link zum LCD:
http://www.ebay.de/itm/HD44780-1602-LCD-Module-Display-Anzeigen-2X16-Zeichen-/250803268440

von Jim M. (turboj)


Lesenswert?

Hier fehlt was entscheidendes:
1
void write_data(int input)
2
{
3
  int high_bit,low_bit;
4
  LCD_DATA_PORT|=(1<<RS); //signal to write chars on display
5
  high_bit=(input&0xF0);   //get the higher 4 bit
6
  low_bit=(input&0x0F);    //get the lower 4 bit
7
  LCD_DATA_PORT=high_bit;  //send the higher 4 bit
8
  e_state();
9
  LCD_DATA_PORT=low_bit;   //send the lower 4 bit
10
  e_state();
11
}
12
13
void write_command(int input)
14
{
15
  int high_bit,low_bit;
16
  LCD_DATA_PORT&=~(1<<RS);  //signal to write command
17
  high_bit=(input&0xF0);   //get the higher 4 bit
18
  low_bit=(input&0x0F);    //get the lower 4 bit
19
  LCD_DATA_PORT=high_bit;  //send the higher 4 bit
20
  e_state();
21
  LCD_DATA_PORT=low_bit;   //send the lower 4 bit
22
  e_state();
23
}

Hint:
In welchem Nibble möchtest Du die Daten haben, in welchem sendest Du sie 
aus?

von Peter D. (peda)


Lesenswert?

Sven S. schrieb:
> Kann mir jemand sagen, wo da der Fehler im Programm liegt?

Beim Init kannst Du noch kein Write_Command nehmen, sondern nur 
Write_Nibble.

Write_Command setzt voraus, daß Du bereits im 4-Bit Mode bist, aber das 
ist ja noch unbestimmt. Und spätestens nach der Schaltung in 8-Bit bist 
Du es definitiv nicht mehr.

von Sven S. (nutellamann)


Lesenswert?

Vielen Dank schon mal für die Antworten.

>Beim Init kannst Du noch kein Write_Command nehmen, sondern nur
>Write_Nibble.

>Write_Command setzt voraus, daß Du bereits im 4-Bit Mode bist, aber das
>ist ja noch unbestimmt. Und spätestens nach der Schaltung in 8-Bit bist
>Du es definitiv nicht mehr.

Ok ich habe es mal so versucht:
1
void init_LCD(void)
2
{
3
  _delay_us(100);         //startup time
4
  LCD_DATA_PORT=0b0010;//4-Bit mode
5
  _delay_us(45);
6
  LCD_DATA_PORT=0b0010;//4-Bit mode
7
  _delay_us(45);
8
  LCD_DATA_PORT=0b1000;//two line display with 5x8 font size
9
  _delay_us(45);
10
  LCD_DATA_PORT=0b0000;
11
  _delay_us(45);
12
  LCD_DATA_PORT=0b1111;//Display on, curser on, curser blink
13
  _delay_us(45);
14
  LCD_DATA_PORT=0b0000;
15
  _delay_us(45);
16
  LCD_DATA_PORT=0b0001;//clear display, cursor on line 1
17
  _delay_us(45);
18
  LCD_DATA_PORT=0b0000;
19
  _delay_us(45);
20
  LCD_DATA_PORT=0b0110;//curser auto increment
21
  _delay_us(45);
22
}


>Hint:
>In welchem Nibble möchtest Du die Daten haben, in welchem sendest Du sie
>aus?

Nach der Hardware sende ich die Daten im unteren Nibble, Softwaremäßig 
bin ich mir da nicht so sicher, aber wenn Du so fragst ist es sicher 
anders herum ;)

Wegen der Unsicherheit, welches Nibble nun angesprochen wird, habe ich 
auch folgendes versucht:
1
void init_LCD(void)
2
{
3
  _delay_us(100);         //startup time
4
  LCD_DATA_PORT=0b00000010;//4-Bit mode
5
  _delay_us(45);
6
  LCD_DATA_PORT=0b00000010;//4-Bit mode
7
  _delay_us(45);
8
  LCD_DATA_PORT=0b00001000;//two line display with 5x8 font size
9
  _delay_us(45);
10
  LCD_DATA_PORT=0b00000000;
11
  _delay_us(45);
12
  LCD_DATA_PORT=0b00001111;//Display on, curser on, curser blink
13
  _delay_us(45);
14
  LCD_DATA_PORT=0b00000000;
15
  _delay_us(45);
16
  LCD_DATA_PORT=0b00000001;//clear display, curser on line 1
17
  _delay_us(45);
18
  LCD_DATA_PORT=0b00000000;
19
  _delay_us(45);
20
  LCD_DATA_PORT=0b00000110;//curser auto increment
21
  _delay_us(45);
22
}

Bringt mich aber auch nicht weiter, könnte es sein, dass ich in 
write_data() und write_command irgendwo eine 4-Bit Verschiebung einbauen 
muss?

von Lötlackl *. (pappnase) Benutzerseite


Lesenswert?

Sven S. schrieb:
> könnte es sein, dass ich in
> write_data() und write_command irgendwo eine 4-Bit Verschiebung einbauen
> muss?

Oh ja, sehe ich gerade. Mal angenommen PD0...3 seien die Datenleitungen 
dann sollte es so aussehen.
1
high_bit=(input&0xF0)>>4;   //get the higher 4 bit

mfg

von Sven S. (nutellamann)


Lesenswert?

** Lötlackl schrieb:
> Oh ja, sehe ich gerade. Mal angenommen PD0...3 seien die Datenleitungen
> dann sollte es so aussehen.high_bit=(input&0xF0)>>4;   //get the higher 4 bit
> mfg

Ok jetzt werden die Daten auf die Bits 0...3 geschoben wie es sein soll, 
weil ich diese Ausgänge ja an die Datenleitungen des LCDs angeschlossen 
habe.

Meine Initialisierung habe ich mit dem LCD Simulator 
http://www.geocities.com/dinceraydin/djlcdsim/djlcdsim.html verifiziert, 
die funktioniert soweit.

Jetzt sieht das Programm so aus:
1
#include <avr/io.h>
2
#include <util/delay.h>
3
/* Connections:
4
 *
5
 *   D4->PORTD0
6
 *  D5->PORTD1
7
 *  D6->PORTD2
8
 *  D7->PORTD3
9
 *  E-> PORTD4
10
 *  RW->PORTD6
11
 *  RS->PORTD6
12
 */
13
#define LCD_DATA_PORT PORTD
14
#define E   PD4
15
#define RW  PD5
16
#define RS  PD6
17
18
void e_state(void)
19
{
20
  _delay_us(20);
21
  LCD_DATA_PORT|=(1<<E);
22
  _delay_us(20);
23
  LCD_DATA_PORT&=~(1<<E);
24
}
25
26
void write_data(int input)
27
{
28
  int high_bit,low_bit;
29
  LCD_DATA_PORT|=(1<<RS); //signal to write chars on display
30
  high_bit=((input&0xF0)>>4);   //get the higher 4 bit
31
  low_bit=((input&0x0F)>>4);    //get the lower 4 bit
32
  LCD_DATA_PORT=high_bit;  //send the higher 4 bit
33
  e_state();
34
  LCD_DATA_PORT=low_bit;   //send the lower 4 bit
35
  e_state();
36
}
37
38
void write_command(int input)
39
{
40
  int high_bit,low_bit;
41
  LCD_DATA_PORT&=~(1<<RS);  //signal to write command
42
  high_bit=((input&0xF0)>>4);   //get the higher 4 bit
43
  low_bit=((input&0x0F)>>4);    //get the lower 4 bit
44
  LCD_DATA_PORT=high_bit;  //send the higher 4 bit
45
  e_state();
46
  LCD_DATA_PORT=low_bit;   //send the lower 4 bit
47
  e_state();
48
}
49
50
void send_string(char input)
51
{
52
  int output;
53
  output=(int)input;
54
  write_data(output);
55
}
56
57
void split_string(char *input)
58
{
59
  while(*input!='\0')
60
  {
61
    send_string(*input++);
62
  }
63
}
64
65
void init_LCD(void)
66
{
67
  _delay_us(100);         //startup time
68
  LCD_DATA_PORT=0b00000010;//4-Bit mode
69
  _delay_us(45);
70
  LCD_DATA_PORT=0b00000000;//two line display with 5x8 font size
71
  _delay_us(45);
72
  LCD_DATA_PORT=0b00001000;
73
  _delay_us(45);
74
  LCD_DATA_PORT=0b00000000;//Display on, curser on, curser blink
75
  _delay_us(45);
76
  LCD_DATA_PORT=0b00001111;
77
  _delay_us(45);
78
  LCD_DATA_PORT=0b00000000;//curser auto increment
79
  _delay_us(45);
80
  LCD_DATA_PORT=0b00000110;
81
  _delay_us(45);
82
}
83
84
void clear_lcd(void)
85
{
86
     write_command(0b00000001);//clear
87
  _delay_us(45);
88
}
89
90
void home_lcd(void)
91
{
92
     write_command(0b00000010);//home position
93
  _delay_us(45);
94
}
95
96
void goto_xy(int x,int y)
97
{
98
  if(y==1)
99
  {
100
    write_command(0b10000000+0b00000000+x);
101
  }
102
  if(y==2)
103
  {
104
    write_command(0b10000000+0b01000000+x);
105
  }
106
}
107
108
int main(void)
109
{
110
  DDRD=0xFF;
111
  init_LCD();
112
  while(1)
113
  {
114
    //split_string("TEST");
115
    //_delay_ms(2000);
116
    //split_string("LCD123");
117
    LCD_DATA_PORT=0b01001000;//write H
118
    _delay_us(50);
119
  }
120
}

Ist es jetzt nicht so, dass in write_data, wenn die 4 Bit des oberen- 
bzw. unteren Nibbles auf den PORTD gegeben werden, das RS Bit mit 0 
gesetzt wird statt 1 zu bleiben, damit die Ausgabe als Zeichen erkannt 
wird?


Aus der Überlegung folgere ich, dass ich normalerweise das RS Bit OR 
Verknüpft hinzufügen muss wie hier:
1
void write_data(int input)
2
{
3
  int high_bit,low_bit;
4
  LCD_DATA_PORT|=(1<<RS); //signal to write chars on display
5
  high_bit=((input&0xF0)>>4);   //get the higher 4 bit
6
  low_bit=((input&0x0F)>>4);    //get the lower 4 bit
7
  LCD_DATA_PORT=high_bit|(1<<RS);  //send the higher 4 bit
8
  e_state();
9
  LCD_DATA_PORT=low_bit|(1<<RS);   //send the lower 4 bit
10
  e_state();
11
}

Danach kann ich aber immer noch keine Ausgabe sehen :(

von Karl H. (kbuchegg)


Lesenswert?

>  low_bit=((input&0x0F)>>4);    //get the lower 4 bit

Und was soll da jetzt noch übrig bleiben, wenn du dir die 4 Bits, die du 
dir frei maskiert hast, jetzt nach rechts rausschiebst?

Irgendwie scheint dir noch wirklich klar zu sein, was du da eigentlich 
erreichen musst. Oder du probierst einfach irgendwas ohne darüber 
nachzudenken, was du da eigentlich tust.
WObei: die Sache mit dem RS hast du gut erkannt.

von Sven S. (nutellamann)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Und was soll da jetzt noch übrig bleiben, wenn du dir die 4 Bits, die du
> dir frei maskiert hast, jetzt nach rechts rausschiebst?

Ich versuche schon, das zu verstehen ;)

Also liege ich richtig, dass nach der Maskierung mit 0x0F das Ergebnis 
immer 0b0000xxxx ist und dann mit der Verschiebung nach rechts somit 
0b00000000 = 0x00 ergibt? Damit wäre es tatsächlich sinnfrei dies zu 
machen :)

von Karl H. (kbuchegg)


Lesenswert?

Sven S. schrieb:
> Karl Heinz Buchegger schrieb:
>> Und was soll da jetzt noch übrig bleiben, wenn du dir die 4 Bits, die du
>> dir frei maskiert hast, jetzt nach rechts rausschiebst?
>
> Ich versuche schon, das zu verstehen ;)
>
> Also liege ich richtig, dass nach der Maskierung mit 0x0F das Ergebnis
> immer 0b0000xxxx ist

Genau.

Wenn du µC programmieren willst, dann muss dir die Wirkung von Und und 
Oder Operationen in Fleisch und Blut übergehen.

Eine binäre Und-Operation ist wie ein Sieb. Dort wo ein 1 Bit sitzt, 
dort ist das Sieb offen und die 'Bits' können durch. Dort wo ein 0-Bit 
sitzt kommt nichts durch und im Ergebnis taucht an dieser Stelle ein 0 
Bit auf


       abcdefgh
       11110000         binäres Und
      ----------

       abcd0000

da du die 4 Bits an der niederwertigen Position brauchst, musst du sie 
um 4 stellen nach rechts verschieben.

       abcd0000        >> 4

       0000abcd

und du hast sie an den Positionen um sie auszugeben.


Die niederwertigen Bits

       abcdefgh
       00001111         binäres Und
     -----------

       0000efgh

diese 4 Bits sind aber schon an der richtigen Position. Da braucht nix 
verschoben werden.

von Karl H. (kbuchegg)


Lesenswert?

Und lern über Datentypen.
int willst du nicht für Bytes benutzen.

Auf Byteebene ist der Datentyp der Wahl ein uint8_t und kein int. Wozu 
den AVR in 16 Bit Arithmetik reinjagen, wenn du nur an 8 Bit 
interessiert bist, und dir ein eventuelles 'Vorzeichen' nicht in die 
Quere kommen soll. Ein uint8_t hat als unsigned Wert per Definition kein 
Vorzeichen. Alle Bits sind einfach nur Bits.

von Sven S. (nutellamann)


Lesenswert?

Ok nochmals Danke. Ich mache morgen mal dran weiter in der Hoffnung, 
damit endlich fertig werden zu können.

von Karl H. (kbuchegg)


Lesenswert?

Da du Code hast, der funktioniert:

Es schadet auch nicht, diesen Code zu studieren und sich anzusehen, wie 
andere die Sache machen. Niemand sagt, dass du diesen Code übernehmen 
musst. Aber man kann viel daraus lernen, indem man anderer Leute Code 
studiert und sich überlegt, warum sie manche Dinge genau so machen, wie 
sie sie machen.

von Sven S. (nutellamann)


Lesenswert?

So ich habe es endlich geschafft zum laufen zu bringen :) In der 
Initialisierung waren die Pausen zwischen den Befehlen zu kurz und ein 
paar Kleinigkeiten habe ich noch geändert. Allerdings habe ich auch 
feststellen müssen, dass im Displaycontroller trotz der Aufschrift 1602A 
nur der kleinere Zeichensatz enthalten ist, in dem auch Japanisch o.ä zu 
finden ist.

Vielen Dank nochmals an alle, die mir geholfen haben.
1
#include <avr/io.h>
2
#include <util/delay.h>
3
/* Connections:
4
 *
5
 *  D4->PORTD0
6
 *  D5->PORTD1
7
 *  D6->PORTD2
8
 *  D7->PORTD3
9
 *  E-> PORTD4
10
 *  RW->PORTD6
11
 *  RS->PORTD6
12
 */
13
#define LCD_DATA_PORT PORTD
14
#define E   PD4
15
#define RW  PD5
16
#define RS  PD6
17
18
void e_state(void)
19
{
20
  _delay_us(50);
21
  LCD_DATA_PORT|=(1<<E);
22
  _delay_us(20);
23
  LCD_DATA_PORT&=~(1<<E);
24
  _delay_us(50);
25
}
26
void write_data(uint8_t input)
27
{
28
  uint8_t high_bit,low_bit;
29
  high_bit=((input&0xF0)>>4);     //get the higher 4 bit
30
  low_bit=(input&0x0F);        //get the lower 4 bit
31
  LCD_DATA_PORT=high_bit|(1<<RS);  //send the higher 4 bit and signal to write chars on display
32
  e_state();
33
  LCD_DATA_PORT=low_bit|(1<<RS);   //send the lower 4 bit  and signal to write chars on display
34
  e_state();
35
}
36
37
void write_command(uint8_t input)
38
{
39
  uint8_t high_bit,low_bit;
40
  LCD_DATA_PORT&=~(1<<RS);  //signal to write command
41
  high_bit=((input&0xF0)>>4);   //get the higher 4 bit
42
  low_bit=(input&0x0F);    //get the lower 4 bit
43
  LCD_DATA_PORT=high_bit;  //send the higher 4 bit
44
  e_state();
45
  LCD_DATA_PORT=low_bit;   //send the lower 4 bit
46
  e_state();
47
}
48
49
void send_string(char input)
50
{
51
  uint8_t output;
52
  output=(uint8_t)input;
53
  write_data(output);
54
}
55
56
void split_string(char *input)
57
{
58
  while(*input!='\0')
59
  {
60
    send_string(*input++);
61
  }
62
}
63
64
void init_LCD(void)
65
{
66
  LCD_DATA_PORT &=~0xFF;  //set all outputs to 0
67
  _delay_ms(30);         //startup time
68
  LCD_DATA_PORT=0b00000010;//4-Bit mode
69
  e_state();
70
  LCD_DATA_PORT=0b00000010;//two line display with 5x8 font size
71
  e_state();
72
  LCD_DATA_PORT=0b00001000;
73
  e_state();
74
  LCD_DATA_PORT=0b00000000;//Display on, curser on, curser blink
75
  e_state();
76
  LCD_DATA_PORT=0b00001111;
77
  e_state();
78
  LCD_DATA_PORT=0b00000000;//clear display and return to home position
79
  e_state();
80
  LCD_DATA_PORT=0b00000001;
81
  e_state();
82
  LCD_DATA_PORT=0b00000000;//curser auto increment scrolling on
83
  e_state();
84
  LCD_DATA_PORT=0b00000111;
85
  e_state();
86
}
87
88
void clear_lcd(void)
89
{
90
  write_command(0b00000001);//clear
91
  _delay_ms(10);
92
}
93
94
void home_lcd(void)
95
{
96
  write_command(0b00000010);//home position
97
  _delay_ms(10);
98
}
99
100
void goto_xy(uint8_t x,uint8_t y)
101
{
102
  if(y==1)
103
  {
104
    write_command(0b10000000+0b00000000+x);
105
    _delay_us(50);
106
  }
107
  if(y==2)
108
  {
109
    write_command(0b10000000+0b01000000+x);
110
    _delay_us(50);
111
  }
112
}
113
114
115
int main(void)
116
{
117
  DDRD=0xFF;
118
  init_LCD();
119
  while(1)
120
  {
121
    home_lcd();
122
    clear_lcd();
123
    split_string("Raupe");
124
    _delay_ms(500);
125
    goto_xy(0,2);
126
    split_string("haha");
127
    _delay_ms(500);
128
    goto_xy(7,2);
129
    split_string("Auto");
130
    _delay_ms(5000);
131
    clear_lcd();
132
    _delay_ms(5000);
133
  }
134
}

von m.n. (Gast)


Lesenswert?

Bevor Du die Routinen Deinem Lehrer zeigst, ändere bitte noch die 
Strings in main() auf:
"Mama"
"Papa"
"Auto"
 :-)

von Karl H. (kbuchegg)


Lesenswert?

Ist ja interessant.

Da gibt es eine Funktion namens 'send_string'
1
void send_string(char input)
2
{
3
  uint8_t output;
4
  output=(uint8_t)input;
5
  write_data(output);
6
}

die einen einzelnen Character am LCD anzeigt aber sicher keinen String. 
Klarer Fall von 'Verwirre deinen Benutzer mit nicht passenden 
Funktionsnamen'.


Dafür gibt es eine Funktion namens 'split_string', die dem Namen nach 
einen String in Einzelteile zerlegt (splittet), aber dem Code nach den 
String auf dem LCD ausgibt
1
void split_string(char *input)
2
{
3
  while(*input!='\0')
4
  {
5
    send_string(*input++);
6
  }
7
}

Weiter so! Du hast das Prinzip der Arbeitsplatzsicherung durch 
unverständlichen und falsch benannten Code komplett richtig verstanden!
Genau so macht man sich in einer Firma unentbehrlich! Dein Pech nur, 
dass LCD Funktionen jetzt nicht Raketentechnik sind und in ein paar 
Minuten richtig gestellt werden können. Aber die Grundidee ist 
ausbaufähig! Ein Funktionsname soll auf keinen Fall einen intuitiven 
Rückschluss darauf zulassen, was die Funktion macht.

von Karl H. (kbuchegg)


Lesenswert?

Und PS:

Die Zusammenfassung von 4 Bits, so wie sie in deinem Fall gemacht wird, 
nennt man in der EDV ein 'Nibble'.
Ein Byte besteht also aus 8 Bit. Es besteht aber auch aus 2 Nibbles - 
einem höherwertigem Nibble und einem niederwertigem Nibble.

die 8 Bit eines Bytes     abcdefgh
                          |  ||  |
                          |  |+--+ Die 4 Bits des niederwertigen Nibbles
                          |  |     oder auch einfach 'Low-Nibble'
                          |  |
                          +--+ Die 4 Bit des höherwertigen Nibbles
                               oder auch einfach 'High-Nibble' genannt.

von Karl H. (kbuchegg)


Lesenswert?

Sven S. schrieb:

> void init_LCD(void)
> {
>   LCD_DATA_PORT &=~0xFF;  //set all outputs to 0
>   _delay_ms(30);         //startup time
>   LCD_DATA_PORT=0b00000010;//4-Bit mode
>   e_state();


Ab hier funktioniert das ganze zwar per Design, ist aber höchst 
missverständlich.
Ab dem Moment, ab dem du erfolgreich auf 4-Bit Interface umgeschaltet 
hast, kannst du nicht mehr Kommandos an das LCD geben, in dem du die 8 
Bit des Kommandos an die Datenleitungen legst und mit dem E-Pin 
wackelst! Ab dem Moment des erfolgreichen Umschaltens werden Kommandos 
ausschliesslich über die Funktion write_command abgewickelt, die dann 
das Kommando korrekt in 2 Happen (wegen 4 Bit Interface) an das LCD 
weitergibt. Ab hier diese Zerlegung selbst zu machen, ist ein 'Ask for 
trouble'.

>   LCD_DATA_PORT=0b00000010;//two line display with 5x8 font size
>   e_state();
>   LCD_DATA_PORT=0b00001000;
>   e_state();
>   LCD_DATA_PORT=0b00000000;//Display on, curser on, curser blink
>   e_state();
>   LCD_DATA_PORT=0b00001111;
>   e_state();

>   LCD_DATA_PORT=0b00000000;//clear display and return to home position
>   e_state();
>   LCD_DATA_PORT=0b00000001;
>   e_state();

Huch. Für das 'clear Display' und 'Cursor Home' hast du eigene 
Funktionen. Warum benutzt du sie nicht?


Der kritische Punkt in der Initialisierung des LCD ist das Umschalten 
auf 4 Bit Modus.(*) Danach bedeutet Initialisieren nur noch Kommandos 
über den offiziellen Weg der 'Kommando-Senden' Funktion an das LCD 
geben, so dass jede Funktionalität einen definierten, dir genehmen Wert 
einnimmt. Daztu hast du die Funktion, damit sich die um die Details wie 
zb die Zerlegung in Nibbles, kümmert.


(*) Und NB. Dein Umschaltcode ist eigentlich falsch. Laut Datenblatt ist 
zum sauberen Umschalten in den 4-Bit Modus mehr zu tun als du da tust. 
Es reicht nicht, wenn du das LCD aus dem 'Strom-an' Zustand auf 4 Bit 
umschalten kannst. Die Umschaltung muss aus JEDEM Zustand heraus 
klappen. Die dokumentierte Sequenz in den Datenblättern kann das und die 
wird sicherlich auch in deiner Code-Vorlage benutzt. Ich sagte schon: 
studier wie andere das machen und überleg dir, warum deren Code so 
aussieht wie er aussieht. Man kann viel lernen, indem man sich anderer 
Leute Code ansieht und studiert.

von Sven S. (nutellamann)


Lesenswert?

Hallo,

das mit dem missverständlichen Namen der Funktion habe ich noch 
geändert, ich hatte mich einfach so gefreut, dass es endlich 
funktionierte ;D

Karl Heinz Buchegger schrieb:
> Huch. Für das 'clear Display' und 'Cursor Home' hast du eigene
> Funktionen. Warum benutzt du sie nicht?

Da habe ich die Funktionen nicht benutzt, weil weiter oben ja die Rede 
davon war, ich könne in der Initialisierung noch nicht auf die 
write_command() Funktion zurückgreifen.

Peter Dannegger schrieb:
> eim Init kannst Du noch kein Write_Command nehmen, sondern nur
> Write_Nibble.

Karl Heinz Buchegger schrieb:
> (*) Und NB. Dein Umschaltcode ist eigentlich falsch. Laut Datenblatt ist
> zum sauberen Umschalten in den 4-Bit Modus mehr zu tun als du da tust.
> Es reicht nicht, wenn du das LCD aus dem 'Strom-an' Zustand auf 4 Bit
> umschalten kannst. Die Umschaltung muss aus JEDEM Zustand heraus
> klappen.

Da habe ich was gelesen, man müsse da drei mal hintereinander die 
Umschaltsequenz benutzen, aber ohne scheint es ja auch zu funktionieren.
Mein "Referenz-Quelltext" ist übrigns von hier: 
http://extremeelectronics.co.in/avr-tutorials/using-lcd-module-with-avrs/ 
Das ist finde ich ein ziemliches Durcheinander, sodass die Quelltexte 
nicht soo hilfreich waren und ich mich mehr an den Datenblättern 
orientiert habe.

Karl Heinz Buchegger schrieb:
> Ab hier funktioniert das ganze zwar per Design, ist aber höchst
> missverständlich.
> Ab dem Moment, ab dem du erfolgreich auf 4-Bit Interface umgeschaltet
> hast, kannst du nicht mehr Kommandos an das LCD geben, in dem du die 8
> Bit des Kommandos an die Datenleitungen legst und mit dem E-Pin
> wackelst!

Ok da könnte ich ja noch versuchen, über die Bitverschiebung zu 
arbeiten, wie auch in der write_command() und write_data() Funktion?

Karl Heinz Buchegger schrieb:
> Die Umschaltung muss aus JEDEM Zustand heraus
> klappen.

Was wäre denn beispielsweise ein Szenario, in dem das nicht 
funktionieren würde?

von Karl H. (kbuchegg)


Lesenswert?

Sven S. schrieb:

> Was wäre denn beispielsweise ein Szenario, in dem das nicht
> funktionieren würde?

Zum Beispiel wenn das LCD schon im 4-Bit Modus ist :-)

Zum Beispiel wenn du einen Prozessor-Reset machst, just in dem Moment in 
dem von einem LCD Kommando das erste Nibble schon übertragen wurde und 
das zweite noch nicht.

von Sven S. (nutellamann)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Zum Beispiel wenn du einen Prozessor-Reset machst, just in dem Moment in
> dem von einem LCD Kommando das erste Nibble schon übertragen wurde und
> das zweite noch nicht.

Ok das überrascht mich jetzt, ich habe noch über keine andere 
Möglichkeit gelsesen, außer die Nibbles hintereinander zu senden. 
Vielleicht habe ich ja was übersehen, ich schau noch mal nach.

Hier lernt man echt mehr als in der Schule thumbs up

von Karl H. (kbuchegg)


Lesenswert?

Sven S. schrieb:
> Karl Heinz Buchegger schrieb:
>> Zum Beispiel wenn du einen Prozessor-Reset machst, just in dem Moment in
>> dem von einem LCD Kommando das erste Nibble schon übertragen wurde und
>> das zweite noch nicht.
>
> Ok das überrascht mich jetzt, ich habe noch über keine andere
> Möglichkeit gelsesen, außer die Nibbles hintereinander zu senden.

Darum gehts aber auch gar nicht.

Es geht darum, dass dann das LCD auf das 2.te Nibble wartet um das 
Kommando zu vervollständigen.
Da kommt dann auch was. Nämlich dein erstes Byte mit dem du nach dem 
Prozessorreset versuchst, das LCD wieder in einen 4 Bit Modus zu 
versetzen.

von Sven S. (nutellamann)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Darum gehts aber auch gar nicht.
>
> Es geht darum, dass dann das LCD auf das 2.te Nibble wartet um das
> Kommando zu vervollständigen.
> Da kommt dann auch was. Nämlich dein erstes Byte mit dem du nach dem
> Prozessorreset versuchst, das LCD wieder in einen 4 Bit Modus zu
> versetzen.

Ah der Reset des Displays hat gefehlt, wobei dies 3 Mal hintereinander 
gemacht werden sollte.
1
LCD_DATA_PORT=0x30;

Damit ist die Initialisierung weniger verwirrend hinsichtlich der 4 Bit 
Datenleitung und dem 8 Bit Wert am Port des Mikrocontrollers.
1
LCD_DATA_PORT=(LCD_DATA_PORT&0x0F)|0b1100;

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.