T6963C

Ich möchte euch hier zeigen, wie ihr ganz einfach ein Grafik fähiges LCD Display in C ansteuern könnt. Wir fangen ganz locker mit den Grundlegenden Informationen an und gehen später über zu den Basis Funktionen wie der Initialisierung des Displays. Wenn wir die Basics hinter uns haben stelle ich noch die ein oder andere Zusatzfunktion vor z. B. das Zeichnen eines Rechtecks.

T6963c_big

Hier könnt Ihr euch das Datenblatt zum Displaycontroller als PDF herunterladen >> Datenblatt

Das Display könnt ihr >> hier (allerdings recht teuer) kaufen. Und >> hier gibt es eine günstigere Variante.

Grundlegendes

Zunächst muss Euch klar werden, dass dies kein einfaches HD44780 Text-Display ist. Hier haben wir es mit einem Grafik-LCD Display zu tun. Aber was bedeutet das jetzt eigentlich genau? Bei einem Textdisplay werden vom PIC zum Display einfache Zeichen im ASCII Code gesendet, welches das Display dann dementsprechend an die gewünschte Stelle darstellt. Zum Beispiel LCD_write(“Hallo”); hatte zur Folge, dass auf dem Display der Text sofort erschienen ist. Bei Grafikdisplays ist das in der Regel nicht mehr so einfach, es sei denn das Display hat einen eigenen Font-Generator an Board, dann können die Zeichen ähnlich gesendet werden. Bei dem Display mit dem wir uns hier beschäftigen ist das der Fall: Wir können sowohl Text darstellen (mit dem LCD eigenen Font-Generator) als auch Grafiken erstellen/zeichen indem wir jeden einzelnen Pixel “von Hand” setzten und/oder löschen. Und dann wären wir auch schon bei einem wichtigen Punkt: Der Speicher des Displays wird in zwei Bereiche aufgeteilt: Text- und Grafik-Bereich. Also wenn wir etwas auf das Display schreiben wollen, dann schreiben wir die Daten (Pixel) in den Speicher des Displays und der T6963c holt diese Daten selbstständig aus dem Speicher und schreibt sie aufs Display. Wichtig dabei ist zu wissen, dass wenn, angenommen, der Textspeicher voll geschrieben ist und somit ja auch das Display voll geschrieben ist, man trotzdem noch Grafiken anzeigen kann, denn diese werden dann übereinander dargestellt. Das ist finde ich ein super Feature des Displays, man kann so den Font des Display und den Grafikmodus gleichzeitig verwenden! Außerdem werden bei diesem Display anders als bei anderen Controllern Reihenweise Bytes geschrieben. Das bedeutet, wenn ihr 0xAA in den Speicher (Grafikbereich) des Displays sendet wird das Ergebnis so aussehen:

T6963C_Byte_01

Und nicht so (Spaltenweise) wie bei vielen anderen Controllern:

T6963C_Byte_02

Anschluss des Displays

T6963C_PIN

Anmerkung: Ich habe die Anschlusspins MD2 und FS über Jumper geschaltet. MD2 ist an 5V gelegt und entspricht damit einer Einstellung von 32 Spalten und FS liegt über einem Pull-down Widerstand auf 0V und sagt damit aus, dass ein Zeichen (ASCII) 8*8 Pixel groß ist. Wer nachgerechnet hat kommt bei 240 Pixeln Breite des Displays und 8 Pixeln auf 30 Spalten. Warum im Datenblatt des Displays aber 32 Spalten stehen weiß ich nicht. Auf jeden Fall sind es 30 sichtbare Spalten. Die Größe von 6*8 Pixeln für ein Font ist hinterher etwas problematisch wenn man gleichzeitig den Grafikmodus verwenden möchte.

Beschreibung der Routinen

Im Forum kam die Frage auf woher eigentlich das LCD_Bus käme. Das ist ein Makro und ich verwende es um den Quelltext leserlicher zu gestalten. Die Makros findet Ihr in den Headerdateien. Zum Beitrag im Forum >> Klick

Initialisierung

Wie üblich starten wir mit der Initialisierung des Displays:

void GLCD_Initialisierung(void)
{
  LCD_RST = 0;
  Delay10TCYx(10);
  LCD_RST = 1;

  LCD_CE = 1;

  // 0x0000 = 0
  GLCD_Daten (0x00); // Grapic home address (low)
  GLCD_Daten (0x00); // ...(high)
  GLCD_Befehl (0x42);

  GLCD_Daten (0x1E); // Graphic area set (low)
  GLCD_Daten (0x00); // ...(high)
  GLCD_Befehl (0x43);

  // 0x1700 = 5888
  GLCD_Daten (0x00); // Text home adress (low)
  GLCD_Daten (0x17); // ...(high)
  GLCD_Befehl (0x40);

  GLCD_Daten (0x1E); // Text area set (low)
  GLCD_Daten (0x00); // ...(high)
  GLCD_Befehl (0x41);

  // Weiteres
  GLCD_Befehl (0x80); // Mode set (OR)
  GLCD_Befehl (0x9C); // Display mode (Grafik und Text)

  Speicher_Loeschen(); // Den gesamten Speicher löschen
}

Zunächst wird ein Reset ausgeführt (Zeilen 16-18) indem die Reset-Leitung auf Low Level gezogen, eine Zeit lang gewartet und anschließend wieder auf High Level (5V) gezogen wird. Durch das aktivieren der CE-Leitung (Chip Select) sprechen wir nun den Controller des LCDs an. Ab jetzt beginnt die Kommunikation mit dem Controller. Wir müssen nun diverse Parameter einstellen, damit wir mit dem Display arbeiten können. Wie ich breits unter Grundlegendes beschrieben habe hat das Display einen Speicher in dem der Inhalt, welcher dann vom Controller selbstständig auf dem Display angezeigt wird, zwischen gespeichert wird. Diesen Speicher müssen wir nun in zwei Bereiche aufteilen: Grafik und Text. In den Kommentaren habe ich geschrieben über welchen Bereich des Speichers sich die einzelnen Bereiche ziehen. Die Home-Adresse gibt an wo der Bereich beginnt: Grafikbereich ab 0x0000 und Textbereich ab 0x1700. Es wird erst der Low-Teil und dann der High-Teil der Adresse übertragen – Text Home: Erst 0x00 dann 0x17 = 0x1700. Durch den Befehl Area-Set wird angegeben über welchen Bereich (Area) sich der jeweilige Bereich erstreckt. Nachdem diese Einstellungen fertig sind wird noch der gewünschte Mode gewählt (siehe Datenblatt) und, dass wir Grafik und Text benutzen möchten. Wenn man jetzt mit der Initialisierung aufhören würde, hätte man das Display voll mit Müll. Da im Speicher irgendwas steht. Daher löschen wir nun noch den gesamten Speicher des Displays (Grafik, Text und Rest) (Löschen = Auffüllen mit 0en). Jetzt ist das Display schon fertig initialisiert und kann verwendet werden.

Senden von Daten/Befehlen

void GLCD_Befehl (unsigned char Befehl)
{
   Busy_Abfrage();

   LCD_Bus = Befehl;
   LCD_CD = 1;
   LCD_WR = 0;
   LCD_RD = 1;
   LCD_CE = 0;
   Delay1TCY();
   LCD_CE = 1;
}

Beim T6963C ist es üblich erst die Daten und dann den zugehörigen Befehl zu senden. Blicken wir nochmal kurz zurück zur Init, dann sehen wir, dass zum Beispiel für den Grafik-Home Bereich erst die Daten übertragen werden und anschließend der Befehl. Außerdem muss vor jedem Übertragen von Daten/Befehlen immer der Status überprüft werden! Das passiert hier in Zeile 197. Danach wird angefangen mit der Übertragung: In der Zeile 199 wird der Befehl auf die Datenleitungen gelegt anschließend werden die Steuerleitungen eingestellt LCD_CD=1 bedeutet, dass es sich um einen Befehl handelt, LCD_WR=0 und LCD_RD=1 bedeutet, dass auf das LCD geschrieben wird und mit LCD_CE wird der “Enable-Strobbe” ausgeführt und der Controller weiß nun, dass er die Datenleitung auslesen kann. Der einzigste Unterschied zur Funktion zum Übertragen von Daten besteht darin, dass LCD_CD bei der Datenübertragung 0 ist.

Abfrage des Busyflags

void Busy_Abfrage(void)
{
   unsigned char Byte;
   TRISD=0xFF;

   LCD_CD=1;
   LCD_WR=1;
   LCD_RD=0;

   do
   {
      LCD_CE=0;
      Delay1TCY();
      Byte = PORTD&0x03;
      LCD_CE=1;
   } while(Byte!=0x03);

   LCD_RD=1;
   TRISD=0x00;
}

Ich denke hier muss nicht viel erklärt werden. Da nun Daten vom LCD gelesen werden sollen muss der Datenport auf Eingang gestellt werden, was mit den TRIS Bits realisiert wird. Dann werden die Steuerleitungen eingestellt auf Daten lesen. Jetzt werden so lange die beiden Statusbits STA0 und STA1 (DB0 und DB1) geprüft bis sie aussagen, dass der Controller wieder bereit ist für neue Daten. Danach nich vergessen den Datenport wieder auf Ausgang zu stellen. Das wars!

Kommentar eines Lesers

Ich habe bezüglich der Busyflag-Abfrage eine E-Mail erhalten mit folgendem Inhalt:

Deine Version [..] (Abfrage des Busyflags):

do
{
   LCD_CE=0;
   Delay1TCY();
   Byte = PORTD&0x03;
   LCD_CE=1;
} while(Byte!=0x03);

Das funktioniert natürlich, ist aber nicht nötig, da beim Status-Read die Status-Bits offensichtlich *nicht* extra gelatcht werden, sondern “hart” auf den Bus gelegt.

Etwas schneller geht’s deswegen so:

LCD_CE=0;
while( !(PORTD&0x03) );
LCD_CE=1;

Text schreiben

void GLCD_Schreiben (unsigned char Zeichen)
{
   unsigned char ASCII;

   ASCII = Zeichen-0x20;
   GLCD_Daten(ASCII);
   GLCD_Befehl(0xC0);
}

void GLCD_Zeichenkette (unsigned char Zeile, unsigned char Spalte, const rom char *Text)
{
   GLCD_Position (Zeile,Spalte);

   while (*Text)
   {
      GLCD_Schreiben(*Text);
      Text++;
   }
}

Mit diesen beiden Funktionen habt ihr die Möglichkeit komfortabel Text auf dem Display auszugeben. Ihr könnt nun einfach mit folgendem Befehl einen Text auf dem Display ausgeben:

void GLCD_Schreiben (unsigned char Zeichen)
{
   unsigned char ASCII;

   ASCII = Zeichen-0x20;
   GLCD_Daten(ASCII);
   GLCD_Befehl(0xC0);
}

void GLCD_Zeichenkette (unsigned char Zeile, unsigned char Spalte, const rom char *Text)
{
   GLCD_Position (Zeile,Spalte);

   while (*Text)
   {
      GLCD_Schreiben(*Text);
      Text++;
   }
}

Die Funktion GLCD_Zeichenkette nimmt nun den Übergebenen String auf (Zeichenkette) und gibt solange das an der Stelle vorhandenes Zeichen an die Funktion GLCD_Schreiben weiter bis der Pointer die 0 erreicht (Endmarkierung). Wichtig ist, dass in der Funktion GLCD_Schreiben 0x20 vom ASCII Zeichen abgezogen werden (Zeile 56), da der Font-Generator nicht ASCII konform ist. Aber wenn man die besagten 0x20 vom ASCII Zeichen abzieht wird alles richtig dargestellt.

An eine bestimmte Postion springen

void GLCD_Schreiben (unsigned char Zeichen)
{
 unsigned char ASCII;

 ASCII = Zeichen-0x20;
 GLCD_Daten(ASCII);
 GLCD_Befehl(0xC0);
}

void GLCD_Zeichenkette (unsigned char Zeile, unsigned char Spalte, const rom char *Text)
{
   GLCD_Position (Zeile,Spalte);

   while (*Text)
   {
     GLCD_Schreiben(*Text);
     Text++;
   }
}

Mit dieser Funktion kann an eine beliebige Stelle im Textspeicher gesprungen werden. Mit den übergebenen Werten aus GLCD_Zeichenkette(Zeile,Spalte,..) wird nun an die gewünschte Adresse im Textspeicher gesprungen damit der Buchstabe/Ziffer/… auch an dem Platz angezeigt wird wie gewollt. Das ist aber bloß Mathematik und sollte mit ein bisschen Gehrinschmalz von jedem nachvollzogen werden können. Wenn ihr aber Schwierigkeiten habt, dann könnt ihr euch im Forum melden.

Ein Pixel setzten/löschen

void GLCD_Pixel(unsigned int Y, unsigned int X, unsigned char Show)
{
    unsigned int ADR; // Zur Berechnung der RAM Adresse
    unsigned char Bit;
    unsigned char Puffer;

    ADR = 0; // Die Grafik Home Adresse

    //Wenn der adressierte Bereich außerhalb des Displays liegt, dann springe sofort zurück!
    if ( (X>240) | (Y>128) | (X==0) | (Y==0) )
    {
       return;
    }

    //Umrechnen der übermittelten Werte in die RAM Adresse
    ADR = ((Y-1)*30)+((X-1)/8);

    Speicher_Adresse(ADR);
    //Nachfragen ob das Display fertig ist
    Busy_Abfrage();

    //Errechnen welches Bit gesetzt werden soll
    Puffer=(X-1)%8;
    Bit=7-Puffer;

    //Übertragen/Ausführen des Befehls
    if(Show)
    {
       GLCD_Befehl (0xF8+Bit);
    }
    else
    {
       GLCD_Befehl (0xF0+Bit);
    }
}

Mit dieser Funktion kann ein Pixel gesetzt oder gelöscht werden. Man übergibt der Funktion folgende Werte: X, Y und Show. Die X und Y Werte sind die Angabe welches Pixel (240×128) und die Variable Show sagt ob ein Pixel gesetzt wird oder gelöscht. Im wesentlichen ist diese Funktion auch nur Mathematik. Wenn man diese Funktion hat ist alles weitere meist nur noch auf diese Funktion aufgebaut. Auch wenn manch einer sagen mag, dass es nicht gut ist alles mit Pixel setzten zu machen, finde ich es am einfachsten und hatte damit nie Probleme. Wenn man nun z.B. ein Rechteck zeichnen will ist es denkbar einfach, da man einfach auf die Pixel setzten Funktion zurück greifen kann.

Eine Linie zeichnen

void GLCD_Linie_S (unsigned char Y1, unsigned char Y2, unsigned char X, unsigned char Show)
{
   unsigned char i;

   if (Show)
   {
      for (i=0;i<(Y2-(Y1-1));i++)
      {
         GLCD_Pixel(Y1+i,X,Setzen);
      }
   }

   else
   {
      for (i=0;i<(Y2-(Y1-1));i++)
      {
         GLCD_Pixel(Y1+i,X,Loeschen);
      }
   }
}

Wie ich eben schon beschrieben habe bauen diese Funktionen auf der Pixel-setzten Funktion auf. Mit etwas C lässt sich so zum Beispiel einfach eine Linie zeichnen. Diese Funktion zeichnet eine senkrechte Linie.

Folgende Funktionen sind im Download enthalten

Funktion Beschreibung
GLCD_Initialisierung Initialisiert das Display
GLCD_Schreiben Überträgt ein Zeichen (ASCII-0x20) auf das LCD
GLCD_Zeichenkette Schreibt eine Zeichenkette z.B.: (“Hallo Welt”)
Speicher_Loeschen Löscht den gesamten Speicher
Grafik_Speicher_Loeschen Löscht nur den Grafik-Speicher
Text_Speicher_Loeschen Löscht nur den Text-Speicher
Speicher_Adresse Springt zu einer Adresse (Grafik Bereich)
GLCD_Position Springt zu einer Adresse (Text-Bereich)
Busy_Abfrage Prüft ob das Display bereit ist
GLCD_Befehl Sendet einen Befehl
GLCD_Daten Sendet Daten
GLCD_Pixel Setzt/Löscht ein Pixel
GLCD_Rechteck Zeichnet ein Rechteck (Rahmen)
GLCD_Rechteck_Voll Zeichnet ein gefülltes Rechteck
GLCD_Linie_S Zeichnet eine senkrechte Linie
GLCD_Linie_W Zeichnet eine waagerechte Linie
GLCD_Radieren Löscht einen bestimmten Bereich (Grafik-Bereich)

Download

Wie gewohnt könnt Ihr die Routinen hier auch herunterladen. Die Routinen sind als Archiv gebunden >> Download 

Leave a Reply

Your email address will not be published. Required fields are marked *