Hallo,
Ich arbeite mit einem PIC18F2550 und dem Microchip C18 Compiler und
möchte jetzt, nach einiger Einarbeitungszeit mein erstes LC-Display
ansteuern.
Ich habe mir anhand des xlcd Sourcecodes eigene Routinen gestrickt, die
das Display an den Ports (PORTA für Daten, PORTC für Controls) ansteuert
wo ich es später (soll mal ein Labornetzteil werden) anschließen will.
Die Standardeinstellung auf PORTB von der xlcd.h kann ich leider nicht
nehmen, da ich die Interrupt-Pins brauche.
Mein Code hat auch nach einigen Stunden gefrickel funktioniert. Am Ende
musste ich feststellen, dass die Probleme gar nicht von meinem Code
herrührten, sondern von den Timings.
In der Standard xlcd werden zwischen den Befehlen 18TCY pause eingelegt,
die "E cycle time" beträgt dann etwa 40cycles, bei meinem PIC mit 48MHz
clock also etwa 40*83ns=3,3µs. Laut Datenblatt meines LCD muss ich
mindestens 500ns einhalten - also sollte es damit gehen. In meinen
ersten Versuchen habe ich der Einfachheit halber die standard Delays
genommen und 20TCY Pause eingelegt.
Mein Problem ist nun, dass es erst ab etwa 2000TCY zuverlässig
funktioniert, aber selbst das nur, wenn ich erst das Display und dann
den PIC einschalte. auch ein Delay von 1s vor der Initialisierung hat da
nicht geholfen.
Bei 2000TCY Pausen brauche ich dann knapp 8000 Cycles um ein einzelnes
Byte zu übertragen.
Im Moment ist das nicht tragisch, aber später wird es den restlichen
Code ganz schön ausbremsen wenn ich zB Periodisch meine Ampere &
Volt-Messwerte à je 5 Zeichen auf dem Display updaten möchte.
Woher kann das kommen? Ich habe leider kein Oszilloskop um die Signale
zu untersuchen. Meine Leitung zum Display ist im Moment ein 25cm
Flachkabel, aber auch ein direktes aufstecken auf meine
Experimentierboard hat nicht geholfen.
Ich bin dankbar für eure Hilfe.
Meinen Code werde ich noch posten, habe mir leider grade irgendwas
kaputtgebastelt, sodass es selbst mit den langen Pausen nicht mehr
funktioniert.
Liebe Grüße, Daniel a.k.a H3PO
_____________________
Edit:
Habe inzwischen rausgefunden, dass bloß meine prozedur zum Lesen nicht
funktionierte und deshalb das Busy-Flag nie zurückgegeben wurde. Wenn
ich stattdessen 10ms warte, funktioniert mein code.
Deshalb hier mal mein code zum lesen - sieht wer nen groben fehler?
1 | //read byte from register x
| 2 | char lcd_read (char rs)
| 3 | {
| 4 | char byte = 0;
| 5 |
| 6 | LCD_LATCH &= 0xF0; //reset
| 7 | LCD_TRIS |= 0x0F; //input
| 8 |
| 9 | LCD_RS = rs;
| 10 | LCD_RW = 1;
| 11 | Delay10TCYx(delay); //rs,rw,data setup delay
| 12 | LCD_E = 1;
| 13 | Delay10TCYx(delay); //data setup delay,
| 14 |
| 15 | byte = (LCD_PORT << 4) & 0xF0; //read high nibble
| 16 |
| 17 | LCD_E = 0;
| 18 | Delay10TCYx(delay); //delay between E cycles
| 19 | LCD_E = 1;
| 20 | Delay10TCYx(delay); //data setup delay,
| 21 |
| 22 | byte |= (LCD_PORT & 0x0F); //read masked low nibble
| 23 |
| 24 | LCD_E = 0;
| 25 | LCD_RS = 0;
| 26 | LCD_RW = 0;
| 27 | }
| 28 |
| 29 | //return busy bit7 as 0 or 1
| 30 | char lcd_busy (void)
| 31 | {
| 32 | //could save some cycles by not shifting
| 33 | return ((lcd_read(0) & 0x80) >> 7);
| 34 | }
|
kannst du in deiner Entwicklungsumgebung die Zeit messen die du einzelne
Pins aktivierst bzw. deaktivierst um mal zu prüfen ob die Pausen im
Programm auch wirklich am Portpin anliegen, falls du kein Oszi zur Hand
hast?
Ansonsten anbei mal ein sehr ausführliches Datenblatt mit sehr vielen
Beispielen
Hallo Thomas,
Danke erstmal, dass du mein Thema so weit hinten im Forum noch beachtet
hast.
Ehrlich gesagt habe ich keine Ahnung, was die MPLAB IDE so alles kann.
Der Editor hat mich schon nach kurzer Zeit dermaßen genervt, dass ich
jetzt in Notepad++ programmiere und per batch an den Compiler übergebe.
Ich werd mir die Debugger-Funktionen von dem Ding mal ansehen.
Oben hätte ich noch dabeischreiben sollen, das schreiben in den DDRAM
funktioniert mit delay = 3, also 30TCY = 2,5µs. Das lesen allerdings
funktioniert nie, egal wie lang ich die Pausen mache.
Deinem Datenblatt kann ich leider nicht viel mehr entnehmen, als ich
schon weiß. Oben meine Routine ist ja quasi eine 1:1 Kopie von der
xlcd-Funktion, welche erwiesenermaßen bei tausenden Leuten funktioniert.
In meiner Funktion oben muss also ein grober programmierfehler sein, ich
kann aber nichts finden.
Für den Fall, dass mein Display einfach eigenwillig ist, habe ich auch
schonmal zusätzliche Delays eingebaut, wo mein Display laut
Timing-Diagramm gerne noch 10-20ns Zeit hätte. Da eine Instruction bei
mir aber schon 83ns dauert, ist das ja eigentlich überflüssig.
Hier hat sich bestimmt doch schon der eine oder andere eine eigene
LCD-Funktionensammlung gebaut, oder? Könnte bitte mal jemand etwas
funktionierendes posten?
In deiner Funktion "char lcd_read(char)" fehlt das return!
*kopf->tisch*
Danke Herr Hepp, das war einfach die Erleuchtung, die man nach ner
durchprogrammierten Nacht braucht, wenn man den Wald vor lauter Bäumen
nicht mehr sieht =)
Jetzt dachte ich grade "yay - Problem gelöst", aber es funktioniert
immer noch nicht. Auch die methode aus xlcd, einfach RW zu setzen, den
Port zu lesen, ein if(LCD_PORT & 0x08) zu machen und anschließend das
untere Nibble auszuclocken, funktioniert nicht. 0x08 in diesem fall,
weil am 4bit-Port ja das obere Nibble vom Statusbyte anliegt.
xlcd-methode inkl. seltsamen originalkommentaren: 1 | char lcd_busy (void)
| 2 | {
| 3 | LCD_RW = 1; // Set the control bits for read
| 4 | LCD_RS = 0;
| 5 | Delay10TCYx(delay);
| 6 | LCD_E = 1; // Clock in the command
| 7 | Delay10TCYx(delay);
| 8 | // 4-bit interface
| 9 | // Lower nibble interface
| 10 | if(LCD_PORT&0x08)
| 11 |
| 12 | {
| 13 | LCD_E = 0; // Reset clock line
| 14 | Delay10TCYx(delay);
| 15 | LCD_E = 1; // Clock out other nibble
| 16 | Delay10TCYx(delay);
| 17 | LCD_E = 0;
| 18 | LCD_RW = 0; // Reset control line
| 19 | beep();
| 20 | return 1; // Return TRUE
| 21 | }
| 22 | else // Busy bit is low
| 23 | {
| 24 | LCD_E = 0; // Reset clock line
| 25 | Delay10TCYx(delay);
| 26 | LCD_E = 1; // Clock out other nibble
| 27 | Delay10TCYx(delay);
| 28 | LCD_E = 0;
| 29 | LCD_RW = 0; // Reset control line
| 30 | Delay10TCYx(12); //10µs
| 31 | return 0; // Return FALSE
| 32 | }
| 33 | }
|
auf http://www.sprut.de/electronic/lcd/index.htm#ks0066u habe ich
übrigens gelesen, dass der KS006 Controller (den hat mein Display) das
Busy-Flag etwas zu früh löscht. Deshalb füge ich vor dem 'return 0;'
eine kleine Pause ein. Oben ist auch noch mein beep(); drin, damit ich
mitkriege wenn das Busy-Flag endlich mal erkannt wird.
> *kopf->tisch*
> Danke Herr Hepp, das war einfach die Erleuchtung, die man nach ner
> durchprogrammierten Nacht braucht, wenn man den Wald vor lauter Bäumen
> nicht mehr sieht =)
Was glaubst du, wie oft ich schon solche Fehler fabriziert habe. =)
Deiner war übrigens auch nicht ganz leicht zu finden. ;)
Mir ist nur ein Rätsel, dass der Compiler nicht gemeckert hat.
Ich warte noch auf die Platine, dann kann ich mich auch endlich
mit einem 2x8 Zeichen KS0066 Controller herumschlagen. Mal sehen
was bei mir nicht geht. Lustig ist ja, dass ich bereits das Programm
fertig geschrieben habe, die Bauteile bei mir sind und ich jetzt
seit 3 Tagen auf dieses dämliche Stück Harz mit Kupfer warte,
um endlich loslegen zu können. XD
Achja, ich habe das Programm mit der PIC18 Simulator IDE getestet.
Und ich verlasse mich auch nicht mehr auf's BusyFlag. In dem
Datenblatt sollten maximale Zeitangaben stehen... diese warte ich
einfach ab.
Viele Grüße
Sebastian
Ich sehe was, was du nicht siehst. Und das heisst...
Enable (Fallende Flanke)
Du musst also Enable setzen und dann wieder löschen und erst dann
wird der Befehl abgearbeitet. In deinem Code setzt du es und frägst
dann gleich den Port ab... da bekommst du natürlich nicht wirklich
etwas verwertbares.
Sebihepp
Hm, das macht laut Timing-Diagramm (Anhang) weder Sinn, noch
funktioniert es.
Ich warte doch nach dem Setzen von Enable eine Zeit ab (gefordert sind
tD = 120ns, ich warte 30Cycles ~= 2490ns), lese den Port und lösche
Enable wieder. Nach dem "Enable = fallende Flanke" liegen die Daten noch
tDH = 20ns am Port an, also zu kurz um sie erfolgreich inneralb eines
Cycles (83ns) zu lesen.
getesteter code: 1 | char lcd_read (char rs)
| 2 | {
| 3 | char byte = 0;
| 4 |
| 5 | LCD_LATCH &= 0xF0; //reset
| 6 | LCD_TRIS |= 0x0F; //input
| 7 |
| 8 | LCD_RS = rs;
| 9 | LCD_RW = 1;
| 10 | Delay10TCYx(delay); //rs,rw,data setup delay
| 11 | LCD_E = 1;
| 12 | Delay10TCYx(delay); //data setup delay,
| 13 |
| 14 | LCD_E = 0;
| 15 | byte = (LCD_PORT << 4); //read high nibble
| 16 |
| 17 | Delay10TCYx(delay); //delay between E cycles
| 18 | LCD_E = 1;
| 19 | Delay10TCYx(delay); //data setup delay,
| 20 |
| 21 | LCD_E = 0;
| 22 | byte |= (LCD_PORT & 0x0F); //read masked low nibble
| 23 |
| 24 | LCD_RS = 0;
| 25 | LCD_RW = 0;
| 26 |
| 27 | return byte;
| 28 | }
| 29 |
| 30 | //return busy bit7 as 0 or 1
| 31 | char lcd_busy (void)
| 32 | {
| 33 | if (lcd_read(0) & 0x80)
| 34 | {
| 35 | beep(); //wenns piept, funzt der scheiss endlich!
| 36 | return 1;
| 37 | }
| 38 | Delay10TCYx(12); //10µs für den fall, dass der ks006 sich zu früh freut
| 39 | return 0;
| 40 | }
|
Edit: Übrigens Danke trotzdem für deine Hilfe, Sebastian, und viel
Erfolg mit deinem kommenden Projekt! Ich bastel hier noch alles auf nem
Steckbrett :)
Funktioniert überhaupt der Beep?
Ich hab' noch nen kleines Steckbrett. Morgen baue ich meine Schaltung
auch mal drauf auf. Mal sehen ob mein Code funktioniert. Der ist
übrigens im Anhang. Das ist zwar Assembler und ich habe ihn auch noch
nicht getestet, aber vielleicht willst du ja mal drüber schauen.
Die Ein oder Andere Eingebung bringt das ab und zu. ;)
Der Code ist für einen PIC18F4550 mit 40MHz Quarzoszillator.
schönen Abend noch.
Sebihepp
Habe das Rätsel grade gelöst. Am Anfang von main.c beim setzen der Flags
habe ich mich bei einem Kommentar verschrieben und dachte deshalb,
'#pragma config PBADEN = OFF' würde die Analog-Inputs auf PORTA (PORTB
wäre richtig gewesen) deaktivieren. Damit Digital Inputs auf PORTA
gehen, muss man ADCON1 = 0x0F setzen. Das habe ich jetzt in die Init
übernommen und meine lcd_read funktioniert jetzt.
Das auf sprut.de in einem Nebensatz erwähnte Phänomen, dass der KS006
das Busy-Flag zu früh entfernt, tritt bei mir tatsächlich auf. Wenn das
Flag 0 wird, muss ich trotzdem vor dem nächsten byte noch 20µs Pause
machen, damit die komplette Übertragung klappt. Habe ich die Pause nicht
drin, fehlen alle paar dutzend übertragene Bytes immer mal wieder
mehrere am Stück.
Ich werde jetzt noch ein paar String-Funktionen bauen und meine fertige
Library dann hier als Anhang hochladen. Timing-Probleme beim ansteuern
von LCD sind ja keine Seltenheit, vielleicht hilft dieser Thread hier
dann ein bisschen weiter.
Danke, Sebastian und Thomas.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
|