Forum: Mikrocontroller und Digitale Elektronik Sonderzeichen LCD HD44780 kompatibel


von safran (Gast)


Lesenswert?

Guten Abend!
Ich habe mir mal die Mühe gemacht, die Oktalcodes für die Darstellung 
von einigen Sonderzeichen auf HD44780-kompatiblen-LCD-Displays 
zusammenzustellen. Umlaute kann man damit bei der Verwendung eines 
Arduinos z.B. wie folgt darstellen:

lcd.print("R\365cklauf"); //wird dargestellt als" Rücklauf"
````
\40  !
\41  "
\42  #
\43  $
\44  %
\45  &
\50  (
\51  )
\52  *
\53  Plus-Zeichen
\54  Komma
\55  Minus-Zeichen
\56  Punkt
\57  Schrägstrich
\72  Doppelpunkt
\73  Semikolon
\74  <
\75  Gleichheitszeichen
\76  >
\77  ?
\100  @
\134  eckige Klammer links
\136  eckige Klammer rechts
\137  accent circonflexe
\138  Unterstrich
\140  accent grave
\173  geschweifte Klammer links
\174  senkrechter Strich
\175  geschweifte Klammer rechts
\176  Pfeil nach rechts
\177  Pfeil nach links
\260  Minuszeichen
\333  Kastenrahmen
\337  hochgestellter Kastenrahmen(wie Potenz)
\340  gr. alpha
\341  ä
\342  ß
\343  klein epsilon
\344  µ
\350  Wurzelzeichen
\351  hoch minus 1
\353  hoch x
\356  n mit Oberstrich ( spanisch )
\357  ö
\363  Zeichen unendlich
\364  Ohm
\365  ü
\366  gr.Summe
\367  pi ( klein )
\371  u mit strich rechts unten
\375  geteilt durch
\377  alle Leuchtpunkte eingeschaltet
````

: Bearbeitet durch Moderator
von Karl H. (kbuchegg)


Lesenswert?

safran schrieb:
> Guten Abend!
> Ich habe mir mal die Mühe gemacht, die Oktalcodes für die Darstellung
> von einigen Sonderzeichen auf HD44780-kompatiblen-LCD-Displays
> zusammenzustellen.

Die Mühe hättest du dir sparen können.
Entsprechende ASCII-Tabellen sind in jedem LCD-Datenblatt enthalten
bzw. werden mit Google und dem Stichwort "hd44780 zeichensatz" in 2 
Sekunden gefunden

http://de.wikipedia.org/wiki/HD44780#Schrift_und_Zeichensatz

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz Buchegger schrieb:
> safran schrieb:
>> Guten Abend!
>> Ich habe mir mal die Mühe gemacht, die Oktalcodes für die Darstellung
>> von einigen Sonderzeichen auf HD44780-kompatiblen-LCD-Displays
>> zusammenzustellen.
>
> Die Mühe hättest du dir sparen können.
> Entsprechende ASCII-Tabellen sind in jedem LCD-Datenblatt enthalten
> bzw. werden mit Google und dem Stichwort "hd44780 zeichensatz" in 2
> Sekunden gefunden
>
> http://de.wikipedia.org/wiki/HD44780#Schrift_und_Zeichensatz


Entschuldige.
Ich hab ganz übersehen, dass du ja Oktalcodes angegeben hast. Benutzt 
zwar keiner, aber die sind in den Tabellen natürlich nicht enthalten.

von safran (Gast)


Lesenswert?

....und dieser Code ist so wunderbar einfach in einen Text einzufügen! 
:-)

von Karl H. (kbuchegg)


Lesenswert?

halb so wild.
da macht man sich einmalig eine Ausgabefunktion, die die wichtigsten 
Ersetzungen zb für die Umlaute durchführt und für den Rest ein paar 
#define, die ins Header File für die LCD-Funktionen dazukommen.


#define OHM   "\xF4"

und schreibt dann zb

   lcd.print( "480" OHM );

dann kann man das Ganze sogar im Code direkt lesen :-)

von Jobst M. (jobstens-de)


Lesenswert?

... und dann ist da noch die Frage, für welchen HD44780 diese Tabelle 
ist. Die gibt es nämlich mit unterschiedlichen Zeichensätzen ...


Gruß

Jobst

von Matthias S. (Firma: matzetronics) (mschoeldgen)


Lesenswert?

Jobst M. schrieb:
> Die gibt es nämlich mit unterschiedlichen Zeichensätzen ...

Allerdings. Ich drücke euch die Daumen, das ihr nie einen mit ROM 
Kennung B04 bekommt - da hat es einige Zeit gedauert, bis ich die 
richtige Übersetzungstabelle drin hatte, weil da nicht mal die ASCII an 
der richtigen Stelle waren...
Die gebräuchlichsten Versionen sind A00 (ASCII und japanisch) und A02 
(ASCII und europäisch), nachzulesen im Hitachi Datenblatt ab Seite 17.

von Alexander Predl (Gast)


Lesenswert?

Sorry für das alte Foren Posting popup... Aus Aktuellem Anlass:

also ich fand die Tabelle echt hilfreich, hat auf Anhieb funktioniert!!

Mein LCD Display ist von Adafruit, verwendet den I2C Bus und ich steuere 
das Ding mit dem Arduino Mega an.

Vielen Dank für die Mühe!

von Tim (Gast)


Lesenswert?

Karl H. schrieb:
> Benutzt zwar keiner

Anscheinend haben wir nach 2 Jahren einen gefunden.

von Elektrodoodle (Gast)


Lesenswert?

Auch wenn der Beitrag zunächst vielleicht unnötig erscheint, mir hat er 
geholfen :D. Da die Ausgabe mit Ascii und UTF-8 vom Arduino nicht 
richtig funktionierte (gab immer ein japanisches Zeichen davor) war ich 
kurz vor dem verzweifeln. Dank deinem Beitrag ging es dann ;)

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Fast 4 Jahre alt und nur ein halbes Jahr nach dem letzten Post:
Wieder Einer, dem Du mit Deiner Liste in kurzer Zeit geholfen hast.

Bei mir waren es 'Durchläufe', wo ein japanisches Schriftzeichen den 
Display-Inhalt auflockerte.

MfG

von JamsingSama (Gast)


Lesenswert?

Huhu,

und zwei Tage später hat es mir auch geholfen. :) Danke

von Gerd (Gast)


Lesenswert?

...und noch einer!
Vielen Dank für die Mühe, das war genau was ich gesucht habe!

von Nop (Gast)


Lesenswert?

Oktal? Kenne ich eigentlich nur von Sprit. ;-)

von Bernd (Gast)


Lesenswert?

Sinnvoll wäre eine Möglichkeit, dem Präprozessor/Compiler beizubringen, 
die Ersetzungen (z.B. ü -> \365) automatisch zur Compilezeit zu machen, 
sodass man Strings ganz einfach so schreiben kann, wie man sie meint. 
Und ohne eine ressourcenfressende Funktion zur Laufzeit bemühen zu 
müssen...

von noch ein gast (Gast)


Lesenswert?

Nop schrieb:
> Oktal? Kenne ich eigentlich nur von Sprit. ;-)

Selbst das kennst du nicht.

von Harald W. (wilhelms)


Lesenswert?

Nop schrieb:

> Oktal? Kenne ich eigentlich nur von Sprit. ;-)

Wir sind hier in einem Elektronikforum.
Da ist dieses Wort für Röhren reservert.
:-)

: Bearbeitet durch User
von Light up the magic in every little part (Gast)


Lesenswert?

Harald W. schrieb:
> Wir sind hier in einem Elektronikforum.
> Da ist dieses Wort für Röhren reservert.

Das haut mich vom Sockel.

von Harald W. (wilhelms)


Lesenswert?

Light up the magic in every little part schrieb:

>> Wir sind hier in einem Elektronikforum.
>> Da ist dieses Wort für Röhren reservert.
>
> Das haut mich vom Sockel.

Jetzt isse putt. :-(

von Klaus (Gast)


Lesenswert?

Hi ich bin auch darüber gestolpert weil mein Hitachi Display die 
Sonderzeichen auch nicht kann :-(

Aaaaber, bei mir funktioniert die Liste garnicht!!

zb sieht das OHM Zeichen wie ein kleines w aus und im Monitorfenster wie 
ein Viereck

und lcd.print("R\365cklauf");
da sieht das ü wie ein "Pfeil nach unten" aus und im Monitorfenster wie 
ein Viereck

Habe Arduino 1.8.5 (die neueste Version)...und alles läuft auf einem 
alten WinXP-Rechner.

Weiß jemand Rat?

von Patrick J. (ho-bit-hun-ter)


Lesenswert?

Hi

Das wird damit zusammen hängen, daß der Zeichensatz in Deinem Display 
ein Anderer ist - chinesisch wäre Da ganz groß im Kurs.
Google wird Dir die Zeichentabellen und Deren Unterschiede zeigen 
können.
Wenn Du eine Bezeichnung für Dein Display hast, um so besser.
Sonst musst Du über den verbauten Controller gehen.

MfG

Hmm, eigentlich hätte Es aber funktionieren sollen :/
Passt der verbaute Controller?
Sonst müsste ich Mal nach meinem 'Durchläufe' Beispiel (vor 10 Monaten 
fand ich diesen Thread auch Mal) heraussuchen.

: Bearbeitet durch User
von Klaus (Gast)


Lesenswert?

Was mich etwas irritiert ist das die Umlaute in dem Monitorfenster auch 
nicht angezeigt werden...

Mein controler ist so eine 2 euro china teil, Nano V3 368 auf dem der 
auch gleich der COM3 CH340G drauf ist.

von Klaus (Gast)


Lesenswert?

Hey keiner Antwortet mehr?!
Ok also ich habe festgestellt, das meine Displays nicht die berühmten 
1602 sind (sie sehen optisch nur so aus), deshalb wird für wohl ein 
andere code benötigt werden :-(
Ich habe aus einem LC-Meter das Display ausgebaut und damit 
funktionieren die Umlaute.

Fall geklärt -> ich muß mir jetzt paar neue 1602 besorgen.

von Busy B (Gast)


Lesenswert?

Hallo,
der Beitrag ist sehr hilfreich, trotz des Alters!
Gibt es auch Codes für die Umlaute in Großschreibung?

von spess53 (Gast)


Lesenswert?

Hi

>Gibt es auch Codes für die Umlaute in Großschreibung?

Für welchen Displaycontroller mit welchem Zeichensatz?

Textdisplays haben die Möglichkeit acht selbst definierte Zeichen 
auszugeben. Das langt normalerweise für 'AÖÜ'.

MfG Spess

von c-hater (Gast)


Lesenswert?

Busy B schrieb:

> Gibt es auch Codes für die Umlaute in Großschreibung?

Das kommt auf das Display an. Typischerweise gibt's ein Datenblatt dazu, 
in dem der oder die Zeichensätze dokumentiert sind. Wenns nur einer ist, 
kannst du also sofort und direkt sehen, welche Glyphen er anbietet und 
über welchen Code diese jeweils zu erreichen sind.

Gibt es mehrere Zeichensätze und weißt du nicht, welcher dieser 
Varianten dein Display entspricht, dann ist die Sache  auch nicht sehr 
viel schwieriger: Du suchst dir einen Code aus, dessen Glyph in jedem 
der möglichen Zeichensätze unterschiedlich aussieht und gibst eben 
diesen Code aus. Aus dem tatsächlich dargestellten Zeichen kannst du 
dann direkt den verwendeten Zeichensatz ermitteln. Wenn du den kennst, 
kannst du wieder die restlichen Codes direkt ablesen.

Kurzfassung: Datenblatt lesen hilft.

von Peter D. (peda)


Lesenswert?

Einfach im lcd_putchar für die 6 Umlaute und 'ß' ein kleines switch/case 
einbauen.
Aber Achtung Fallgrube, das char Zeichen muß im switch zu uint8_t 
gecasted werden, sonst schlägt der Vergleich fehl!

von Karl B. (gustav)


Angehängte Dateien:

Lesenswert?

spess53 schrieb:
> Textdisplays haben die Möglichkeit acht selbst definierte Zeichen
> auszugeben. Das langt normalerweise für 'AÖÜ'.

Hi @Spess,

Den Thread kennen wir ja noch. ;-)
Stichwort:

"user defined characters hd44780"

Es sind - wie bekannt - die unbenutzten Fernschreibcode-Steuerzeichen, 
deren Platz (Adressen) man eben noch anderweitig verwenden kann.

Hat nur den Nachteil, dass nach Abschalten der Versorgungsspannung diese 
Charakters verschwinden, und beim "Neustart" jedesmal wieder eine 
Routine durchlaufen werden muss. So seh' ich das jedenfalls.

ciao
gustav

von Dominik (Gast)


Lesenswert?

Karl B. schrieb:
> Hat nur den Nachteil, dass nach Abschalten der Versorgungsspannung diese
> Charakters verschwinden, und beim "Neustart" jedesmal wieder eine
> Routine durchlaufen werden muss. So seh' ich das jedenfalls.

Je nachdem wie lange der Strom weg war. Finde ich persönlich übrigens 
die beste Eigenschaft der User-Defined-Characters, da es sich um Ram 
handelt schreibt man es nicht kaputt und das ermöglicht einem eine sehr 
interessante Nutzung, die allerdings fast nirgends erwähnt wird (auch in 
den Tutorials hier auf der Seite nicht).

Normalerweise erzeugt man den Character und gibt diesen im Anschluss auf 
dem Display aus. Damit kann man schöne Bargraph-Anzeigen und ähnliches 
erzeugen, allerdings bei miserabler Auflösung und hoher 
Platzverschwendung.

Es geht aber auch umgekehrt: Character initialisieren, auf das Display 
ausgeben und dann "manipulieren".
Sprich den CGRam beschreiben während der Character bereits angezeigt 
wird. Damit lassen sich dann nette Animationen oder Bargraphs erstellen, 
die eine wesentlich höhere Auflösung haben. Ein senkrechter Bargraph auf 
einem Xx4er LCD kann dann bis zu 5 unterschiedliche Werte mit je einer 
Auflösung von 32 (5x8er Zeichen) Stufen darstellen und kostet nur eine 
Spalte.

Gruß Dominik

von Karl B. (gustav)


Angehängte Dateien:

Lesenswert?

Dominik schrieb:
> und das ermöglicht einem eine sehr
> interessante Nutzung, die allerdings fast nirgends erwähnt wird (auch in
> den Tutorials hier auf der Seite nicht).

OK,
wer suchet, der findet.
Ist zwar schon etwa 2 Jahre her. Sicher noch verbesserungsbedürftig. 
Aber als Quick-and-dirty-Lösung schon einmal nicht ganz verkehrt.
Die "Fallstricke" sind eben in den Screenshots noch kurz angerissen.


Ein Witz im Programm ist, dass in den Tabellen die Nullterminierung 
nicht verwendet wird, sonst könnte man die Null nicht als Pixelwert 
verwenden. Die Tabelle würde da stoppen.

cpi  temp2, 0x09    ; insgesamt achtmal Pixelwerte holen
breq  print_end0  ; wenn 9, dann zu print_end0


ciao
gustav

: Bearbeitet durch User
von Jörg R. (joggel67)


Lesenswert?

Wie schön dass es doch immer noch so alte Foren gibt in denen man immer 
wieder hilfreiche Tips findet.
Danke für die Tabelle, so schnell und einfach hab ich noch keine Umlaute 
auf`s Display gebracht....

von werner (Gast)


Lesenswert?

zwei Jahre später kann man einfach eine entsprechende Library verwenden, 
die die deutschen Umlaute direkt ausgeben kann:

https://werner.rothschopf.net/202003_arduino_liquid_crystal_umlaute.htm

klappt für HD44780U/ROM Code A00 und für den ST7070

von merciMerci (Gast)


Lesenswert?

werner schrieb:
> zwei Jahre später kann man einfach eine entsprechende Library verwenden,
> die die deutschen Umlaute direkt ausgeben kann:

Hi Werner,
bin auf deiner Seite unterwegs gewesen ..., viel/gute Arbeit deine 
Projekt Tutorials!

Kleiner Hinweis zu deinen verwendeten "Timeouts" ...
http://oneweekwonder.blogspot.com/2017/04/sign-up-to-signed-timeouts.html

ich würde mal darüber nachdenken ... :-)!
1
if (millis() - previousMillis > 3 * 1000UL && digitalRead(outputPin) == HIGH)

von Bauform B. (bauformb)


Lesenswert?

Bernd schrieb:
> Sinnvoll wäre eine Möglichkeit, dem Präprozessor/Compiler beizubringen,
> die Ersetzungen (z.B. ü -> \365) automatisch zur Compilezeit zu machen,
> sodass man Strings ganz einfach so schreiben kann, wie man sie meint.
> Und ohne eine ressourcenfressende Funktion zur Laufzeit bemühen zu
> müssen...

Wer den gcc sowieso aus den Quellen selbst baut, kann das einbauen 
(glaube ich; ungetestet, sieht aber einfach aus). In 
src/libiconv-1.xx/HACKING heißt es:
1
Adding new encodings
2
====================
3
For an indication which files need to be modified when adding
4
a new encoding, look for example at the 2007-05-25 ChangeLog
5
entry for RK1048.  The lib/*.h file for an encoding is usually
6
generated by one of the tools in the tools/directory.
7
All you need to provide is the conversion table in the format
8
of the many *.TXT files.
Im wesentlichen ist das eine Tabelle <HD44780-encoding> zu 
<unicode-encoding>. Zum Beispiel ein Ausschnitt aus 
src/libiconv-1.15/tests/ISO-8859-15.TXT, wo das Euro-Zeichen umkodiert 
wird:
1
0xA2    0x00A2
2
0xA3    0x00A3
3
0xA4    0x20AC
4
0xA5    0x00A5
Ganz Faule könnten einfach eine vorhandene Tabelle umbauen, die sonst 
niemand braucht ;)

Mit "-fexec-charset=HD44780-A00" würde man dem gcc sagen, dass er die 
neue Tabelle benutzen soll.

von Henrik H. (Firma: TU Chemnitz) (heha)


Lesenswert?

Das mit dem Hack des C-Compilers KANN man machen. Der richtige Weg zur 
Darstellung von '\', '~', Kleinbuchstaben mit Unterlänge (g,j,p,q,y) 
sowie Umlauten und Sonderzeichen geht NUR mit einer Transkodierung.

Dabei ist zu unterscheiden, ob die 8 selbst definierbaren Zeichen 
statisch oder dynamisch (also je nach tatsächlicher Anzeige) festgelegt 
werden.

Und es ist zu unterscheiden, ob die Anzeigestrings parallel auch via 
serielle Schnittstelle (o.ä.) ausgegeben werden sollen oder nicht.

* Parallelausgabe seriell: Konvertierung CP1252 oder (moderner aber 
wegen Multi-Byte komplizierter) UTF-8 in Hitachi-Kode ZUR LAUFZEIT.

* Keine Parallelausgabe, selbst definierte Zeichen DYNAMISCH: 
Transkodierung ZUR LAUFZEIT.

* Keine Parallelausgabe, selbst definierte Zeichen STATISCH: 
Transkodierung ZUR COMPILEZEIT möglich.

Im folgenden wird der letzte (einfachste) Fall betrachtet.

Für den Programmierer sieht so ein Funktionsaufruf angenehm aus:

    lcd_puts("tränenüberströmt");

Oder auch

    int t, // Zehntel °C
        r; // Zehn Ω
    lcd_printf("T =%5,1d °C und R =%5,2d kΩ", t, r);

Zu dem eigenwilligen Formatstring ein anderer Beitrag:
"Ein selbst programmiertes printf() mit Komma und Dezimalstellen".

Mit den Mitteln von C++ (ab C++14) ist es endlich möglich, genau dieses 
zur Compilezeit zu erledigen: Das Transkodieren der Zeichenkette in 
Hitachi-Codebytes und das Ablegen in den Flash-Speicher.
Dreh- und Angelpunkt ist das constexpr-Schlüsselwort.

Als erstes sollte klar sein, dass ein String wie "°C" aus 2 oder 3 Byte 
bestehen kann, je nachdem ob der Quelltext in ANSI (CP1252) oder UTF-8 
gespeichert ist. Standardmäßig nimmt gcc solche Strings buchstäblich, 
und das Ergebnis hängt von der Editor-Codeseite ab!!

Als Konsequenz ist so etwas komplett zu vermeiden!

Portabel aber unschön lesbar sind Hexadezimal- oder Oktal-Ersetzungen 
wie oben abgegeben. Mehrere Möglichkeiten:

    "\xDFC" // °C
    "\xDF" "C"  // nutzt C/C++-Zeichenkettenzusammensetzung
    "\337C" // °C
    "\337""C"  // wiederum getrennt, Leerzeichen unnötig
    #define GRD "\xDF"
    GRD"C"

Aber genau das will man ja bei einem gut lesbaren Quelltext vermeiden.
Zeichenersetzung zur Compilezeit ist SYNTAKTISCHER ZUCKER.
Es sollte einem klar sein dass die Ersetzungsroutinen nicht gerade 
leicht lesbar sind und der Compiler auch mehr Zeit braucht.
Zum praktischen Teil ein weiterer Abschnitt.

von Henrik H. (Firma: TU Chemnitz) (heha)


Lesenswert?

Zwischendurch ein paar C-Stolperfallen:

* Woher detektiert der Compiler das Ende des Oktal-Zeichens?

    "\3370"
    "\33[A"
    "\0337C"

* Lösung: Der Compiler liest maximal 3 Ziffern ein und bricht bei der 
ersten Nicht-Ziffer ab. Was passiert wenn '8' oder '9' angegeben werden 
ist implementationsabhängig und sollte eine Warnung generieren. Auch was 
passiert wenn die Oktalzahl größer 255 wird ist implementationsabhängig.

'\0' ist nichts anderes als ein oktal angegebenes Zeichen.

* Woher detektiert der Compiler das Ende von Hexadezimal-Zeichen?

    "\xak" // = {0x0A,'k'}
    "\x909" // = {0x90, '9'}

* Lösung: Genau wie bei Oktalzahlen maximal 2 Ziffern, bei der ersten 
Nicht-Hexadezimalziffer abbrechend.

* Von welchem Typ ist der Ausdruck

    "abc"[1]

* Lösung: Nicht char, sondern (einpauken!!) int.

von Henrik H. (Firma: TU Chemnitz) (heha)


Lesenswert?

Die Konvertierroutine kann so aussehen:

    typedef unsigned char byte; // dem Volk aufs Maul geschaut statt 
uint8_t

    namespace hd44780{
    namespace a00{
    constexpr byte tr(wchar_t wc) {
     switch (wc) {
      case L'Ä': return 0x11;
      case L'Ö': return 0x12;
      case L'Ü': return 0x13;
      case L'~': return 0x14;
      case L'\\': return 0x15;
      case L'¥': return 0x5C;
      case L'→': return 0x7E;
      case L'←': return 0x7F;
      case L'°': return 0xDF;
      case L'ä': return 0xE1;
      case L'ß':
      case L'β': return 0xE2;
      case L'ε': return 0xE3;
      case L'µ': return 0xE4;
      case L'ö': return 0xEF;
      case L'Ω':
      case L'Ω': return 0xF4;
      case L'ü': return 0xF5;
      case L'÷': return 0xFD;
      case L'p':
      case L'q': return byte(wc+0x80); // mit Unterlänge
     }
     return byte(wc);
    }

Die Verwendung von wchar_t mag erst mal erschrecken. Da die 
Transkodierung zur Compilezeit stattfindet wird aber kein RAM oder Flash 
verschwendet. wchar_t (hier: BMP0) sorgt dafür, dass jedes Zeichen 
gleich breit ist.
So ist kein Kuddelmuddel mit der Kodierung des Quelltextes zu erwarten, 
da gcc automatisch richtig (in UTF-16) transkodiert.

von Henrik H. (Firma: TU Chemnitz) (heha)


Lesenswert?

Die Konvertierroutine *tr()* im vorhergehenden Beitrag mag verwundern, 
dass  2× auftaucht. Einmal als „Buchstabenartiges Symbol“ (U+2126) 
und einmal als griechischer Großbuchstabe (U+03A9). Laut 
Unicode-Standard soll letzteres bevorzugt werden, aber wer weiß das 
schon?

Jetzt geht's ans Eingemachte: Wie konvertiert man einen String? In C 
geht das nicht.
Hier wohl nicht die beste / eleganteste Methode, aber so habe ich's erst 
mal gelöst. Noch im Namensraum hd44780::a00:

    template<size_t N> struct str{
      byte a[N] {};  // ohne {} geht's nicht!
      constexpr str(const wchar_t s[]) {
        for (size_t i=0; i<N; i++) a[i] = tr(s[i]);
      }  // Array besetzen
      operator const byte*() const {return a;}
    };

Um die Stringlänge N zu erhalten bin ich über ein #define gegangen:

    }  // Ende Namensraum hd44780::a00
    #define A00(s) a00::str<sizeof L##s/2>(L##s)
    }} // Ende Namensraum HD44780

Um die Bytekette im Flash abzulegen ist dann zu schreiben:

    static const auto PROGMEM mystr = hd44780::A00("tränenüberströmt");

Das Schlüsselwort auto erlaubt das Trennen von Variablenname und 
Konstruktor. Der Compiler eliminiert dabei den Kopierkonstruktor *=*.
Das #define pappt das L an den String voran, dass man's nicht vergisst.

Um darauf (lesend) zuzugreifen kann man in etwa schreiben:

    lcd_puts_P(mystr);

Immer noch umständlich. Ich muss mir daher noch ausdenken, wie mystr 
anonym gemacht werden kann.
Und wie der Funktionsaufruf A00() durch einen eleganteren Suffixoperator 
ersetzt werden kann. Das Endziel sieht so aus:

    static const byte PROGMEM mystr[] = "tränenüberströmt"_a00;

: Bearbeitet durch User
von Henrik H. (Firma: TU Chemnitz) (heha)


Lesenswert?

So, nun zum Generieren anonymer Zeichenketten im Flash. Ein Blick in 
<avr/pgmspace.h>, Makro PSTR, zeigt wie es geht:

    #define P00(s) (__extension__({static const PROGMEM 
hd44780::a00::str<sizeof L##s/2> c(L##s); c.a;}))

Es ist tatsächlich ein weiteres Makro erforderlich. A00 und P00 können 
nicht zusammengelegt werden. Der Typecast-Operator ist dazu überflüssig. 
Die Anwendung ist wie bei PSTR:

    lcd_puts_P(P00("Test ÄÖÜäöüß °C kΩ"));

Das erscheint erst mal komfortabel genug.

Lästig ist, dass man beim AVR immer die beiden Adressräume, RAM und 
Flash, im Hinterkopf behalten muss. Dabei gibt es beim Arduino einen 
Lösungsansatz für die meisten AVR, die ≤ 32 KByte Flash haben: Der 
Adressraum wird von-neumannisiert, indem Flash-Adressen mit gesetztem 
Bit 15 markiert werden:

    #define F00(s) (__extension__({static const PROGMEM 
hd44780::a00::str<sizeof L##s/2> c(L##s); c.a+0x8000;}))

Die Warnung „index out of range“ muss unterdrückt werden.

Die Anwendung ist dann:

    lcd_puts(F00("Test ÄÖÜäöüß °C kΩ"));

lcd_puts muss dann zur Laufzeit unterscheiden, ob eine RAM- oder 
Flash-Adresse vorliegt. Leider macht das der LPM-Assemblerbefehl nicht 
schon selbst wie bei modernen, vierstelligen PIC-Controllern, sondern 
das muss die Software machen. Da die Fallunterscheidung nicht viel 
kostet, kann man das per Byte machen:

    inline byte read_byte(const void*p) {
     byte r;
     asm( " sbrc %B1,7  \n" // Skip wenn Bit 15 gelöscht
          "  lpm %0,%a1 \n" // Wenn Bit 15 gesetzt, führe lpm aus
          " sbrs %B1,7  \n" // Skip wenn Bit 15 gesetzt
          "  ld  %0,%a1 \n" // Wenn Bit 15 gelöscht, führe ld aus
     :"+r"(r):"z"(p));
     return r;
    }

Die Funktion read_byte liest also vom RAM oder Flash je nach Bit 15 
der Adresse. So als ob der Flash „hinten“ im RAM eingeblendet liegt. 
Wegmskieren muss man überflüssige Bits bei lpm nicht; der Flash 
erscheint mehrfach im 64-KByte-Adressraum. Hingegen ld versucht bei 
überflüssigen Bits externen RAM anzusprechen. Daher geht es nur so 
herum, nicht andersherum.
Schade, dass sich das Adress-Inkrement (lpm r,Z+ bzw. ld r,Z+) nicht so 
einfach einbauen lässt.

Zwar könnte man das gleiche Verfahren auch mit dem EEPROM machen, dann 
ist's aber nicht mehr inline sinnvoll. Dann schlägt das unintelligente 
Register-Handling bei Funktionsaufrufen von gcc zu, und der Code bläht 
sich irre auf.

Die Von-Neumannisierung lässt sich auch durch ein geschicktes 
Linkerskript bewerkstelligen. Das erspart das Unterdrücken der 
Out-Of-Range-Warning und das Addieren magischer Offsets, dürfte aber 
Programmer und Debugger erheblich durcheinander bringen.

: Bearbeitet durch User
von Wastl (hartundweichware)


Lesenswert?

Henrik H. schrieb:
> Zwischendurch ein paar C-Stolperfallen:

Henrik H. schrieb:
> So, nun zum Generieren anonymer Zeichenketten im Flash.

Henrik H. schrieb:
> Jetzt geht's ans Eingemachte:

Das hast du alles seeeehr schön gemacht!

Nur für die Leute der Zielgruppe, die gerade eben mal ein Character
LCD zum Laufen gebracht hat, werden dies Böhmische Dörfer sein und
bleiben.

von Rick (rick)


Lesenswert?

@Henrik
Deine schöne Ausführung sollte auf eine Wikiseite!

von Falk B. (falk)


Lesenswert?

Rick schrieb:
> Deine schöne Ausführung sollte auf eine Wikiseite!

Ist als Link gespeichert.

https://www.mikrocontroller.net/articles/HD44780#Siehe_auch

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.