Forum: Mikrocontroller und Digitale Elektronik ATMEGA16 LCD 2x8 ansteuern (4Bit-Interface)


von Ralf (Gast)


Lesenswert?

Hey,

Ich habe ein Problem. Probiere nun schon seit 2 Tagen mein 2x8 LCD zum 
laufen zu bringen, doch kriege es nicht her. Es leuchten nur die 
schwarzen Balken in der ersten Zeile.

Datenblatt LCD:
http://docs-europe.electrocomponents.com/webdocs/0f25/0900766b80f25db8.pdf


Hier der Programmcode:
/********************************
 *   Diplom_Prototyp.c      *
                 
 *   Created: 21.12.2011    *
 *   Author: Raphael Tschernitz  *
 *******************************/

#define  F_CPU 16000000UL
#define  En PINA6;
#define  Rs PINA4;
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>

void delayms(int x)    // Spart so speicherplatz..
{
  _delay_ms(x);
}
void delayus(int y)
{
  _delay_us(y);
}

void init()
{
  DDRA=0xFF;    // DDRA als Ausgang
  DDRC=0xFF;
  PORTA=0x00;
  PORTC=0x00;
}

void send(int VeeEnRsRWData4321)
{
  PORTA=1<<En;        // Enable
  delayus(50);
  PORTA=VeeEnRsRWData4321;  // Vee En Rs RW Data4-1
  delayus(100);
  PORTA=0<<En;        // Werte übernehmen
  delayus(100);        // Bisele Warta
}

void WriteChar(int VeEnRsRWData4321)
{
  PORTA=1<<Rs;
  delayus(50);
  PORTA=1<<En;        // Enable
  delayus(50);
  PORTA=VeEnRsRWData4321;  // Vee En Rs RW Data4-1
  delayus(100);
  PORTA=0<<En;        // Werte übernehmen
  delayus(50);
  PORTA=0<<Rs;
  delayus(100);        // Bisele Warta
}

void lcd_init()
{
  /* PA0  Data1
     1      2
     2    3
     3    4
     4    RS
     5    RW
     6    En
     7    Vee
  */

  /*  Power on
    Wait > 40ms
    ((Vee En RW RS D4 D3 D2 D1))
    0    0  0  0  0  0  1  1   // Function set
    Wait > 37us
    0    0  0  0  0  0  1  0   // Function set
    0    0  0  0  N  F  X  X   // N = 0; 1-line display, F = 0; 5x8 dot 
character font (meins: 0 0 0 0 1 0 0 0)
    Wait > 37us
    0    0  0  0  0  0  1  0   // Function set again :S
    0    0  0  0  N  F  X  X   // N = 0; 1-line display, F = 0; 5x8 dot 
character font (meins: 0 0 0 0 1 0 0 0)
    Wait > 37us
    0    0  0  0  0  0  0  0   // Display on/off control
    0    0  0  0  1  D  C  B   // D = 0; Display off, C = 0; Cursor off, 
B = 0; Blinking off (meins: 0 0 0 0 1 1 0 0)
    Wait > 37us
    0    0  0  0  0  0  0  0   // Display clear
    0    0  0  0  0  0  0  1
    Wait > 1.52ms
    0    0  0  0  0  0  0  0   // Entry mode set
    0    0  0  0  0  1  ID S   // I/D = 1; Increment by 1, S = 0; No 
shift (meins: 0 0 0 0 0 1 1 0)
    Wait ??
  */

  delayms(1000);

   // Function set
   send(0b01000011);

   // Function set
   send(0b01000010);
   send(0b01001000); // 1-Line Display, 5x8 dot character font
   // Function set again :S
   send(0b01000010);
   send(0b01001100); // 2-Line Display, 5x8 dot character font

   // Display on/off control
   send(0b01000000);
   send(0b01001100); // Display on, Cursor off, Blinking off

   // Display clear
   send(0b01000000);
   send(0b01000001);
   delayms(2);

   // Entry mode set
   send(0b01000000);
   send(0b01000100); // no increment by 1, no shift
  delayms(2);
}

int main(void)
{
  init();
  lcd_init();
  int i=0;
  while(i<10)        // nur zum Anzeigen, dass While-Schleife startet
  {
    PORTC=0x0F;
    delayms(100);
    PORTC=0xF0;
    delayms(100);
    i++;
  }
  PORTC=0x00;

    while(1)
    {
    // H
    WriteChar(01011000);
    WriteChar(01011000);
    delayms(500);
    }
}

Könnt Ihr einen Fehler finden, bin echt am verzweifeln. :S

von Εrnst B. (ernst)


Lesenswert?

Ersetze:
Ralf schrieb:
> // Spart so speicherplatz..

durch:
// Verschwendet so enorm Speicherplatz.

;)

von Joe (Gast)


Lesenswert?

Da hilft nur eins: mit dem Oszi prüfen, ob alle Signale auch so 
anliegen, wie sie sollen.

Joe

von Krapao (Gast)


Lesenswert?

>  init();
>  lcd_init();

Dein init() fummelt bereits an den PORTA Leitungen zum LCD. Die 1000ms 
Wartezeit in lcd_init() bis das LCD aus dem Power-On-Reset kommt, 
verpufft.

> void lcd_init()
> {
>   delayms(1000);
>   send(0b01000011); // Function set

Das ist sehr unterschiedlich zu den bewährten LCD Routinen aus dem 
Tutorial in der Artikelsammlung.

Dort wird eine Initialierung verwendet um Kommandomodus zu kommen, die 
auch arbeitet, wenn das LCD im 8-Bit oder 4-Bit Modus ist.

>  /* PA0  Data1
>     1      2
>     2      3
>     3      4

Da kommst du nicht weiter. Du musst für das 4-Bit Interface am LCD auf 
DB4-DB7 ankommen, die unteren Datenleitungen DB0-DB3 am LCD sind 
wirkungslos!

PA0: AVcc AGND und ARef sind korrekt beschaltet?

>     WriteChar(01011000);

Die Zahl 01011000 ist nicht die Zahl, die du meinst, dass sie ist.

von Krapao (Gast)


Lesenswert?

um Kommandomodus => um in den Kommandomodus

von Karl H. (kbuchegg)


Lesenswert?

Zum Stil des Programmes. Das hier
1
void send(int VeeEnRsRWData4321)
2
{
3
  PORTA=1<<En;        // Enable
4
  delayus(50);
5
  PORTA=VeeEnRsRWData4321;  // Vee En Rs RW Data4-1
6
  delayus(100);
7
  PORTA=0<<En;        // Werte übernehmen
8
  delayus(100);        // Bisele Warta
9
}

ist beispielsweise kompletter Unsinn.
Du musst dir bewusst sein, dass eine Zuweisung an einen Port immer ALLE 
Portpins ändert. Ein


  PORTA=1<<En;        // Enable

ändert nicht nur die Enable Leitung, sondern auch die Datenleitungen, 
die am selben Port liegen. Ein

  PORTA=VeeEnRsRWData4321;  // Vee En Rs RW Data4-1

ändert ebenfalls alle Portbits. Du verlangst hier nicht mehr oder 
weniger vom Aufrufer, als das er dir ein Datenbyte übergibt, in dem die 
entsprechenden Bits für Enbale und Rs bereits richtig gesetzt sind. 
Wozu? Um dieses Detail kann sich die Funktion problemlos selber kümmern 
und du hast beim Aufruf der Funktion ein Detailproblem weniger, um dass 
du dich beim Aufruf kümmern musst.

Das hier
  PORTA=0<<En;        // Werte übernehmen
.... no comment.
Auch hier ändern sich wieder ALLE Portpins. Sie werden alle zu 0. Was 
das LCD sieht (sehen würde, wenn du die korrekten Datenpins am LCD 
benutzt), wenn du genau im Umschalten des Enable Pins auch die 
Datenleitungen auf 0 fallen lässt, das wissen nur die Götter.

von Krapao (Gast)


Lesenswert?

>  _delay_ms(x);
>  _delay_us(y);

Sabotiert die Funktionen; die arbeiten mit variablem Argument anders als 
gedacht.

>   PORTA=VeeEnRsRWData4321;  // Vee En Rs RW Data4-1

Ignoriert IMHO die Timing Vorgaben aus dem Datenblatt. Die RS und RW 
müssen vor und nach dem Toggle von EN gesetzt sein.

>   PORTA=0<<En;        // Werte übernehmen

Im Moment des Pegelwechsels werden so LOW-Pegel vom LCD übernommen. Die 
Zeit tH (Data Hold Time, min. 10 ns) wird nicht eingehalten.

Möglicherweise gibt es weitere Timingprobleme; habe ich nicht weiter 
kontrolliert.

von Ralf (Gast)


Lesenswert?

Vielen Dank für die schnellen Antworten. Das mit dem init() werde ich 
gleich mal ausprobieren. Die Datenleitungen sind schon korekt, hab sie 
hier nur mit dem falschen Namen kommentiert. (Data1 entspricht Data4 
usw.) Sorry das ich das nicht erwähnt hab.
Ich wollte mal das Programm selberschreiben, damit ich es auch komplett 
verstehe und weiß was ich gemacht habe. Wenn es einmal Funktioniert 
werde ich mich wahrscheinlich immer mehr der bewährten Art annähern bis 
das Programm perfekt ist.

@ ernst:
Ne ich habs ausprobiert, auf diese Art braucht das Programm nur einen 
drittel vom Speicherplatz dens zuvor gebraucht hat ;)

@ Joe:
Ich hab schon überprüft ob die Signale richtig ankommen und dort konnte 
ich keine Probleme feststellen. :)

von Ralf (Gast)


Lesenswert?

Ups, das stimmt. Hab das PORTA=1<<EN; jetzt auf PORTA|=1<<EN geändert, 
jetzt ändert sich nur noch das Enable.
Die Funktion werde ich später noch verbessern, dass es jetzt etwas 
undhandlich ist, ist noch egal.

Also zuerst RW und RS auf 0 setzen, dann EN auf 1, kurz darauf die Daten 
setzten, während Daten gesetzt sind EN wieder auf 0 und Daten werden 
übernohmen?

Pseudocode:
PORTA|=0<<RW,0<<RS
delay (tas=0-10ns)
PORTA|=1<<EN
PORTA|=Daten
delay (tpw=460ns)
PORTA|=0<<EN
delay (th=10ns)

Wie kann ich 4 bits gleichzeitig setzten?
Wenn Daten zB: Daten=0b0101
und ich möchte diese Daten auf PINA0-3 setzen:
PORTA|=Daten; ... setzt das dann die Daten auf
die niedrigsten Bits also 0bxxxx0101 ?

von Karl H. (kbuchegg)


Lesenswert?

> PORTA|=0<<RW,0<<RS

Du schreibst jetzt hundert mal
* Mit einer Oder-Operation kann man keine Bits auf 0 setzen.
  Mit einem Oder kann man Bits auf 1 ziehen, aber nicht umgekehrt.
  Dazu braucht man ein Und.
* Eine 0 kann man nach links schieben so oft man will, sie bleibt
  immer 0. (Oder was gleichwertig ist, da ein Linksschieben einer
  Multiplikation mit 2 entspricht: 0 kann man mit jeder beliebigen Zahl
  multiplizieren, das Ergebnis wird wieder 0 sein)


Bitmanipulation

Aber eigentlich sind das alles Dinge, die man mit seinen ersten 
Programmen, bei denen es um Led-ein/Led-aus, Lauflicht, Blinklicht etc. 
geht, alle aussortiert und bis zum Erbrechen übt. Danach ist das Thema 
'Wie werden einzelne Pins bzw. Pingruppen an einem Port korrekt und 
nebenwirkungsfrei angesprochen' durch und man wendet sich schwereren 
Dingen zu. Das korrekte und sichere Hantieren mit bitweisem 
Und/Oder/Nicht und den entsprechenden Maskenbytes (und wie man sie 
konstruiert) ist sozusagen das Kleine-Einmaleins der µC Programmierung.

von al3ko (Gast)


Lesenswert?

Hi Ralf,

hast du mal das LCD Beispiel aus dem Tutorial hier ausprobiert? So 
könntest du wenigstens prüfen/eingrenzen, ob es am Display liegt oder 
nicht.

Bei mir funktioniert der Code aus dem Tutorial einwandfrei.


Gruß

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.