www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik LCD (HD44780) timing extrem träge


Autor: Daniel R. (h3po)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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?
//read byte from register x
char lcd_read (char rs)
{
  char byte = 0;

  LCD_LATCH &= 0xF0;    //reset
  LCD_TRIS |= 0x0F;    //input

  LCD_RS = rs;
  LCD_RW = 1;
  Delay10TCYx(delay);    //rs,rw,data setup delay
  LCD_E = 1;
  Delay10TCYx(delay);    //data setup delay,

  byte = (LCD_PORT << 4) & 0xF0;  //read high nibble

  LCD_E = 0;
  Delay10TCYx(delay);    //delay between E cycles
  LCD_E = 1;
  Delay10TCYx(delay);    //data setup delay,
  
  byte |= (LCD_PORT & 0x0F);  //read masked low nibble  

  LCD_E = 0;
  LCD_RS = 0;
  LCD_RW = 0;
}

//return busy bit7 as 0 or 1
char lcd_busy (void)
{
  //could save some cycles by not shifting  
  return ((lcd_read(0) & 0x80) >> 7);
}

Autor: Thomas O. (kosmos)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Daniel R. (h3po)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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?

Autor: Sebastian Hepp (sebihepp)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
In deiner Funktion "char lcd_read(char)" fehlt das return!

Autor: Daniel R. (h3po)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
*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:
char lcd_busy (void)
{
        LCD_RW = 1;                     // Set the control bits for read
        LCD_RS = 0;
        Delay10TCYx(delay);
        LCD_E = 1;                      // Clock in the command
        Delay10TCYx(delay);
                                        // 4-bit interface
                                        // Lower nibble interface
        if(LCD_PORT&0x08)

        {
                LCD_E = 0;              // Reset clock line
                Delay10TCYx(delay);
                LCD_E = 1;              // Clock out other nibble
                Delay10TCYx(delay);
                LCD_E = 0;
                LCD_RW = 0;             // Reset control line
                beep();
                return 1;               // Return TRUE
        }
        else                            // Busy bit is low
        {
                LCD_E = 0;              // Reset clock line
                Delay10TCYx(delay);
                LCD_E = 1;              // Clock out other nibble
                Delay10TCYx(delay);
                LCD_E = 0;
                LCD_RW = 0;             // Reset control line
                Delay10TCYx(12);  //10µs
                return 0;               // Return FALSE
        }  
}

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.

Autor: Sebastian Hepp (sebihepp)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> *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

Autor: Sebastian Hepp (sebihepp)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Daniel R. (h3po)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
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:
char lcd_read (char rs)
{
  char byte = 0;

  LCD_LATCH &= 0xF0;      //reset
  LCD_TRIS |= 0x0F;      //input

  LCD_RS = rs;
  LCD_RW = 1;
  Delay10TCYx(delay);      //rs,rw,data setup delay
  LCD_E = 1;
  Delay10TCYx(delay);      //data setup delay,
  
  LCD_E = 0;
  byte = (LCD_PORT << 4);    //read high nibble

  Delay10TCYx(delay);      //delay between E cycles
  LCD_E = 1;
  Delay10TCYx(delay);      //data setup delay,
  
  LCD_E = 0;
  byte |= (LCD_PORT & 0x0F);  //read masked low nibble  

  LCD_RS = 0;
  LCD_RW = 0;

  return byte;
}

//return busy bit7 as 0 or 1
char lcd_busy (void)
{
  if (lcd_read(0) & 0x80)
  {
  beep();    //wenns piept, funzt der scheiss endlich!
  return 1;
  }
  Delay10TCYx(12);    //10µs für den fall, dass der ks006 sich zu früh freut
  return 0; 
}

Edit: Übrigens Danke trotzdem für deine Hilfe, Sebastian, und viel 
Erfolg mit deinem kommenden Projekt! Ich bastel hier noch alles auf nem 
Steckbrett :)

Autor: Sebastian Hepp (sebihepp)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Daniel R. (h3po)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.