Forum: Mikrocontroller und Digitale Elektronik LCD Routine von Peter Danneger für 4x20


von Alejandro P. (alejo)


Angehängte Dateien:

Lesenswert?

Hallo,
ich verwende die LCD Routine von Peter Danneger, funktioniert übrigens 
einwandfrei!

Habe ein 4x20 LCD bekommen aber da funktioniert leider nicht bzw. es 
funktioniert aber wie ein LCD 2x16 (Routine ist für 2x16).

im Datei lcd_drv.c ist die Formatierung dafür:

#########################################

void lcd_pos( u8 line, u8 column )
{
  if( line & 1 )
    column += 0x40; // estaba 40

  lcd_command( 0x80 + column );
}

########################################

bin ich aber leider nicht ganz schlau geworden, kann mich jemand helfen?

Gruß

von Herbert K. (avr-herbi)


Lesenswert?

Hallo,
ohne den Typ des Displays bzw./und des Kontrollers wäre es mehr ein 
Raten, hier zu helfen.
Vielleicht mal ergänzen?!
Viele Grüße Herbert

von Kluchscheißernder N. (kluchscheisser)


Lesenswert?

Alejandro P. schrieb:
> Hallo,
> ich verwende die LCD Routine von Peter Danneger, funktioniert übrigens
> einwandfrei!

Naja, man sollte den Code, den man verwendet, auch verstehen und nicht 
nur unverstanden benutzen.

>
> Habe ein 4x20 LCD bekommen aber da funktioniert leider nicht bzw. es
> funktioniert aber wie ein LCD 2x16 (Routine ist für 2x16).

Das 4x20 ist von der Ansteuerung her wie ein 2x40 organisiert. Es teilen 
sich also zwei Zeilen einen DD-RAM-Bereich. Wie die Zeilen mechanisch 
angeordnet sind, ist vo, Modell (Routing der Platine) abhängig. Also 
Datenblatt befragen oder ausprobieren. Meist ist die Zeilenaufteilung 
etwas "verwürfelt".

>
> im Datei lcd_drv.c ist die Formatierung dafür:
>
> #########################################
>
> void lcd_pos( u8 line, u8 column )
> {
>   if( line & 1 )
>     column += 0x40; // estaba 40
>
>   lcd_command( 0x80 + column );
> }
>
> ########################################
>
> bin ich aber leider nicht ganz schlau geworden, kann mich jemand helfen?

Die Funktion sorgt lediglich dafür, dass ungerade Zeilen im zweiten 
DD-RAM-Bereich landen (Offset 64). Für die Aufteilung innerhalb eines 
DD-RAM-Bereiches ist diese Funktion nicht zuständig. Entweder wird das 
woanders gemacht, oder Du musst Dich selbst darum kümmern. Betrachte das 
LCD als 2x40, dann müsstest Du zurecht kommen.

>
> Gruß

von Peter D. (peda)


Lesenswert?

1
void lcd_pos( u8 line, u8 column )
2
{
3
  switch( line ){
4
    case 1: column += 0x20;
5
    case 2: column += 0x20;
6
    case 3: column += 0x20;
7
  }
8
  lcd_command( 0x80 + column );
9
}


Peter

von Herbert K. (avr-herbi)


Lesenswert?

Vielleicht hilft dieser Beitrag weiter:
Beitrag "LCD Treiber Routine 4*40"

von LCD (Gast)


Lesenswert?

Welcher Typ ist der LCD-Kontroller?

Da gibt es einen von Samsung, KS0073, der lt. Datenblatt
fast 100% kompatibel zum HD44780 ist.
"Fast" bedeutet in diesem Falle:
--andere Zeilenadressen
--andere Init-Sequenz, hier rate ich dringend(!) sich das Dateblatt
  des Herstellers anzusehen!!!!

von Karl H. (kbuchegg)


Lesenswert?

Du musst dir im Datenblatt ansehen, wo die Zeilenanfänge deines LCD sind 
(also an welcher Startadresse). Zur Not kann man das auch einfach 
ausprobieren.

In diesem Code wird 0x80 mit etwas verundet. 0x80 ist offensichtlich die 
Kennung im Byte, dass es sich um einen Positionierbefehl handelt. Der 
Rest ist die Adresse, an die der Cursor gesetzt werden soll.

Wo kommt diese Adresse her?
Das ist in erster Linie die Spaltennummer, die du angibst. Die 
Formulierung line & 1 ist nichts anderes, als das Abtesten, ob eine 
ungerade Zeilennummer angegeben wurde. Da der Originalcode von maximal 2 
Zeilen ausgeht, ist das gleichbedeutend mit der Abfrage, ob die 
Zeilennummer gleich 1 ist. Der einzige Unterschied: Wenn du dich vertust 
und zb eine Zeilennummer von 3 angibst, funktioniert diese Abfrage immer 
noch und alles landet in Zeile 1
Wenn dem so ist, du also eine Zeilennummer von 1 angegeben hast, wird 
zur Spaltennummer noch 0x40 dazuaddiert. Und genau das ist der Offset, 
den das LCD benutzt um die angezeigten Zeilen zu trennen.

Ein LCD hat einfach nur einen linearen Speicher. Also so

      0   1   2   3   4   5   6   7   8   9   10
    +---+---+---+---+---+---+---+---+---+---+---+
    |   |   |   |   |   |   |   |   |   |   |   |
    +---+---+---+---+---+---+---+---+---+---+---+

Das LCD ist jetzt so gebaut, dass es sich aus diesem Speicher bestimmte 
Bereiche herausholt, die es in den einzelnen Zeilen anzeigt.

      0   1   2   3   4   5   6   7   8   9   10
    +---+---+---+---+---+---+---+---+---+---+---+
    |   |   |   |   |   |   |   |   |   |   |   |
    +---+---+---+---+---+---+---+---+---+---+---+
    |               |       |               |
    <-- Zeile 1 ---->       <-- Zeile 2 ---->


Wenn du also ein A in der ersten Zeile ganz links anzeigen willst, dann 
schreibst du es hier hin

      0   1   2   3   4   5   6   7   8   9   10
    +---+---+---+---+---+---+---+---+---+---+---+
    | A |   |   |   |   |   |   |   |   |   |   |
    +---+---+---+---+---+---+---+---+---+---+---+
    |               |       |               |
    <-- Zeile 1 ---->       <-- Zeile 2 ---->

also an Adresse 0

WIllst du das A an den Anfang der zweiten Zeile, dann wird das A hier 
hin geschrieben

      0   1   2   3   4   5   6   7   8   9   10
    +---+---+---+---+---+---+---+---+---+---+---+
    |   |   |   |   |   |   | A |   |   |   |   |
    +---+---+---+---+---+---+---+---+---+---+---+
    |               |       |               |
    <-- Zeile 1 ---->       <-- Zeile 2 ---->

also an Adresse 6 (in diesem Beispiel)

Das ist im Grunde schon alles, was man von seinem LCD wissen muss: 
Welche Adressdistanz liegt zwischen den einzelnen Zeilen. Und Achtung: 
Die Zeilen können auch durcheinander sein: Im Speicher kann es sein, 
dass auf Zeile 1 die Zeile 3 folgt und dann erst 2 und 4.

Du musst daher jetzt:
Im Datenblatt nachsehen, wie das bei deinem LCD ist.
Daraus eine Berechnungsmethode ableiten, wie du die Adresse ausrechnen 
kannst, wenn du Zeilennummer und Spaltennummer gegeben hast.

In meinem Beispiel wäre das zb

    <----- Zeilenoffset --->
      0   1   2   3   4   5   6   7   8   9   10
    +---+---+---+---+---+---+---+---+---+---+---+
    |   |   |   |   |   |   | A |   |   |   |   |
    +---+---+---+---+---+---+---+---+---+---+---+
    |               |       |               |
    <-- Zeile 1 ---->       <-- Zeile 2 ---->


Zeilenoffset: 6

Adresse = Spaltennummer + Zeilenoffset * Zeilennummer

Probiers aus: Wenn du in Zeile 2 ( Zeilennummer: 1) an die Spalte 3 
(Spaltennummer: 2) schreiben willst

  Adresse = 2 + ( 6 * 1 )
  Adrsse = 8

In der Grafik kann man sich davon überzeugen, dass das tatsächlich das 
3te Zeichen in der 2ten Zeile wäre.

von Link zu (Gast)


Lesenswert?

@ Karl heinz Buchegger (kbuchegg)
Nur so aus Neugier.
Machst du die ASCII-Zeichnungen jedes mal neu, oder kopierst du die aus 
alten Beiträgen in die neuen? ;-)

von Karl H. (kbuchegg)


Lesenswert?

Neu

Geht schneller als kopieren :-)

von Robert W. (rweber)


Lesenswert?

z.B. mit dem hier: http://www.jave.de/

von Alejandro P. (alejo)


Lesenswert?

Hallo,
hatte nicht mehr im Foro geschaut!
@Herbert, @ Kluchscheißender, @Peter Dannegger und an alle
vielen Dank für eure nette Hilfe und Antworten

@Karl heinz Buchegger
sehr nette und hilfreiche Einführung vielen Dank!

so, LCD ist ein GM-C2004A von Goldentek aber ich glaube ist etwas alt da 
ich kein Datenblatt gefunden habe! habe paar ähnlicher gefunden aber ich 
glaube nicht identisch.

Herbert K. schrieb:
> ohne den Typ des Displays bzw./und des Kontrollers wäre es mehr ein
> Raten, hier zu helfen.

Jein, von mein 2x16 habe ich kein Datenblatt gefunden aber ich habe 
rumprobiert bis ich es am laufen bekommen habe (wie Karl Heinz sagt!),da 
ist auf alle Fälle ein HD44780 drin und war von daher einfacher, im 4x20 
tippe ich mehr auf ein KS0070.

Kluchscheißender Kluchscheißer schrieb:
> Naja, man sollte den Code, den man verwendet, auch verstehen und nicht
> nur unverstanden benutzen.

du hast vollkommen Recht nun gibt es tausende LCD Routinen und natürlich 
nicht alle gleich, die Routine von Tutorial hat bei mir funktioniert 
(mit dem 4x20) und ich habe zu mindestens versucht es zu verstehen.
Die Routine von Peter war die Einzige die in meinem 2x16 auf Anhieb 
funktioniert hat und das beste daran ist, dass man jede einzelne Pin im 
verschiedene Ports anschliessen kann, deswegen möchte die selbe Routine 
für mein 4x20 LCD verwenden.

Karl heinz Buchegger schrieb:
> Da der Originalcode von maximal 2 Zeilen ausgeht, ist das leichbedeutend
> mit der Abfrage, ob die Zeilennummer gleich 1 ist.

das hate ich auf alle Fälle verstanden.

Peter Dannegger schrieb:

> void lcd_pos( u8 line, u8 column )
> {
>   switch( line ){
>     case 1: column += 0x20;
>     case 2: column += 0x20;
>     case 3: column += 0x20;
>   }
>   lcd_command( 0x80 + column );
> }

Jup, das ist der richtiger Richtung muß ich noch ein bißchen rumspielen.

Herbert K. schrieb:
> Vielleicht hilft dieser Beitrag weiter:
> Beitrag "LCD Treiber Routine 4*40"

das Probiere auf jedem Fall auch noch!

eine Frage noch, wie kann ich hier Code einfügen damit es so aussiet wie 
bei Petter?

Gruß

von Klaus W. (mfgkw)


Lesenswert?

Alejandro P. schrieb:
> eine Frage noch, wie kann ich hier Code einfügen damit es so aussiet wie
> bei Petter?
1
[c]
2
int main()
3
{
4
    printf( "soso..." );
5
}
6
[/c]
ergibt:
1
int main()
2
{
3
    printf( "soso..." );
4
}

von Alejandro P. (alejo)


Lesenswert?

Hallo Klaus,
Super vielen Dank.

von Alejandro P. (alejo)


Lesenswert?

Hallo,

Peter Dannegger schrieb:
1
void lcd_pos( u8 line, u8 column )
2
{
3
  switch( line ){
4
    case 1: column += 0x20;
5
    case 2: column += 0x20;
6
    case 3: column += 0x20;
7
  }
8
  lcd_command( 0x80 + column );
9
}

so hat es auf alle Fälle funktioniert, ich habe ein lange Text 
geschrieben um herauszufinden wo jeder Zeile anfängt und das ergabt:

1 Zeile bei 0
2 Zeile bei 40
3 Zeile bei 20
4 Zeile bei 54

das bedeutet wenn ich auf Zeile 4 schreiben will dann kann ich so 
schreiben:
1
lcd_pos( 0, 54 );
2
lcd_text( (u8*)"Vierte 1234567891234" );

und es fuktioniert! ok soweit so gut.

Nun wenn ich aber in der "switch - case" der Anfang von jede Zeile 
ändere dann funktioniert nur bei dem Zeile 4,
also wenn ich es so ändere:
1
case 3: column += 0x54;
kann ich dann so schreiben:
1
lcd_pos( 3, 0 );
2
lcd_text( (u8*)"Vierte 1234567891234" );

und funktioniert aber sobald ich eine von der andere "case" ändere dann 
kommt nur unsinn in der ganze LCD und ich verstehe nicht warum !

von Peter D. (peda)


Lesenswert?

Die Case laufen ineinander über, d.h. case 1 macht alle 3 Additionen 
(was bestimmt falsch ist, case 3 müßte zuerst stehen).

Schreib einfach noch ein break; am Ende jeder Case-Zeile, dann sollte es 
gehen.


Peter

von Alejandro P. (alejo)


Lesenswert?

o man!
ich bin ein Idiot!

Peter Dannegger schrieb:
> Die Case laufen ineinander

> Schreib einfach noch ein break; am Ende jeder Case-Zeile, dann sollte es
> gehen.

klar!
aber warum kann man manchmal so was selber nicht sehen? ich habe kurz 
gedacht aber dachte es liegt wo anders, Sch... naja heute Abend probiere 
es wieder.


Vielen Dank.

von Peter D. (peda)


Lesenswert?

In dem anderen Thread steht:
1
#define LCD_DDADR_LINE2         0x40
2
#define LCD_DDADR_LINE3         0x14
3
#define LCD_DDADR_LINE4         0x54

Dann müßte gehen:
1
void lcd_pos( u8 line, u8 column )
2
{
3
  switch( line ){
4
    case 1: column += LCD_DDADR_LINE2 - LCD_DDADR_LINE3;
5
    case 2: column += LCD_DDADR_LINE3 - LCD_DDADR_LINE4;
6
    case 3: column += LCD_DDADR_LINE4;
7
  }
8
  lcd_command( 0x80 + column );
9
}

Gegenüber der Version mit break; spart man 2 RJMP ein und einen Zyklus 
bei case 2.


Peter

von Klaus W. (mfgkw)


Lesenswert?

Dann schlage ich doch mal folgendes vor:
1
  static uint8_t   tab_zeilenoffset[] =
2
    {
3
      0x80+0x00,
4
      0x80+0x40,
5
      0x80+0x14,
6
      0x80+0x54,
7
    };
8
  lcd_command( tab_zeilenoffset[line] );

Das halte ich für recht übersichtlich, problemlso anzupassen,
und sollte nicht zu langsam laufen.

Bzw. LCD_DDADR_LINE... eingesetzt natürlich.

PS: Falls man dem Frieden mit den Feldgrenzen nicht traut,
kann man ja auch line%4 nehmen.

von Peter D. (peda)


Lesenswert?

Noch besser ist es, lcd_pos als Inline-Funktion im h-File zu definieren.
Denn dann wird sie komplett wegoptimiert, da ja Zeile und Spalte fast 
immer Konstanten sind.
Es bleibt dann nur noch
1
lcd_command( Konstante );
übrig.


Peter

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.