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?
Poste den (kompletten) Quellcode, dann kann Dir geholfen werden... Hardware ist soweit ok?
Hinweis PD3-PD5 reicht nicht. Es werden PD0-PD5 = PC0-PC5 benutzt!
@ 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
|
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
@ 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 | }
|
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
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?
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
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.
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.
@ 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.
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.
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
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
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
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
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
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
@ 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
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
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
@ 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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.