www.mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik HD44780 kompatibles Display - Initialisierung klappt nicht


Autor: Jörg (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich bin ein lernwilliger Anfänger, was die Programmierung und die 
AVR-Mikrocontroller angeht.
Ich programmiere in C mit dem GCC Plugin für das AVR-Studio.
Als Lernprojekt habe ich mir vorgenommen ein Text LCD-Display 
anzusteuern.

Leider klappt bei mir nicht einmal die Initialisierung.
Da ich schon seit dem Wochenende viel versucht habe (ich habe auch die 
Suchfunktion benutzt), wende ich mich nun an euch.

Meine Hardware:
- Der AVR: ATMEGA16, interner Quarz 8Mhz
- Das Display: 4*20 Zeichen (Die Bezeichnung des LCD-Displays lautet: 
YM-2004A Series)

Durch meine Recherche habe ich schon rausgefunden, dass das Display 
entweder den Controller KS0066 oder den SPLC780 nutzt und diese 
eventuell anders initialisiert werden.
Im Anhang ist einmal das kleine Datenblatt des Displays(welches ich beim 
Kauf dabei bekommen habe).
Außerdem habe ich ein großes Datenblatt und ein Datenblatt für den 
KS0066, welche ich gegoogelt habe angehängt.

Fehler:
Sobald das LCD-Diplay Strom bekommt sehe ich in der 1.und der 3. Zeile 
Balken. Sonst passiert nichts - ich nehme stark an, dass die Balken nach 
der Initialisierung verschwinden sollten.

Mein Programm:

Ich bin bei der Initialisierung nach dem Datenblatt des KS0066 
vorgegangen (die Initialisierung vom HD44780 habe ich aber auch schon 
versucht).
#ifndef F_CPU
#define F_CPU 8000000UL 
#endif

#include <avr/io.h>
#include <util/delay.h>

#define RS     PD4
#define RW     PD5
#define DB7    PD3
#define DB6    PD2
#define DB5    PD1
#define DB4    PD0
#define E      PD6

void lcd_init( void );

// ***** Hauptprogramm *****
int main(void){

  lcd_init ();                                 // Display initialisieren    
                                               
while();                                       // Endlosschleife

}

void lcd_init( void )
{

//************  power on  *********************
//********  wait more than 30ms  **************
_delay_ms (50); 

//*************  Function set  ****************
// LOW
PORTD &= ~(1<<RS)  // RS LOW    
    |(1<<RW)  // RW LOW
    |(1<<DB7)  // DB7 LOW
    |(1<<DB6)  // DB6 LOW
          // DB5 HIGH
    |(1<<DB4);  // DB4 LOW
// HIGH
PORTD |= (1<<DB5);
//--------------------------------------------
// LOW
PORTD &= ~(1<<RS)  // RS LOW    
    |(1<<RW)  // RW LOW
    |(1<<DB7)  // DB7 LOW
    |(1<<DB6)  // DB6 LOW
          // DB5 HIGH
    |(1<<DB4);  // DB4 LOW
// HIGH
PORTD |= (1<<DB5);
//--------------------------------------------
// LOW
PORTD &= ~(1<<RS)  // RS LOW    
    |(1<<RW)  // RW LOW
          // DB7 HIGH
          // DB6 HIGH
          // DB5 HIGH
    |(1<<DB4);  // DB4 HIGH
// HIGH
PORTD |= (1<<DB7)
    |(1<<DB6)
    |(1<<DB5);  

//*********** wait more than 39us  **************
_delay_us (50);

//********  Display ON/OFF Control  *************
// LOW
PORTD &= ~(1<<RS)  // RS LOW    
    |(1<<RW)  // RW LOW
    |(1<<DB7)  // DB7 LOW
    |(1<<DB6)  // DB6 LOW
    |(1<<DB5)  // DB5 LOW
    |(1<<DB4);  // DB4 LOW
//--------------------------------------------
// LOW
PORTD &= ~(1<<RS)  // RS LOW    
    |(1<<RW);  // RW LOW
          // DB7 HIGH
          // DB6 HIGH
          // DB5 HIGH
          // DB4 HIGH
// HIGH
PORTD |= (1<<DB7)
    |(1<<DB6)
    |(1<<DB5)
    |(1<<DB4);

//********** wait more than 39us  ****************
_delay_us (50);

//******************  Display Clear **************
// LOW
PORTD &= ~(1<<RS)  // RS LOW    
    |(1<<RW)  // RW LOW
    |(1<<DB7)  // DB7 LOW
    |(1<<DB6)  // DB6 LOW
    |(1<<DB5)  // DB5 LOW
    |(1<<DB4);  // DB4 LOW
//--------------------------------------------
// LOW
PORTD &= ~(1<<RS)  // RS LOW    
    |(1<<RW)  // RW LOW
    |(1<<DB7)  // DB7 LOW
    |(1<<DB6)  // DB6 LOW
    |(1<<DB5);  // DB5 LOW
          // DB4 High
// HIGH
PORTD |= (1<<DB4);

//********* wait more than 1,53ms  ****************
_delay_ms (5);

//************  Entry Mode Set ********************
// LOW
PORTD &= ~(1<<RS)  // RS LOW    
    |(1<<RW)  // RW LOW
    |(1<<DB7)  // DB7 LOW
    |(1<<DB6)  // DB6 LOW
    |(1<<DB5)  // DB5 LOW
    |(1<<DB4);  // DB4 LOW
//--------------------------------------------
// LOW
PORTD &= ~(1<<RS)  // RS LOW    
    |(1<<RW)  // RW LOW
    |(1<<DB7)  // DB7 LOW
          // DB6 HIGH
          // DB5 High
    |(1<<DB4);  // DB4 LOW
// HIGH
PORTD |= (1<<DB6)
    |(1<<DB5);

//*************Initializstion end*******************

}

Vielen Dank für eure Hilfe!

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
// LOW
>PORTD &= ~(1<<RS)  // RS LOW
>    |(1<<RW)  // RW LOW
>    |(1<<DB7)  // DB7 LOW
>    |(1<<DB6)  // DB6 LOW
>          // DB5 HIGH
>    |(1<<DB4);  // DB4 LOW

Das Ergebnis dieses Befehls ist:

// RS LOW
// RW High
// DB7 High
// DB6 High
// DB5 Low
// DB4 High

Autor: Jörg (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Danke für den Hinweis.

Ich hatte versucht den Code zu kürzen um die Lesbarkeit zu verbessern.

Ich habe allerdings viele Versionen ausprobiert. Die Folgende 
funktioniert auch nicht:
#ifndef F_CPU
#define F_CPU 8000000UL 
#endif

#include <avr/io.h>
#include <util/delay.h>

#define RS    PD4
#define RW    PD5
#define DB7    PD3
#define DB6    PD2
#define DB5    PD1
#define DB4    PD0
#define E    PD6

void lcd_init( void );


// ***** Hauptprogramm *****
int main(void){

  _delay_ms (15);                               // wait min 15 mSec. after Power ON
  lcd_init ();     
                                               // Display initialisieren    
  for (;;);

}

void lcd_init( void )
{

//************  power on  *********************

//********  wait more than 30ms  **************
_delay_ms (50); 

//*************  Function set  ****************
// LOW
PORTD &= ~(1<<RS);  // RS LOW    
PORTD &= ~(1<<RW);  // RW LOW
PORTD &= ~(1<<DB7);  // DB7 LOW
PORTD &= ~(1<<DB6);  // DB6 LOW
          // DB5 HIGH
PORTD &= ~(1<<DB4);  // DB4 LOW
// HIGH
PORTD |= (1<<DB5);
//--------------------------------------------
// LOW
PORTD &= ~(1<<RS);  // RS LOW    
PORTD &= ~(1<<RW);  // RW LOW
PORTD &= ~(1<<DB7);  // DB7 LOW
PORTD &= ~(1<<DB6);  // DB6 LOW
          // DB5 HIGH
PORTD &= ~(1<<DB4);  // DB4 LOW
// HIGH
PORTD |= (1<<DB5);
//--------------------------------------------
// LOW
PORTD &= ~(1<<RS);  // RS LOW    
PORTD &= ~(1<<RW);  // RW LOW
          // DB7 HIGH
          // DB6 HIGH
          // DB5 HIGH
PORTD &= ~(1<<DB4);  // DB4 HIGH
// HIGH
PORTD |= (1<<DB7);
PORTD |= (1<<DB6);
PORTD |= (1<<DB5);  

//*********** wait more than 39us  **************
_delay_us (50);

//********  Display ON/OFF Control  *************
// LOW
PORTD &= ~(1<<RS);  // RS LOW    
PORTD &= ~(1<<RW);  // RW LOW
PORTD &= ~(1<<DB7);  // DB7 LOW
PORTD &= ~(1<<DB6);  // DB6 LOW
PORTD &= ~(1<<DB5);  // DB5 LOW
PORTD &= ~(1<<DB4);  // DB4 LOW
//--------------------------------------------
// LOW
PORTD &= ~(1<<RS);  // RS LOW    
PORTD &= ~(1<<RW);  // RW LOW
          // DB7 HIGH
          // DB6 HIGH
          // DB5 HIGH
          // DB4 HIGH
// HIGH
PORTD |= (1<<DB7);
PORTD |= (1<<DB6);
PORTD |= (1<<DB5);
PORTD |= (1<<DB4);

//********** wait more than 39us  ****************
_delay_us (50);

//******************  Display Clear **************
// LOW
PORTD &= ~(1<<RS);  // RS LOW    
PORTD &= ~(1<<RW);  // RW LOW
PORTD &= ~(1<<DB7);  // DB7 LOW
PORTD &= ~(1<<DB6);  // DB6 LOW
PORTD &= ~(1<<DB5);  // DB5 LOW
PORTD &= ~(1<<DB4);  // DB4 LOW
//--------------------------------------------
// LOW
PORTD &= ~(1<<RS);  // RS LOW    
PORTD &= ~(1<<RW);  // RW LOW
PORTD &= ~(1<<DB7);  // DB7 LOW
PORTD &= ~(1<<DB6);  // DB6 LOW
PORTD &= ~(1<<DB5);  // DB5 LOW
          // DB4 High
// HIGH
PORTD |= (1<<DB4);

//********* wait more than 1,53ms  ****************
_delay_ms (5);

//************  Entry Mode Set ********************
// LOW
PORTD &= ~(1<<RS);  // RS LOW    
PORTD &= ~(1<<RW);  // RW LOW
PORTD &= ~(1<<DB7);  // DB7 LOW
PORTD &= ~(1<<DB6);  // DB6 LOW
PORTD &= ~(1<<DB5);  // DB5 LOW
PORTD &= ~(1<<DB4);  // DB4 LOW
//--------------------------------------------
// LOW
PORTD &= ~(1<<RS);  // RS LOW    
PORTD &= ~(1<<RW);  // RW LOW
PORTD &= ~(1<<DB7);  // DB7 LOW
          // DB6 HIGH
          // DB5 High
PORTD &= ~(1<<DB4);  // DB4 LOW
// HIGH
PORTD |= (1<<DB6);
PORTD |= (1<<DB5);

//*************Initializstion end*******************

}
  



Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Weisst du was mich an deinem Code stört, außer das die Timings
teilweise fehlen? RW wird angesteuert (was man gar nicht braucht
wenn man RW auf Masse legt), Enable aber nicht.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg schrieb:

> Ich habe allerdings viele Versionen ausprobiert. Die Folgende
> funktioniert auch nicht:


Meiner Meinung nach schneidest du dich mit so einem Schei.... nur ins 
eigene Fleisch.



> void lcd_init( void )
> {
>
> //************  power on  *********************
>
> //********  wait more than 30ms  **************
> _delay_ms (50);
>
> //*************  Function set  ****************
> // LOW
> PORTD &= ~(1<<RS);  // RS LOW
> PORTD &= ~(1<<RW);  // RW LOW
> PORTD &= ~(1<<DB7);  // DB7 LOW
> PORTD &= ~(1<<DB6);  // DB6 LOW
>           // DB5 HIGH
> PORTD &= ~(1<<DB4);  // DB4 LOW
> // HIGH
> PORTD |= (1<<DB5);
> //--------------------------------------------
> // LOW
> PORTD &= ~(1<<RS);  // RS LOW
> PORTD &= ~(1<<RW);  // RW LOW
> PORTD &= ~(1<<DB7);  // DB7 LOW
> PORTD &= ~(1<<DB6);  // DB6 LOW
>           // DB5 HIGH
> PORTD &= ~(1<<DB4);  // DB4 LOW
> // HIGH
> PORTD |= (1<<DB5);

usw. usw.

du musst jeden einzelnen Pin über 25 Statements verfolgen um zu wissen, 
wann genau der jeweilige Pin auf 0 bzw auf 1 ist.

Mir ist schon klar, dass das Ziel darin besteht, die Initialisierung 
über die Bühne zu bringen, ohne das die anderen Pins verändert werden.

Aber leg fürs erste nicht zuviel Gewicht auf dieses Ziel! Denn dadurch 
verlierst du es erst mal aus den Augen.

Fürs erste ist es völlig ausreichend, wenn du die Initalisierung so 
machst, dass du an den Port D zu Beginn jeden neues 
Initialisierungsschrittes das anzulegende Bitmuster komplett anlegst. 
Also einfach eine Zuweisung an PORTD. Dann hinen nach noch ein Toggeln 
mit dem E-Pin und das wars.

Mach dir die Sache nicht unnötigerweise zu komplex. Zumindest nicht am 
Anfang.

Kein Mensch stört sich bei einem Anfänger bei seinen ersten Schritten 
daran, wenn du anstelle von ...
//********  wait more than 30ms  **************
_delay_ms (50); 

//*************  Function set  ****************
// LOW
PORTD &= ~(1<<RS);  // RS LOW    
PORTD &= ~(1<<RW);  // RW LOW
PORTD &= ~(1<<DB7);  // DB7 LOW
PORTD &= ~(1<<DB6);  // DB6 LOW
          // DB5 HIGH
PORTD &= ~(1<<DB4);  // DB4 LOW
// HIGH
PORTD |= (1<<DB5);


... das so machst:
//********  wait more than 30ms  **************
  _delay_ms (50); 

  PORTD = (1<<DB5);


Weil ichs gerade sehe: Wo wedelst du eigentlich mit dem Enable-Pin?

Autor: Jörg (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich denke, dass ich das auch noch nicht ganz verstanden habe.

Zu RW:
Ich habe RW an einen PORT-Pin angeschlossen, daher setzte ich diesen 
immmer auf LOW.

Zu E:
Wann muss ich E denn ansteuern?
Im Datenblatt konnte ich in dem Flussdiagramm nicht finden, dass E 
angesteuert wird. Daher steuere ich E auch nicht an.


Danke für deine Hilfe.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg schrieb:

> Im Datenblatt konnte ich in dem Flussdiagramm nicht finden, dass E
> angesteuert wird.

Im Datenblatt gibt es sicherlich ein Diagramm, welches die elektrischen 
Signale zeigt. Und dort taucht E auf

> Daher steuere ich E auch nicht an.

> Wann muss ich E denn ansteuern?

Immer dann, wenn der Zustand deiner Port-Bits fertig eingestellt ist.
Das LCD kann ja nicht hellsehen, dass dein Code jetzt mit Manipulation 
der Port-Bits fertig ist und es daher die anliegenden Signale übernehmen 
soll.

Ich weiß, der Name "Enable" ist etwas schlecht gewählt. Aber im Grunde 
ist das nichts anderes als die Leitung: "Alle Datenbits sind fertig 
eingestellt, R/W hab ich eingestellt, Command/Data hab ich eingestellt; 
jetzt bist du drann - mach was damit!"

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>Wann muss ich E denn ansteuern?
>Im Datenblatt konnte ich in dem Flussdiagramm nicht finden, dass E
>angesteuert wird.

Mittleres Datenblatt Seite 4 und 5. Man muss schon
ziemlich blind sein um das zu übersehen.

Autor: Jörg (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das auf Seite 4 und 5 im 2. Datenblatt hatte ich schon gesehen, 
allerdings wusste ich nicht, dass ich auch bei der Initialisierung diese 
Grafik beachten muss.

Ich dachte dies gilt nur, wenn ich nach der Initialisierung Daten in das 
Display schreibe.

Nun weiss ich es besser. Danke!

Muss ich E nun also ansteuern wenn ich die PINS DB7-DB4 ändere, oder 
immer erst nach einem "gesamten Initialisierungsschritt"

Mit Initialisierungsschritt meine ich die Schritte, welche im Datenblatt 
3(das des KS0066) auf Seite 27 aufgeführt sind.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg schrieb:
> Das auf Seite 4 und 5 im 2. Datenblatt hatte ich schon gesehen,
> allerdings wusste ich nicht, dass ich auch bei der Initialisierung diese
> Grafik beachten muss.

Immer!
Wie gesagt: Das LCD kann ja nicht hellsehen, wann die restlichen Pins 
ihren endgültigen Zustand angenommen haben.

> Muss ich E nun also ansteuern wenn ich die PINS DB7-DB4 ändere, oder
> immer erst nach einem "gesamten Initialisierungsschritt"

Laut Datenblatt geht E auf High
dann ändern sich die Datenpins
und dann geht E wieder auf Low

Siehe Timingdiagramm

Mit dem Übergang von E von High auf Low übernimmt das LCD die Daten.

Wobei das nicht soooo heiß gegessen wird.
Du kannst auch vorher die Datenleitungen einstellen, dann geht E auf 
High, wartet ein bischen und geht wieder auf Low. Entscheidend ist nur, 
dass die Datenleitungen (+R/W +Command) mit dem Wechsel von E von High 
auf Low übernommen werden und das E eine gewisse Mindestzeit auf High 
gehalten werden muss.

Warum studierst du den nicht anderen Code für solche Details? Im 
Tutorial gibt es einen Abschnitt über das LCD!

> Mit Initialisierungsschritt meine ich die Schritte, welche im Datenblatt
> 3(das des KS0066) auf Seite 27 aufgeführt sind.

Das hat nichts mit der Initialisierung zu tun.
Das ist der generelle Mechanismus, der einzuhalten ist, wenn Daten zum 
LCD übertragen (Write) oder vom LCD gelesen (Read) werden.
Ist wie beim Telefon: Du musst zuerst die Verbdinung herstellen und erst 
dann reden, nicht umgekehrt. Das ist beim Telefon der grundsätzliche 
Vorgang. Was du deinem Gegenüber mitteilst, ist davon unberührt.

Autor: Jörg (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe mir verschiedensten Code angeguckt, jedoch wollte ich für den 
Anfang nur "das Nötigste" programmieren.

Für mich als Anfänger ist es noch etwas schwer durch den Code zu finden, 
wenn dieser in vielen Funktionen (mit verschiedenen Parameterübergaben) 
aufgeteilt ist.
Und ich muss jetzt feststellen, dass viel mehr code nötig ist als ich 
dachte. UND VORALLEM VERSTEHE ICH WARUM.

Ich habe noch Probleme diesen Abschnitt deines Beitrages zu verstehen:
"Entscheidend ist nur,
dass die Datenleitungen (+R/W +Command) mit dem Wechsel von E von High
auf Low übernommen werden und das E eine gewisse Mindestzeit auf High
gehalten werden muss.
"

Heisst das die Leitungen RW und RS(also Command?) sind erst HIGH und 
werden gleichzeitig mit E auf LOW gesetzt?

Und woher weiss ich wie lange ich E halten muss? Im Datenblatt steht da 
glaub ich nichts zu?

Vielen Dank, dass Ihr auf meine Anfängerfragen nicht total genervt 
reagiert.... ich merke selbst, dass die Fragen auf unterstem Niveau 
sind.

Gruß

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg schrieb:
> Ich habe mir verschiedensten Code angeguckt, jedoch wollte ich für den
> Anfang nur "das Nötigste" programmieren.

Das ist das Problem:
Woher weißt du, was nötig ist und was Zugabe?

> Für mich als Anfänger ist es noch etwas schwer durch den Code zu finden,
> wenn dieser in vielen Funktionen (mit verschiedenen Parameterübergaben)
> aufgeteilt ist.

Dann musst du bei den einfachen Funktionen anfangen zu analysieren und 
dir merken, was sie machen. Ein gut gewählter Funktionsname hilft dabei.

Was macht ?
static void lcd_enable( void )
{
    LCD_PORT |= (1<<LCD_EN);     // Enable auf 1 setzen
    _delay_us( LCD_ENABLE_US );  // kurze Pause
    LCD_PORT &= ~(1<<LCD_EN);    // Enable auf 0 setzen
}

Aha. Das schickt einen Puls auf dem E-Pin raus. Also einmal auf 1, etwas 
warten und wieder zurück auf 0. Jetzt weißt du schon, aus dem 
Datenblatt, das du das immer brauchst, um eine Ausgabe abzuschliessen. 
Die Erwartung ist daher, dass diese Funktion wohl am Ende einer Ausgabe 
aufgerufen wird.

Schaun wir mal
static void lcd_out( uint8_t data )
{
    data &= 0xF0;                       // obere 4 Bit maskieren
 
    LCD_PORT &= ~(0xF0>>(4-LCD_DB));    // Maske löschen
    LCD_PORT |= (data>>(4-LCD_DB));     // Bits setzen
    lcd_enable();
}

Jup. Offenbar richtig. die Funktion lcd_out gibt etwas am Port aus und 
hinten nach wird der Enable Puls gesetzt. lcd_out enthält alles was 
notwendig ist um etwas an das LCD zu schicken.

Wo wird sie verwendet?
Na zb in der lcd_init. Anstelle sich in der lcd_init ständig mit den 
Port Pins rumzuschlagen, wird einfach nur lcd_out aufgerufen. lcd_out 
weiß dann schon, was da alles zu machen ist (inklusive Enable Pin 
toggeln)

> "Entscheidend ist nur,
> dass die Datenleitungen (+R/W +Command) mit dem Wechsel von E von High
> auf Low übernommen werden und das E eine gewisse Mindestzeit auf High
> gehalten werden muss.
> "
>
> Heisst das die Leitungen RW und RS(also Command?) sind erst HIGH und
> werden gleichzeitig mit E auf LOW gesetzt?


Nein
Das heisst dass du R/W und RS ebenfalls einstellen musst, bevor auf E 
der Wechsel 1 - warten - 0 erfolgt.

> Und woher weiss ich wie lange ich E halten muss?

Schau ins Timing Diagramm. Da sind Pfeile drinnen, die jeweils die 
Mindestzeiten angeben.

Autor: Jörg (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe nun mein Progreamm noch mehrmals komplett überarbeitet.
Leider bekomme ich das Display nicht initialisiert.

Es wäre super, wenn Ihr mir sagen könntet ob mein Code jetzt richtig ist 
und ich vllt. einfach nur die falschen Kommandos zur Initialisierung 
benutze? -Aber welche sind dann richtig? - kann ich irgend etwas an den 
Fusebits falsch eingestellt haben, was nun Pins am Port D 
blockiert(durch mehrfachbelegung der pins)?

Besonders interessieren würde es mich, ob folgende Stellen korrekt sind:
instruction &= 0x0F;  //ändere nur unteres Nibble(DB4-DB7)
PORTD = instruction;   //schreibe in DB4-DB7 unteres Nibble von instruction
Und dann:
lcd_write_instruction(0x02);

Mit diesem Code soll an DB5(angeschlossen an PD1) eine 1 anliegen, 
während die Pins PD 4,5,6,7 unverändert bleiben.
#ifndef F_CPU
#define F_CPU 8000000UL 
#endif

#include <avr/io.h>
#include <util/delay.h>

void lcd_write_instruction(uint8_t instruction);
void lcd_write_data(uint8_t data);
void lcd_init (void); 

#define DB4    PD0
#define DB5    PD1
#define DB6    PD2
#define DB7    PD3
#define RS    PD4
#define RW    PD5
#define E    PD6


// ***** Hauptprogramm *****
int main(void){
//Ausgänge konfigurieren
DDRD = 0xff;     //D0-7 als Ausgänge
PORTD = 0x00;    //Ausgänge auf 0

_delay_ms (150);   // wait min 15 mSec. after Power ON
lcd_init ();       // Display initialisieren
                       
while(1);

}

void lcd_write_instruction(uint8_t instruction)
{

PORTD &= ~(1<<RS);      //RS=0
PORTD &= ~(1<<RW);    //RW=0
_delay_us (0.06);    //60ns(tSU1) warten
PORTD |= (1<<E);    //E=1
_delay_us (0.255);    //warte 255ns (450ns(tW)-195ns(tSU2))

instruction &= 0x0F;  //ändere nur unteres Nibble(DB4-DB7)
PORTD = instruction;   //schreibe in DB4-DB74 unteres Nibble von instruction

_delay_us (0.195);     //warte 195ns(tSU2)
PORTD &= ~(1<<E);    //E=0
_delay_us (0.01);     //warte 10ns(tH2)
_delay_us (0.5);    //warte 500ns (1000-(10+450+40)
}

void lcd_write_data(uint8_t data)
{
PORTD |= (1<<RS);    //RS=1
PORTD &= ~(1<<RW);    //RW=0
_delay_us (0.06);    //60ns(tSU1) warten
PORTD |= (1<<E);    //E=1
_delay_us (0.255);    //warte 255ns (450ns(tW)-195ns(tSU2))

data &= 0x0F;      //ändere nur unteres Nibble(DB4-DB7)
PORTD = data;       //schreibe in DB4-DB7 unteres Nibble von data

_delay_us (0.195);     //warte 195ns(tSU2)
PORTD &= ~(1<<E);    //E=0
_delay_us (0.01);     //warte 10ns(tH2)
_delay_us (0.5);    //warte 500ns (1000-(10+450+40)
}

void lcd_init( void )
{
//Power-ON
_delay_ms(30);

//Funktion Set
lcd_write_instruction(0x02);
lcd_write_instruction(0x02);
lcd_write_instruction(0x0E);

_delay_us(40);
//Display on/off Control
lcd_write_instruction(0x00);
lcd_write_instruction(0x0F);

_delay_us(40);
//Display Clear
lcd_write_instruction(0x00);
lcd_write_instruction(0x01);

_delay_ms(2);
//Entry Mode set
lcd_write_instruction(0x00);
lcd_write_instruction(0x06);
}


Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Jörg schrieb:

> void lcd_write_instruction(uint8_t instruction)
> {
>
> PORTD &= ~(1<<RS);      //RS=0
> PORTD &= ~(1<<RW);    //RW=0
> _delay_us (0.06);    //60ns(tSU1) warten
> PORTD |= (1<<E);    //E=1
> _delay_us (0.255);    //warte 255ns (450ns(tW)-195ns(tSU2))
>
> instruction &= 0x0F;  //ändere nur unteres Nibble(DB4-DB7)
> PORTD = instruction;   //schreibe in DB4-DB74 unteres Nibble von
> instruction

Damit ist dann die ganze schöne Einstellung, die du vorher für RS, RW 
und E gemacht hast, zur Geschichte geworden. Gone with the wind

RS und RW sind hier noch egal, weil sie sowieso 0 sind. Aber E ist nicht 
egal


> void lcd_write_data(uint8_t data)
> {
> PORTD |= (1<<RS);    //RS=1
> PORTD &= ~(1<<RW);    //RW=0
> _delay_us (0.06);    //60ns(tSU1) warten
> PORTD |= (1<<E);    //E=1
> _delay_us (0.255);    //warte 255ns (450ns(tW)-195ns(tSU2))
>
> data &= 0x0F;      //ändere nur unteres Nibble(DB4-DB7)
> PORTD = data;       //schreibe in DB4-DB7 unteres Nibble von data

... während hier dann auch das RS Bit auch noch gelöscht wurde


Schluck deinen Stolz runter und hol dir die LCD Routinen aus dem 
Tutorial. Überpüf noch, ob die Initialisierung die Bytes schickt, die 
bei dir laut Datenblatt vorgeschrieben sind und bring es erst mal zum 
Laufen. Und dann studierst du den Tuorialcode im Detail.

Ich weiß schon, du willst das alles selbst schreiben, bla bla bal ... 
und überhaupt. Aber solange dir solche läppischen Fehler passieren, bist 
du einfach noch nicht soweit mehrer Leitungen simultan zu bedienen ohne 
dass du ungewollt durch die Manipulation einer Leitung eine andere 
irrtümlich veränderst ohne es zu merken. Ist nicht weiter schlimm, das 
wird schon werden. Aber ein LCD ist ein denkbar ungeeignetes und 
frustrierendes Objekt, um sich daran zu versuchen.

Autor: holger (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>data &= 0x0F;      //ändere nur unteres Nibble(DB4-DB7)
>PORTD = data;       //schreibe in DB4-DB7 unteres Nibble von data

Und was ist mit dem oberen Nibble von data?
Willst du das nicht senden? Das musst du aber!

Autor: Jörg (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Es müsste also so lauten: ?
while(1);
}

void lcd_write_instruction(uint8_t instruction)
{

PORTD &= ~(1<<RS);      //RS=0
PORTD &= ~(1<<RW);    //RW=0
_delay_us (0.06);    //60ns(tSU1) warten
PORTD |= (1<<E);    //E=1
_delay_us (0.255);    //warte 255ns (450ns(tW)-195ns(tSU2))

PORTD |= instruction;   //schreibe in DB1-DB4 unteres Nibble von instruction

_delay_us (0.195);     //warte 195ns(tSU2)
PORTD &= ~(1<<E);    //E=0
_delay_us (0.01);     //warte 10ns(tH2)
_delay_us (0.5);    //warte 500ns (1000-(10+450+40)
}

void lcd_init( void )
{

//Power-ON
_delay_ms(30);

//Funktion Set
lcd_write_instruction(0x02);
lcd_write_instruction(0x02);
lcd_write_instruction(0x0E);

_delay_us(40);
//Display on/off Control
lcd_write_instruction(0x00);
lcd_write_instruction(0x0F);

_delay_us(40);
//Display Clear
lcd_write_instruction(0x00);
lcd_write_instruction(0x01);

_delay_ms(2);
//Entry Mode set
lcd_write_instruction(0x00);
lcd_write_instruction(0x06);
}


Ich habe den code aus dem Tut auch nciht zum funktionieren bekommen ...

Danke für eure Gedult

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.