Forum: Compiler & IDEs Frage zum Thema LCD Library für die Arduinos
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
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.
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
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
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
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.
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:
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
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.
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.
|