Forum: Compiler & IDEs LCD nicht nur für einen Port in C


von Hans M. (gast4711)


Lesenswert?

Habe zwar die LCD-Routine aus dem AVR-GCC-Tutorial mit Erfolg getestet:
Lcd.c:
1
#include <avr/io.h>
2
#include "lcd-routines.h"
3
int main(void){
4
  lcd_string("Hello World!");
5
  while(1);
6
  return 0;
7
}

Leider habe ich aber eine vorgegebene Hardware, an der RS und EN nicht 
an PD4 bzw. PD5 liegen, sondern
#define LCD_RS        PB7
#define LCD_EN        PB6
Diese Umdefinitionen reichen allein natürlich nicht aus, um das Programm 
wieder zum Laufen zu bringen.
Habe auch keine Ahnung vom AVR (Kenne nur 8051, wo man mit sbit RS=P1^2; 
und dann RS=1; einzelne Portleitungen setzen kann) und weiß nicht wie 
das bei Atmel geht, geschweige denn, was so komplexe Sequenzen wie
LCD_DDR = LCD_DDR | 0x0F | (1<<LCD_RS) | (1<<LCD_EN);
genau machen.

Vielleicht findet sich ja hier ein Profi, der sich herausgefordert fühlt 
das zu realisieren (oder hat so etwas schon einmal für verschiedene 
Portpins gemacht)?

Wäre sehr, sehr dankbar, wenn er es hier posten könnte !!!


------------------------------------------------------------------------ 
------------------------------------------------
Hier nochmal alle Quelltexte:
lcd-routines.h:
1
void lcd_data(unsigned char temp1);
2
void lcd_string(char *data);
3
void lcd_command(unsigned char temp1);
4
void lcd_enable(void);
5
void lcd_init(void);
6
void lcd_home(void);
7
void lcd_clear(void);
8
void set_cursor(uint8_t x, uint8_t y);
9
#define F_CPU 8000000
10
#define CLEAR_DISPLAY 0x01
11
#define CURSOR_HOME   0x02
12
#define LCD_PORT      PORTD
13
#define LCD_DDR       DDRD
14
#define LCD_RS        PD4
15
#define LCD_EN        PD5
lcd-routines.c:
1
 
2
#include <avr/io.h>
3
#include "lcd-routines.h"
4
#include <util/delay.h>
5
void lcd_data(unsigned char temp1)
6
{
7
   unsigned char temp2 = temp1;
8
  LCD_PORT |= (1<<LCD_RS);        // RS auf 1 setzen
9
  temp1 = temp1 >> 4;
10
  temp1 = temp1 & 0x0F;
11
  LCD_PORT &= 0xF0;
12
  LCD_PORT |= temp1;               // setzen
13
  lcd_enable();
14
  temp2 = temp2 & 0x0F;
15
  LCD_PORT &= 0xF0;
16
  LCD_PORT |= temp2;               // setzen
17
  lcd_enable();
18
  _delay_us(42);
19
}
20
void lcd_command(unsigned char temp1)
21
{
22
  unsigned char temp2 = temp1;
23
  LCD_PORT &= ~(1<<LCD_RS);        // RS auf 0 setzen
24
  temp1 = temp1 >> 4;              // oberes Nibble holen
25
  temp1 = temp1 & 0x0F;            // maskieren
26
  LCD_PORT &= 0xF0;
27
  LCD_PORT |= temp1;               // setzen
28
  lcd_enable();
29
  temp2 = temp2 & 0x0F;            // unteres Nibble holen und maskieren
30
  LCD_PORT &= 0xF0;
31
  LCD_PORT |= temp2;               // setzen
32
  lcd_enable();
33
 _delay_us(42);
34
}
35
void lcd_enable(void)
36
{
37
  LCD_PORT |= (1<<LCD_EN);
38
  _delay_us(1);                   // kurze Pause
39
  LCD_PORT &= ~(1<<LCD_EN);
40
}
41
void lcd_init(void)
42
{
43
  LCD_DDR = LCD_DDR | 0x0F | (1<<LCD_RS) | (1<<LCD_EN);   // Port auf Ausgang schalten
44
  _delay_ms(15);
45
  LCD_PORT &= 0xF0;
46
  LCD_PORT |= 0x03;            
47
  LCD_PORT &= ~(1<<LCD_RS);      // RS auf 0
48
  lcd_enable();
49
  _delay_ms(5);
50
  lcd_enable();
51
  _delay_ms(1);
52
  lcd_enable();
53
  _delay_ms(1);
54
  LCD_PORT &= 0xF0;
55
  LCD_PORT |= 0x02;
56
  lcd_enable();
57
  _delay_ms(1);
58
  lcd_command(0x28);
59
  lcd_command(0x0C); 
60
  lcd_command(0x06);
61
  lcd_clear();
62
}
63
void lcd_clear(void)
64
{
65
  lcd_command(CLEAR_DISPLAY);
66
  _delay_ms(5);
67
}
68
void lcd_home(void)
69
{
70
  lcd_command(CURSOR_HOME);
71
  _delay_ms(5);
72
}
73
void set_cursor(uint8_t x, uint8_t y)
74
{
75
  uint8_t tmp;
76
  switch (y) {
77
    case 1: tmp=0x80+0x00+x; break;    // 1. Zeile
78
    case 2: tmp=0x80+0x40+x; break;    // 2. Zeile
79
    case 3: tmp=0x80+0x10+x; break;    // 3. Zeile
80
    case 4: tmp=0x80+0x50+x; break;    // 4. Zeile
81
  }
82
  lcd_command(tmp);
83
}
84
void lcd_string(char *data)
85
{
86
  while(*data) {
87
     lcd_data(*data);
88
     data++;
89
  }
90
}

von Karl H. (kbuchegg)


Lesenswert?

Hans Mustermann wrote:
> Habe zwar die LCD-Routine aus dem AVR-GCC-Tutorial mit Erfolg getestet:
> Lcd.c:
>
1
> #include <avr/io.h>
2
> #include "lcd-routines.h"
3
> int main(void){
4
>   lcd_string("Hello World!");
5
>   while(1);
6
>   return 0;
7
> }
8
>
>
> Leider habe ich aber eine vorgegebene Hardware, an der RS und EN nicht
> an PD4 bzw. PD5 liegen, sondern
> #define LCD_RS        PB7
> #define LCD_EN        PB6

Was ist mit den restlichen Datenleitungen? Liegen die ebenfalls
am Port B? Welche Pins?

Wenn die Datenleitungen ebenfalls am PORTB, Pins PB0 bis PB4 liegen,
dann reichen folgende Änderungen aus:


> #define LCD_PORT      PORTB
> #define LCD_DDR       DDRB
> #define LCD_RS        PB7
> #define LCD_EN        PB6

und das LCD sollte am PortB laufen.

von roboter (Gast)


Lesenswert?

Pin #-LCD  Bezeichnung-LCD  Pin-µC
1  Vss  GND
2  Vcc  5V
3  Vee  GND oder Poti (siehe oben)
4  RS  PD4 am AVR
5  RW  GND
6  E  PD5 am AVR
7  DB0  offen
8  DB1  offen
9  DB2  offen
10  DB3  offen
11  DB4  PD0 am AVR
12  DB5  PD1 am AVR
13  DB6  PD2 am AVR
14  DB7  PD3 am AVR

das oben steht in der beschreibung. also musste db4 bis db7 an 
port0-port3 anschliessen.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

Wenn die Datenleitungen wie im Tutorial an PORTD liegen und nur die 
Steuerleitungen wie von dir angegeben an PORTB, dann könntest du so 
vorgehen
1
#define LCD_PORT      PORTD
2
#define LCD_DDR       DDRD
3
#define LCD_RS        PD4
4
#define LCD_EN        PD5

ersetzt du durch
1
#define LCD_CTRL_PORT      PORTB
2
#define LCD_CTRL_DDR       DDRB
3
#define LCD_RS             PB7
4
#define LCD_EN             PB6 
5
6
#define LCD_DATA_PORT      PORTD
7
#define LCD_DATA_DDR       DDRD

In jedem Statement (Zeile) mit LCD_RS oder LCD_EN ersetzt du LCD_PORT 
durch LCD_CTRL_PORT.

Und in jeder anderen Zeile mit LCD_PORT ersetzt du LCD_PORT durch 
LCD_DATA_PORT

Den Abschnitt
1
  // Port auf Ausgang schalten
2
  LCD_DDR = LCD_DDR | 0x0F | (1<<LCD_RS) | (1<<LCD_EN);

ersetzt du durch
1
  // Ports auf Ausgang schalten
2
  LCD_DATA_DDR = LCD_DATA_DDR | 0x0F;
3
  LCD_CTRL_DDR = LCD_CTRL_DDR | (1<<LCD_RS) | (1<<LCD_EN);

von Hans M. (gast4711)


Lesenswert?

kbuchegg wrote:
>Wenn die Datenleitungen ebenfalls am PORTB, Pins PB0 bis PB4 liegen,
>dann reichen folgende Änderungen aus: ...
Für so eine einfache Portsubstitution hätte ich dann doch das Forum 
nicht bemüht :)

stefan wrote:
>...

Habe jetzt wie beschrieben
#define LCD_CTRL_PORT      PORTB
#define LCD_CTRL_DDR       DDRB
#define LCD_RS             PB7
#define LCD_EN             PB6
#define LCD_DATA_PORT      PORTD
#define LCD_DATA_DDR       DDRD
und in LCD_RS- bzw LCD_EN-Zeilen LCD_PORT durch LCD_CTRL_PORT ersetzt
und in jeder anderen Zeil LCD_PORT durch LCD_DATA_PORT ersetzt und
es klappt tatsächlich!

Weiß nur noch nicht ganz sicher, ob alle andern, "freien" Pins 
(PD4...PD7 und PB0...PB5) noch uneingeschränkt anderweitige Aufgaben 
erfüllen können, ohne von LCD-Routinen beeinflußt zu werden.

Denn für einen AVR-Neuling sind im Gegensatz zu RS=1 bei Intel so Arten 
von “Schiebeoperationen“ zum Portsetzen wie
PORTB |= (1<<PB0);
doch etwas kryptisch (muß mich bei Zeiten mal da einarbeiten; auch ADW 
und Interruptsteuerung...).

Dank‘ Dir auf jeden Fall Stefan für die Hilfe!

von Karl H. (kbuchegg)


Lesenswert?

Hans Mustermann wrote:
> kbuchegg wrote:
>>Wenn die Datenleitungen ebenfalls am PORTB, Pins PB0 bis PB4 liegen,
>>dann reichen folgende Änderungen aus: ...
> Für so eine einfache Portsubstitution hätte ich dann doch das Forum
> nicht bemüht :)

Dann solltest du dir zumindest vormerken, das nächste
vollständige Angaben in einer Anfrage zu machen :-)

von klaus (Gast)


Lesenswert?

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial

da drin ist die lcd-routine ein bisschen beschissen erstellt worden.
der lernende fragt sich , wo werden die pins0-pins3 in den routinen 
herausgestellt. es ist kein bezug da.

also, wenn was dargestellt werder soll, ein  bisschen mehr licht ins 
dunkel, dann hört auch die diskussion auf.

von Stefan B. (stefan) Benutzerseite


Lesenswert?

klaus wrote:
> http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
>
> da drin ist die lcd-routine ein bisschen beschissen erstellt worden.

Ein "bisschen beschissen" gibt es nicht. Du solltest dir überlegen, 
welche Motivation du einem Artikelautor mit einem solchen Statement 
gibst, für dich und andere das Tutorial zu verbessern. Nein, ich bin 
nicht der Autor. Ja, ich finde, der Autor hat gute Arbeit geleistet.

> der lernende fragt sich , wo werden die pins0-pins3 in den routinen
> herausgestellt. es ist kein bezug da.
>
> also, wenn was dargestellt werder soll, ein  bisschen mehr licht ins
> dunkel, dann hört auch die diskussion auf.

Ich habe versucht aus deinem Text deinen Wunsch heraus zu klauben. Wenn 
es darum geht anzuzeigen, wo die Leitungen DB4 bis DB8 des LCD am AVR 
angeschlossen sind, dann steht das im Tutorialtext eindeutig drin. In 
dem Sourcecode steht es nicht eindeutig drin. Die Pinbelegung habe ich 
mit einem Kommentar im Quelltext ergänzt. Ich hoffe, das ist auch im 
Sinn des Tutorialautors.

von Karl H. (kbuchegg)


Lesenswert?

klaus wrote:
> http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
>
> da drin ist die lcd-routine ein bisschen beschissen erstellt worden.
> der lernende fragt sich , wo werden die pins0-pins3 in den routinen
> herausgestellt. es ist kein bezug da.

Es wird auch davon ausgegangen, dass 'der Lernende' zumindest
über Grundkenntnisse in C verfügt, bevor er sich an ein LCD wagt.

Für jemanden mit Grundkenntnissen, der auch bereit ist
den Code den er da abtippt zu verstehen, ist es absolut kein
Problem im veröffentlichten Code die entsprechenden Stellen
zu finden, zu verstehen und zu ändern.

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

Hans Mustermann wrote:
> Habe auch keine Ahnung vom AVR (Kenne nur 8051, wo man mit sbit RS=P1^2;
> und dann RS=1; einzelne Portleitungen setzen kann) und weiß nicht wie
> das bei Atmel geht

Eigentlich ganz genau so, wie beim 8051, nur daß man noch die 
Direction-Bits setzen muß.

Die Bitzugriffe kann man mit einem Macro (SBIT) definieren.

Anbei mal ein Beispiel für 2*40 LCD im 4Bit-Mode mit jedem beliebigen 
Pin.


Peter

von roboter (Gast)


Lesenswert?

...Für jemanden mit Grundkenntnissen, der auch bereit ist
den Code den er da abtippt zu verstehen, ist es absolut kein
Problem im veröffentlichten Code die entsprechenden Stellen
zu finden, zu verstehen und zu ändern....

da sehe ich in einem tut anders, da gehört aufklärung rein, auch im 
code.

von roboter (Gast)


Lesenswert?

....Anbei mal ein Beispiel für 2*40 LCD im 4Bit-Mode mit jedem 
beliebigen
Pin.......


dieses beispiel ist auch für anfänger wunderbar geschrieben, klasse.

mfg

von bastle (Gast)


Lesenswert?

typedef unsigned char  u8;
typedef   signed char  s8;
typedef unsigned short u16;
typedef   signed short s16;
typedef unsigned long  u32;
typedef   signed long  s32;

welchen vorzug hat diese schreibweise? text zu sparen? u8 statt uint8_t 
!

von Karl H. (kbuchegg)


Lesenswert?

roboter wrote:
> ...Für jemanden mit Grundkenntnissen, der auch bereit ist
> den Code den er da abtippt zu verstehen, ist es absolut kein
> Problem im veröffentlichten Code die entsprechenden Stellen
> zu finden, zu verstehen und zu ändern....
>
> da sehe ich in einem tut anders, da gehört aufklärung rein, auch im
> code.


Ist ganz einfach.
Wenn du denkst, dass diese Stellen nicht gut genug einen
Sachverhalt beschreiben, dann ändere sie. Genau aus diesem
Grund ist das ja ein Wiki.

von Spitti (Gast)


Lesenswert?

Warum wird eigentlich bei dir der C code So Farbig angezeigt?
Bei mir geht das garnicht!

Das beispiel von Peter Danneger klappt super!
Damit habe ich vorhin zum ersten mal ein Display angesteuert!

von Chris W. (verleihnix85)


Lesenswert?

Ich hab mal den Code verändert.

"3 mal w".verleihnix.890m.com/index.php?p=1_16

der nimmt meine URL sonst nicht an

die lcd-routines.h
1
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus
2
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
3
//
4
5
void lcd_string(char *data);
6
7
void lcd_enable(void);
8
void lcd_init(void);
9
void lcd_home(void);
10
void lcd_clear(void);
11
void set_cursor(uint8_t x, uint8_t y);
12
13
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!
14
 
15
#define F_CPU 8000000
16
 
17
// LCD Befehle
18
 
19
#define CLEAR_DISPLAY 0x01
20
#define CURSOR_HOME   0x02
21
 
22
// Pinbelegung für das LCD, an verwendete Pins anpassen
23
 
24
#define LCD_PORT_4    PORTD
25
#define LCD_DDR_4     DDRD
26
#define LCD_D4      PD1
27
28
#define LCD_PORT_5    PORTC
29
#define LCD_DDR_5     DDRC
30
#define LCD_D5      PC1
31
32
#define LCD_PORT_6    PORTC
33
#define LCD_DDR_6     DDRC
34
#define LCD_D6      PC2
35
36
#define LCD_PORT_7    PORTC
37
#define LCD_DDR_7     DDRC
38
#define LCD_D7      PC3
39
40
#define LCD_RS_PORT    PORTC
41
#define LCD_RS_DDR    DDRC
42
#define LCD_RS        PC5
43
44
#define LCD_EN1_PORT  PORTB
45
#define LCD_EN1_DDR    DDRB
46
#define LCD_EN1       PB0



die lcd-routines.c
1
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus
2
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
3
//
4
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar
5
 
6
#include <avr/io.h>
7
#include "lcd-routines.h"
8
#include <util/delay.h>
9
 
10
 
11
// sendet ein Datenbyte an das LCD
12
 
13
void lcd_data(unsigned char temp1)
14
{
15
   unsigned char temp2 = temp1;
16
 
17
   LCD_RS_PORT |= (1<<LCD_RS);        // RS auf 1 setzen
18
 
19
   temp1 = temp1 >> 4;
20
   temp1 = temp1 & 0x0F;
21
   
22
   LCD_PORT_4 &= ~(1<<LCD_D4);
23
   LCD_PORT_5 &= ~(1<<LCD_D5);
24
   LCD_PORT_6 &= ~(1<<LCD_D6);
25
   LCD_PORT_7 &= ~(1<<LCD_D7);
26
 
27
   if(temp1 & 0x01) LCD_PORT_4 |=(1<<LCD_D4) ;// setzen
28
   if(temp1 & 0x02) LCD_PORT_5 |=(1<<LCD_D5) ;
29
   if(temp1 & 0x04) LCD_PORT_6 |=(1<<LCD_D6) ;
30
   if(temp1 & 0x08) LCD_PORT_7 |=(1<<LCD_D7) ;
31
   lcd_enable();
32
   
33
   
34
 
35
   temp2 = temp2 & 0x0F;
36
   
37
   LCD_PORT_4 &= ~(1<<LCD_D4);
38
   LCD_PORT_5 &= ~(1<<LCD_D5);
39
   LCD_PORT_6 &= ~(1<<LCD_D6);
40
   LCD_PORT_7 &= ~(1<<LCD_D7); 
41
   
42
   if(temp2 & 0x01) LCD_PORT_4 |=(1<<LCD_D4) ;// setzen
43
   if(temp2 & 0x02) LCD_PORT_5 |=(1<<LCD_D5) ;
44
   if(temp2 & 0x04) LCD_PORT_6 |=(1<<LCD_D6) ;
45
   if(temp2 & 0x08) LCD_PORT_7 |=(1<<LCD_D7) ;
46
   lcd_enable();
47
48
   
49
   _delay_us(42);
50
}
51
 
52
// sendet einen Befehl an das LCD
53
 
54
void lcd_command(unsigned char temp1)
55
{
56
   unsigned char temp2 = temp1;
57
 
58
   LCD_RS_PORT &= ~(1<<LCD_RS);        // RS auf 0 setzen
59
 
60
   temp1 = temp1 >> 4;              // oberes Nibble holen
61
   temp1 = temp1 & 0x0F;
62
   
63
   LCD_PORT_4 &= ~(1<<LCD_D4);
64
   LCD_PORT_5 &= ~(1<<LCD_D5);
65
   LCD_PORT_6 &= ~(1<<LCD_D6);
66
   LCD_PORT_7 &= ~(1<<LCD_D7);            // maskieren
67
   
68
   if(temp1 & 0x01) LCD_PORT_4 |=(1<<LCD_D4) ;// setzen
69
   if(temp1 & 0x02) LCD_PORT_5 |=(1<<LCD_D5) ;
70
   if(temp1 & 0x04) LCD_PORT_6 |=(1<<LCD_D6) ;
71
   if(temp1 & 0x08) LCD_PORT_7 |=(1<<LCD_D7) ;
72
   lcd_enable();
73
 
74
   temp2 = temp2 & 0x0F;
75
   
76
   LCD_PORT_4 &= ~(1<<LCD_D4);
77
   LCD_PORT_5 &= ~(1<<LCD_D5);
78
   LCD_PORT_6 &= ~(1<<LCD_D6);
79
   LCD_PORT_7 &= ~(1<<LCD_D7);        // unteres Nibble holen und maskieren
80
   
81
   if(temp2 & 0x01) LCD_PORT_4 |=(1<<LCD_D4) ;// setzen
82
   if(temp2 & 0x02) LCD_PORT_5 |=(1<<LCD_D5) ;
83
   if(temp2 & 0x04) LCD_PORT_6 |=(1<<LCD_D6) ;
84
   if(temp2 & 0x08) LCD_PORT_7 |=(1<<LCD_D7) ;
85
   lcd_enable();
86
   
87
   _delay_us(42);
88
}
89
 
90
// erzeugt den Enable-Puls
91
92
void lcd_enable(void)
93
{
94
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers einfügen
95
   
96
   // http://www.mikrocontroller.net/topic/81974#685882
97
   LCD_EN1_PORT |= (1<<LCD_EN1);
98
  
99
 
100
    _delay_us(1);                   // kurze Pause
101
   // Bei Problemen ggf. Pause gemäß Datenblatt des LCD Controllers verlängern
102
   // http://www.mikrocontroller.net/topic/80900
103
   LCD_EN1_PORT &= ~(1<<LCD_EN1);
104
   
105
   
106
  
107
} 
108
// Initialisierung: 
109
// Muss ganz am Anfang des Programms aufgerufen werden.
110
 
111
void lcd_init(void)
112
{
113
   // Ports auf Ausgang schalten
114
   LCD_DDR_4 |=(1<<LCD_D4);
115
   LCD_DDR_5 |=(1<<LCD_D5);
116
   LCD_DDR_6 |=(1<<LCD_D6);
117
   LCD_DDR_7 |=(1<<LCD_D7);
118
   
119
   LCD_EN1_DDR |= (1<<LCD_EN1);
120
 
121
   LCD_RS_DDR  |= (1<<LCD_RS);
122
   // muss 3mal hintereinander gesendet werden zur Initialisierung
123
   
124
   _delay_ms(15);
125
   
126
   LCD_PORT_4 |=  (1<<LCD_D4);
127
   LCD_PORT_5 |=  (1<<LCD_D5);
128
   LCD_PORT_6 &= ~(1<<LCD_D6);
129
   LCD_PORT_7 &= ~(1<<LCD_D7);  
130
  
131
   LCD_RS_PORT &= ~(1<<LCD_RS);      // RS auf 0
132
   lcd_enable();
133
 
134
   _delay_ms(5);
135
   lcd_enable();
136
 
137
   _delay_ms(1);
138
   lcd_enable();
139
   _delay_ms(1);
140
 
141
   // 4 Bit Modus aktivieren
142
 
143
   
144
   
145
   LCD_PORT_4 &=  ~(1<<LCD_D4);
146
   LCD_PORT_5 |=   (1<<LCD_D5);
147
   LCD_PORT_6 &=  ~(1<<LCD_D6);
148
   LCD_PORT_7 &=  ~(1<<LCD_D7);  
149
   
150
   lcd_enable();
151
   _delay_ms(1);
152
 
153
   // 4Bit / 2 Zeilen / 5x7
154
   lcd_command(0x28);
155
    
156
   // Display ein / Cursor aus / kein Blinken
157
   lcd_command(0x0C); 
158
 
159
   // inkrement / kein Scrollen
160
   lcd_command(0x06);
161
 
162
   lcd_clear();
163
}
164
 
165
// Sendet den Befehl zur Löschung des Displays
166
 
167
void lcd_clear(void)
168
{
169
   lcd_command(CLEAR_DISPLAY);
170
   _delay_ms(5);
171
}
172
 
173
// Sendet den Befehl: Cursor Home
174
 
175
void lcd_home(void)
176
{
177
   lcd_command(CURSOR_HOME);
178
   _delay_ms(5);
179
}
180
 
181
// setzt den Cursor in Zeile y (1..4) Spalte x (0..15)
182
 
183
void set_cursor(uint8_t x, uint8_t y)
184
{
185
  uint8_t tmp;
186
 
187
  switch (y) {
188
    case 1: tmp=0x80+0x00+x;  break;    // 1. Zeile
189
    case 2: tmp=0x80+0x40+x;  break;    // 2. Zeile
190
    case 3: tmp=0x80+0x10+x;  break;    // 3. Zeile
191
    case 4: tmp=0x80+0x50+x;  break;    // 4. Zeile
192
  }
193
  lcd_command(tmp);
194
}
195
 
196
// Schreibt einen String auf das LCD
197
 
198
void lcd_string(char *data)
199
{
200
    while(*data) {
201
        lcd_data(*data);
202
        data++;
203
    }
204
}

falls es Fehler gibt bitte melden
Ich hoffe ich konnte helfen

von Sebastian Z. (hometronix)


Lesenswert?

Genial, genau nach so einer angepassten LCD Routine habe ich die letzten 
Tage gesucht und nun funktioniert endlich mein LCD an dem AVR NET I/O 
von Pollin, vielen Dank an Chris ;-)

Ich verwende PortD und Pin0 sowie Pin1 sind schon belegt vom seriellen 
Anschluss, somit musste ich die Daten Pins ab Port2 bis Port5 verwenden 
und irgendwie habe ich es vorher mit der Maskierung nie hinbekommen. RS 
habe ich an PD6 und EN an PD7, RW konnte ich zum Glück auf GND legen 
denn der PortD ist nun voll belegt.

Das LCD funktioniert jetzt jedenfalls problemlos. Was mich nur etwas 
verwundert, ich bekomme folgende Warnung beim Make All:

lcd-routines.c:185: warning: 'tmp' may be used uninitialized in this 
function

In der Zeile 185 steht --> uint8_t tmp;

Der Befehl set_cursor funktioniert bei mir trotzdem ohne Probleme.

von Hanns W. (hannsw)


Lesenswert?

Sebastian Z. wrote:
>.......
> Das LCD funktioniert jetzt jedenfalls problemlos. Was mich nur etwas
> verwundert, ich bekomme folgende Warnung beim Make All:
>
> lcd-routines.c:185: warning: 'tmp' may be used uninitialized in this
> function
>
> In der Zeile 185 steht --> uint8_t tmp;

  Ja schon, aber was ist mit tmp, wenn "y" < 1 oder > 4 ist?
  dann wäre tmp nicht initialisiert, und das prog stürzt ab.

>
> Der Befehl set_cursor funktioniert bei mir trotzdem ohne Probleme.

weil Du die Function sicher immer nur mit 1 < y < 5 aufrufst.

Gruß Hanns

von Sebastian Z. (hometronix)


Lesenswert?

Hanns Weil wrote:
>
> weil Du die Function sicher immer nur mit 1 < y < 5 aufrufst.
>
> Gruß Hanns

Ja, stimmt. Ich verwende für y in meinen "set_cursor" Befehlen immer nur 
1 oder 2, da ich ein zweizeiliges LCD besitze.

Sollte ich dann lieber tmp am Anfang standardmäßig auf irgendeinen Wert 
setzen? Prinzipiell sollte es bei mir ja keinen Programmabsturz geben, 
da meine Befehle korrekt mit Zeile 1 oder Zeile 2 verwendet werden.

von Hanns W. (hannsw)


Lesenswert?

Sebastian Z. wrote:
...
> Sollte ich dann lieber tmp am Anfang standardmäßig auf irgendeinen Wert
> setzen? Prinzipiell sollte es bei mir ja keinen Programmabsturz geben,
> da meine Befehle korrekt mit Zeile 1 oder Zeile 2 verwendet werden.

Im Prinzip ja, wenn Du "sauber" arbeiten möchtest.Oder Du lässt es so, 
und schaltest die Warnungen ab.

Zum "sauber" arbeiten gehörte auch im switch ein default-statement und 
dann keine ANzeige:
z.b. so, wobei du Dich wunderst, warum nichts angezeigt wird:
1
void set_cursor(uint8_t x, uint8_t y)
2
{
3
  uint8_t tmp;
4
  // vermeide Compliler-Meldung
5
  tmp = 0; 
6
7
  switch (y) {
8
    case 1: tmp=0x80+0x00+x;  break;    // 1. Zeile
9
    case 2: tmp=0x80+0x40+x;  break;    // 2. Zeile
10
    case 3: tmp=0x80+0x10+x;  break;    // 3. Zeile
11
    case 4: tmp=0x80+0x50+x;  break;    // 4. Zeile
12
    // falsche Parameter übergaben:
13
    default:{
14
              //kannst DU ne Warnung ausgeben und notieren? 
15
              // log-fle oder so? auf alle Fälle zurück aus der Funktion
16
              return;
17
            } 
18
         
19
  }
20
  // hier hätten wir dann die korrekten Parameter,
21
  // und können den cursor setzen
22
  lcd_command(tmp);
23
}

Wobei Du im Fehler-Falle dann weiter oben in Deinem Programm - nach dem 
Aufruf - Probleme mit der eigentlichen Textanzeige bekommst, wenn der 
Cursor nicht gesetzt werden konnte.

Daher kannst Du folgendes machen:
aus der void machst Du ne boolean:
1
boolean set_cursor(uint8_t x, uint8_t y)
2
{
3
  uint8_t tmp;
4
   // ...
5
    // falsche Parameter übergaben im switch
6
    default:return false;         
7
  }
8
  // hier hätten wir dann die korrekten Parameter,
9
  // und können den cursor setzen
10
  lcd_command(tmp);
11
  return true;
12
}

und im Hauptprogramm
1
   // .. Cursor fehlertolerant setzen
2
{
3
   if ( set_cursor( x,  y) ) {
4
       // zeige Daten an
5
   }else {
6
       // zeige Fehler an
7
   }


Gruß Hanns

von Simon K. (simon) Benutzerseite


Lesenswert?

Chris W. wrote:
Die Warnung kriegst du ganz einfach weg:
1
 void set_cursor(uint8_t x, uint8_t y)
2
 {
3
   uint8_t tmp;
4
 
5
   switch (y) {
6
     case 1: tmp=0x80+0x00+x;  break;    // 1. Zeile
7
     case 2: tmp=0x80+0x40+x;  break;    // 2. Zeile
8
     case 3: tmp=0x80+0x10+x;  break;    // 3. Zeile
9
     case 4: tmp=0x80+0x50+x;  break;    // 4. Zeile
10
     default:
11
       return;
12
   }
13
   lcd_command(tmp);
14
 }

von Sebastian Z. (hometronix)


Lesenswert?

Vielen Dank Euch beiden, ich habe mich dann doch für die letztere 
Möglichkeit entschieden, da es am einfachsten und schnellsten ging.

Nun bekomme ich keine Warnung mehr und etwas anderes als 1 oder 2 werde 
ich bei diesem zweizeiligen LCD sowieso nicht verwenden.

Grüße
Sebastian

von Hanns W. (hannsw)


Lesenswert?

Simon K. wrote:
> Chris W. wrote:
> Die Warnung kriegst du ganz einfach weg:
>
>
1
>  void set_cursor(uint8_t x, uint8_t y)
2
>  {
3
>    uint8_t tmp;
4
> 
5
>    switch (y) {
6
>      case 1: tmp=0x80+0x00+x;  break;    // 1. Zeile
7
>      case 2: tmp=0x80+0x40+x;  break;    // 2. Zeile
8
>      case 3: tmp=0x80+0x10+x;  break;    // 3. Zeile
9
>      case 4: tmp=0x80+0x50+x;  break;    // 4. Zeile
10
>      default:
11
>        return;
12
>    }
13
>    lcd_command(tmp);
14
>  }
15
>
Simon, hast Du es mal getestet?
Ich meine im default Falle ist tmp immer noch nicht mit einem Wert 
versehen, und das meckert der Kompiler doch an!

Sebastian, ist mit Simons Variante die Fehlermeldung tatsächlich weg?


Gruß Hanns

von Sebastian Z. (hometronix)


Lesenswert?

Hanns Weil wrote:
> Sebastian, ist mit Simons Variante die Fehlermeldung tatsächlich weg?
>
>
> Gruß Hanns

Ja, ich bekomme nun wirklich keine Warnung mehr angezeigt!

Grüße
Sebastian

von Simon K. (simon) Benutzerseite


Lesenswert?

Hanns Weil wrote:
> Simon, hast Du es mal getestet?
> Ich meine im default Falle ist tmp immer noch nicht mit einem Wert
> versehen, und das meckert der Kompiler doch an!

Ja, aber andererseits wird die (uninitialisierte) Variable auch nicht 
mehr verwendet, wenn man die Funktion per return beendet ;)

Compiler sind verdammt schlaue Leute! :D

von Hanns W. (hannsw)


Lesenswert?

Aber um das letzte Wort zu behalten:
Wir sind uns einig, dass zum "ordentlichen" Programmieren das Init einer 
Variablen gehört, oder??

Gruß Hanns

von Peter D. (peda)


Lesenswert?

Sebastian Z. wrote:
> Genial, genau nach so einer angepassten LCD Routine habe ich die letzten
> Tage gesucht und nun funktioniert endlich mein LCD an dem AVR NET I/O
> von Pollin, vielen Dank an Chris ;-)


Ich wundere mich bloß, warum es keinem auffällt, das sehr viel Code 
verschwendet wird.
Z.B. taucht dieser Abschnitt 4-mal auf:
1
   temp1 = temp1 & 0x0F;
2
   
3
   LCD_PORT_4 &= ~(1<<LCD_D4);
4
   LCD_PORT_5 &= ~(1<<LCD_D5);
5
   LCD_PORT_6 &= ~(1<<LCD_D6);
6
   LCD_PORT_7 &= ~(1<<LCD_D7);            // maskieren
7
   
8
   if(temp1 & 0x01) LCD_PORT_4 |=(1<<LCD_D4) ;// setzen
9
   if(temp1 & 0x02) LCD_PORT_5 |=(1<<LCD_D5) ;
10
   if(temp1 & 0x04) LCD_PORT_6 |=(1<<LCD_D6) ;
11
   if(temp1 & 0x08) LCD_PORT_7 |=(1<<LCD_D7) ;
12
   lcd_enable();

Ehe ich etwas zweimal hinschreibe, mache ich daraus ne Unterfunktion.
Das ist zum einen Schreibfaulheit und zum anderen Zuverlässigkeit.
Was nur einmal dasteht, kann auch nur einmal falsch sein (muß nur einmal 
korrigiert werden).

Es ist eine beliebte Fehlerquelle, in Copy&Paste Codemonstern etwas 
nicht an allen Stellen zu korrigieren und sich dann zu wundern, warum 
der Fehler noch da ist.


Mit Unterfunktionen wird dann so ein LCD-Treiber gleich viel schlanker 
und durchschaubarer:

Beitrag "Re: LCD nicht nur für einen Port in C"


Zum Thema Default-Zweig:
Ich würde als Default die 1.Zeile setzen.
Return halte ich für die schlechteste Lösung, da man damit Fehler 
verschleiert, d.h sich die Fehlersuche nur selber schwer macht.


Peter

von Hanns W. (hannsw)


Lesenswert?

Peter Dannegger wrote:
------
> Es ist eine beliebte Fehlerquelle, in Copy&Paste Codemonstern etwas
> nicht an allen Stellen zu korrigieren und sich dann zu wundern, warum
> der Fehler noch da ist.
>
>
> Mit Unterfunktionen wird dann so ein LCD-Treiber gleich viel schlanker
> und durchschaubarer:
>

Völlige Zustimmung

> Zum Thema Default-Zweig:
> Ich würde als Default die 1.Zeile setzen.

> Return halte ich für die schlechteste Lösung, da man damit Fehler
> verschleiert, d.h sich die Fehlersuche nur selber schwer macht.

Peter, wenn Du die 1.Zeile als default benutzt, verschleierst Du 
ebenfalls Fehler, indem Du fehlerhafte Parameter einem fehlerfreien 
gleichsetzt!

Ich bin nach wie vor der Meinung, daß ein
1
default: return false;
einer boolean funktion, mit auswertung des Rückgabewertes in der 
aufrufenden func ( z.B Anzeige in Zeile 1 blinkend) des sicherere Weg 
ist.

Klar ist mir, daß in diesem Fall sicherlich keine Zeile außerhalb des 
Displays aufgerufen werden wird; aber, wie Du selber sagst, gehört 
mehrfach zu benutzender Code in eine eine Subroutine. Wenn diese sich 
später einmal verselbständigt, und void bleibt, wirst Du irgenwann mal - 
bei Deiner Lösung - nicht mehr wissen, ob  nun ein Fehler auftrat oder 
nicht.
Aber vielleicht bin ich da zu pingelig

Gruß Hanns

von Martin (Gast)


Lesenswert?

Hallo,

ich habe mit disem Code gerade mein LCD am laufen zu bekommen :-) bzw. 
Teilerweise :-(
habe ein 4x20 LCD und wenn ich mit dem gesamte Code compiliere und 
flashe kommen nur die erste 2 Zeile mit meine String

"hallo1ghhghjgjghgjkk" (20 Zeile)

wie bekomme ich die 3 und 4 Zeile ??

hier steht:


   // 4Bit  2 Zeilen  5x7
   lcd_command(0x28);

wie ist dann für die 4 Zeile? habe rum probiert aber leider nichts !

Gruß
Martin

von Pete K. (pete77)


Lesenswert?

4 zeilige Displays haben meistens zwei Controller mit 2 Enable 
Leitungen.

Such mal nach Wintek 2704. Das ist so ein Dislay und hierfür gibt es 
auch schon fertigen Code. (Ist ein Pollin Display)

von Lothar S. (magic33)


Lesenswert?

Ich hab das Problem das ich an einem Port bin nur ein durcheinander mit 
den Pins habe

kann mir da mal jemand das auseinanderdividieren
ich bring das LCD nicht ans laufen.
Pinnbelegung
// LCD_RS =PA3 , LCD_EN = PA1 ; DB4= PA0 DB5= PA2 DB6 = PA4 DB7=PA6 == 
LCD_AD =0x55 LCD_ADA = 0xAA
1
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus
2
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial
3
//
4
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar
5
 
6
#include <avr/io.h>
7
#include "lcd-routines.h"
8
#include <avr/delay.h>
9
10
// LCD_RS =PA3 , LCD_EN = PA1 ; DB4= A0 DB5= A2 DB6 = PA4 DB7=PA6 == LCD_AD =0x55 LCD_ADA = 0xAA
11
 
12
// sendet ein Datenbyte an das LCD
13
 
14
void lcd_data(unsigned char temp1)
15
{
16
   unsigned char temp2 = temp1;
17
 
18
   LCD_PORT |= (1<<LCD_RS);        // RS auf 1 setzen
19
 
20
   temp1 = temp1 >> 4;
21
   temp1 = temp1 & LCD_AD;
22
   LCD_PORT &= LCD_ADA;
23
   LCD_PORT |= temp1;               // setzen
24
   lcd_enable();
25
 
26
   temp2 = temp2 & LCD_AD;
27
   LCD_PORT &= LCD_ADA;
28
   LCD_PORT |= temp2;               // setzen
29
   lcd_enable();
30
   
31
   _delay_us(42);
32
}
33
 
34
// sendet einen Befehl an das LCD
35
 
36
void lcd_command(unsigned char temp1)
37
{
38
   unsigned char temp2 = temp1;
39
 
40
   LCD_PORT &= ~(1<<LCD_RS);        // RS auf 0 setzen
41
 
42
   temp1 = temp1 >> 4;              // oberes Nibble holen
43
   temp1 = temp1 & LCD_AD;            // maskieren
44
   LCD_PORT &= LCD_ADA;
45
   LCD_PORT |= temp1;               // setzen
46
   lcd_enable();
47
 
48
   temp2 = temp2 & LCD_AD;            // unteres Nibble holen und maskieren
49
   LCD_PORT &= LCD_ADA;
50
   LCD_PORT |= temp2;               // setzen
51
   lcd_enable();
52
   
53
   _delay_us(42);
54
}

von Michael U. (amiga)


Lesenswert?

Hallo,

4x20 haben üblicherweise nur einen Controller, 80 Zeichen ist die 
Ramgröße der 44780 und kompatiblen.

Initialisiert als 2-zeilig, die 1 und 3. Zeile sowie die 2. und 4. Zeile 
sind dann von den Adressen direkt hintereinander.

Erst bei mehr als 80 Zeichen (z.B. das Pollin 4x27) ist ein zweiter 
Controller nötig.

Gruß aus Berlin
Michael

von Lothar S. (magic33)


Lesenswert?

Es geht nicht um die Adresse der speicherstellen sondern um die 
Portbelegung.

ich wollte nicht das grosse programm verwenden das jeden pin des LCD an 
einem anderen port hat

von Peter D. (peda)


Lesenswert?

Lothar Sammel wrote:
> ich wollte nicht das grosse programm verwenden das jeden pin des LCD an
> einem anderen port hat


http://www.mikrocontroller.net/attachment/30300/lcd_drv.zip

Was ist denn daran groß?


Peter

von dummy (Gast)


Lesenswert?

>ich wollte nicht das grosse programm verwenden das jeden pin des LCD an
>einem anderen port hat

Das wirst du bei deiner verpfuschten Portbelegung aber müssen.

von magic (Gast)


Lesenswert?

ok danke werd ich wohl so machen müssen

win reinschiften der temp in die portD adresse
nach dem motto
PortD.0=temp1.0 geht nicht?

von dummy (Gast)


Lesenswert?

>win reinschiften der temp in die portD adresse
>nach dem motto
>PortD.0=temp1.0 geht nicht?

Jetzt auch noch mit Strukturen arbeiten?
Wenn du wenigstens die Datenleitungen für
das LCD in einer Reihe am Port angeschlossen hättest,
dann könnte man das Beispiel aus dem Tutorial
relativ einfach anpassen. Du musstest sie aber
unbedingt alle auseinanderreissen. Nimm Peters
Code. Das was man tun müsste um deine Pinbelegung
gerade zu biegen wird auch nicht kleiner.

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.