PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C-Schönheiten (C18) und LC-Display



theodrin
20.07.2007, 12:32
Hallo!

Ich versuche schon länger ein LC-Display anzusteuern, aber es funktioniert nicht. Ich hab da zwar einen Initialisierungscode programmiert, aber der funktioniert anscheinend nicht. Leider kann ich jetzt nicht den Code mitschicken, kommt erst später.

Aber ich versuche auch ein wenig Ordnung da reinzubringen. Da hab ich was im Netz gefunden, womit man Bits Namen geben kann. Das sah so aus:

z.B:



#pragma char LCD_CTRL @ PORTB

Bit LCD_RS @ PORTB.1;


Leider hat der Compiler (C18) nur den ersten Teil verstanden. Aber die direkte Bit-Zuweisung hat nicht funktioniert. Was hab ich falsch gemacht? Bzw. Wie kann ich das anders oder besser machen?

Liebe Grüße,
Norbert

TheScientist
20.07.2007, 16:09
Hi,

ich habe ebenfalls ein Problem ein LCD mit einem 18F4550 anzusteuern. Ich arbeite erst seit kurzem mit den 18ern. Compilieren tue ich ebenfalls mit C18. Hier mal der fürs LCD relevante Code:


//Überprüfe LCDStatus
void waitForLCD(void)
{
//Steuerleitungen einstellen
LATE |= 0b00000010; //RW
LATE &= 0b11111011; //RS

//RD4..7 Inputs
LATD &= 0b00001111;
TRISD = 0xFF;

//Busyschleife
cBusy = 0x80;
do
{
LATE |= 0b00000001;
Delay1TCY();
cBusy = PORTD;
LATE &= 0b11111110;
Delay1TCY();
LATE |= 0b00000001;
Delay1TCY();
LATE &= 0b11111110;
}while((cBusy & 0b10000000) != 0x00);

//Leitungen zurücksetzen
LATE &= 0b11111001; //RW, RS
TRISD = 0x0F;
LATD &= 0b00001111;

return;
}

//Übertrage Steuerdaten an LCD
void sendCWToLCD(char cWord)
{
//Busy-Check
waitForLCD();

//Steuerleitungen einstellen
LATE &= 0b11111000;

//Übertragen der Bits 4..7
LATD &= 0b00001111;
LATD |= cWord & 0b11110000;
LATE |= 0b00000001;
Delay1TCY();
LATE &= 0b11111110;

//Übertragen der Bits 0..3
LATD &= 0b00001111;
LATD |= (cWord & 0b00001111) << 4;
LATE |= 0b00000001;
Delay1TCY();
LATE &= 0b11111110;
}

//Übertrage Daten an LCD
void sendDWToLCD(char cWord)
{
//Busy-Check
waitForLCD();

//Steuerleitungen einstellen
LATE &= 0b11111100; //RW
LATE |= 0b00000100; //RS

//Übertragen der Bits 4..7
LATD &= 0b00001111;
LATD |= cWord & 0b11110000;
LATE |= 0b00000001;
Delay1TCY();
LATE &= 0b11111110;

//Übertragen der Bits 0..3
LATD &= 0b00001111;
LATD |= (cWord & 0b00001111) << 4;
LATE |= 0b00000001;
Delay1TCY();
LATE &= 0b11111110;

LATE &= 0b11111011; //RS
}

//Initialisierung des LCD
void initLCD(void)
{
//15ms Warten
Delay10KTCYx(10);

//Sende Init-Byte (1)
LATD &= 0b00001111;
LATD |= 0b00110000;
LATE |= 0b00000001;
Delay1TCY();
LATE &= 0b11111110;

//5ms Warten
Delay10KTCYx(5);

//Sende Init-Byte (2)
LATD &= 0b00001111;
LATD |= 0b00110000;
LATE |= 0b00000001;
Delay1TCY();
LATE &= 0b11111110;

//100us Warten
Delay100TCYx(10);

//Sende Init-Byte (3)
LATD &= 0b00001111;
LATD |= 0b00110000;
LATE |= 0b00000001;
Delay1TCY();
LATE &= 0b11111110;

Delay100TCYx(10);

//4-bit
LATD &= 0b00001111;
LATD |= 0b00100000;
LATE |= 0b00000001;
Delay1TCY();
LATE &= 0b11111110;

Delay100TCYx(10);

//Display löschen
sendCWToLCD(0b00000001);

//4-bit Interface und 5x8 Matrix
sendCWToLCD(0b00101000);

//Display aus
sendCWToLCD(0b00001000);

//Cursorrichtung rechts, Shift aus
sendCWToLCD(0b00000010);

//Display ein
sendCWToLCD(0b00001100);
}

Pinbelegung:

D4..7 -> RD4..7
E -> RE0
RW -> RE1
RS -> RE2

Nur damit ihr euch nicht wundert: An RD0..3 sind Eingänge für ne Tastermatrix.

Wenn ich das Programm laufen lasse passiert nix. Das Display zeigt nix bzw. bei runtergedrehter Kontrastspannung schwarze Kästen an, als wäre es nicht initialisiert.

mfg The Scientist

theodrin
21.07.2007, 11:31
Hallo!

So Leute, ich hab jetzt den Code.

Hier kommt er:



wait_ms(50);


TRISA = 0b00111111; //PORT A wird als Eingang gesetzt, bis auf die RA6(OSC für Oszillator verwendet) (RA7 gibt es nicht)
TRISB = 0b00000000; //Port B wird als Ausgang gesetzt
TRISC = 0b11000000; //PORT C wird als Ausgang gesetzt nur die RC6 und RC7 werden als Eingang gesetzt, für meine beiden gelben Taster
TRISD = 0b11110000; //PORT D: RD4-7 werden als Eingang gesetzt für meine 4 roten Taster; RD0-RD3 werden als Ausgang gesetzt für etwaige Verwendung (Ansteuerung der MOSFETs, Schrittmotor
TRISE = 0b00001111;

LATD = 0b00000000;
wait_ms(50);
wait_ms(50);
LATB = 0x30;
wait_ms(50);
wait_ms(50);
LATD = 0b00000100;
wait_ms(50);wait_ms(50);wait_ms(50);wait_ms(50);
LATD = 0b00000000;

wait_ms(50);wait_ms(50);
LATD = 0b00000100;
wait_ms(50);wait_ms(50);wait_ms(50);wait_ms(50);
LATD = 0b00000000;

wait_ms(50);wait_ms(50);
LATD = 0b00000100;
wait_ms(50);wait_ms(50);wait_ms(50);wait_ms(50);
LATD = 0b00000000;

wait_ms(50);wait_ms(50);
LATD = 0b00000100;
wait_ms(50);wait_ms(50);wait_ms(50);wait_ms(50);
LATD = 0b00000000;

LATD = 0b00000100;
wait_ms(50);wait_ms(50);wait_ms(50);wait_ms(50);
LATD = 0b00000000;


wait_ms(50);wait_ms(50);wait_ms(50);wait_ms(50);
LATB = 0b00111000;

LATD = 0b00000100;
wait_ms(50);wait_ms(50);wait_ms(50);wait_ms(50);wa it_ms(50);
LATD = 0b00000000;
wait_ms(50);wait_ms(50);wait_ms(50);wait_ms(50);wa it_ms(50);wait_ms(50);

LATB = 0b00001111;

LATD = 0b00000100;
wait_ms(50);wait_ms(50);wait_ms(50);wait_ms(50);wa it_ms(50);wait_ms(50);
LATD = 0b00000000;
wait_ms(50);wait_ms(50);wait_ms(50);wait_ms(50);wa it_ms(50);

LATB = 0b00000001;

LATD = 0b00000100;
wait_ms(50);wait_ms(50);wait_ms(50);wait_ms(50);wa it_ms(50);wait_ms(50);
LATD = 0b00000000;
wait_ms(50);wait_ms(50);wait_ms(50);wait_ms(50);wa it_ms(50);wait_ms(50);

LATB = 0b00000110;

LATD = 0b00000100;
wait_ms(50);wait_ms(50);wait_ms(50);wait_ms(50);wa it_ms(50);wait_ms(50);wait_ms(50);wait_ms(50);
LATD = 0b00000000;
wait_ms(50);wait_ms(50);wait_ms(50);wait_ms(50);wa it_ms(50);wait_ms(50);wait_ms(50);wait_ms(50);


Ist vielleicht ein wenig wirr war, aber ich hab da schon so viele Delay-Zeiten reingegegeben, dass es ja reicht. Aber es funktioniert trotzdem nicht.

Noch zur Erklärung. Ich verwende den 8-Bit Modus, und mein Datenregister ist das Register B. RS ist RD0, R/W ist RD1, E ist RD2

So ich hoffe ihr findets einen Feher. Und ich hoffe man kennt sich aus. ;-)

lg,
Norbert

Mobius
24.07.2007, 09:15
@theodrin (1. post):
Also das "Bit" hab ich erlich noch nie bei dem C18 gesehen, wobei ich aber sagen muss, dass ich mich bei dem eher an den standard synthax von C halte , so weit es geht :). Bei dem geht den Bits namen zu geben am einfachsten mit einem #define (also bei deinem Beispiel zB "#define LCD_RS PortB.1").

@TheScientist: mMn bist du zu schnell für das LCD. Schon bei der Abfrage der Busy-Flags musst du nach dem Enable mindestens 40µs warten, bevor das LCD bereit ist, die Daten herauszurücken. Aber das "Delay1TCY()" verzögert nur um 1 Befehlszyklus (also ein "nop" in ASM). Ich weiß jetzt nicht, wie hoch (oder eher hoffentlich, wie niedrig) du dein PIC getaktet hast, aber schon bei einem externen Takt von 4MHz ist das zu wenig. Bei Sprut (http://www.sprut.de/electronic/lcd/index.htm#befehle) stehen eigentlich recht gut die Timings für die einzelnen Schritte zum Ansprechen des Displays drin.

@theodrin: Wie schaut deine wait-routine aus? C18 benutz ich auch nicht seit sehr langem, aber mir ist noch keine Funktion untergekommen die wait_ms() heißt. Hast du auch geschaut, ob die wirklich 1ms Verzögerung erstellt (also pin toggeln und mit osci oder Frequenzcounter nachmessen)?

Ist das eure vollständige Initialisierung? Weil ihr müsst noch den ADC von den Pins des PICs abschalten.

lg
Mobius

TheScientist
24.07.2007, 10:36
Moin,

habs jetzt hinbekommen. Es lag kurioserweise am Flachbandstecker. Beim Zurammendrücken scheinen einige Messer nicht richtig in das Kabel gedrungen zu sein. Neues Kabel - läuft.

@Mobius: Der PIC läuft mit Int. PLL+20MHz Quarz bei 48MHz. Laut den Timings im Datenblatt ist das viel zu schnell. Aber lustigerweise funktioniert es ja. Und zwar muss ich nach dem Enable immer min. 220ns warten. Wie lange das Display dann für einen Befehl braucht, ist ja im Prinzip egal, da es solange ja "Busy" ist. Und das abfragen des Busy-Flags dauert laut Datenblatt 0µs.
Mit meinen 48MHz müsste ich ja intern bei 12Mips sein, oder? Also würde ich beim Enable nur etwa 83ns warten. Werde das zur Sicherheit mal verdreifachen.

mfg The Scientist

theodrin
24.07.2007, 12:37
Hallo!

Danke für die Beschreibung mit dem #define. Ich hab die andere Variante nur wo gesehen und dachte halt, dass ich das auch so verwenden könnte. Aber egal, wenns so geht, dann freuts mich.

Bei mir läuft das Display leider immer noch nicht. Ich verwende übrigens auch den PIC18F4550. Ich hab nen 20Mhz-Quarz. Das mit dem PLL versteh ich nicht ganz. Was heißst das noch mal? Weil da kann ich ja 4 verschiedene Konfigurationseinstellungen dazu setzen.
Also das ein Befehlszyklus vier Takte braucht, weiß ich, also das man halt die Frequenz vom Quarz durch 4 teilen muss um die Frequenz zu erhalten.
Genau wie das TheScientist gemacht hat mit seinen 12Mips. Aber wie bringt er mit einem 20Mhz-Quarz 48Mhz raus, also dann 12Mips und nicht 5Mips?

lg
Norbert

TheScientist
24.07.2007, 12:56
Ich verwende folgende Konfiguration:

Vorteiler für PLL: 5 d.h. der PLL wird mit 20MHz/5=4MHz gespeist. Daraus macht er 96MHz.

Die Teile ich für die CPU nochmal durch 2, also 48MHz. Mehr geht auch nicht.

In C18 sieht das dann so aus:



//Konfiguration
#pragma config PLLDIV = 5, CPUDIV = OSC1_PLL2, USBDIV = 2, FOSC = HSPLL_HS // Int. 48MHz, Ext.-Src. 20MHz
#pragma config PWRT = ON
#pragma config BOR = OFF
#pragma config VREGEN = OFF
#pragma config WDT = OFF
#pragma config MCLRE = ON
#pragma config PBADEN = OFF
#pragma config LVP = OFF


mfg The Scientist

Mobius
25.07.2007, 08:07
Also PLL ist ein Phase Lock Loop, d.h. ein interner Oscillator, dessen Flanken mit den Flanken eines externen (für den PLL gesehen) Quelle synchronisiert werden. Bei dem PIC ist es so, dass der PLL fest auf einer Frequenz von 96Mhz läuft und dass er mit einem 4MHz Takt "gespeist" werden muss. Die 96MHz werden dann zur Erzeugung der USB-Frequenzen und des CPU-Taktes verwendet (geteilt durch 2 ergibt diese 48MHz internen Takt, das nochmal durch 4 ergibt 12MHz also 12 Million Instructions per Second = Mips).
Danach ist es nur noch ein Rechenspiel, da TheScientist einen 20MHz Quarz am Chip hängen hat ergibt sich, dass er den Takt durch 5 teilen muss um auf die 4MHz Speisefreuquenz für den PLL zu kommen.

Der riesen Vorteil von dem PLL ist, dass man nur eine kleinen externen Takt braucht und man doch mit dem µC sau schnelle unterwegs ist. Und die Störungen die ein 4MHz Quarz erzeugt sind bei weitem nicht so groß, als wenn man einen 48MHz in der Schaltung hat, die seine hochfreuenten Störungen überall reinspeißt.

Die Einstellung des PLLs wird sehr schön im Datenblatt erklärt (also wie man was setzten muss, damit es funktioniert :) ). Oder du übernimmst 1:1 den Code von TheScientist und dann musst du nur noch den Wert des Vorteiler für den PLL ändern (immer so wählen, dass du auf einen internen Takt von 4MHz kommt, setzt natürlichen einen Quarz mit einem vielfachen von 4MHz voraus ;) ).

lg
Mobius

theodrin
25.07.2007, 19:05
Hallo!

Na das ist für mich ne Neuigkeit. Danke, das klingt ja echt gut.

Aber noch ne Frage: Den PLL kann ich ein und ausschalten oder?
Weil ich hab nämlich bei FOSC = HS stehen und damit verwende ich ja die PLL nicht oder? Das heißt für mich gilt das nicht, oder?
Wenn ich schreibe FOSC = HSPLL_HS wie The Scientist, dann zählt das schon, aber so wie ich es im Moment habe nicht, oder?
Also es reicht ein Ja wenn ich recht habe, oder ein Nein, wenn ich Unrecht habe. :-)

lg,
Norbert

theodrin
25.07.2007, 19:22
Hallo!

Ich hab gerade versucht, meine wundervollen Namen wie LCD_RS zu verwenden aber der Compiler schreibt mir einen Fehler hin. Was hab ich falsch gemacht?

Das ganze ist jetzt so definiert:


/** Portbezeichnungen ************************************************** ******/

#define LCD_DATA PORTB
#define LCD_DATA_TRIS TRISB
#define LCD_CTRL PORTD
#define LCD_CTRL_TRIS TRISD

#define LCD_RS LCD_CTRL.0;
#define LCD_RW LCD_CTRL.1;
#define LCD_E LCD_CTRL.2;


Und dann wollte ich halt z.B das LCD_RS Bit setzen und hab hingeschrieben: LCD_RS = 1;
und daraufhin schreibt mir der Compiler einen Fehler hin. Was funktioniert da bitte nicht?

lg
Norbert

Edit: Ich hab was vergessen. Also die Namen für die Register funktionieren. Also die Namen für ein ganzes Register. Da kann ich z.B schreiben LCD_DATA = 12; also das funktioniert, aber mit den Bits das geht nicht.

theodrin
31.07.2007, 08:03
Hallo!

So jetzt funktioniert das! Also erstmal zum LCD-Display: Mein Fehler war so banal wie er nur sein kann. Ähnlich wie bei TheScientist. Es lag am Kabel. Es war alles richtig geklemmt, nur hab ich genau seitenverkehrt angeschlossen. Und ja, was soll ich sagen, es funktioniert jetzt.

Zweitens funtioiert meine Namenszuweisung zu nem PIN. In C18 funktioniert das anscheinend so:




//Register benennen
#define LCD_DATA PORTB

//Pin benennen
#define LCD_E PORTDbits.RD3



Ja das war die Hexerei. So geht das.

Noch ne Frage zum den Delayfunktionen. Also für den der die kennt:
Die schauen z.B so aus: (So weit ich micht nicht irre:)
Delay1kTCYs(0-255);
Da wartet er jetzt 1000*(0-255) - Zyklen. Die Frage. Wartet er jetzt 1000*(0-255) - Takte oder Befehle. Weil wenn Befehle wartet er ja 4*länger. Ist ja doch was.

lg
Norbert

Mobius
31.07.2007, 22:15
Ein Zyklus ist IMMER für den Befehlszyklus gesehen, also ja, er wartet eigentlich 1000*(0-255)*4 Quarz-Takte (eigentlich nichts anderes, als wenn du ein Haufen "nop"-Befehle in Assembler hintereinander schreibst). Vielleicht wäre es aber interessant einen der Timer für diese Aufgabe auszuborgen, nat. wenn du noch einen frei hast, weil du dann mit Interrupts und einer State-Mashine den cpu nicht so stark beanspruchen würdest (vor allem bei USB-Anwendungen sehr wichtig ;) ). Ka, wie trivial oder auch nicht es ist (schätz mal auf net so einfach, muss es mri mal durch den Kopf gehen lassen, bin aber zu müd dafür).

Und freu mich, dass es mit dem LCD geklappt hat :D. Das #define ist a gut zu wissen, man lernt nie aus ;) :D.
lg
Mobius