Forum: Compiler & IDEs Frage zum Thema LCD Library für die Arduinos


von Thomas D. (lightscape66)


Lesenswert?

Hallo,

an einem Arduino Uno habe ich ein 44xxx-kompatibles Display das sich so 
weit auch sehr gut ansteuern lässt. Für eine Anwendung habe ich mir 8 
Sonderzeichen definiert.

Diese lassen sich mit
1
lcd.write(0);
2
lcd.write(1);
3
lcd.write(2);
4
lcd.write(3);
5
lcd.write(4);
6
lcd.write(5);
7
lcd.write(6);
8
lcd.write(7);
ausgeben.

Das ist aber so gar nicht effizient, da ich größere Mengen davon 
ausgeben möchte. Die Funktion lcd.write kann scheinbar nur ein Zeichen 
ausgeben. lcd.print hingegen beherrscht auch die Ausgabe ganzer 
Zeichenketten.

Jetzt kommt die Krux an der Sache:
Das selbstdefinierte Zeichen 0 benötige ich sehr oft, ist aber in der 
Sprache C leider der String-Terminator. Ein
1
lcd.print ("\03\04\00\05\07");
gibt deswegen nur 2 Zeichen aus.

Gibt es eine elegante Lösung für das Problem?

Bye,
Tom

von Karl H. (kbuchegg)


Lesenswert?

Thomas Dieckmann schrieb:

> Gibt es eine elegante Lösung für das Problem?

Schreib dir eine eigene Ausgabefunktion, in der du Zeichen ummappst. Du 
nimmst irgendeines der Sonderzeichen her, das du NIE benötigst (zb # 
oder $ oder ... ) und lässt es als Stellvertreter für deine 
Spezialzeichen stehen.

void lcd_my_writechar( char c )
{
  if( c == '#' )
    lcd.write( 0 );
  else if( c == '§' )
    lcd.write( 1 );
  else if( c == '°' )
    lcd.write( 2 );

  else
    lcd.write( c );
}

void lcd_my_print( const char * str )
{
  while( *str )
    lcd_my_writechar( *str++ );
}


gibst du jetzt mittels
   lcd_my_print( "hallo#" );

aus, dann ersetzt dir die Character Ausgabefunktion den '#' durch das 
von dir definierte Sonderzeichen mit dem Code 0.


Es ist nicht verboten, dass man sich selbst Funktionen schreibt, die 
einem Arbeit abnehmen, bzw. Dinge einfacher benutzbar machen, indem sie 
sich um Kleinkram kümmern.

von Karl H. (kbuchegg)


Lesenswert?

PS:
Es gibt im ASCII Code gerade im Bereich Steuerzeichen so einige Zeichen, 
die auf einem LCD nicht wirklich Sinn machen.

Auch die meisten der C Escpae Sequenzen kann man umfunktionieren.

zb Tabulator   \t
   Bell        \a
   Backspace   \b
   Formfeed    \f

sieh dir mal die ganzen Escape Sequenzen durch. Die kann man fast alle 
missbräuchlich verwenden.
http://de.wikipedia.org/wiki/Escape-Sequenz#Escape-Sequenzen_in_C_und_verwandten_Programmiersprachen

von Thomas D. (lightscape66)


Lesenswert?

Hallo,

Danke erst einmal für die schnelle Reaktion.
Ich arbeite generell mit Funktionen, schon alleine, um den Programmcode 
lesbarer und strukturierter zu gestalten.
Das mit dem Um-Mappen hatte ich mir auch gedacht, macht aber wenig Sinn, 
da auch hier für jedes einzelne Zeichen die lcd.write-Routine aufgerufen 
wird.
Mir geht es bei der Sache aber um Eleganz UND Performance.
Ich hab's wegen der Eleganz derzeit mit einer char-Matrix gelöst. Das 
kostet mich aber leider etwas von dem knappen RAM und es ist auch nicht 
sehr schnell.
Ich suche im Prinzip eine lcd.write-Funktion, die vom Aufruf z.B. so 
aussehen könnte:
1
lcd.my_write ( 3, 0, 0, 6);
D.h. für die Ausgabe von 4 Sonderzeichen nur einen Funktionsaufruf, 
wenige Bytes Programmcode und weniger RAM-Auslastung. Es sieht fast so 
aus, als müsste ich mir die bestehende Library umbauen, dass sie diese 
Funktion enthält.
Somit stellt sich die nächste Frage, ob man eine Funktion so basteln 
kann, dass man ihr beliebig viele Argumente übergeben kann?

Bye,
Tom

von Karl H. (kbuchegg)


Lesenswert?

Thomas Dieckmann schrieb:


> Das mit dem Um-Mappen hatte ich mir auch gedacht, macht aber wenig Sinn,
> da auch hier für jedes einzelne Zeichen die lcd.write-Routine aufgerufen
> wird.

Ja, und?
Was denkst du was die anderen Funktionen in der lcd Klasse machen. Zb 
lcd.print
Auch die greifen letzten Endes wieder für jedes Zeichen auf lcd.write 
zurück.

> Mir geht es bei der Sache aber um Eleganz UND Performance.
> Ich hab's wegen der Eleganz derzeit mit einer char-Matrix gelöst. Das
> kostet mich aber leider etwas von dem knappen RAM und es ist auch nicht
> sehr schnell.

Schnell?
Bei einem Text-LCD ist das ziemlich wurscht.

> Ich suche im Prinzip eine lcd.write-Funktion, die vom Aufruf z.B. so
> aussehen könnte:
1
lcd.my_write ( 3, 0, 0, 6);
> D.h. für die Ausgabe von 4 Sonderzeichen nur einen Funktionsaufruf,

und was ist da jetzt an einer eigenen Funktion unelegant?
Du kannst natürlich auch die LCD Klasse mit der entsprechenden Funktion 
erweitern, wenn dir das lieber ist.

  lcd.write_mapped( "Text mit # Sonderzeichen, die gemappt werden" );

> wenige Bytes Programmcode und weniger RAM-Auslastung.

noch weniger Bytes Programmcode bzw. RAM-Auslastung als eine Funktion, 
die einzelne CHaracter mit einer if-else Kette oder einem switch-case in 
andere Codes ummapt, kann ich mir kaum vorstellen.

 Es sieht fast so
> aus, als müsste ich mir die bestehende Library umbauen, dass sie diese
> Funktion enthält.
> Somit stellt sich die nächste Frage, ob man eine Funktion so basteln
> kann, dass man ihr beliebig viele Argumente übergeben kann?

Variadische FUnktionen, aber das willst du nicht wirklich tun

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:

> Du kannst natürlich auch die LCD Klasse mit der entsprechenden Funktion
> erweitern, wenn dir das lieber ist.
>
>   lcd.write_mapped( "Text mit # Sonderzeichen, die gemappt werden" );

oder von der vorhandenen LCD Klasse eine neue Klasse ableiten (warum ist 
mir das nicht früher eingefallen! Arduino ist ja C++)
1
#include <LiquidCrystal.h>
2
3
class MappedLiquidCrystal : public LiquidCrystal
4
{
5
  public:
6
    // Die Konstruktoren, die du brauchst um die Parameter
7
    // an die Basisklasse bereit zu stellen
8
    MappedLiquidCrystal ....
9
10
    using LiquidCrystal::print;
11
    void print( const char * string );
12
    void write( byte char );
13
};
14
15
inline void MappedLiquidCrystal::write( byte data )
16
{
17
  if( c == '#' )
18
    LiquidCrystal::write( 0 );
19
  else if( c == '§' )
20
    LiquidCrystal::write( 1 );
21
  else if( c == '°' )
22
    LiquidCrystal::write( 2 );
23
24
  else
25
    LiquidCrystal::write( c );
26
}
27
28
inline void MappedLiquidCrystal::print( const char * string )
29
{
30
  while( *string )
31
    write( *string++ );
32
}

Das gibt einem dann auch noch eine gute Position, die Definition der 
Sonderzeichen im Konstruktor unterzubringen und so nicht darauf zu 
vergessen. Und sie wäre dann sogar noch ein guter Platz, um überhaupt 
anwendungsspezialisierten Funktionen eine Heimstätte in deinem Projekt 
zu geben. Du hast dann praktisch eine für die Anwendung 
massgeschneiderte LCD Klasse, die genau das kann, was du in DIESER 
Applikation benötigst.
Noch eleganter ist dann nur noch Magie.

von Thomas D. (lightscape66)


Lesenswert?

Hallo,

erst jetzt komme ich dazu, mich des Themas mal wieder an zu nehmen. Ich 
muss gestehen, von Objektorientierung habe ich nicht viel Ahnung und bei 
so kleinen Progrämmchen ist das meiner Meinung nach mit Kanonen auf 
Spatzen geschossen.
Zudem habe ich im Moment massive Probleme, wieder in die 
C-Programmierung selber rein zu kommen. Ich begreife einfach den Mumpf 
mit den Pointern nicht so wirklich. Ich habe zwar ein Lehrbuch dazu, 
aber damit komme ich einfach nicht weiter... vielleicht bin ich zu blöd 
dazu, das umzusetzen. Mein letzter Kontakt mit C ist halt schon 20 Jahre 
her und da habe ich mich da auch eher schlecht als recht 
durchgewurstelt, einfach weil sich niemand mal die Zeit genommen hat, 
mir die ganzen Zusammenhänge verständlich zu erklären.

Natürlich habe ich mir die LiquidCrystal-lib inzwischen angeschaut und 
ich werde die wohl einfach um eine Funktion erweitern, die das tut, was 
ich will. Statt *lcd.write* soll das Teil dann beispielsweise 
*lcd.writes* heißen und diese Funktion soll dann nicht ein Zeichen 
ausgeben, sondern mehrere.

Ich habe also ein Array:
1
char matrix[176];
Dort habe ich die Character-Codes hinterlegt, die ich benötige. In der 
Regel sind das halt die Werte von 0-7 und 32.

Jetzt möchte ich also der Funktion lcd.writes den Pointer auf dieses 
Array übergeben und die Anzahl der auszugebenden Bytes. Die Funktion 
greift dann über den Pointer auf den Array-Inhalt zu und gibt die 
gewünschte Anzahl von Zeichen aus, wie es eben lcd.write auch macht. 
(Die Funktion heißt, glaube ich, send)
Das ist vielleicht nicht wirklich elegant, aber ich denke, da ich die 
Funktion grundsätzlich neu definiere, müsste der erzeugte Code deutlich 
performanter sein.

Das fertige Programm soll später einfach eine Uhr mit etlichen 
Sonderfunktionen (diverse Alarmzeiten, Schaltausgänge, evtl. HF-Sender 
für HomeEasy, evtl. V24-Kommandointerface für weitere Steuerfunktionen) 
werden. Als Display nutze ich ein LCD mit 4*20 Zeichen, kompatibel zu 
dem Standard-Hitachi-Display. Die Ziffern sollen für die 
Standard-Anzeige das gesamte Display nutzen und das funktioniert bislang 
so weit wunderbar; ich habe nur das Gefühl, dass der Code etwas 
schneller arbeiten sollte.... Daher der ganze Aufstand. ;-)

Wenn Dich interessiert, was ich bislang zusammen codiert habe, poste ich 
beim nächsten Mal gerne den Quelltext.

Ciao,
Thomas

von Karl H. (kbuchegg)


Lesenswert?

Thomas Dieckmann schrieb:

> Wenn Dich interessiert, was ich bislang zusammen codiert habe, poste ich
> beim nächsten Mal gerne den Quelltext.

Wozu?
Du nimmst ja sowieso keinen Rat an.


> ich habe nur das Gefühl, dass der Code etwas schneller arbeiten sollte
Das liegt aber selten an den LCD Routinen. Wenn du 5 Ausgaben pro 
Sekunde auf das LCD machst, bist du als Mensch mit Ablesen überfordert. 
Und 5 Ausgaben pro Sekunde kriegt auch der langsamste Ausgabe-Code 
problemlos hin.

Denn worauf läuft es denn letzten Endes ganz unten hinaus (ich geh von 
4Bit Ansteuerung aus)?
  Nibble extrahieren
  Nibble anlegen
  warten
  E-Pin hochziehen
  warten
  E-Pin runer
  das andere Nibble extrahieren
  Nibble anlegen
  warten
  E-Pin hochziehen
  warten

demgegenüber (vor allem dem warten), fallen die 8 ifs, die zu jeweils 
einem Compare mit einer Konstanten, einem Branch und fallweise einem LDI 
expandieren, überhaupt nicht ins Gewicht. Ohne das jetzt gerechnet zu 
haben, behaupte ich mal wir reden da über Prozente im einstelligen 
Bereich - wenn überhaupt. Und das 5 oder 10 mal pro Sekunde - lohnt 
nicht. Da bringt eine saubere Organisation mehr.

von Thomas D. (lightscape66)


Lesenswert?

Hallo,

ab und an nehme ich sehr gerne den Rat anderer Leute an. Aber mag sein, 
dass wir irgendwie aneinander vorbei geredet haben.

Ich habe mir also in der LiquidCrystal-library in der Datei 
LiquidCrystal.cpp eine eigene Funktion angelegt:
1
inline void LiquidCrystal::writes(char *mat, int stpos, int lange) {
2
   for (int c = stpos; c < stpos + lange; c++) {
3
      send(mat[c], HIGH);
4
   }
5
}

Natürlich habe ich auch die Keywords und die .h ergänzt.


Um nun eine Jumbo-Ziffer aus zu geben, mache ich folgendes:
1
char matrix[176] = {
2
   4,  0,  0,  6,      2, 32, 32,  3,      2, 32, 32,  3,      5,  1,  1,  7,    // 0
3
  32,  1,  2, 32,     32, 32,  2, 32,     32, 32,  2, 32,      1,  1,  5,  1,    // 1
4
   0,  0,  0,  6,      1,  1,  1,  7,      2, 32, 32, 32,      5,  1,  1,  1,    // 2
5
   0,  0,  0,  6,     32,  1,  1,  7,     32, 32, 32,  3,      1,  1,  1,  7,    // 3
6
   2, 32, 32, 32,      5,  1,  5,  1,     32, 32,  2, 32,     32, 32,  2, 32,    // 4
7
   4,  0,  0,  0,      5,  1,  1,  1,     32, 32, 32,  3,      1,  1,  1,  7,    // 5
8
   4,  0,  0,  0,      5,  1,  1,  1,      2, 32, 32,  3,      5,  1,  1,  7,    // 6
9
   0,  0,  6, 32,     32,  1,  7,  1,     32, 32,  3, 32,     32, 32,  3, 32,    // 7
10
   4,  0,  0,  6,      5,  1,  1,  7,      2, 32, 32,  3,      5,  1,  1,  7,    // 8
11
   4,  0,  0,  6,      5,  1,  1,  7,     32, 32, 32,  3,      1,  1,  1,  7,    // 9
12
   2,  1,  1,  3,      5,  7,  5,  7,      4,  6,  4,  6,      2,  0,  0,  3     // Error Symbol
13
};
14
15
16
17
void paint(int ziffer, int stelle){
18
  int izeile, zeiger;
19
20
  zeiger = ziffer;
21
  if (ziffer < 0 || ziffer > 9) zeiger = 10;
22
23
  xkoord = xpos[stelle-1];
24
  if (stelle < 1 || stelle > 4) xkoord = 0;
25
26
  for (izeile = 0; izeile < 4; izeile++)
27
  {
28
     lcd.setCursor(xkoord,izeile);
29
     lcd.writes(matrix,zeiger*16+izeile*4,4);
30
  }
31
}

Also, ich finde das sehr elegant und ich schätze auch, dass das recht 
schnellen Code gibt. Wenn ich da jetzt irgendwas zusammen gecodet haben 
sollte, was "man" einfach nicht tut, erkläre es mir und vielleicht 
begreife ich es dann ja auch?

Ciao,
Thomas



Karl Heinz Buchegger schrieb:
> Thomas Dieckmann schrieb:
>
>> Wenn Dich interessiert, was ich bislang zusammen codiert habe, poste ich
>> beim nächsten Mal gerne den Quelltext.
>
> Wozu?
> Du nimmst ja sowieso keinen Rat an.
>
>
>> ich habe nur das Gefühl, dass der Code etwas schneller arbeiten sollte
> Das liegt aber selten an den LCD Routinen. Wenn du 5 Ausgaben pro
> Sekunde auf das LCD machst, bist du als Mensch mit Ablesen überfordert.
> Und 5 Ausgaben pro Sekunde kriegt auch der langsamste Ausgabe-Code
> problemlos hin.
>
> Denn worauf läuft es denn letzten Endes ganz unten hinaus (ich geh von
> 4Bit Ansteuerung aus)?
>   Nibble extrahieren
>   Nibble anlegen
>   warten
>   E-Pin hochziehen
>   warten
>   E-Pin runer
>   das andere Nibble extrahieren
>   Nibble anlegen
>   warten
>   E-Pin hochziehen
>   warten
>
> demgegenüber (vor allem dem warten), fallen die 8 ifs, die zu jeweils
> einem Compare mit einer Konstanten, einem Branch und fallweise einem LDI
> expandieren, überhaupt nicht ins Gewicht. Ohne das jetzt gerechnet zu
> haben, behaupte ich mal wir reden da über Prozente im einstelligen
> Bereich - wenn überhaupt. Und das 5 oder 10 mal pro Sekunde - lohnt
> nicht. Da bringt eine saubere Organisation mehr.

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.