Hallo,
ich habe für ein Bastelprojekt auf einem Steckbrett einen PIC18F1220
aufgebaut und daran ein LCD Display angeschlossen. Das Display hat einen
SSD1803 Kontroller, der kompatibel ist zu einem HD44780. Ich komme aber
mit der Initialisierung nicht ganz klar. Ich habe dabei die Routine
angewendet, die in diesem Datenblatt zu finden ist:
http://www.lcd-module.de/fileadmin/eng/pdf/zubehoer/ssd1803_2.0.pdf
Ich habe das so verstanden, dass im 4-Bit Modus die unteren vier Bits
bei der Initialisierung ignoriert werden können. Trotzdem sind sie im
Datenblatt vorhanden. Ich habe also eine Routine geschrieben, die nur
die oberen vier Bits berücksichtigt. Und im ersten Funtion Set wird auch
schon auf den 4-Bit Modus umgeschalten. Leider hat sich am Display
nichts getan. Ich hab dann ein wenig gegoogled, und diese Seite
gefunden:
http://web.alfredstate.edu/weimandn/lcd/lcd_initialization/lcd_initialization_index.html
Hier habe ich jedoch eine vollkommen andere Routine gefunden. Hier wird
z.B. die Umschaltung auf den 4-Bit Modus erst viel später gemacht und
viele Worte unterscheiden sich von denen im anderen Datenblatt. Ich bin
mir auch nicht ganz sicher welche Bits für die vielen Variablen wie N,
F, C, B usw nötig sind. Ich habe folgenden Code mit der Routine aus dem
ersten Datenblatt geschrieben:
1
#include<stdio.h>
2
#include<stdlib.h>
3
#include<xc.h>
4
5
#pragma config OSC = INTIO2 // Oscillator Selection bits (Internal RC oscillator, port function on RA6 and port function on RA7)
6
#pragma config PWRT = ON // Power-up Timer Enable bit (PWRT enabled)
7
#pragma config WDT = OFF // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))
8
#pragma config MCLRE = OFF // MCLR Pin Enable bit (RA5 input pin enabled, MCLR disabled)
9
10
#define _XTAL_FREQ 4000000 // set to 4 MHz
11
#define LCD_RS LATBbits.LB3 // Makro for controll Bits
12
#define LCD_RW LATBbits.LB2 // -//-
13
#define LCD_E LATBbits.LB1 // -//-
14
15
voidinit_lcd(void);
16
voidenable(void);
17
voidled_blinken(void);
18
19
voidmain(void){
20
21
TRISA=0x00;//
22
TRISB=0x00;// Ports Output
23
24
OSCCONbits.IRCF=110;// 4 MHz
25
ADCON1bits.PCFG=1111111;// all Pins analog
26
27
init_lcd();
28
29
while(1)
30
{
31
__delay_ms(2);
32
}
33
}
34
35
voidinit_lcd(void)
36
{
37
/*
38
* LCD-Input on Ports RB4 - RB7
39
* RS on RB3
40
* R/W on RB2
41
* E on RB1
42
*/
43
44
// start initialisation routine
45
46
LCD_E=0;
47
__delay_ms(50);
48
LATB=0b00100000;// Function Set
49
enable();
50
__delay_us(50);
51
LATB=0b00000000;// Extended Function Set
52
enable();
53
__delay_us(50);
54
LATB=0b00100000;// Function Set
55
enable();
56
__delay_us(50);
57
LATB=0b00000000;// Return Home
58
enable();
59
__delay_ms(2);
60
LATB=0b00000000;// Display ON/OFF Control
61
enable();
62
__delay_us(50);
63
LATB=0b00000000;// Clear Display
64
enable();
65
__delay_ms(2);
66
LATB=0b00100000;// Function Set
67
enable();
68
__delay_us(50);
69
LATB=0b00000000;// Entry Mode Set
70
enable();
71
__delay_us(50);
72
LATB=0b00100000;// Function Set
73
enable();
74
__delay_ms(2);
75
76
// end initialisation routine
77
78
// Cursor Home
79
__delay_ms(2);
80
LATB=0x00;
81
enable();
82
LATB=0x00;
83
enable();
84
//////////////
85
86
// Write Character
87
__delay_ms(2);
88
LATB=0b00110000;// Upper Nibble
89
LCD_RS=1;// Data
90
enable();
91
LATB=0b00000000;// Lower Nibble
92
LCD_RS=1;// Data
93
enable();
94
//////////////
95
}
96
97
98
99
voidenable(void)
100
{
101
__delay_us(20);
102
LCD_E=1;
103
__delay_us(20);
104
LCD_E=0;
105
__delay_us(20);
106
}
Welche Routine ist denn nun richtig?
Kurze Anmerkung zum Quelltext:
Das Display ist an PortB angeschlossen, und zwar so dass die oberen 4
Bits die Datenleitung darstellen, Bit 3 ist RS, Bit 2 ist RW und Bit 1
ist E. Die unteren vier Bits des Displays sind offen. Der Code zum
beschreiben der Datenleitungen ist auch sehr simpel gehalten. Zu
Testzwecken schreibe ich die Daten quasi per Hand auf die Leitung.
Einfach 4bit ignorieren geht nicht, Du mußt dem Controller schon
mitteilen in welchem Modus Du überträgst. Seriel, 4bit, 8bit geht
prinzipiell.
Entweder sind die Pins herausgeführt und müssen die entsprechenden
Signale bekommen, oder auf dem LCD gibt es Lötbrücken die gesetzt werden
müssen.
Die Doku ist meist extrem bescheiden. SSD1803 Datenblatt, Pinning und
Multimeter führen zum Ziel.
Gerne gemacht: Das Display reagiert schon lange, aber die Kontrast
Einstellung ist vermurkst und man sieht nix.
Hangel Dich einfach Stück für Stück durch das Datenblatt bis Du erste
Reaktionen bekommst.
Die Codes die bei anderen angeblich super laufen haben bei mir oft nicht
funktioniert und mussten umfangreich angepasst werden.
Das Timing darf nicht zu knapp sein. Die Controller sind oft
schneckenlangsam.
Versuchs mit dieser 4-bit Init:
http://www.sprut.de/electronic/lcd/#init
So hat's bei mir immer funktioniert. Weiter oben auf der Seite findest
auch die die Bedeutung von N, F, C, B,...
Im zweiten Link ist erwähnt, dass der Controller tatsächlich die unteren
vier Bits ignoriert, wenn er sich im 4-Bit Modus befindet. Komisch ist
nur, dass dort die Umschaltung auf 4-Bit erst relativ spät passiert,
trotzdem sind die unteren vier Bits nicht aufgeführt. Im Datenblatt zum
SSD1803 wird nicht erwähnt, was genau der Controller macht, jedoch wird
dort gleich im ersten Wort auf 4-Bit umgeschalten. Kann es sein dass ich
dann immer die kompletten 8 Bit senden muss? Also im 4-Bit Modus immer
mit zwei aufeinanderfolgenden Worten? Das würde dann Sinn machen. Ich
werde mir aber noch mal die Routine von Sprut genau anschauen. Den Pin
für die Kontrastspannung habe ich auf Masse gelegt, so dass eigentlich
maximaler Kontrast eingestellt sein sollte.
EDIT: Und eine Sache noch. Das Display ist vierzeilig, es wird aber
immer nur die Einstellung ein- oder zweizeilig erwähnt. Was mache ich
damit? Werden die vier Zeilen intern als zwei lange Zeilen gehandhabt?
ich denke mal das es sich um ein Bitfeld handelt, von dem jedes Feld 1
Bit groß ist!
Also dürfte das nicht klappen. Es wirkt sich wohl aber nicht aus, da der
PIC sowieso mit 4MHz startet?
Was sollen die leeren #defines?:
1
#define LCD_RS LATBbits.LB3 // Makro for controll Bits
Man kann die Bits einzeln ansprechen, bzw auch mehrere auf einmal. In
diesem Fall ist das Feld IRCF 3 Bit breit und beinhaltet die
Konfiguration für die Taktfrequenz. Möchte man die Bits einzeln
ansprechen, benutzt man IRCF1, IRCF2 und IRCF3. Das sollte also so
funktionieren. Sieht man genau so auch in verschiedenen Tutorials.
Was meinst du mit leeren #defines? Jedes #define ist doch korrekt
zugewiesen?
Lukas M. schrieb:> OSCCONbits.IRCF = 110; // 4 MHz
Das schreibt aber 110 dezimal in die 3 bits, das entspricht dann binär
01101110, dieses Mal hattest du Glück, dass die unteren 3 bits von
110dez gleich 110bin sind, für die Zukunft solltest du dir aber
angewöhnen
1
OSCCONbits.IRCF=0b110;
zu schreiben.
> ADCON1bits.PCFG = 1111111; // all Pins analog
Das sollte besser
1
ADCON1bits.PCFG=0b1111111;
heißen.
Lukas M. schrieb:> EDIT: Und eine Sache noch. Das Display ist vierzeilig, es wird aber> immer nur die Einstellung ein- oder zweizeilig erwähnt
Optisch ist es vierzeilig, elektrisch aber zweizeilig.
Ich konnte mich jetzt leider aus zeitlichen Gründen nicht mehr mit
diesem Projekt befassen, jetzt bin ich aber wieder dazu gekommen etwas
daran zu machen. Ich hab mal versucht eure Vorschläge so weit es geht
umzusetzten. Die Kontrastspannung wird jetzt über ein 10k Poti
eingestellt und ich habe die Initialisierungsroutine von Sprut
angewendet. Am Display tut sich aber immer noch nichts. Ich hab mal
einen Schaltplan und das Programm angehängt. Könnte mir jemand sagen, ob
die Ausgabe der Symbole auf dem Display richtig umgesetzt ist? Diese
Befehle befinden sich am Ende der Init-Routine. Ich wüsste leider nicht
mehr wo der Fehler liegen könnte.
1
#include<stdio.h>
2
#include<stdlib.h>
3
#include<xc.h>
4
5
#pragma config OSC = INTIO2 // Oscillator Selection bits (Internal RC oscillator, port function on RA6 and port function on RA7)
6
#pragma config PWRT = ON // Power-up Timer Enable bit (PWRT enabled)
7
#pragma config WDT = OFF // Watchdog Timer Enable bit (WDT disabled (control is placed on the SWDTEN bit))
8
#pragma config MCLRE = OFF // MCLR Pin Enable bit (RA5 input pin enabled, MCLR disabled)
9
10
#define _XTAL_FREQ 4000000 // set to 4 MHz
11
#define LCD_RS LATBbits.LB3 // Makro for controll Bits
12
#define LCD_RW LATBbits.LB2 // -//-
13
#define LCD_E LATBbits.LB1 // -//-
14
15
voidinit_lcd(void);
16
voidinit_lcd2(void);
17
voidenable(void);
18
19
voidmain(void){
20
21
TRISA=0x00;//
22
TRISB=0x00;// Ports Output
23
24
OSCCONbits.IRCF=0b110;// 4 MHz
25
ADCON1bits.PCFG=0b1111111;// all Pins digital
26
27
//init_lcd();
28
init_lcd2();
29
30
while(1)
31
{
32
__delay_ms(2);
33
}
34
}
35
36
37
38
voidinit_lcd2(void)
39
{
40
LCD_E=0;
41
__delay_ms(150);
42
43
LATB=0b00110000;// 8 Bit Mode
44
enable();
45
__delay_ms(50);
46
LATB=0b00110000;// 8 Bit Mode
47
enable();
48
__delay_ms(50);
49
LATB=0b00110000;// 8 Bit Mode
50
enable();
51
__delay_ms(50);
52
53
LATB=0b00100000;// 4 Bit Mode
54
enable();
55
__delay_ms(50);
56
57
LATB=0b00100000;
58
enable();
59
__delay_ms(50);
60
LATB=0b10000000;// 2 Row, 5x8 Dot Matrix
61
enable();
62
__delay_ms(50);
63
64
LATB=0b00000000;
65
enable();
66
__delay_ms(50);
67
LATB=0b10000000;// Display Off
68
enable();
69
__delay_ms(50);
70
71
LATB=0b00000000;
72
enable();
73
__delay_ms(50);
74
LATB=0b00010000;// Clear Display
75
enable();
76
__delay_ms(50);
77
78
LATB=0b00000000;
79
enable();
80
__delay_ms(50);
81
LATB=0b01100000;// Cursor right, No Shift
82
enable();
83
__delay_ms(50);
84
85
LATB=0b00000000;
86
enable();
87
__delay_ms(50);
88
LATB=0b11000000;// Display On
89
enable();
90
__delay_ms(50);
91
92
// Cursor Home
93
__delay_ms(2);
94
LATB=0b00000000;
95
enable();
96
LATB=0b00010000;
97
enable();
98
//////////////
99
100
// Write Character
101
__delay_ms(2);
102
LATB=0b00110000;// Upper Nibble
103
LCD_RS=1;// Data
104
enable();
105
LATB=0b00000000;// Lower Nibble
106
LCD_RS=1;// Data
107
enable();
108
//////////////
109
}
110
111
voidenable(void)
112
{
113
__delay_us(20);
114
LCD_E=1;
115
__delay_us(20);
116
LCD_E=0;
117
__delay_us(20);
118
}
EDIT: Ich seh grad dass ich im Schaltplan eine kleine Verbindung
vergessen hab. Das Poti ist natürlich an Vee angeschlossen. Und an allen
Kreuzungspunkten der Leitungen sind keine Verbindungen, sofern kein
Bommel an diesem Kreuzungspunkt eingezeichnet ist.
Siehst du denn irgendeinen Effekt, wenn du die Kontrastspannung
veränderst? Normal müssten sich die Zeichenpositionen mal als dunkle
Vierecke abzeichnen.
Ja da tut sich was. Aber egal ob die Reihen hell oder dunkel angezeigt
werden, es werden keine Zeichen dargestellt. Es muss also irgendwo an
der Software liegen. Die Schaltung sollte passen, is ja nicht so
kompliziert...
Na da hast du schon mal mehr Glück alsich mit meinem ersten Display. das
brauchte nämlich eine negative Kontrastspannung.
Viele "Kompatible" brauchen deutlich längere Zeiten als das Orginal. Das
war das 2. Problem, worüber ich damals gestolpert bin. Da sollte man am
Anfang erst mal mit zu knapp ran gehen.
So, hab mir noch mal den Code angeschaut ... sobald du den 4-Bit-Modus
aktiviert hast, musst du doch alle weiteren Bytes in 2 Schritten
übertragen. Das sehe ich nicht.
Ist zwar für den AVR, aber für den Ablauf kannst du hier noch mal
reinschauen:
http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/LCD-Ansteuerung
An sonsten hab ich erst mal keine weiteren Ideen.
Ich würde in der "init_lcd2" ganz am Anfang dafür sorgen das die
RW-Leitung auf "schreiben" steht (also LOW) und die RS-Leitung auf
"control select" (also LOW) steht. Nach einem PowerOnReset ist der
Zustand der LATCH u. PORT Register undefiniert.
Sauberer wäre ohnehin die Aufteilung in 2 Routinen z.B.
LCD_control(char) und LCD_data(char) in welchen die RS-Leitung
entsprechend bedient wird und das übergeben Datenbyte in 2 Nibble
geteilt und auf den Bus belegt wird.
Michael L. schrieb:> So, hab mir noch mal den Code angeschaut ... sobald du den 4-Bit-Modus> aktiviert hast, musst du doch alle weiteren Bytes in 2 Schritten> übertragen. Das sehe ich nicht.
Macht er ja, in dem er auf dem Papier sich aus einem Byte 2 Byte bastelt
und ausgibt:
clear Display 0b00000001 sieht dann eben so aus
Lukas M. schrieb:> LATB = 0b00000000;> enable();> __delay_ms(50);> LATB = 0b00010000; // Clear Display> enable();> __delay_ms(50);
Spaghetticode eben.....
Ja der Code ist nicht sehr schön, aber ich will auch erstmal nur sehen
ob es geht. Daher gebe ich alles Datenwörter einzeln per Hand aus.
Chris B. schrieb:> Ich würde in der "init_lcd2" ganz am Anfang dafür sorgen das die> RW-Leitung auf "schreiben" steht (also LOW) und die RS-Leitung auf> "control select" (also LOW) steht. Nach einem PowerOnReset ist der> Zustand der LATCH u. PORT Register undefiniert.
Das mache ich ja. Ich habe die Steuerpins an die unteren Pins des PortB
angeschlossen. Wenn ich schreibe
LATB = 0bxxxx0000
dann werden die entsprechenden Pins gesetzt. Ist etwas verwirrend, da
dieses Umstand in einem Kommentar steht, der in der ersten Routine
init_lcd() weiter oben steht:
1
/*
2
* LCD-Input on Ports RB4 - RB7
3
* RS on RB3
4
* R/W on RB2
5
* E on RB1
6
*/
Aber dadurch sollten diese Pins alle auf Low gesetzt werden, bevor zum
ersten mal enable() aufgerufen wird.
Ich habe auch schon die Delayzeiten sehr großzügig gewählt. Ich könnte
diese jetzt noch weiter erhöhen, aber kann es wirklich daran liegen? Sie
sind meiner Meinung nach schon sehr hoch.
Lukas M. schrieb:> Das mache ich ja. Ich habe die Steuerpins an die unteren Pins des PortB> angeschlossen. Wenn ich schreibe>> LATB = 0bxxxx0000
Ok, das habe ich nicht bedacht, daran soll es nicht liegen.
Delayzeiten sind lang genug (eventuel die erste Zeit von 150ms auf 250ms
erhöhen falls es ein besonders lahmes Display ist).
Wichtiger aber:
Die Low Voltage Programmierung (ist eine Funktion von RB5) abschalten
mit:
#pragma config LVP = OFF
(Siehe Datenblatt 19.9.
When Low-Voltage Programming is
enabled, the RB5 pin can no longer be
used as a general purpose I/O pin. 9
ES LEBT!!
Dieser verdammte RB5 wars tatsächlich! Vielen Dank für eure Hilfe! Ich
denke dass auch meine erste Init-Routine fehrlerhaft war, dass kann ich
jetzt alles mal in Ruhe überprüfen. Auf die Idee dass es an dem Port
liegt wär ich so schnell nicht gekommen. Ich finde es als Anfänger auch
manchmal schwierig, sich im Datenblatt zurecht zu finden. Weil manche
Optionen lassen sich einfach in Registern einstellen und manche lassen
sich nur über Configbits einstellen. Ich denke dass is dann einfach
Erfahrung...