Ansteuern eines KS0066 Controllers
| Die Routinen sind in Assembler und C (Compiler C18 und XC8) vorhanden! Diese Dokumentation bezieht sich auf die Assembler Variante. Im Download-Bereich sind die Ansteuerungsroutinen aber auch in C vorhanden! |
Inhaltsverzeichnis |
Allgemein
Hier geht es um die Ansteuerung eines HD44780 kompatiblen LCD Displays. Um genau zu sein habe ich das EA DIPS082-HN gewählt. Hierbei handelt es sich um ein 2x8 Zeichen LCD von electronik Assembley. Bei Reichelt gibt es das Modul für ca. 15€ inklusive LED Hintergrundbeleuchtung. Das Display eignet sich hervorragend für kleinere Projekte. Ich habe das Display auch schon bei meinem Roboter eingesetzt um die Akkuspannung einzublenden.| Wem diese Art der Ansteuerung zuwider ist, der kann ja mal die Variante mit den C18-Libraries versuchen. Eine Beschreibung wie das funktioniert findet ihr hier. |
Programm Beschreibung
| Bitte beachten: Die Assembler Routinen werden nicht mehr gepflegt! |
Das Programm beginnt mit der Initialisierung des Displays. Wie auch bei einem Grafik LCD gibt es auch bei einem alpha Numerischen Display gewisse Steuerleitung. Die da wären RS, RW und die Enable Leitung. Zunächst einmal werden alle drei Steuerleitungen auf Low ( =0V ) gezogen um einen definierten Zustand der Steuerleitungen zu gewährleisten und eine Warteschleife durchlaufen. Dies ist wichtig da LCDs nach anlegen der Versorgungsspannung nicht sofort Einsatzbereit sind man muss eine Gewisse Zeit abwarten bis man sie ansprechen kann. Wie lange, das kann man im Datenblatt des Controllers nachlesen.
Steuerleitungen
| Enable | Wird benötigt um dem LCD zu signalisieren, dass sich Daten/Befehle auf der Steuerleitung befinden. |
| RS | Ist zum wählen zwischen Daten (1) und Befehlen(0). |
| R/W | Zum unterscheiden zwischen Lesen(1) und Schreiben (0) wobei die Methode des Lesens im wesentlichen kaum gebraucht wird. |
Die Initialisierung des LCD Displays
;•••••••••••••••••••••• Init_LCD ;•••••••••••••••••••••• Bcf LCD_E Bcf LCD_RS Bcf LCD_RW ;WARTEZEIT von 15ms movlw D'255' ;Schreibt (dezimal) 255 ins Arbeitsregister movwf Wartezeit ;Schreibt das Arbeitsregister nach “Wartezeit” call Warten ;Ruft das Unterprogramm “Warten” auf ;Es wird zunächst 3 mal das Display in den 8 Bit Modus versetzt ;um sicher zustellen, dass man ohne Komplikationen in den 4 Bit Modus ;übergehen kann. Dies ist notwendig, da man nie weiß in welchem Zustand ;sich das Display im Aufganblick befindet! ;Unbedingt angepasst werden müssen die Wartezeiten! ;#1 Movlw B'00110000' Movwf PORTB Call LCD_Enable ;WARTEZEIT von 5ms movlw D'255' ;Schreibt (dezimal) 255 ins Arbeitsregister movwf Wartezeit ;Schreibt das Arbeitsregister nach “Wartezeit” call Warten ;Ruft das Unterprogramm “Warten” auf ;#2 Movlw B'00110000' Movwf PORTB Call LCD_Enable ;WARTEZEIT von 1ms movlw D'255' ;Schreibt (dezimal) 255 ins Arbeitsregister movwf Wartezeit ;Schreibt das Arbeitsregister nach “Wartezeit” call Warten ;Ruft das Unterprogramm “Warten” auf ;3# Movlw B'00110000' Movwf PORTB Call LCD_Enable ;WARTEZEIT von 1ms movlw D'255' ;Schreibt (dezimal) 255 ins Arbeitsregister movwf Wartezeit ;Schreibt das Arbeitsregister nach “Wartezeit” call Warten ;Ruft das Unterprogramm “Warten” auf ;Jetzt kann das Display in den 4 Bit Modus versetzt werden Movlw B'00100000' Movwf PORTB Call LCD_Enable movlw D'255' ;Schreibt (dezimal) 255 ins Arbeitsregister movwf Wartezeit ;Schreibt das Arbeitsregister nach “Wartezeit” call Warten ;Ruft das Unterprogramm “Warten” auf Movlw 0x28 ; Functionset Call LCD_Command Movlw 0x0C ; Display on Call LCD_Command Movlw 0x01 ; Display clear Call LCD_Command Movlw 0x06 ; Entry Mode Call LCD_Command Return
Wie ich bereits oben erwähnt habe muss dem Display eine gewisse Vorlaufzeit gewährt werden bevor es angesprochen werden kann. Hierfür rufe ich eine kleine Warteschleife auf (call Warten). Im Anschluss wird auf den Datenport der Code für den 4 Bit Modus gelegt, da ich nur 4 der 8 Datenleitungen verwenden möchte. Dies spart IOs am PIC. Grundsätzlich lässt sich jedes LCD Display mit einem HD44780 Controller im 4 Bit Modus betreiben. Dies hat allerdings einen Hacken, wenn man das überhaupt so nennem will. Die Software wird ein klein wenig aufwendiger, da jetzt die Information in zwei Paketen übermittelt werden muss. Aber das ist kein Problem und ist schnell geschehen.
Damit der Controller jetzt weiß, dass sich die Informationen auf der Datenleitung befinden muss ihm ein "Strobbe" gesendet werden. Das heisst am Enable Pin des Displays muss ein Impuls erfolgen. Dann weiß der Controller, dass er die Daten vom Port einlesen kann.
Das Enable Strobbe
;•••••••••••••••••••••• LCD_Enable ;•••••••••••••••••••••• Bsf LCD_E movlw D'20' ;Schreibt (dezimal) 255 ins Arbeitsregister movwf Wartezeit ;Schreibt das Arbeitsregister nach “Wartezeit” call Warten ;Ruft das Unterprogramm “Warten” auf Bcf LCD_E movlw D'20' ;Schreibt (dezimal) 255 ins Arbeitsregister movwf Wartezeit ;Schreibt das Arbeitsregister nach “Wartezeit” call Warten ;Ruft das Unterprogramm “Warten” auf Return
Hier wird lediglich die Enable Leitung auf High ( =5V ) angehoben eine kurze Zeit gewartet und danach wieder auf Low ( =0V ) gezogen, danach wird wieder eine Warteschleife durchlaufen und zurück zum Ursprungsort gekehrt. Das Enable Unterprogramm ist dafür da, dem LCD Controller mitzuteilen, dass sich Steuer/Daten-Informationen auf dem ( 4 Bit ) Datenbus befinden und abgerufen werden sollen und dies geschieht durch einen so genannten "Enable Strobbe" einem Impuls gewisser Länge und dies ist natürlich auch wieder im Datenblatt nachzulesen. Nachdem mit dem Code dem Controller gesagt wurde, dass das Display im 4 Bit Modus arbeitet ( 4 Bit Modus heißt, dass nur 4 Leitungen zum Display führen ), werden noch die restlichen Konfigurationen für das Display durchgeführt. Es müssen folgende
Parameter geklärt/eingestellt werden:
- 8/4 Bit Datenlänge (=Anzahl der Zuleitungen)
- Anzahl der Zeilen des Displays (in unserem Fall 2)
- Ob der Cursor ein/aus und blinkend sein soll
- Cursor auto- Inkrement (Automatisch eine Stelle weiter gehen)
- Display ein/aus und löschen
Nachdem die restlichen Einstellungen (grüner Kasten) erledigt sind (siehe Init) ist das Display schon fertig initialisiert und man kann los legen es voll zu schreiben. Und etwas auf dem Display anzuzeigen ist wirklich total einfach, auch weil das Display schon einen Font Genrator integriert hat (Das bedeutet es sind bereits Buchstaben, Zahlen und Sonderzeichen vorgegeben und können einfach im ASCII Format übertragen werden). Und geschrieben wird am Ende so:
Movlw 'B' ;Schreibt das ASCII Zeichen 'B' in das W-Reg Call LCD_Output ;Ruft das UP 'LCD-Output' auf Movlw 'a' Call LCD_Output Movlw 't' Call LCD_Output Movlw 't' Call LCD_Output Movlw 'e' Call LCD_Output Movlw 'r' Call LCD_Output Movlw 'i' Call LCD_Output Movlw 'e' Call LCD_Output
Wenn man etwas auf dem Display anzeigen möchte wird das Unterprogramm - LCD_Output - aufgerufen. Das Zeichen, welches man darstellen möchte, wird dem Unterprogramm mit "übergeben" indem wir es vorher in das Arbeitsregister speichern. Also wird bei dem Beispiel oben auf dem Display geschrieben - Batterie.
Unterprogramm zur Ausgabe von Daten/Text
;•••••••••••••••••••••• LCD_Output ;•••••••••••••••••••••• Movwf LCD_Daten ;Die Eingangsinformation sichern Bcf LCD_E Bsf LCD_RS Bcf LCD_RW ;Warten um Busyabfrage zu umgehen movlw D'255' ;Schreibt (dezimal) 255 ins Arbeitsregister movwf Wartezeit ;Schreibt das Arbeitsregister nach “Wartezeit” call Warten ;Ruft das Unterprogramm “Warten” auf ;Obere Hälfte des Datenpakets übertragen Movfw LCD_Daten ;Die Eingangsdaten zurück holen Movwf PORTB ;Und auf PORTB ausgeben Call LCD_Enable ;Untere Hälfte des Datenpakets übertragen Swapf LCD_Daten,0 ;Die Daten umdrehen, da die unteren 4 Bit zuerst gesendet werden Movwf PORTB ;Und auf PORTB ausgeben Call LCD_Enable Return
Wir erinnern uns zurück, dass wir zuvor die Information (das Zeichen, welches dargestellt werden soll) in das Arbeitsregister gespeichert haben und das erste was in diesem UP gemacht wird ist, den Inhalt des W-Reg in die Variable "LCD_Daten" zu schreiben. Als nächstes werden die Steuerleitungen so eingestellt, dass wir Daten schreiben können. Anschließend wird eine Warteschleife eingeleitet (nur um nicht die Busyabfrage machen zu müssen). Wenn die Warteschleife komplett durchlaufen ist, können wir sicher sein, dass das LCD bereit ist um neue Informationen aufzunehmen und starten mit der Übertragung des 1. Paketes. Zunächst übertragen wir die obere Hälfte des Datenports (also RB7-4) und anschließend wird der Inhalt von "LCD_Daten" gedreht und die untere Hälfte übertragen. So haben wir es geschafft die gesamten 8 Bit mit nur 4 Leitungen zu übertragen! Wichtig ist, dass nach jedem Datenpaket (4 Bit) einmal ein Enable Strobbe durch geführt wird.
Das Unterprogramm zum Übertragen von Befehlen unterscheidet sich von dem für Daten nur in einem Punkt und ihr könnt Euch wahrscheinlich auch schon denken in welchem. Genau! Die Steuerleitung RS.
Unterprogramm zur Übertragung von Befehlen
;•••••••••••••••••••••• LCD_Command ;•••••••••••••••••••••• Movwf LCD_Daten ;Die Eingangsinformation sichern Bcf LCD_E Bcf LCD_RS Bcf LCD_RW ;Warten um Busyabfrage zu umgehen movlw D'255' ;Schreibt (dezimal) 255 ins Arbeitsregister movwf Wartezeit ;Schreibt das Arbeitsregister nach “Wartezeit” call Warten ;Ruft das Unterprogramm “Warten” auf ;Obere Hälfte des Datenpakets übertragen Movfw LCD_Daten ;Die Eingangsdaten zurück holen Movwf PORTB ;Und auf PORTB ausgeben Call LCD_Enable ;Untere Hälfte des Datenpakets übertragen Swapf LCD_Daten,0 ;Die Daten umdrehen, da die unteren 4 Bit zuerst gesendet werden Movwf PORTB ;Und auf PORTB ausgeben Call LCD_Enable Return
Busy Abfrage
Ich habe in diesem Programm auf die Busy Abfrage verzichtet und an Stelle dessen eine einfache Warteschleife eingebaut. Also was ist eigentlich diese "Busy Abfrage"? Eigentlich sagt es der Name schon: Man fragt beim Display (bzw. beim Controller) nach ob er derzeit beschäftigt (=busy) ist. Denn wir dürfen ihm nur neue Daten übermitteln, wenn er nicht busy ist. Die Abfrage läuft immer im selben Schema ab: Man setzt die Steuerleitungen für Lesen! und gibt einen Enable Strobbe aus. Jetzt schaltet man den Datenport um auf Eingang (Tris) und fragt eine der Datenleitungen ab (beim HD44780 DB7). Wechselt der Zustand auf "Bereit für Daten", kann fort gefahren werden. Nicht vergessen die Tris Bits wieder auf Ausgang zu stellen.
Wie komme ich in die 2. Zeile?
Ein LCD Display ist in Adressen unterteilt so kann man verschiedene Positionen erreichen. In der Regel stehen die Adressen im Datenblatt (halten Sie Ausschau nach DD-RAM).
So gelangen Sie in die 2. Zeile
Movlw 0xC0 Call LCD_Command
Der Wert ist deshalb 0xC0 und nicht 0x40, da bei dem Befehl zum ändern der DD-RAM Adresse das Bit 7 gesetzt sein muss!
DD-RAM Adressen beim 2*8 LCD
Display Position 0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 DDRAm Adresse 0x40 0x41 0x42 0x43 0x44 0x45 0x46 0x47
Die DD-RAM Adressen für andere Displaytypen entnehmen Sie bitte dem Datenblatt
Was tun, wenn es nicht funktioniert
Wenn Sie sich zu 100% sicher sind, dass sie die Programmierung richtig haben, dann kontrollieren sie noch einmal alle Leitungen vom PIC zum Display und gehen Sie dabei behutsam vor. Überprüfen Sie auch ob zwischen einzelnen Leitungen ein Kurzschluss besteht. Wenn auch das alles in Ordnung war, dann überprüfen Sie die Kontrastspannung des Displays. Wenn ihr trotz aller Bemühung nicht weiter kommt, dann könnt ihr euch gerne im Forum melden und werdet mit Sicherheit unterstützt.
Befehls- und ASCII Tabelle
Download ( ASM/C (C18 und XC8) )
Routinen in Assembler Download
Routinen in C (C18) Download
Verwendet ihr den XC8 Compiler, so nutzt bitte diesen Download:
Routinen in C Download
| Bitte beachten: In der aktuellen Verion (1.5.xc8) müssen im 4 Bit Modus die Datenleitungen auf den unteren 4 Bit des gewählten Ports des PIC liegen. Dadurch dürft ihr auch derzeit das Makro MSB_USED nicht setzten! In der nächsten Version wird dies automatisch umschaltbar sein! |
C Routinen (C18)
Datei KS0066_C18.c
| Bitte beachten: In der aktuellen Verion (1.5.c18) müssen im 4 Bit Modus die Datenleitungen auf den oberen 4 Bit des gewählten Ports des PIC liegen. Dadurch dürft ihr auch derzeit das Makro MSB_USED nicht löschen! In der nächsten Version wird dies automatisch umschaltbar sein! |
/* ****************************************************************************** File: KS0066_C18.c* Project: --* Author: Nicolas Meyertöns* Version: 1.5.c18* Web: www.PIC-Projekte.de* ****************************************************************************/#include "KS0066.h"#include <p18cxxx.h> void Init_LCD(void){ LCD_RS=LCD_RW=LCD_E=0; msDelay(15);/*1*/ LCD_DATA=0b00110000; // Auf 8 Bit setzten LCD_enable(); msDelay(5);/*2*/ LCD_DATA=0b00110000; // ... LCD_enable(); msDelay(1);/*3*/ LCD_DATA=0b00110000; // ... LCD_enable(); msDelay(1);#if MODE_4_BIT LCD_DATA=0b00100000; // Auf 4 Bit setzen LCD_enable(); msDelay(5);#endif LCD_write(0x28,0); // Functionset (2 Zeilien) LCD_write(0x0C,0); // Display on LCD_write(0x01,0); // Display clear msDelay(2); // 2ms Pause LCD_write(0x06,0); // Entry Mode msDelay(1);}/** Kommunikation mit dem Displaycontroller** Hinweis: Für die Datenübertragung werden aktuell* nur(!) die unteren vier Bit eines Ports* verwendet! (v.1.5.c18)** Parameter k: Tooglen zwischen Daten(k=1) und Befehlen(k=0) für das Display*/ void LCD_write(unsigned char Info, unsigned char k){ if (k==0) LCD_RS=0;else LCD_RS=1; LCD_RW=0; LCD_busy();#if MODE_4_BIT// MSB zuerst LCD_DATA= Info&0xF0; // Daten auf den Bus legen LCD_enable(); // Enable Strobbe durchführen// LSB LCD_DATA= (Info<<4)&0xF0; LCD_enable();#endif#if MODE_4_BIT == 0 LCD_DATA = Info; enableLCD();#endif}/** Ausführen eines Enable Impulses** Durch den Impuls auf der Enable Leitung übernimmt der LCD Controller die Daten,* welche zu diesem Zeitpunkt auf dem Bus liegen.*/ void LCD_enable(void){ LCD_E=1; Delay10TCYx(2); // Etwa 20µs warten LCD_E=0;}/** Warteschleife für ca. 1ms** Hinweis: Das Makro CLK_FREQ in der Datei KS0066_C18.h muss* richtig eingestellt sein, sonst werden die* Wartezeiten komplett falsch berechnet!* Parameter: time: Anzahl der ms die pausiert werden soll (max. 255)*/ void msDelay(unsigned char time){ unsigned int xWait = CLK_FREQ / 1000; while( xWait >= 10000){ Delay10KTCYx(1); xWait -= 10000;} while( xWait >= 1000){ Delay1KTCYx(1); xWait -= 1000;} while( xWait >= 100){ Delay100TCYx(1); xWait -= 100;} while( xWait >= 10){ Delay10TCYx(1); xWait -= 10;}}
Datei: KS0066_C18.h
| Das Makro CLK_FREQ muss von euch mit der von euch verwendeten Frequenz beschrieben werden (in Hz)! |
/* ****************************************************************************** File: KS0066_C18.h* Project: --* Author: Nicolas Meyertöns* Version: 1.5.c18* Web: www.PIC-Projekte.de* ****************************************************************************/#ifndef KS0066_H#define KS0066_H/** Hier müsst ihr die Anpassung, für die von euch gewählte Pinbelegung vornehmen!**/#define LCD_RS LATAbits.LATA2#define LCD_RW LATBbits.LATB4#define LCD_E LATAbits.LATA1#define LCD_Data LATC/** Notwendige Konfigurationen** Kurzbeschreibung der Parameter:** MODE_4_BIT: Die Initialisierung ist standardmäßig auf den* 4 Bit Modus eingestellt. Wenn ihr aber die* Ansteuerung im 8 Bit Modus verwendet möchtet,* dann müsst ihr dieses Makro löschen.** MSB_USED: Diese Einstellung ist nur notwendig, wenn ihr das* Display im 4 Bit Modus ansteuern möchtet.* Ihr müsst dann angeben ob ihr die Datenleitungen* auf den oberen oder unteren 4 Bit eines Port gelegt* habt. Liegen die Datenleitungen zum Beispiel so:** -> D7 --> PORTx7* -> D6 --> PORTx6* -> D5 --> PORTx5* -> D4 --> PORTx4** Dann müsst ihr das MSB_USED setzten!*/#define MODE_4_BIT 1#define MSB_USED 1/** Gebt an mit welchem Takt ihr den PIC betreibt (in Hz)*/#define CLK_FREQ 8000000 void Init_LCD(void); void LCD_write(unsigned char Info, unsigned char k); void LCD_enable(void); void msDelay(unsigned char time);#endif KS0066_H
Autoren
Nico 16:51, 19. Aug. 2011 (CEST)