Forum: Mikrocontroller und Digitale Elektronik Port wechseln für LCD-Display


von Dietmar P. (dietmar2)


Lesenswert?

Hallo,

war schon einmal mit dem LCD-Display-Thema hier im Forum (serielles 
LCD-Display nach U. Radig), das ging nicht.

Habe mir jetzt die Schaltung versuchsweise einmal nach dem 
"AVR-GCC-Tutorial/LCD-Ansteuerung"
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung
aufgebaut und die Software nach der Seite "Erweiterte LCD-Ansteuerung" 
in meinen Atmega32 geflasht.

Das hat auf Anhieb funktioniert!, Jetzt das "Aber".

Die Software verwendet in der lcd-routines.h den PORTD. Der Port ist bei 
mir belegt. Wollte auf PORTC, oder PORTA ausweichen. Habe in 
lcd-routines.h
aus dem "D" jeweils ein "C" bzw. "A" gemacht (PORTD --> PORTC, DDRD --> 
DDRC, PD3 --> PC5, etc.). Für PORTC habe ich auch das JTAG abgeschaltet 
und Verfügbarkeit der Pins geprüft.

Es wird nichts wie auf PORTD angezeigt.
Muss ich da noch mehr umstellen?
Hat das schon jemand einmal probiert?

von Pete K. (pete77)


Lesenswert?

Poste den (kompletten) Quellcode, dann kann Dir geholfen werden...

Hardware ist soweit ok?

von Rolf D. (rolfdegen)


Lesenswert?

Hinweis PD3-PD5 reicht nicht. Es werden PD0-PD5 = PC0-PC5 benutzt!

von Dietmar P. (dietmar2)


Lesenswert?

@ Rolf Degen:

Dass (fast) der gesamte PORTC notwendig ist, ist mir schon klar. Ich 
hatte angenommen, wenn ich als erstes PORTD und DDRD auf C umstellen, 
zus. zu PD2 - 4 auf PC2 - 4 (hatte mich vertan, ist PD2 - 4, nicht 3 - 
5), dann ist das i.O. Offenbar aber nicht. Bitte mal den für Pete 
geposteten Code ansehen.

@ Pete K. hier mal der Quellcode von lcd-routines.h (ist halt etwas 
viel):
1
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus
2
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung
3
//
4
void lcd_init(void);
5
void lcd_enable(void);
6
void lcd_out(uint8_t data);
7
void lcd_data(uint8_t data);
8
void lcd_command(uint8_t data);
9
void lcd_clear(void);
10
void lcd_home(void);
11
void lcd_setcursor(uint8_t x, uint8_t y);
12
void lcd_string(const char *data);
13
void lcd_generatechar(uint8_t code, const uint8_t *data);
14
 
15
////////////////////////////////////////////////////////////////////////////////
16
// Hier die verwendete Taktfrequenz in Hz eintragen, wichtig!
17
 
18
//#define F_CPU 3686400
19
#define F_CPU 8000000 
20
////////////////////////////////////////////////////////////////////////////////
21
// Pinbelegung für das LCD, an verwendete Pins anpassen
22
 
23
//  LCD DB4-DB7 <-->  PORTD Bit PD4-PD7
24
#define LCD_PORT      PORTD
25
#define LCD_DDR       DDRD
26
#define LCD_DB        PD4
27
 
28
//  LCD RS      <-->  PORTD Bit PD2     (RS: 0=Data, 1=Command)
29
#define LCD_RS        PD2
30
 
31
//  LCD EN      <-->  PORTD Bit PD3     (EN: 1-Impuls für Daten)
32
#define LCD_EN        PD3
33
 
34
////////////////////////////////////////////////////////////////////////////////
35
// LCD Ausführungszeiten (MS=Millisekunden, US=Mikrosekunden)
36
 
37
#define LCD_BOOTUP_MS           15
38
#define LCD_ENABLE_US           1
39
#define LCD_WRITEDATA_US        46
40
#define LCD_COMMAND_US          42
41
 
42
#define LCD_SOFT_RESET_MS1      5
43
#define LCD_SOFT_RESET_MS2      1
44
#define LCD_SOFT_RESET_MS3      1
45
#define LCD_SET_4BITMODE_MS     5
46
 
47
#define LCD_CLEAR_DISPLAY_MS    2
48
#define LCD_CURSOR_HOME_MS      2
49
 
50
////////////////////////////////////////////////////////////////////////////////
51
// LCD Befehle
52
 
53
// Clear Display -------------- 0b00000001
54
#define LCD_CLEAR_DISPLAY       0x01
55
 
56
// Cursor Home ---------------- 0b0000001x
57
#define LCD_CURSOR_HOME         0x02
58
 
59
// Set Entry Mode ------------- 0b000001xx
60
#define LCD_SET_ENTRY           0x04
61
 
62
#define LCD_ENTRY_DECREASE      0x00
63
#define LCD_ENTRY_INCREASE      0x02
64
#define LCD_ENTRY_NOSHIFT       0x00
65
#define LCD_ENTRY_SHIFT         0x01
66
 
67
// Set Display ---------------- 0b00001xxx
68
#define LCD_SET_DISPLAY         0x08
69
 
70
#define LCD_DISPLAY_OFF         0x00
71
#define LCD_DISPLAY_ON          0x04
72
#define LCD_CURSOR_OFF          0x00
73
#define LCD_CURSOR_ON           0x02
74
#define LCD_BLINKING_OFF        0x00
75
#define LCD_BLINKING_ON         0x01
76
 
77
// Set Shift ------------------ 0b0001xxxx
78
#define LCD_SET_SHIFT           0x10
79
 
80
#define LCD_CURSOR_MOVE         0x00
81
#define LCD_DISPLAY_SHIFT       0x08
82
#define LCD_SHIFT_LEFT          0x00
83
#define LCD_SHIFT_RIGHT         0x01
84
 
85
// Set Function --------------- 0b001xxxxx
86
#define LCD_SET_FUNCTION        0x20
87
 
88
#define LCD_FUNCTION_4BIT       0x00
89
#define LCD_FUNCTION_8BIT       0x10
90
#define LCD_FUNCTION_1LINE      0x00
91
#define LCD_FUNCTION_2LINE      0x08
92
#define LCD_FUNCTION_5X7        0x00
93
#define LCD_FUNCTION_5X10       0x04
94
 
95
#define LCD_SOFT_RESET          0x30
96
 
97
// Set CG RAM Address --------- 0b01xxxxxx  (Character Generator RAM)
98
#define LCD_SET_CGADR           0x40
99
 
100
#define LCD_GC_CHAR0            0
101
#define LCD_GC_CHAR1            1
102
#define LCD_GC_CHAR2            2
103
#define LCD_GC_CHAR3            3
104
#define LCD_GC_CHAR4            4
105
#define LCD_GC_CHAR5            5
106
#define LCD_GC_CHAR6            6
107
#define LCD_GC_CHAR7            7
108
 
109
// Set DD RAM Address --------- 0b1xxxxxxx  (Display Data RAM)
110
#define LCD_SET_DDADR           0x80
111
 
112
#define LCD_DDADR_LINE1         0x00
113
#define LCD_DDADR_LINE2         0x40
114
#define LCD_DDADR_LINE3         0x10
115
#define LCD_DDADR_LINE4         0x50

von avr (Gast)


Lesenswert?

Der "Fehler" sollte in der Routine LCD_init liegen.
Die ist glaube ich nicht für den Portwechsel (bzw. Pinwechsel)
geeignet.
Poste mal deine Routine + Portwunsch (C oder A).

avr

von Dietmar P. (dietmar2)


Lesenswert?

@ AVR

Hier mal den Code lcd-routines.c, in der die Funktion lcd_init enthalten 
ist. Es gibt außer einem kleinen main.c nur die beiden lcd-routines.c 
und .h.
Der Portwunsch wäre PORTC, Pins wie im Original bei PORTD.
1
// Ansteuerung eines HD44780 kompatiblen LCD im 4-Bit-Interfacemodus
2
// http://www.mikrocontroller.net/articles/HD44780
3
// http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung
4
//
5
// Die Pinbelegung ist über defines in lcd-routines.h einstellbar
6
 
7
#include <avr/io.h>
8
#include "lcd-routines.h"
9
#include <util/delay.h>
10
 
11
////////////////////////////////////////////////////////////////////////////////
12
// Initialisierung: muss ganz am Anfang des Programms aufgerufen werden.
13
 
14
void lcd_init(void)
15
{
16
    // verwendete Pins auf Ausgang schalten
17
    uint8_t pins = (0x0F<<LCD_DB)|(1<<LCD_RS)|(1<<LCD_EN);
18
    LCD_DDR |= pins;
19
 
20
    // initial alle Ausgänge auf Null
21
    LCD_PORT &= ~pins;
22
 
23
    // warten auf die Bereitschaft des LCD
24
    _delay_ms(LCD_BOOTUP_MS);
25
    
26
    // Soft-Reset muss 3mal hintereinander gesendet werden zur Initialisierung
27
    lcd_out(LCD_SOFT_RESET);
28
    _delay_ms(LCD_SOFT_RESET_MS1);
29
 
30
    lcd_enable();
31
    _delay_ms(LCD_SOFT_RESET_MS2);
32
 
33
    lcd_enable();
34
    _delay_ms(LCD_SOFT_RESET_MS3);
35
 
36
    // 4-bit Modus aktivieren 
37
    lcd_out(LCD_SET_FUNCTION|LCD_FUNCTION_4BIT);
38
    _delay_ms(LCD_SET_4BITMODE_MS);
39
 
40
    // 4-bit Modus / 2 Zeilen / 5x7
41
    lcd_command(LCD_SET_FUNCTION|LCD_FUNCTION_4BIT|LCD_FUNCTION_2LINE|LCD_FUNCTION_5X7);
42
 
43
    // Display ein / Cursor aus / Blinken aus
44
    lcd_command(LCD_SET_DISPLAY|LCD_DISPLAY_ON|LCD_CURSOR_OFF|LCD_BLINKING_OFF); 
45
 
46
    // Cursor inkrement / kein Scrollen
47
    lcd_command(LCD_SET_ENTRY|LCD_ENTRY_INCREASE|LCD_ENTRY_NOSHIFT);
48
 
49
    lcd_clear();
50
}
51
 
52
////////////////////////////////////////////////////////////////////////////////
53
// Erzeugt einen Enable-Puls
54
 
55
void lcd_enable(void)
56
{
57
    LCD_PORT |= (1<<LCD_EN);     // EN auf 1 setzen
58
    _delay_us(LCD_ENABLE_US);    // kurze Pause
59
    LCD_PORT &= ~(1<<LCD_EN);    // EN auf 0 setzen
60
}
61
 
62
////////////////////////////////////////////////////////////////////////////////
63
// Sendet eine 4-bit Ausgabeoperation an das LCD
64
 
65
void lcd_out(uint8_t data)
66
{
67
    data &= 0xF0;                       // obere 4 Bit maskieren
68
 
69
    LCD_PORT &= ~(0xF0>>(4-LCD_DB));    // Maske löschen
70
    LCD_PORT |= (data>>(4-LCD_DB));     // Bits setzen
71
    lcd_enable();
72
}
73
 
74
////////////////////////////////////////////////////////////////////////////////
75
// Sendet ein Datenbyte an das LCD
76
 
77
void lcd_data(uint8_t data)
78
{
79
    LCD_PORT |= (1<<LCD_RS);    // RS auf 1 setzen
80
 
81
    lcd_out(data);              // zuerst die oberen, 
82
    lcd_out(data<<4);           // dann die unteren 4 Bit senden
83
 
84
    _delay_us(LCD_WRITEDATA_US);
85
}
86
 
87
////////////////////////////////////////////////////////////////////////////////
88
// Sendet einen Befehl an das LCD
89
 
90
void lcd_command(uint8_t data)
91
{
92
    LCD_PORT &= ~(1<<LCD_RS);    // RS auf 0 setzen
93
 
94
    lcd_out(data);               // zuerst die oberen, 
95
    lcd_out(data<<4);            // dann die unteren 4 Bit senden
96
 
97
    _delay_us(LCD_COMMAND_US);
98
}
99
 
100
////////////////////////////////////////////////////////////////////////////////
101
// Sendet den Befehl zur Löschung des Displays
102
 
103
void lcd_clear(void)
104
{
105
    lcd_command(LCD_CLEAR_DISPLAY);
106
    _delay_ms(LCD_CLEAR_DISPLAY_MS);
107
}
108
 
109
////////////////////////////////////////////////////////////////////////////////
110
// Sendet den Befehl: Cursor Home
111
 
112
void lcd_home(void)
113
{
114
    lcd_command(LCD_CURSOR_HOME);
115
    _delay_ms(LCD_CURSOR_HOME_MS);
116
}
117
 
118
////////////////////////////////////////////////////////////////////////////////
119
// Setzt den Cursor in Zeile y (1..4) Spalte x (0..15)
120
 
121
void lcd_setcursor(uint8_t x, uint8_t y)
122
{
123
    uint8_t data;
124
 
125
    switch (y)
126
    {
127
        case 1: data=LCD_SET_DDADR|LCD_DDADR_LINE1|x; break;    // 1. Zeile
128
        case 2: data=LCD_SET_DDADR|LCD_DDADR_LINE2|x; break;    // 2. Zeile
129
        case 3: data=LCD_SET_DDADR|LCD_DDADR_LINE3|x; break;    // 3. Zeile
130
        case 4: data=LCD_SET_DDADR|LCD_DDADR_LINE4|x; break;    // 4. Zeile
131
        default: return;                                   // für den Fall einer falschen Zeile
132
    }
133
    lcd_command(data);
134
}
135
 
136
////////////////////////////////////////////////////////////////////////////////
137
// Schreibt einen String auf das LCD
138
 
139
void lcd_string(const char *data)
140
{
141
    while(*data)
142
    {
143
        lcd_data(*data);
144
        data++;
145
    }
146
}
147
 
148
////////////////////////////////////////////////////////////////////////////////
149
// Schreibt ein Zeichen in den Character Generator RAM
150
 
151
void lcd_generatechar(uint8_t code, const uint8_t *data)
152
{
153
    // Startposition des Zeichens einstellen
154
    lcd_command(LCD_SET_CGADR|(code<<3));
155
 
156
    // Bitmuster übertragen
157
    for (uint8_t i=0; i<8; i++)
158
    {
159
        lcd_data(data[i]);
160
    }
161
}

von avr (Gast)


Lesenswert?

Die Routinen scheinen OK.
Hier für EN an PORTC PC3, RS an PORTC PC2 und Daten 4-7 an PORTC
PC 4-7.

wenn die verdrahtung stimmt sollte es gehn. (JTAG ist ja aus)
1
//  LCD DB4-DB7 <-->  PORTC Bit PC4-PC7
2
#define LCD_PORT      PORTC
3
#define LCD_DDR       DDRC
4
#define LCD_DB        PC4
5
 
6
//  LCD RS      <-->  PORTC Bit PC2     (RS: 0=Data, 1=Command)
7
#define LCD_RS        PC2
8
 
9
//  LCD EN      <-->  PORTC Bit PC3     (EN: 1-Impuls für Daten)
10
#define LCD_EN        PC3

avr

von Dietmar P. (dietmar2)


Lesenswert?

Genauso hatte ich es gemacht und es ging leider nicht.

JTAG hatte ich mit 2 x

  MCUCSR |= (1<<JTD);  // JTAG (PORTC)

abgeschaltet. Die Pins an PORTC sind danach schaltbar, habe es 
überprüft.
PORTA bringt leider auch kein Ergebnis.
Verdrahtung stimmt, PORTD geht ja.

Noch eine Idee?

von avr (Gast)


Lesenswert?

Also in den Programmausschnitten ist nichts zu sehen.

Gibt es im Programm noch Routinen die auf Port A bzw. C
zugreifen bzw. Sonderfunktionen an den Pins aktivieren?

Evtl. JTAG mal über die Fuses abschalten (wenn du ISP
programmierst natürlich).

avr

von Dietmar P. (dietmar2)


Lesenswert?

Nein, ich habe bewußt nur die LCD-Routine ein ein Mini-main.c zum 
Probieren:
1
#include <avr/io.h>
2
#include "lcd-routines.h"
3
 
4
int main(void)
5
{
6
    lcd_init();
7
 
8
    set_cursor(0,1);
9
    lcd_string("Test - Zeile 1");
10
    set_cursor(0,2);
11
    lcd_string("Test - Zeile 2");
12
 
13
    while(1)
14
    {
15
    }
16
 
17
    return 0;
18
}

Die Fuses habe ich auch schon über das Makefile gesetzt.

von Thomas (kosmos)


Angehängte Dateien:

Lesenswert?

benutze einen oder mehrere 74HC573 Latch parallel an einem Port, dann 
kannst du Daten Ausgeben und dem entsprechendem Latch sagen für wen Sie 
sind.

Ich hatte damit schon einen EEPROM Programmierer gebaut da ich hier sehr 
viele Adress, Daten- und Steuerleitungen brauchte und zusätzlich ein LCD 
angeschlossen habe.

von Dietmar P. (dietmar2)


Lesenswert?

@ Thomas:

Danke für die Info.
Gibt es dazu evtl. auch noch ein Schaltplan als Beispiel und vor allen 
Dingen C-Software Beispiele?
Bin ja schon mit der Schaltung von Ulrich Radig nicht klar gekommen.

von Thomas (kosmos)


Lesenswert?

ließ dir mal das Datenblatt durch wie alles funktioniert.

Im Prinzip gibts du die Daten an einen Port aus und musst dann über 
einen anderen Port ein paar Steuerleitungen ändern.

Daten vom Eingang einlesen und Daten aus dem Latch an den Ausgang geben, 
dann Eingänge wieder hochohmig schalten und der Latch hält das 
gespeicherte Signal an den Ausgängen vor. Dann gibts noch einen 
Transparenten Modus Eingang=Ausgang

Wie man einen Ausgang ansteuerst kannst du dir im AVR Tutorial 
anschauen.

von Dietmar P. (dietmar2)


Lesenswert?

Danke Thomas,
werde mir das einmal zu Gemüte führen.

von Peter D. (peda)


Lesenswert?

Parallele Latches sind nur sinnvoll an größeren MCs mit externem 
Adreß-Datenbus.
Und sie benötigen eine ganz erhebliche Layoutarbeit, um die vielen 
Leitungen zu routen.

MCs ohne externen Bus erweitert man besser per SPI (74HC165/74HC595).
Die sind außerdem kaskadierbar, d.h. man benötigt keine separaten Enable 
für jedes Register.
Und das Layout wird auch deutlich einfacher.


Peter

von Peter D. (peda)


Lesenswert?

Die meisten LCD-Routinen haben das Problem, daß sie an vielen 
verschiedenen Stellen auf das LCD zugreifen.
Vergißt man dann nur eine Stelle zu ändern, krachts.

Macht man dagegen eine einzige Nibble-Funktion und ruft nur diese auf, 
ist das Ändern der Pins ganz easy und sicher:

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


Peter

von Dietmar P. (dietmar2)


Lesenswert?

Hallo Peter,

danke für den C-Code.

Du ziehst verschiedene Ports an. Kann ich alles auf einen Port legen, 
z.B. PortC (muss dann natürlich JTAG abschalten)?

Gruß
Dietmar

von Rolf D. (rolfdegen)


Angehängte Dateien:

Lesenswert?

Hier eine Lösung,um mit möglich wenig Portpins ein LC-Display mit einem 
ATmega anzusteuern:

http://www.cczwei-forum.de/cc2/thread.php?postid=23657#post23657

von Dietmar P. (dietmar2)


Lesenswert?

Hallo Rolf,

danke für die Info.

Hardware verstanden, wo finde ich den C-Code dafür.
Ein Tausch auf Atmega 32 dürfte ja wohl problemlos sein.

Gruß
Dietmar

von Peter D. (peda)


Lesenswert?

Dietmar P. schrieb:
> Du ziehst verschiedene Ports an. Kann ich alles auf einen Port legen,

Ausnahmslos jede 6 IOs gehen, nur die Defines anpassen.
Die LCD.C bleibt unverändert.


Oder LCD mit einem Pin:

Beitrag "LCD über nur einen IO-Pin ansteuern"


Peter

von Falk B. (falk)


Lesenswert?

@  Peter Dannegger (peda)

>Macht man dagegen eine einzige Nibble-Funktion und ruft nur diese auf,
>ist das Ändern der Pins ganz easy und sicher:

Richtig, aber das ist im Code im Tutorial auch so.

MfG
Falk

von Rolf D. (rolfdegen)


Lesenswert?

Dietmar P. schrieb:
> Hallo Rolf,
>
> danke für die Info.
>
> Hardware verstanden, wo finde ich den C-Code dafür.
> Ein Tausch auf Atmega 32 dürfte ja wohl problemlos sein.
>
> Gruß
> Dietmar

Hier: http://www.cczwei-forum.de/cc2/thread.php?postid=24510#post24510

Gruß Rolf

von Peter D. (peda)


Lesenswert?

Falk Brunner schrieb:
> Richtig, aber das ist im Code im Tutorial auch so.

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung#Die_LCD_Routinen

Also ich sehe da sogar 6 Nibble-Routinen, die alle geändert werden 
müssen bei Pinumordnung (beliebige Pins).
Je 2 in lcd_data, lcd_command und lcd_init.


Peter

von Falk B. (falk)


Lesenswert?

@  Peter Dannegger (peda)

>Also ich sehe da sogar 6 Nibble-Routinen, die alle geändert werden
>müssen bei Pinumordnung (beliebige Pins).

Eben DAS ist vom OP gar nicht gefordert bzw. nötig.

"Die Software verwendet in der lcd-routines.h den PORTD. Der Port ist 
bei
mir belegt. Wollte auf PORTC, oder PORTA ausweichen. Habe in 
lcd-routines.h
aus dem "D" jeweils ein "C" bzw. "A" gemacht (PORTD --> PORTC, DDRD -->
"

Und genau das sollte funktionieren. Warum es bei ihm nicht geht, weiß 
der Geier.

MFG
Falk

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.