Mikrocontroller – Ein Leitfaden für Anfänger – Unser erstes LCD-Programm schreiben
Teilen
Mikrocontroller - Eine Einführung für Anfänger - Unser erstes LCD-Programm schreiben
Nachdem wir nun fast alles wissen, was wir über die Schnittstelle zwischen LCD und Mikrocontroller wissen müssen, können wir direkt mit der Programmierung beginnen. Die Reihenfolge, in der wir die Pins des LCD steuern müssen, kennen wir aus dem vorherigen Tutorial. Listen wir die Reihenfolge auf, damit wir verstehen, wie das Programm erstellt wird.
Wenn wir einen Befehl oder ein Zeichen an das LCD senden möchten, müssen wir zunächst prüfen, ob das LCD beschäftigt ist oder nicht. Wenn nicht, dann ist es in Ordnung, den Befehl oder das Zeichen zu senden. Werfen wir einen Blick auf die Abfolge der Anweisungen, die wir verwenden müssen, um den Busy- Status zu überprüfen. Erwägen Sie auch einen Blick auf das Datenblatt des LCD-Prozessors. Dort finden Sie alle wunderschönen Informationen und Spezifikationen, die Sie jemals über LCDs wissen wollten. Verloren? Zählen Sie auf mich, um Ihnen zu helfen, diese Informationen in diesen Tutorials zu entschlüsseln.
(1) Stellen Sie die Datenrichtung des Mikrocontroller-Ports auf Eingang ein. Nennen wir diesen DDR-Port
als DataDir
(2) Stellen Sie den mit dem LCD RW verbundenen Pin auf ON (Lesemodus)
(3) Stellen Sie den mit dem LCD RS verbundenen Pin auf OFF (Befehlsmodus)
(4) Schalten Sie den Enable ein, dann nach einer kleinen Verzögerung wieder aus.
(5) Lesen Sie den D7-Pin, um festzustellen, ob es eine 1 oder eine 0 ist.
(6) Führen Sie die Nummern 4 und 5 aus, bis der D7 auf 0 steht (was bedeutet, dass er nicht beschäftigt ist).
Die obigen Anweisungen würden in einer eigenen Unterroutine enthalten sein. Wir könnten sie so etwas wie Check_If_The_LCD_Is_Busy nennen. Es könnte so aussehen:
{
PORTD &= ~(1<<2); //Mr. LCDs Befehlsmodus einschalten (RS aus)
PORTD |= (1<<7); //Mr. LCD auf Lesen stellen (RW an)
while (PORTB >= 0x80); //D7-Pin ist eine "1" bei jeder Zahl über 0x80 (das ist Hex)
{
DDRB = 0xFF; //PortB als Ausgang einstellen
Dieser BlinkLight()-Befehl ist nur eine Routine, um den Enable ein- und wieder auszuschalten. Das LCD benötigt dies, um die erforderliche Aktion ausführen zu können. Es ist, als ob man Mr. LCD einen Tritt gibt, damit er seine Arbeit macht! Der Code könnte so aussehen:
{
asm volatile ("nop");
asm volatile ("nop");
PORTD &= ~(1<<5); //Enable ausschalten, damit Mr. LCD sich konzentrieren kann
Ok, hier ist ein kurzes Quiz... Können Sie mir sagen, mit welchem Pin der Enable am Mikrocontroller verbunden ist? Machen Sie sich vorerst keine Sorgen um den Port, nur um den Pin.
Hier sind einige neue Befehle: asm, volatile und "nop". Der "nop" ist eigentlich ein Befehl innerhalb der Assemblersprache (auch Assembler genannt). Dies ermöglicht es dem Mikrocontroller lediglich, einige Nanosekunden zu warten. Laut Datenblatt muss der Enable etwa 500 ns (Nanosekunden) eingeschaltet bleiben.
Wir haben nun genug Munition in unserem Arsenal, um den Code für das Senden eines Befehls oder eines Zeichens an das LCD-Display zu erstellen. Die beiden sind sich tatsächlich sehr ähnlich. Zuerst prüfen wir, ob das LCD nicht beschäftigt ist. Wir stellen den RS auf Aus, um einen Befehl zu senden, und auf Ein, um ein Zeichen zu senden. Der Port muss die Ausgangsrichtung haben und der Port muss dem entsprechenden Zeichen oder Befehl entsprechen. Dann wäre der RW Lese-/Schreibmodus für den Schreibmodus aus. Das LCD kann mit dem Enable (dem BlinkLight-Befehl) aufleuchten. Das LCD führt dann magischerweise die Aktion aus (zeigt das Zeichen an oder folgt Ihrer Anweisung - "Befehl"). So könnte dieser Code aussehen:
{
PORTB = command;
PORTD &= ~((1<<2)|(1<<2)); //RS (Befehlsmodus) und RW (Schreibmodus) ausschalten
BlinkLight();
DDRB = 0;
void SendCharacter(unsigned char character)
{
PORTB = character;
PORTD &= ~(1<<7); //RW (Schreibmodus) ausschalten
PORTD |= (1<<2); //RS (Zeichenanzeigemodus) einschalten
BlinkLight();
DDRB = 0;
Sie fragen sich jetzt, warum ich die Datenrichtung für den Port nicht einstelle. Nun, die Datenrichtung für Port B ist immer im Ausgangsmodus, es sei denn, es wird geprüft, ob das LCD beschäftigt ist. Innerhalb der Routine CheckIfBusy() stelle ich den Port zuerst auf Eingang und am Ende der Routine CheckIfBusy() wieder auf Ausgang.
Wäre es nicht eine Qual, wenn Sie die Positionen der Drähte vom LCD zum Mikrocontroller ändern müssten, wenn Sie sich zum Beispiel dazu entschließen, den Port zu wechseln? Das ist durchaus möglich, denn die Ports haben auch andere Funktionen. Sie müssten fast jede Codezeile durchgehen und die Pin- und Portspezifikationen ändern. Eine einfachere Möglichkeit wäre, diese Pins, Ports und sogar die Portrichtung am Anfang des Programms zuzuweisen. Dies geschieht mit der "#define"-Anweisung. Es erklärt sich im Wesentlichen selbst, wenn man das Programm betrachtet. Im Wesentlichen erstellen Sie einen Proxy (Proxy... ein böser Klon, aber mit einem anderen Namen... denken Sie darüber nach, im Jahr 2245 würden Sie Ihren Klon nicht mit demselben Namen nennen? Oder doch?) für diese Zahlen und Portbezeichnungen und füllen das Programm stattdessen mit diesen Proxys. Hier ist das Programm in diesem Stadium, das Befehle und Zeichen an das LCD senden kann.
#include
#define MrLCDsCrib PORTB
#define DataDir_MrLCDsCrib DDRB
#define MrLCDsControl PORTD
#define DataDir_MrLCDsControl DDRD
#define LightSwitch 5
#define ReadWrite 7
#define BiPolarMood 2
void Check_IF_MrLCD_isBusy(void);
void Peek_A_Boo(void);
void Send_A_Command(unsigned char command);
void Send_A_Character(unsigned char character);
void Send_A_String(char *string);
int main(void)
{
Send_A_Command(0x01); //Bildschirm löschen 0x01 = 00000001
_delay_ms(2);
Send_A_Command(0x38);
_delay_us(50);
Send_A_Command(0b00001110);
_delay_us(50);
Send_A_Character(0x4E); //N
Send_A_Character(0x65); //e
Send_A_Character(0x77); //w
Send_A_Character(0x62); //b
Send_A_Character(0x69); //i
Send_A_Character(0x65); //e
Send_A_Character(0x48); //H
Send_A_Character(0x61); //a
Send_A_Character(0x63); //c
Send_A_Character(0x6B); //k
Send_A_Character(0x2E); //.
Send_A_Character(0x63); //c
Send_A_Character(0x6F); //o
Send_A_Character(0x6D); //m
Send_A_String("Patrick");
while(1)
{
void Check_IF_MrLCD_isBusy()
{
MrLCDsControl |= 1<
while (MrLCDsCrib >= 0x80)
{
DataDir_MrLCDsCrib = 0xFF; //0xFF bedeutet 0b11111111
void Peek_A_Boo()
{
asm volatile ("nop");
MrLCDsControl &= ~1<
void Send_A_Command(unsigned char command)
{
MrLCDsCrib = command;
MrLCDsControl &= ~ ((1<
MrLCDsCrib = 0;
void Send_A_Character(unsigned char character)
{
MrLCDsCrib = character;
MrLCDsControl &= ~ (1<
MrLCDsCrib = 0;
Hier
ist das Ergebnis des obigen Programms:
Sie haben also tatsächlich bis zum Ende dieser Seite gescrollt und WOLLEN MEHR! Ich bin so begeistert und würde Sie auf mein nächstes Tutorial zum Hinzufügen von String-Funktionalität zu diesem LCD-Tutorial verweisen. Wenn Sie andererseits völlig verloren sind, können Sie zum vorherigen Tutorial gehen, um mehr über das Innenleben des LCDs zu erfahren, oder Sie können einfach ganz von vorne anfangen.