Forum: Mikrocontroller und Digitale Elektronik (PIC) LCD Initialisieren


von Lukas M. (luki55)


Lesenswert?

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
void init_lcd(void);
16
void enable(void);
17
void led_blinken(void);
18
19
void main(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
void init_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
void enable (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.

von Michael K. (Gast)


Lesenswert?

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.

von Max H. (hartl192)


Lesenswert?

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,...

von Lukas M. (luki55)


Lesenswert?

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?

von Ingo (Gast)


Lesenswert?

Bin zwar kein PIC-Experte, aber
1
OSCCONbits.IRCF = 110;          // 4 MHz
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
2
#define LCD_RW LATBbits.LB2     //      -//-
3
#define LCD_E LATBbits.LB1      //      -//-

Ingo

von Lukas M. (luki55)


Lesenswert?

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?

von Max H. (hartl192)


Lesenswert?

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.

von Lukas M. (luki55)


Lesenswert?

Hmm ok. Ich bin mir aber sicher dass es in einem Turorial auch genau so 
geschrieben wurde. Aber ich werd in Zukunft anders machen.

von Max H. (hartl192)


Lesenswert?

Nachtrag:
1
ADCON1bits.PCFG = 0b1111111;
Setzt alle Pins digital, dein Kommentar passt nicht zum Code.

von Lukas M. (luki55)


Lesenswert?

Danke für den Hinweis! Sollten natürlich alles digitale Ports werden, 
habs nur im Kommentar falsch geschrieben.

von Lukas M. (luki55)


Angehängte Dateien:

Lesenswert?

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
void init_lcd(void);
16
void init_lcd2(void);
17
void enable(void);
18
19
void main(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
void init_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
void enable (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.

von Michael L. (michaelx)


Lesenswert?

Siehst du denn irgendeinen Effekt, wenn du die Kontrastspannung 
veränderst? Normal müssten sich die Zeichenpositionen mal als dunkle 
Vierecke abzeichnen.

von Lukas M. (luki55)


Lesenswert?

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...

von Michael L. (michaelx)


Lesenswert?

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.

von Chris B. (dekatz)


Lesenswert?

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.....

von Lukas M. (luki55)


Lesenswert?

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.

von Chris B. (dekatz)


Lesenswert?

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

von Lukas M. (luki55)


Lesenswert?

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...

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.