LCD Routine von Peter Danneger für 4x20

OP #1721088
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ß
Angehängte Dateien:
#1721424
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ß
Gast #1721432
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!!!!
#1721433
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.
OP #1721592
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ß
OP #1722221
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 !
OP #1722370
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.
#1722533
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
#1722547
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren