Grafik-Display‎ > ‎

3 TextDisplay

http://sites.prenninger.com/arduino-uno-r3/grafik-display-1/3-textdisplay

http://www.linksammlung.info/

http://www.schaltungen.at/

                                                                                              Wels, am 2016-06-12

BITTE nützen Sie doch rechts OBEN das Suchfeld  [                                                              ] [ Diese Site durchsuchen]

DIN A3 oder DIN A4 quer ausdrucken
**********************************************************************************
DIN A4  ausdrucken
*********************************************************


Untergeordnete Seiten (4):

1 Grafik-Display
2 Display-Grundlagen
3 TextDisplay

4 GrafikDisplay
5 Projekte


3. TEXTDISPLAY 128x64 dots
Im Teil zu den Display Grundlagen haben Sie gesehen, wie sich das Display auf einfache Weise ansteuern lässt.
Die vorgestellte Balkendarstellung kann durchaus nützlich sein, wenn es um die Visualisierung von Messdaten geht.
Es kommt allerdings oft vor, dass mehr Informationen dargestellt werden müssen als relative Messwerte.
Aus diesem Grund wird in den folgenden Kapiteln das Thema Zeichenausausgabe auf dem Display behandelt.
Zunächst sind Sie dabei selbst gefragt, einen ersten eigenen Zeichensatz zu kreieren.
Damit Sie nicht alles per Hand entwickeln und den HEX-Code selbst berechnen müssen, hilft ein praktisches Onlinetool.
Doch zunächst die Grundlagen.

3.1 1 CharSet - der erste Schritt zum Textdisplay
Einen eigenen Zeichensatz zu erstellen hört sich recht kompliziert an, ist aber im Grunde ganz einfach. Aber was ist eigentlich ein Zeichensatz?


Abb. 3.1 Ein Zeichensatz mit 16 Zeichen   7x5

Zunächst einmal muss man wissen, dass ein Text nichts weiter als eine Aneinanderreihung von Bytes ist.
Ein Byte steht für genau ein Zeichen im Text.
Welches Zeichen hinter diesem Byte steckt, muss irgendwo definiert sein. International gibt es dafür die ASCII-Norm, die jedem der 255 möglichen Bytes ein Zeichen zuweist.
Dennoch können diese Zeichen unterschiedlich dargestellt werden.
Man kennt das aus Textverarbeitungsprogrammen, wo es verschiedene Schriftarten (Fonts) gibt.
Ein Font definiert das Aussehen von Buchstaben oder Zahlen in einem Programm oder auf einem Display.
Ihr Display hat intern keinen eigenen Font, also weiß es auch nicht, wie beispielsweise ein A auf dem Display aussehen soll.

                                                                            Seite 28




8 Zeilen mit 21 ASCII-Zeichen   8x8=64  (21x6=126+2=128)


MENU > Werkzeuge > Port: "COM5 (Arduino Uno)"
MENU > Datei > Sketchbook > DisplayBeispiele > ChatSet.ino
Hackerl = Verifizieren
Pfeil = Hochladen

ORDNER ChatSet    |  ChatSet.ino  |  Display.cpp  |  Display.h  |

 0  1   2  ...   F 

#include "Display.h"
#include "SPI.h"

                                                                                 // 7x5 Font 0-9, A-F
const unsigned char hexFont [] = {
  0x3E, 0x41, 0x41, 0x41, 0x3E, 0x00, 0x42, 0x7F, 0x40, 0x00, 0x42, 0x61, 0x51, 0x49, 0x46, 0x22,
  0x41, 0x49, 0x49, 0x36, 0x0C, 0x0B, 0x08, 0x7C, 0x08, 0x4F, 0x49, 0x49, 0x49, 0x31, 0x3E, 0x49,
  0x49, 0x49, 0x32, 0x01, 0x01, 0x71, 0x09, 0x07, 0x36, 0x49, 0x49, 0x49, 0x36, 0x26, 0x49, 0x49,
  0x49, 0x3E, 0x7E, 0x09, 0x09, 0x09, 0x7E, 0x7F, 0x49, 0x49, 0x49, 0x36, 0x3E, 0x41, 0x41, 0x41,
  0x22, 0x7F, 0x41, 0x41, 0x22, 0x1C, 0x7F, 0x49, 0x49, 0x49, 0x41, 0x7F, 0x09, 0x09, 0x09, 0x01
};

Display lcd = Display();

void setup() {
  lcd.init(20);
  lcd.clearDisplayRAM();
  lcd.setPageAddress(3);                                                // 0=1 Zeile bis 7=8 Zeile
  lcd.setColumnAddress(15);                                           // Abstand vom linken Rand in dots

  for (byte character = 0; character<16; character++) {     // for all characters 16 (eben für 16 Zeichen 0 bis F) ODER 10  für 0 bis 9
    byte position = character*5;                                       // compute start position
    for (byte i=0; i < 5; i++) {                                           // write 5 bytes
      lcd.writeData(hexFont[position++]);
    }
    lcd.writeData(0x00);                                                  // write a blanc
  }
}

void loop() {
}




                                                                     


Deswegen müssen Sie einen eigenen Schriftsatz erstellen, mit dem sich die Buchstaben auf dem Display gut leserlich darstellen lassen.

In Ihrem ersten Zeichensatz soll es zunächst einmal 8 verschiedene Zeichen geben.
Jedes dieser Zeichen soll 5 Pixel breit und maximal 8 Pixel hoch sein.
Das bedeutet, dass Sie für ein Zeichen fünf Hexadezimalzahlen benötigen.
Möchten Sie beispielsweise eine primitive Eins darstellen, indem Sie einfach einen Strich zeichnen, sähe das Zeichen in Hexadezimalschreibweise folgendermaßen aus:
0x00, 0x00, 0x00, 0xFF, 0x00 - eine sicherlich sehr einfache Eins, aber durchaus erkennbar.
Diesen ersten Zeichensatz, der zunächst nur aus einem Zeichen besteht, definieren Sie als Konstante zu Beginn Ihres Programms:

001 const unsigned char hexFont [ ] — {0x00, 0x00, 0x00, 0xFF, 0x00,);


Außerdem programmieren Sie noch den üblichen Anfang dazu, den Sie bereits aus den ersten Kapiteln kennen.
In die setup-Funktion programmieren Sie die neu gelernte Funktion clearDisplayRAM, damit keine Überbleibsel aus anderen Versuchen die Darstellung verunreinigen.
Setzen Sie mit den Befehlen setPageAddress und setColumnAddress die Position des Pointers auf die dritte Seite und die 15. Spalte, damit die Zeichen besser zur Geltung kommen.
Zuletzt kommt der wichtigste Teil mit diesen beiden verschachtelten for-Schleifen, die ebenfalls Teil der setup-Routine sind:

001 for (byte character = 0; character<1; character++) {
002      byte position = character*5;
003
004    for (byte i-0; i < 5; i++) {
005    lcd.writeData(hexFont[position++]);
006    }
007 lcd.writeData(0x00);
008 }

Die äußere for-Schleife sorgt dafür, dass alle Ihre Zeichen (engl. character) nacheinander aufgerufen werden.
In diesem ersten Versuch gibt es nur ein Zeichen, weswegen die Schleife sinnloserweise nur bis Eins zählt (definiert durch character <1, den zweiten Parameter der for-Schleife).
In der Schleife wird dann die Position des Zeichens innerhalb der von Ihnen definierten Konstante hexFont festgelegt.
Das erscheint in diesem Fall ebenfalls zunächst sinnlos, allerdings werden bei mehreren Zeichen die Hexadezimalzahlen einfach aneinandergereiht,

                                                                       Seite 29




und das Programm muss genau berechnen können, welche 5 Hex-Zahlen zu diesem Zeichen gehören.
Was folgt, ist eine Schleife, die 5 Hexadezimalzahlen, deren Position zuvor ermittelt wurde, nacheinander auf das Display überträgt.
Danach wird noch eine leere Spalte eingefügt, damit die Zeichen getrennt und gut voneinander zu unterscheiden sind.
Damit ist der erste Versuch bereits so gut wie fertig.
Sie können das fertige Programm in den Controller laden.
Auf dem Display erscheint nun ein ziemlich verloren wirkender Strich in der Landschaft.
Deswegen entwickeln Sie zusätzlich eine einfache Zwei.
Der Hexcode dafür lautet 0x79, 0x49, 0x49, 0x49, 0x4F.



Abb. 3.2: So entsteht die 2


Diesen fügen Sie direkt hinter die definierte Eins in die geschweiften Klammern der hex Font-Konstanten ein.
Bevor der Upload gestartet werden kann, müssen Sie noch in der ersten, äußeren for-Schleife die Zählweite mit character<2 anstelle von character<1 erweitern.
Nach dem Upload sehen Sie zusätzlich eine 2, die allerdings auch nicht durch besondere Schönheit glänzt.
Deswegen wird hier eine handlichere Möglichkeit vorgestellt, Ihren ersten längeren Zeichensatz zu entwerfen.

Auf der Internetseite:
http://tiny.systems/article/mosoic.html
finden Sie ein Tool, mit dem es sehr einfach ist, einen Zeichensatz zu erstellen.

tiny.systems Thomas Baum (29.07.2014)

Mosaic: Online Bitmap2Hex Konverter

Mosaic konvertiert BMP-Dateien (.bmp) nach Hex und vereinfacht die Portierung von Bilder oder Schriftarten in dein µController-Projekt. Zum direkt loslegen einfach in die Box malen. Oder alternativ:
• 1-Bit BMP erstellen
• Datei hochladen
• ByteArray in eigenes Projekt einbinden
(Mosaic is an online Bitmap to Hex (bmp2hex) converter. Just use the paint box or upload your monocrom bitmap)




Abb. 3.3: Die 16 erstellten Zeichen 1 Zeile 0 bis 7  2 Zeile 8 bis F






Das Ziel ist, 8 oder 16  Zeichen zu designen.
Es ist Ihnen überlassen, ob Sie acht beliebige Buchstaben oder Zahlen oder eine Kombination aus beidem erstellen.
Hier die Vorgehensweise für die Zahlen 0-7 ODER 0 bis F

                                                                       Seite 30




Auf der Thomas Baum Mosaic-Seite http://tiny.systems/article/mosoic.html finden Sie in der Mitte eine graue Fläche.
Wenn Sie an eine beliebige Stelle in der Fläche klicken, aktivieren Sie ein Pixel.
Klicken Sie auf dasselbe Pixel noch einmal, verschwindet es wieder.
Klicken und halten Sie die Maustaste gedrückt, während Sie den Mauszeiger über die Fläche bewegen.
Wie Sie sehen, können Sie so auch ganze Zeichnungen erstellen.
Die Zeichenfläche hat zunächst eine Größe von 16 x 16 Pixeln.
Für Ihre 8 ()16 Zeichen wäre es aber von Vorteil, wenn eine Fläche von 20 (40) x 16 Pixeln zur Verfügung stünde, denn ein Zeichen ist 5 x 8 Pixel groß.
Deswegen vergrößern Sie den Bereich einfach durch 4x (24x) Klicken auf das rechte / horizontale Symbol unter der Zeichenfläche.
Nun können Sie oben links in der Ecke mit Ihrem ersten Symbol beginnen.
Beim Entwerfen der Zahlen hat sich übrigens herausgestellt, dass eine Höhe von sieben Pixeln eine durchaus symmetrische und deutliche Darstellung ermöglicht.
Aber probieren Sie selbst. Das 2 Zeichen kommt ohne Leerstelle direkt neben das erste Zeichen.
Die Abstände zwischen den Symbolen erzeugt das Programm schließlich selbstständig.
Wenn Sie das Ende der Zeile erreicht haben, nutzen Sie einfach die nächsten 4 (8) Zeilen darunter.
Nach einigem Probieren sehen die 4 (8) Zahlen zum Beispiel so aus, wie in der folgenden Abbildung gezeigt.


Sollten Sie mit Ihren Zeichen unzufrieden sein, können Sie mit Klick auf das X die gesamte Fläche löschen.
Wenn Sie fertig sind, klicken Sie auf den Haken unten links.
Es erscheint nun ein recht langer HEX-Code, den Sie anstelle Ihrer ersten Versuche in die hexFont-Konstante kopieren.

                                                                       Seite 31








Nun passen Sie die for-Schleife noch auf 8 (16) Zeichen an und laden das Programm auf den Controller.
Hat alles funktioniert, sehen Sie Ihre 8 (16) Zeichen nacheinander auf dem Display.
Der erste Schritt zur komplexen Textausgabe ist damit getan.

Tipp
Das fertige Charset-Beispiel im Ordner verfügt übrigens sogar über 16 verschiedene Zeichen.
Damit lässt sich schon fast ein ganzer Text ausgeben.


3.2  ASCII — der komplette Zeichensatz
In diesem Beispiel werden Sie den kompletten Zeichensatz aus der ASCII-Norm benutzen.
ASCII steht für „American Standard Code for Information Interchange" und ist eine Codetabelle für international gebräuchliche Schriftzeichen.
Darin enthalten sind unter anderem die Ziffern 0 bis 9 und die Buchstaben a bis z, jeweils klein- und großgeschrieben, also alles, was Sie für eine einfache Textausgabe benötigen.


Abb. 3.4: Die Textausgabe des Beispiels

Damit der Quelltext etwas übersichtlicher erscheint, ist der komplette Zeichensatz in eine eigene Datei ausgelagert.
Doch das ist nicht die einzige Änderung.
Sie werden eine neue Library benutzen, die TextDisplay-Library.
Dabei handelt es sich um eine Bibliothek, die die bekannte Display-Library enthält, im Zusammenhang mit Textausgaben aber erweiterte Funktionen bereitstellt.
Als Vorlage können Sie aber dennoch das HelloWorldDlY-Beispiel benutzen.


MENU > Werkzeuge > Port: "COM5 (Arduino Uno)"
MENU > Datei > Sketchbook > DisplayBeispiele > ASCII.ino
Hackerl = Verifizieren
Pfeil = Hochladen

ORDNER ASCII    |  ASCII.ino  |  Display.cpp  |  Display.h  | Font.h  |  TextDisplay.cpp  |  TextDisplay.h  | 


#include "TextDisplay.h"
#include "SPI.h"

TextDisplay display = TextDisplay();

void setup() {
  display.init(20);
  int x=1, y=2, result;
  char buffer[50];
  result = x + y;
  sprintf(buffer, "Test: %d + %d = %d", x, y, result);
  display.print(buffer);
}

void loop() {}

 

                                                                      Seite 32




Ihr neues Programm beginnt wieder mit der Einbindung der Bibliothek-Dateien, diesmal aber der TextDisplay.h-Datei anstelle von Display.h.
Sie bindet intern die Display-Bibliothek mit ein, sodass Sie sich um diesen include-Befehl nicht kümmern müssen.
Nun erstellen Sie eine neue Instanz vom Typ TextDisplay, sodass Sie am Anfang Ihres Programms die folgenden veränderten Zeilen einfügen, die auch für alle nachfolgenden Kapitel zum Textdisplay gleich lauten:

001 #include "TextDisplay.h"
002 #include "SPI.h"
003
004 TextDisplay display = TextDisplay();
005
006 void setup() { }

Den Hauptteil des Programms fügen Sie erneut in die setup-Funktion ein.
Das hat den Vorteil, dass Ihr Programm nur einmal ausgeführt wird und nicht, wie es in der loop-Funktion der Fall wäre, in einer ständigen Schleife läuft.
Ausgegeben werden soll in diesem ersten Versuch eine einfache Rechenaufgabe inklusive Ergebnis.
Programmieren Sie also zunächst einmal die Initialisierung des Displays und drei Integer-Variablen, von denen zwei direkt die Werte 1 und 2 zugewiesen bekommen.

001 void setup() ) {
002
003 display.init(0);
004 int x=1, y=2, result;
005
006 char buffer[50];
007 result = x + y;
008 sprintf(buffer, "Test: %d + %d = %d", x, y, result);
009 display.print(buffer);
010
011 }

Der hier benutzte init-Befehl unterscheidet sich übrigens von dem zuvor verwendeten.
Wenn Sie in die TextDisplay.cpp-Datei sehen, wissen Sie auch, warum.
In dieser init-Funktion wird nämlich zunächst einmal das LCD-Display wie gewohnt initialisiert.
Doch anschließend wird sowohl das RAM des Diplays gelöscht als auch der Pointer in die obere linke Ecke des Displays gesetzt.

                                                                      Seite 33




Diese Befehle können Sie sich also in Ihrem Hauptprogramm sparen.
Als Nächstes definieren Sie ein Zeichenarray vom Typ char mit einer Größe von 50 Stellen.
Das benutzen Sie als Buffer (Puffer) für Ihre Ausgabe.

001 char buffer[50];

Das Ergebnis der Addition soll in die Variable result übertragen werden.
Deswegen folgt in der nächsten Zeile die einfache Zuweisung.

001 result = x + y;

Damit Sie Ihren Text ausgeben können, müssen Sie die einzelnen Textbausteine zusammensetzen.
Dazu benötigen Sie den zuvor definierten Buffer.
Das Zusammensetzen selbst ist dabei gar nicht so einfach.

001 sprintf(buffer, "Test: %d + %d = %d", x, y, result);

Sie benötigen nämlich den Befehl sprintf aus der C-Standardbibliothek.
Der Befehl setzt sich aus mehreren Befehlen zusammen.
print dient dazu, eine Zeichenkette über den Standartausgabekanal auszugeben.
printf ist ähnlich, das f steht allerdings für formal, denn der String wird zunächst noch formatiert.
Das bedeutet z.B. dass Platzhalter durch Variablenwerte ersetzt werden.
Genaueres steht im nächsten Abschnitt.
Das s in sprintf steht für store und bedeutet, dass der mit printf formatierte Text nicht direkt ausgegeben, sondern in einen Buffer zwischengespeichert wird.
Deswegen ist der erste Parameter des Befehls auch der von Ihnen definierte Buffer.

Sie haben sich vielleicht über die Ausdrücke %d gewundert, die im Text des zweiten Parameters zu finden sind.
Diese Zeichenkombinationen sind die Platzhalter.
Nach den Anführungszeichen finden Sie dann als Parameter die Integer-Werte, die eingefügt werden sollen.
In Ihrem Text finden sich drei Platzhalter für drei Variablen, die alle von links nach rechts anstelle von %d eingefügt werden.
Für verschiedene Variablentypen gibt es auch verschiedene Platzhalter.
Bei manchen Typen können Sie außerdem noch Sonderwünsche äußern.
Die folgende Tabelle fasst ein paar Möglichkeiten kurz zusammen.
Wenn Sie mögen, können Sie einfach die Platzhalter ersetzen und herausfinden, welche Formatierungsmöglichkeiten sich daraus ergeben.

                                                                      Seite 34





Formatierungszeichen    Bedeutung                                               Beispiel
%d, %i                  Dezimale Darstellung vorzeichenbehafteter Ganzzahlen    10, 123
%x, %X                  Hexadezimale Darstellung                                a4, B5
%f                      Gleitkommazahl                                          3.1415
%c                      Einzelnes Zeichen                                       A, b
%s                      Zeichenfolge                                            Hallo, test


Tipp
Falls Sie einmal ein Prozentzeichen ausgeben möchten, verwenden Sie %%.
Bei der Formatierung von Gleitkommazahlen kann es sinnvoll sein, die Genauigkeit für die Ausgabe festzulegen.
Möchten Sie beispielsweise vier Stellen vor und zwei Stellen nach dem Komma reservieren, lautet die Schreibweise %4.2f.


Zu guter Letzt muss der Buffer natürlich noch ausgegeben werden.
Dafür steht in der TextDisplay-Library die Funktion print zur Verfügung.

001 display.print (buffer);

In der print-Funktion wird der übergebene Text im Hintergrund in einzelne Buchstaben aus Hex-Code konvertiert, um anschließend mit den bekannten Funktionen der display-Library in das RAM des Displays übertragen zu werden.
Nach dem Upload finden Sie den Text mit den eingefügten Variablen auf dem Display angezeigt.
Damit haben Sie eine einfache Möglichkeit erprobt, Text zur Laufzeit auf dem Display anzuzeigen.

Tipp
Warum nicht einfach Strings statt char-Arrays für die Datenübergabe benutzen?
Arduino unterstützt von Haus aus tatsächlich Strings als Variablentypen.
Die Verwendung von Strings bringt natürlich sinnvoll eingesetzt viele Vorzüge mit sich, vor allem, wenn es um Zeichenkettenauswertung und -Manipulation geht.
Die Nachteile überwiegen jedoch und schlagen sich in einem höheren Speicherverbrauch und mehr Rechenzeit nieder.


                                                                      Seite 35



3.3 1 SerialTextDisplay - Textausgabe und PuTTY

Im vorangegangenen Beispiel wurde die Möglichkeit vorgestellt, vorher im Quelltext definierten Text auf dem Display anzuzeigen.
Im folgenden Programm bekommen Sie die Möglichkeit, einen vom PC gesendeten Text live auf dem Display erscheinen zu lassen.
Außerdem lernen Sie ein kleines Tool kennen, das PuTTY heißt und Ihnen bei der Textübertragung zur Seite steht.
Zunächst geht's aber um das Programm.
Nach der gewohnten Initialisierung als TextDisplay folgt die setup-Routine, die folgendermaßen aussieht:

001 void setup() {
002   displayjnit();
003   Serial.begin(9600);
004   display.println("SerialTextDisplay");
005   display.println("--------------------");
006   display.println("");
007   display.println("COM Port");
008   display.println("9600 / 8-N-1");
009   delay(5000);                           // Wartezeit 5 Sekunden
010   display.clear():
011 }


Sie enthält zunächst nichts Ungewohntes, nur den bekannten init -Aufruf und Einstellungen für die serielle Schnittstelle.
Außerdem werden ein paar erste Zeilen auf das Display übertragen, die unter anderem die Baudrate und andere Konfigurationen enthalten.
Interessanter wird es in der loop-Routine:

001 void loop() {
002    if (Serial.available() > 0) {
003          display.print(Serial.read());
004    }
005    delay(30);                           // Wartezeit 30ms
006
}

Hier wird zunächst einmal getestet, ob überhaupt Daten von der seriellen Schnittstelle empfangen wurden, und zwar mit der Funktion Seriei.available( ).
Ist das der Fall, werden die Daten über Serial.read() ausgelesen und über den display-Print-Befehl auf das Display übertragen.
Ein delay von 30 ms ist wichtig, damit alle Daten sauber ausgegeben werden können.
Wenn Sie später einen Text eintippen, werden Sie deswegen eine leichte Verzögerung bemerken.

                                                                      Seite 36






Abb. 3.5: Der Anfangstext beim Controller-Reset

Das ist bereits das vollständige Programm, das Sie nun auf den Controller übertragen.
Nach dem Upload können Sie einen ersten Test mit dem Serial Monitor der Arduino-Oberfläche wagen.
Dazu starten Sie das Terminal durch das Symbol oben rechts oder indem Sie unter Tools auf Serial Monitor klicken.
Da der Controller automatisch resetet wird, erscheint erneut für 5 Sekunden die Anfangsbotschaft, die Sie in der Setup-Routine definiert haben.
Wenn das Display leer wird, können Sie eine Nachricht eintippen und mit Enter oder Senden übertragen.
Ihre Nachricht erscheint nun direkt auf dem Display.
Sie fragen sich vielleicht, wozu Sie überhaupt noch ein anderes Tool wie
z.B. PuTTY brauchen?
Es ist Ihnen zwar möglich, Text auf das Display zu übertragen, allerdings sind die Zeilen schnell voll und Sie haben keine Möglichkeit, die Zeichen wieder zu löschen.
Außerdem können Sie nicht einfach mit Return in eine neue Zeile springen und dort mit Ihrem Text fortfahren.
Das liegt daran, dass der Serial Monitor von Arduino nur den im Textfeld stehenden Text überträgt und nicht die Tastatureingaben selbst, die schließlich ebenfalls nur ASCII-Zeichen sind.
Deswegen benötigen Sie ein zusätzliches Programm.

Sie können sich das Tool PuTTY von der Internetseite

PuTTY Download Page
http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html  

herunterladen.
 
Das kleine Terminalprogramm hat sich als eine Art Standard für SSH- und Telnet-Verbindungen etabliert.
Hier kommt es allerdings als serielles Terminal zum Einsatz.
Auf dem ersten Blick sieht das Programm, im Gegensatz zum gewohnten Serial Monitor der Arduino Software, unübersichtlich aus.
Das liegt vor allem an den vielen verschiedenen Funktionen, von denen Sie aber nur eine benötigen.
Deswegen können Sie bei Programmstart einfach den Connection type auf Serial umstellen
und die COM-Nummer des Controllers sowie die Baudgeschwindigkeit von 9.600 eingeben.

                                                                      Seite 37




 Dann klicken Sie auf den Open-Button.



Abb. 3.6: Die PuTTY-Oberfläche mit Serial als Verbindungsart



Was sich nun öffnet, ist auf den ersten Blick nicht mehr als ein schwarzes Fenster.
Dieses Fenster kann aber einiges mehr, als man zunächst erahnt.
Nach einer Wartezeit von 5 Sekunden können Sie in das schwarze Fenster einfach drauflos tippen.
Der Text erscheint direkt auf dem Display, wie Sie es bereits gesehen haben.
Der Clou ist aber, dass Sie den Text einfach mit der Löschtaste bearbeiten oder per Enter in die nächste Zeile springen können, also ganz so, wie Sie es von einem Texteditor gewohnt sind.

Tipp
Falls Sie statt des Fensters eine Fehlermeldung sehen, sollten Sie noch einmal die COM-Nummer überprüfen oder sichergehen, dass der Serial Monitor von Arduino nicht die Schnittstelle blockiert.



3.4 Metering — Messdaten ausgeben
Das folgende Programm ist das vorerst letzte zum Thema Textdisplay.
Es verdeutlicht, wie gemessene Werte und Portzustände übersichtlich auf dem Display dargestellt werden können.

                                                                      Seite 38






Abb. 3.7: Portzustände auf dem Display ausgegeben


Zu Beginn des Programms stehen die gewohnten Quelltextteile aus vorangegangenen Programmen.
In der Setup-Funktion steht die gewohnte Initialisierung des Displays.
Direkt danach folgt allerdings ein neuer Befehl:

001  DDRD = B00000000;

Dieser Befehl ist den meisten Arduino-Nutzern vermutlich nicht geläufig, obwohl man eine ganze Menge Quelltextzeilen damit sparen kann.
Mit diesem Befehl wird direkt in das Richtungsregister des Mikrocontrollers geschrieben.
Genauer gesagt, werden alle Register des Ports D als Eingänge geschaltet.
Das bedeutet auf Arduino-Ebene, dass die Pins 0-7 als Input konfiguriert werden.
DDR steht für Data Direction Register.
Eine 1 anstelle einer 0 in der Zuweisung würde den entsprechenden pin als Ausgang setzen.
In diesem Fall sind allerdings alle pins als Eingänge definiert.
Der verwendete Befehl wirkt zwar etwas unleserlicher, allerdings ist er wesentlich kürzer, als achtmal den pinMode-Befehl zu verwenden.
Das war schon der gesamte Quelltext der Setup-Routine.
Nun wird die loop-Funktion vorgestellt.

001 void loop()  {
002 char buffer[50]:
003 display.clear();
004 display.println("Digital Input(PortD):");


In ihr wird zunächst einmal der bereits bekannte char buffer definiert.
Er enthält wieder 50 Plätze für Zeichen.
Als Nächstes wird der Display-Buffer gelöscht.
Auf dem nun leeren Display können Sie mit display.println den ersten Text ausgeben, beispielsweise die Zeile: „Digital Input(Port0):';
denn Sie möchten den Zustand des PortsD ausgeben, den Sie zuvor als Eingang definiert haben.

                                                                      Seite 39




println funktioniert fast wie print, nur mit der Ausnahme, dass der Pointer automatisch in die nächste Zeile springt.
Die nächste Zeile enthält nun den Zustand des Ports in zwei verschiedenen Formen:
einmal als Dezimalzahl und einmal als Hexadezimalzahl.
Dazu müssen Sie den Text aber zunächst durch die sprintf -Funktion in eine passende Form bringen:

001 sprintf(buffer, "Dec: %03d Hex: Ox%02X", PIND, PIND);
002 display.println(buffer);
003 display.println("");


Die beiden Platzhalter, am %-Zeichen zu erkennen, werden jeweils durch den Inhalt von PIND ersetzt.
Bei PIND handelt es sich um das Zustandsregister von PortD.
Das Zustandsregister wird, anders als das Richtungsregister am Anfang, durch den Befehl PortD ausgelesen.
Eine Besonderheit liegt in der Form der Platzhalter.
Die Zahl direkt nach dem %-Zeichen gibt nämlich an, wie viele Vorkommastellen ausgegeben werden.
In diesem Fall also 3.
Dadurch ist die Darstellung immer einheitlich, auch wenn der Zustand als Dezimalzahl nur 7 oder allgemein eine nicht dreistellige Zahl ist.
Neben den digitalen Portzuständen möchten Sie auch die Werte der analogen Eingänge darstellen.
Deswegen schreiben Sie zunächst eine passende Überschrift und kümmern sich schließlich um die Ausgabe:

001 display.println("Analog Input(PortC):");
002 sprintf(buffer, "A0: %04d A3: %04d", analogRead(A0), analogRead(A3));
003     display.println(buffer);
004    sprintf(buffer, "Al: %04d A4: %04d", analogRead(A1), analogRead(A4));
005    display.println(buffer);
006    sprintf(buffer, "A2: %04d A5: %04d", analogRead(A2), analogRead(A5));
007    display.println(buffer);
008    delay(2000);   // Wartezeit 2 Sekunden


Wie gehabt, werden die Platzhalter durch die analogen Werte ersetzt.
Anschließend geben Sie den Buffer über den bekannten Befehl aus.
Zuletzt fügen Sie noch ein kleines Delay von 2 Sekunden ein, damit die Anzeige nicht zu sehr flackert, und laden das Programm auf Ihren ARDUINO-Controller.

                                                                      Seite 40




Das Ergebnis ist eine übersichtliche Anzeige der Portzustände, die sie
z.B. zur Überwachung verschiedener Sensoren einsetzen können.


3.5  Blick in die Library II — die Klasse Textdisplay
In diesem Kapitel liegt der Fokus auf der Textdisplay-Library, die Sie in den vorangegangenen Beispielen anstelle der Display-Library benutzt haben.
Es werden ein paar neue Funktionen vorgestellt und ein Einblick in den Char-Set gegeben, der Ihnen die Darstellung von Buchstaben, Zahlen und Sonderzeichen ermöglicht.
Wenn Sie die Header-Datei der Textdisplay-Library öffnen, finden Sie einige Elemente, die auch in der Header-Datei der Display-Library zu finden waren.
Ein neues Element ist allerdings hinzugekommen, nämlich der #include- „Display.h"-Befehl.
Die Display-Library wird also in die Textdisplay-Library eingebunden.
Das bedeutet, dass sich das Textdisplay der Funktionen aus der alten Library bedient.
Die Klasse TextDisplay bietet folgende Funktionen:

001 class TextDisplay {
002
003  public:
004    TextDisplay()  { } ;
005    void init(byte contrast);
006    void println(const char* string);
007    void print(const char string);
008    void clear(void);
009    byte getLastCharPosition(byte line);
010   void wirteTextBuffer();
011
012  private:
013    Display lcd;
014    byte textBuffer[8][22];
015    byte curserPositionY, curserPositionX;
016  };


Die aufgelisteten Funktionen sind Ihnen in den vorangegangenen Beispielen bereits begegnet.
Nur die privaten Variablen sind Ihnen bisher verborgen geblieben.
Was genau die Funktionen bewirken, wird allerdings nur deutlich, wenn Sie die TextDisplay.cpp-Datei öffnen.

                                                                      Seite 41



001 /*
002 TextDisplay.cpp - Buffered alphanumeric display for a ST7565 based 12864 LCD.
003  Thomas Baum, mailto:th.baum@online.de, 2014-09-12
004 */
005
006 #include "TextDisplay.h"
007 #include "Display.h"
008 #include "Font.h"


Auch hier finden Sie altbekannte Elemente vor.
Ein paar Kommentarzeilen geben zusätzliche Informationen preis und die benötigten Header-Dateien werden eingebunden.
Zusätzlich wird allerdings auch eine Datei mit dem Namen Font.h eingebunden, die den ASCII-Zeichensatz enthält.
Als erste Methode der Klasse finden Sie die init-Funktion.

001 void TextDisplay::init(byte contrast) {
002    lcd = Display( );
003    lcd.init(contrast);
004    lcd.clearDisplayRAM();
005    lcd.setPageAddress(0);
006    lcd.setColumnAddress(0);
007
008    curserPositionY = 0;
009    curserPositionX = 0;
010    textBuffer[MAX_CHARACTERS_Y][MAX_CHARACTERS_X]:
011   }

In der Initialisierungsroutine wird zunächst eine Instanz vom Typ Display erstellt, ganz nach dem Schema der ersten Programme.
Der Kontrastwert, der ein Parameter der init-Funktion ist, wird einfach an die Initialisierungsmethode des Displays durchgereicht.
Es folgen Aufrufe von Funktionen aus der Displayklasse, die Sie bereits kennengelernt haben.
Neu ist, dass die 2 Variablen curserPositionY/-X auf Null gesetzt werden.
Wie Sie aus der Header-Datei wissen, handelt es sich hierbei um Private-Variablen, die dem Nutzer der Library in der Regel verborgen bleiben.
Ähnlich verhält es sich mit der Variablen textBuffer.
Bei dieser Variablen gibt es eine Besonderheit, es handelt sich nämlich um ein zweidimensionales Array,
dessen Größe jeweils durch die Konstanten MAX_CHARACTERS_Y und MAX_CHARACTERS_X festgelegt ist.
Der Unterschied zu einem einfachen Array ist, dass jede Position des Arrays noch ein Array enthält.
Man kann sich das wie eine Tabelle vorstellen, bei der nur der X- und der Y-Wert zusammen eine eindeutige Information liefern.

                                                                      Seite 42





An dieser Stelle wird der zweidimensionale Buffer dazu benötigt, den zu schreibenden Text zeilen- und spaltenweise zwischenzuspeichern.
Dieser Speicher, Text-Buffer genannt, ist die interne Darstellung des Textdisplays.
Die Prozeduren in dieser Klasse sind meist sehr komplex und umfangreich.
Deswegen muss darauf verzichtet werden, jede der Methoden einzeln zu erläutern.
Mit den bisherigen Informationen sollten sie jedoch weitgehend selbsterklärend sein.
Zwei Prozeduren werden hier genauer unter die Lupe genommen: print und writeTextBuffer.


001 void TextDisplay::print(char character) {
002 switch (character) {
003 case 255:
004             (...)
005 case 127:                       // Delete (Putty Backspace)
006             (...)
007 case 13:                         // CR
008             (...)
009 default:
010             (...)
011  }
012 writeTextBuffer();
013 }

Die Funktion print ist in dieser Klasse dafür zuständig, dass jedes Zeichen den richtigen Platz im Text-Buffer erhält, bevor alles zur Anzeige übertragen wird.
Ein Buffer ist nötig, da der Speicher des Displays nicht ausgelesen werden kann.
Zudem enthält er ja auch nur reine Grafikinformationen.
Da Sie aber auch einzelne Buchstaben löschen und ersetzen möchten, ohne den ganzen Text neu schreiben zu müssen, gibt es dafür einen eigenen Buffer, also einen lesbaren und veränderbaren Arbeitsspeicher mit dem Inhalt des Textdisplays.
Dieses Verfahren wird später auch beim Grafikdisplay eine große Rolle spielen.
In der print-Funktion werden die Zeichen zunächst analysiert, denn einige haben eine besondere Bedeutung.
Die Analyse geschieht mit einer sogenannten Switch-case-Abfrage.
Im Grunde könnte man diese auch mit einer Reihe von If-then-else-Abfragen realisieren, mit Switch ist es allerdings um einiges komfortabler.
Entspricht character einem der hier angegebenen Zeichen, wird der Quelltext dahinter behandelt, der Rest wird übersprungen.

                                                                      Seite 43




Das erste behandelte Sonderzeichen hat den Dezimalwert 255 und sorgt dafür, dass der komplette Buffer-Inhalt gelöscht wird.
Das geschieht, indem alle Zeilen und Spalten des Text-Buffer-Arrays durchgegangen werden und überall eine 0 eingetragen wird.
Anschließend setzt das Programm die Cursorposition, also den Pointer, der auf die aktuelle Position im Array zeigt, zurück auf 0/0 (Zeile 0 / Position 0).
Das Byte 127 löscht ebenfalls Inhalt aus dem Buffer, allerdings nur das letzte Zeichen.
Das ist anhand der Cursorposition leicht zu ermitteln, nur ein besonderer Fall muss in einer If-Abfrage behandelt werden.
Wenn Sie nämlich am Anfang der nächsten Zeile sind, muss der Cursor zunächst in die vorherige Zeile springen und dann anhand der Hilfsfunktion get-LastCharPosition die zuletzt mit einem Zeichen beschriebene Position finden.
In jedem Fall wird das Zeichen an der entsprechenden Position gelöscht, indem es im Buffer durch eine 0 ersetzt wird.
Das Byte-13 hat in der ASCII-Norm ebenfalls einen besonderen Stellenwert.
Es wird auch als Carriage Return (dt. Wagenrücklauf = neue Zeile] bezeichnet und entspricht Ihrer Entertaste.
Es sorgt dafür, dass der Cursor in die nächste Zeile und dort an den Anfang der Zeile springt - allerdings nur, falls die letzte Zeile des Displays noch nicht erreicht wurde.
Die Switch-Bedingung default ist erfüllt, wenn keiner der vorherigen Fälle eingetreten ist.
Sollte das der Fall sein, heißt das also, dass es sich um keines der zuvor festgelegten Sonderzeichen handelt, sondern um ein gewöhnliches Zeichen.
In diesem Fall wird es einfach an die aktuelle Cursorposition geschrieben, die daraufhin um einen Zähler erhöht wird.
Sollte das Ende einer Zeile erreicht worden sein, springt der Cursor in die nächste.
Der Buffer ist also verändert worden - aber wie wird der Inhalt des Buffers auf die Pixel des Displays übertragen?

001 void TextDisplay::writeTextBuffer() {
002    for (byte y=0; y<MAX_CHARACTERS_Y; y++) {
003        lcd.setPageAddress(y);
004        lcd.setColumnAddress(0);
005        for (byte x=0; x<MAX_CHARACTERS_X; x++) {
006             int position = textBuffer[y][x] * CHARACTER_WIDTH;
007             for (byte i=0; i<CHARACTER_WIDTH; i++)  {
008                lcd.writeData(pgm_read_byte(font7x5 + position++));
009         }
010     }
011  }

                                                                      Seite 44






Die writeTextBuffer-Methode ist für das Übertragen des Buffers auf das Display zuständig.
Bei jedem Aufruf wird der gesamte Inhalt des Buffers auf das Display übertragen.
Das geschieht in den beiden oben abgebildeten For-Schleifen, die Zeilen und Spalten behandeln.
Interessant sind hier vor allem die innerste For-Schleife und die Zeile davor.

001  int position = textBuffer[y][x] * CHARACTER_WIDTH;

Wie der Name der Funktion erahnen lassen, wird hier eine Position berechnet - aber welche Position?
Um das genauer zu verstehen, müssen Sie einen Blick in die Datei Font.h werfen.
Hier sind einmal ein paar Zeilen ausgeschnitten:

001 #define CHARACTER_WIDTH 6
002 #define MAX_CHARACTERS_X 22
003 #define MAX_CHARACTERS_Y 8
004
005 PROGMEM byte font7x5[] = {
006 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,       // Code for char ®NULL
007 0x3E, 0x55, 0x51, 0x55, 0x3E, 0x00,      // Code for char  
008 0x3E, 0x6B, 0x6F, 0x6B, 0x3E, 0x00,     // Code for char [... .  ]


Sie finden zu Beginn neben den üblichen Zeilen drei wichtige Definitionen, nämlich einmal CHARACTER_WIDTH,
die die Länge eines einzelnen Zeichens angibt, und die Variablen MAX_CHARACTERS_X und MAX_ CHARACTERS_Y,
die die maximalen Zeichen pro Spalte und pro Zeile definieren.
Es folgt ein sehr großes eindimensionales Array mit dem Namen font7x5 vom Typ Byte.
Der Übersicht halber sind jeweils 6 Bytes in eine Reihe geschrieben.
Jede Zeile entspricht in dieser Darstellung einem Zeichen.
Rechts bei den Kommentaren finden Sie immer Angaben, um welches Zeichen es sich handelt.
Die ersten Zeichen sind nicht gerade üblich, wenn Sie allerdings weiter herunterscrollen, finden Sie das Zeichen 1, das Sie bereits einmal selbst definiert haben.

001  0x00, 0x42, 0x7F, 0x40, 0x00, 0x00,            // Code for char 1

Falls Sie die Reihen durchzählen, werden Sie feststellen, dass die 1 sich an der 49. Position befindet (die erste Zeile als 0 gezählt].

                                                                      Seite 45





Laut ASCII-Norm entspricht die 1 dem Dezimalwert 49.
Wenn Sie sich noch einmal die Berechnung der Positionsvariablen ansehen, stellen Sie fest, dass der Dezimalwert des aktuellen Zeichens (textBuffer [y][x]) mit dem Wert der Zeichenbreite multipliziert wird.
Das entspricht also in dem eindimensionalen Fontarray genau der Position des ersten Bytes Ihres Zeichens.

001 for (byte i=0; i<CHARACTER_WIDTH; i++) {
002             lcd.writeData(pgm_read_byte( font7x5 + position++));
003  }

Zuletzt werden in der For-Schleife, beginnend bei der gerade bestimmten Position, die 6 Bytes des Zeichens nacheinander an das Display übertragen.
Die äußeren For-Schleifen liefern dann das nächste Zeichen.
In dem Beispiel SerialTextDisplay haben Sie schon fleißig Buchstaben auf das Display übertragen.
Allerdings stellen Sie sich nun eventuell die Frage, wie Sie die Zeichen im unteren ASCII-Bereich auf das Display bringen können.
Der unten abgebildete Quelltext stellt die Symbole der Positionen 0-47 auf dem Display dar.

001 include "TextDisplay.h"
002 include "SPI.h"
003
004 TextDisplay display = TextDisplay( );
005
006 void setup() {
007    display.init(0);
008    display.clear();
009    for (int i    0; i< 48; i++) {
010         display.print (i);
011    }
012 }
013
014 void loop( ) {  }


                                                                      Seite 46





Funktionsumfang der Klasse TextDisplay

API-Funktion (Textdisplay 22.8 Zeichen)   Parameter                   Erklärung
init                                                             byte contrast  [0-63]       Initialisierung des Displays mit übergebendem Kantrastwert [Hardware SPI], Hintergrundbeleuchtung nicht schaltbar, maximale Übertragungsgeschwindigkeit
println                                                        Zeichenkette                   Gibt eine Zeichenkette in der aktuellen Zeile aus [mit anschließendem Zeilenumbruch]
print                                                           Zeichenkette                  Gibt eine Zeichenkette in der aktuellen Zeile aus
clear                                                                                               Löscht den internen Textpuffer und setzt die aktuelle
Position auf Anfang
get LastCharPosition                                   byte line [0-7]                  Ermittelt die Position des letzten Zeichens in einer angegebenen Zeile
writeTextBuffer                                                                                 Stellt den Inhalt des Textpuffers auf dem Display dar

                                                                      Seite 47




DIN A4 ausdrucken
*********************************************************

Impressum: Fritz Prenninger, Haidestr. 11A, A-4600 Wels, Ober-Österreich, mailto:schaltungen@schaltungen.at
ENDE






Comments