
Es gehört auch zum guten Ton bzw. zur guten Optik eine kleine LCD als
Anzeige für das Mikrocontroller Projekt zu verwenden. Als Beispiel das
Projekt MPPT-II
welches einige Herausforderungen stellte. Z.B. der Text-Font, welcher
Micropython zur Verfügung stellt, ist mehr schlecht als recht. Es gibt
ausschließlich einen 8x8-Bit-Font welcher kaum lesbar ist und wenn man
ihn hoch-skaliert, sieht er ziemlich hässlich aus. Deshalb wurden für
die Messwerte ein eigener Font implementiert wie hier mit dem
Source-Code in der Zip-Datei dokumentiert.
Die Beispiele in C und Python, welche die Display Hersteller mitliefern, sind leider sehr aufwändig programmiert. Klar, es soll ja für verschiedene Mikrocontroller und diverse Displays funktionieren. Da sind aber auch Tabellen mit Text Fonts und Grafiken dabei ohne Aufschluss darüber wie diese entstanden sind; Sie sind einfach magisch da. Und dass ein Bildchen in ein Array gepackt eine 2MB großen C-Datei benötigt, ist fast unglaublich; jedenfalls unpraktisch und langsam in der Entwicklungs-IDE.
Hier sind Beispiele, wie man eigene Text-Fonts, Bitmaps und Grafiken einfach und schnell implementieren kann.
Es gibt (zumindest hier) 3 Arten von Grafiken:
Gut geeignet für eine Text-Font Bitmap ist Gimp.
Dazu wird ein neues Bild mit entsprechender Größe definiert und dann
ein Text mit allen Buchstaben eingefügt. Der Hintergrund muss weiß sein
und die Schriftfarbe Schwarz. Beim schreiben des Textes sollte "Kanten
glätten" ausgeschaltet werden um scharfe und regelmäßige Buchstaben zu
erhalten. Die Schriftart "Liberation-Mono-Bold" eignet sich gut. Die
Text-Größe muss entsprechend der gewünschten Größe eingestellt werden.
Nach dem Einfügen können noch einzelne Korrekturen nach Wunsch
vorgenommen werden, z.B. mit dem Stift, scharfe Kante, ein Pixel groß,
Farben schwarz und weiß. Insbesondere kleine Schriften sind teilweise
etwas unschön. z.B. könnte man den Punkt in der Null entfernen. Oder die
Höhe etwas vergrößern und die wenigen Zeichen anpassen welche über den Rand ragen (z.B.
Klammern, j, g, y, ; usw). Auch die Abstände
müssen genau sein. Beim 14x9 Pixel Font muss jeder Buchstabe im
Feldanfang beginnen, also bei 0, 9, 18, 27 usw. Die fertige Grafik wird
dann als .png-Datei exportiert in der entsprechenden Pixel-Größe.
Bei einer Bitmap Grafik zählt nur eine gesamte beliebige Größe. Das Vorgehen sonst ist aber genau wie beim Text-Font.
Die Bitmaps sind als Muster angeordnet in einem Array von
Integer-Worten (Wort = eine Anzahl Bit bzw. Bytes, z.B. 16-Bit = 2 Byte,
32-Bit = 4Byte). In der Vertikalen sind die einzelnen Bits der Werte,
in der Horizontalen fortlaufende Worte. Da die Bit-Breite der Worte oft
vom Rechner abhängt, gibt es in der Regel C-Typen mit entsprechender
Bitbreite, namentlich uint16_t und uint32_t. Bitmaps bis 16 Bit
verwenden also uint16_t, sonst uint32_t. Mehr als 32-Bit geht meist
auch, ist aber für Text Fonts auf einem Mikrocontroller normalerweise
nicht nötig. Hier ein Beispiel des 14x9 Pixel Fonts.

Um einen Text aufs Display zu schreiben, verwenden wir also eine
Funktion, welche den Text verarbeitet (Puts()) und die einzelnen
Buchstaben an eine Funktion gibt, welche diese dann schreibt (Putc()).
Der Buchstabe wird mit einem Zeiger im Array adressiert, also Zeiger = 9
x Buchstabe. Dann werden die Worte des Arrays einzeln an eine Funktion
gegeben, welche das Muster dekodiert und aufs Display bringt
(Bitmap()). Dazu wird eine Funktion verwendet, welche die Pixel
schreibt (PutPixel()).
PutsXY("Text", Font, X, Y, Fg, Bg, Attr) ->
PutcXY('Char', Font, X, Y, Fg, Bg, Attr) -> Bitmap(Map, X, Y, Fg,
Bg, Attr) -> PutPixel(X, Y, Color).
Letztere kann dann in den Framebuffer oder direkt aufs Display schreiben.
Dabei ist der Font ein Index, welcher in einer Tabelle auf das
Font-Array zeigt. So kann ein Font aus mehreren ausgewählt werden.
X, Y ist die Display-Position. Die Funktionen PutsXY() und PutcXY geben
den neuen X-Wert zurück. So kann man Texte und Buchstaben aneinander
reihen.
Man kann auch noch Text Arrtibute verarbeiten. So kann ein Text
unterstrichen werden, indem immer das letzte Bit gesetzt wird. Oder es
können Abstände zwischen den Zeichen eingefügt oder entfernt werden.
PutsXY könnte zusätzlich den Text Links, Mitte oder Rechts ausrichten.
PutBitmap kann außerdem Transparent (nur Vordergrund) oder Noch (nur
Hintergrund) schreiben. So ist eine Vielzahl von Optionen für einen Font
möglich.
Bei einer Bitmap Grafik gehen wir ähnlich vor. Bei der Codierung werden aber zusätzlich die Dimensionen am Anfang des
Arrays eingetragen. So muss man nicht nicht um die Größe kümmern sondern
kann einfach den Zeiger auf das Array und die X-Y Position an eine
Funktion übergeben.
PutBitmap(Array, X, Y, Fg, Bg, Attr) -> Bitmap(Map, X, Y, Fg,
Bg, Attr) -> PutPixel(X, Y, Color).
Für die Attribute gibt es hier aber nur den Transparent und Noch Modus.
Wir verwenden also das Beispiel mit dem 14x9
Pixel Text Font, welcher mit Gimp erstellt wurde und als Datei _font_14x09.png exportiert.
Erst wandeln wir sie in eine unformatierte monochrom Datei. Dazu verwenden wir "convert" aus dem Tool "Imagemagick" mit dem Kommando:
convert -depth 8 +antialias -threshold 50% _font_14x09.png _font_14x09.gray
Das Tool erstellt damit die Datei _font_14x09.gray, welche wir nun mit dem Python-Script gray2c.py
lesen und daraus ein C-Array erstellen mit dem Kommando:
./gray2c.py -w=855 -s=16 _font_14x09.gray
Die Breite -w=n und Höhe -s=n kann man mit dem exiftool
auslesen bzw. ist von Gimp bei der Erstellung bekannt. Die Bitbreite
-s=n muss auf die nächsten 4 aufgerundet werden damit die
Anzahl Hex-Ziffern bestimmt werden kann.
Nun erhalten wir die Datei _font_14x09.arr.
Sie enthält die C-Definition
für das Array _font_14x09[]. Man kann sie in .h umbenennen oder mehrere
Fonts, Bitmaps und Grafiken in einer .h Datei zusammenfassen.
Nun auch noch das Beispiel mit der Bitmap-Grafik BitmapCool.png. Erst konvertieren mit:
convert -depth 8 +antialias -threshold 50% BitmapCool.png BitmapCool.gray
Dann wieder mit gray2c.py:
./gray2c.py -w=120 -s=20 -h BitmapCool.gray
Der Schalter -h dient dazu die Dimension in den Header von BitmapCool.arr zu schreiben, sonst ist alles gleich.
Man kann es auch einfacher mit den Shell-Scripts Font_convert und Bitmap_convert erstellen.
Diese lesen die Dimension mit exiftool und stellen die richtigen Parameter ein. Beispiel:
./Font_convert _font_14x09.png
./Bitmap_convert BitmapCool.png
Möchte man die Daten für ein Python Programm nutzen, kann man einfach
die Array Definition anpassen.
Für eine Grafik kann man z.B. LibreOffice
Draw verwenden. Dazu zur Begrenzung ein Rechteck in entsprechender
Größe platzieren. Z.B. für eine 160x128 Pixel Grafik würde man ein
Rechteck von 160x128mm erstellen und das Raster auf 1mm Stellen. Danach
können beliebige Elemente gezeichnet werden (Farbige Boxen, Kreise,
Texte, Logos usw.). Am Ende kann das Begrenzungsrechteck entfernt werden
oder die Farben entsprechend eingestellt dass es unsichtbar wird (außer
man will es sehen können).
Dann alles markieren (Ctrl-A), exportieren als .png Datei (Option "Auswahl" markieren), die Skalierung wählen (in
unserem Fall 160x128 Pixel) und dann speichern bzw. exportieren.
Auch die Farbgrafik ist in einem Array angeordnet. Wir verwenden hier 8-Bit pro Pixel, also 8-Bit Farben mit folgender Anordnung:

Um die 3 Farben Rot, Grün und Blau in ein Byte mit 8-Bit zu packen,
werden meist einfach 2-3 Bit pro Farbe genommen, also 3-Bit Rot, 3-Bit
Grün, 2-Bit Blau. Rot
und Grün hätten dann zwar 8 Stufen, Blau hätte aber nur 4 Stufen, was
unausgewogen ist. Deshalb wird hier eine andere Methode verwendet. Für
Rot und Blau werden je
6-Stufen verwendet, für Grün 7-Stufen. Wir nehmen an, dass wir ein Bild
mit 24-Bit Farben haben, also pro Farbe ein Byte mit dem Wert 0...255.
Also reduzieren wir erst auf die 6 bzw. 7 Farbstufen:
Red = int(Red * (6.0 / 255.5));
Grn = int(Grn * (7.0 / 255.5));
Blu = int(Blu * (6.0 / 255.5));
Der Teiler 255.5 wird verwendet um korrekt gerundete Werte zu erhalten. Danach werden die Farben codiert mit:
Pixelfarbe = (36 * Grn) + (6 * Red) + Blu
Somit haben wir den Wert 0 für schwarz und 251 für weiß.
Zum Dekodieren auf die 6-7 Stufen verwenden wir dann:
Grn = Pixelfarbe / 36;Danach werden die Farben für das Display auf 6-7 Bit skaliert (16-Bit RGB):
Pixelfarbe -= 36 * Grn;
Red = Pixelfarbe / 6;
Pixelfarbe -= 6 * Red;
Blu = Pixelfarbe;
Color = ((Red * 6) << 11) | ((Grn * 10) << 5) | (Blu * 6);
Die Farbwerte 252-255 werden nicht benötigt. Dies erlaubt Steuerwerte
einzufügen und eine rudimentäre Daten-Komprimierung zu verwenden.
Angenommen, unsere Grafik ist eine Zeichnung mit Flächen und Linien wie das Beispiel-Testbild. Das
heißt, es kommen immer wieder mehrere Pixel derselben Farbe hintereinander vor.
So wird der Steuerwert 255 für eine Wiederholung verwendet. Die auf den
Wert 255 folgende Zahl ist die Anzahl Wiederholungen der letzten Farbe. Also z.B. die
Wertefolge 0, 255, 19 bedeutet, Pixelfarbe schwarz, 19 mal wiederholt,
also 20 schwarze Pixel.
Im weiteren wird der Wert 254 als Markierung für das Ende der Grafik verwendet.
Und schlussendlich sind die ersten 2 Byte im Array die Dimension des Bildes (H
x W), also z.B. 128, 160 bedeutet das Bild ist 128
Pixel hoch und 160 Pixel breit. Sollen Bilder mit Dimensionen von mehr als 255 codiert
werden, müsste man die Dimensionen anders codieren, z.B. mit je 2 Byte. Oder als Parameter an die Funktion mit übergeben.
Mit dieser Methode wird das gezeigte Testbild in der C-Source Datei lediglich 24kB groß und belegt für die 20480 Pixel (160x128) nur 5748 Byte Speicher.
Ähnlich wie mit den Bitmaps wandeln wir das Bild erst in eine
unformatierte Datei um, diesmal mit Farbe und je ein Byte für Rot, Grün und Blau. Wir verwenden als Beispiel
das Textbild.png, erstellt mit LibreOffice Draw:convert -depth 8 +antialias Testbild.png Testbild.rgb
Danach erstellen wir das C-Array mit rgb2c.py:./rgb2c.py -w=160 -h=128 Testbild.rgb
Die Breite -w=n und Höhe -h=n kann man mit dem exiftool auslesen bzw. ist von Zeichnungsprogramm bei der Erstellung bekannt.
Man kann es auch einfacher mit den Shell-Scripts Graphic_convert
erstellen. Dieses liest die Dimension mit exiftool und stellt die richtigen Parameter ein. Beispiel:
./Graphic_convert Testbild.png
Die Ausgabedatei Testbild.arr
enthält die C-Definition
für das Array Testbild[]. Man kann sie in .h umbenennen oder mehrere
Fonts, Bitmaps und Grafiken in einer .h Datei zusammenfassen.
Hier nochmals alle Scripts und Bilder in einer Zip-Datei.
Die Stripts müssen nach dem Kopieren ausführbar gemacht werden (chmod
+x scriptname, auch *.py).
In der Zip-Datei ist auch ein Verzeichnis mit
einem Beispiel-Source-Code für ein Application-Interface. Voraussetzungen:
Die Datei lcd_api.c enthält Funktionen
welche Texte,
Logos und Bilder aufs Display schreiben. Sie nutzen die Funktion
lcd_Pixel(x, y, color)
(Oben als PutPixel(X, Y, Color) beschrieben), welche zur Verfügung
stehen muss und eine
entsprechende
Deklaration inkludiert sein muss. Des weiteren muss die Display
Auflösung in den globalen Variablen tftHeight und tftWidth gespeichert
und deklariert sein.
Grundsätzlich gibt
es 2 Möglichkeiten zum Beschreiben des Displays:
fonts.h Header mit den Font Tabellen
BitmapCool.h Bild und Header der BitmapTestbild.h Bild und Header des Testbildes
CMakeLists.txt Benötigt der Compiler und Linker
lcd_api.c/-.h Application Interface Beispiel Codelcd.c Demo Beispiel Code