FT8xx Library

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

von Benutzer:Rudolph

Unter dem Namen EVE - Embbedded Video Engine - hat FTDI eine Reihe von Chips auf den Markt gebracht die es ermöglichen auch kleinere MikroController an TFT-Displays anzuschliessen. Die erste Generation kam bereits Anfang 2013 mit dem FT800 und FT801 auf den Markt, gefolgt von der EVE2 Generation mit den Chips FT810, FT811, FT812 und FT813, die neueste Generation ist EVE3 mit BT815 und BT816.

Da mich die bestehenden Implemtierungen nicht überzeugt haben und ich mich andererseits tiefer mit den FT8xx beschäftigen wollte, habe ich mich gegen Ende 2015 an die Arbeit gemacht, meine eigene Funktions-Bibliothek zu schreiben.

Hardware

Es gibt eine Reihe fertiger FT8xx Module mit TFT die man über eine SPI Schnittstelle mit einem quasi beliebigen MikroController verbinden kann.
Angefangen habe ich mit einem VM800B von FTDI, dieses 3,5" TFT mit 320x240 Pixeln hat einen resistiven Touch-Screen, Pegel-Wandler, 3,3V Regler und einen Verstärker mit Lautsprecher verbaut.
Als nächstes kam dann ein FT800CB-HY50B von HAOYU dazu welches ich später auf FT810 umgelötet habe. Die Module von HAOYU haben ebenfalls Pegelwandler und 3,3V Regler, aber keinen Verstärker für Audio.
Dann habe ich ein RVT70UQFNWC0x von Riverdi benutzt, diese habe keine Pegel-Wandler und benötigen sowohl 3,3V als auch 5V.

Aktuell bevorzuge ich die Module von Matrix Orbital Zum Beispiel habe ich schon mehrere Projekte mit den EVE2-70G gemacht, besitzte aber auch EVE2-35G, EVE2-43G und EVE2-50G. Diese Module benötigen 3,3V und Stecker und Pin-Belegung sind mit den Modulen von Riverdi kompatibel.

Für erste Schritte mit den BT816 hat mir Matrix Orbital freundlicherweise eine ausgediente Prototypen-Platine mit einem BT816 zur Verfügung gestellt, diese habe ich mit dem TFT vom EVE2-43G kombiniert. Inzwischen habe ich ein RVT43ULBNWC00, aber eigentlich warte ich darauf die EVE3-xxG von Matrix Orbital kaufen zu können.


Test-Projekt


Software

Da mir Multi-Plattform Support wichtig ist besteht die "Library" aus mehreren Quelltext-Modulen die im Projekt-Context compiliert werden.
Implementiert für und benutzt mit habe ich die Library mit eigenen 90CANxx AVR Platinen, Arduino (miniPro), Renesas RH850, Infineon Aurix, ATSAMC21E18A (Cortex M0+) und ATSAME51J19A (Cortex M4F).

Die "Library" selbst besteht aktuell aus diesen Dateien:
EVE.h - Definitionen und Macros
EVE_commands.c - Die eigentlichen Funktionen, Plattform unabhängig
EVE_commands.h - exportiert die benötigten Funkionen
EVE_config.h - Timings für diverse Displays mit FT8xx/BT81x und Abstrahierungs Funktionen die auf der verwendeten Plattform automatisch ausgewählt werden. Diese Funktionen sind zumeist Einzeiler und aus dem Grund als "static inline" definiert.
EVE_target.c - Plattform-spezifische Funktionen die etwas umfangreicher sind, im Moment nur für die DMA-Funktionen des ATSAMC21E18 genutzt.
EVE_target.h - exportiert die benötigten Funkionen

Verwendung

Erstmal muss ich dazu raten, einen Blick in die Datasheet/Programmers-Guides von FTDI/Bridgetek zu werfen:
DS_FT801.pdf
FT800_Series_Programmer_Guide.pdf
DS_FT81x.pdf
FT81X_Series_Programmer_Guide.pdf
DS_BT81X.pdf
BRT_AN_033_BT81X_Series_Programming_Guide.pdf

Die FT8xx/BT8xx arbeiten Objekt-Orientiert und die Anzeige erfolgt mit einer Display-Liste, die Liste wird von meiner Library mit Hilfe des Kommando-Co-Prozessors aktualisiert.
Der Co-Prozessor verwendet einen 4096 Byte grossen FIFO Speicher, jedes Kommando belegt mindestens 4 Byte und es muss auch immer auf volle 4 Byte aufgefüllt werden. Es passen also maximal 1024 Kommandos in den FIFO und effektiv viel weniger "Objekte" weil diese meistens ein vielfaches von 4 Byte benötigen. Ein EVE_cmd_text() zum Beispiel benötigt 4 Byte für das Kommando, 4 Byte für die Koordinaten, 4 Byte für Zeichensatz und Optionen und dann noch Platz für den eigentlichen Text. Ein "Hallo Welt" kommt so in Summe auf 24 Byte im FIFO, sind zwar eigentlich nur 22, dann wird aber noch mit Null-Bytes aufgefüllt.
Diese 24 Byte werden vom Kommando-Co-Prozessor interpretiert und in die Display-Liste gepackt die maximal 8kB groß sein darf.
Die Anzeigen erfolgt als einzelne Bilder der Buchstaben, aus "Hallo Welt" wird in der Display-Liste am Ende eine Folge von 15 oder 25 32-Bit Kommandos, also für "Hallo Welt" braucht EVE 60 oder 100 Bytes Speicher der Display-Liste.

Wofür ist das wichtig? Nun, man muss schon ein wenig aufpassen wie viel man versucht in die Display-Liste zu stecken, hat man zu viele Einträge läuft der FIFO über und die Anzeige funktioniert nicht.
Und noch viel schneller laufen die 8kB der Display-Liste über.
Der Überlauf wird von meiner Library nicht aufgefangen, dafür kostet das einfach zu viel Performance.

Die FT80x haben 256 kByte Grafik-Speicher, die FT81x/BT81x haben 1 MByte, Bilder werden zum Programm-Start gepackt in diesen Speicher übertragen und dort vom FT8xx entpackt. Zum Laden wird entweder cmd_inflate() oder cmd_loadimage() benutzt, ersteres empfiehlt sich mit monochrom-Bildern, letzteres ist für .jpg Bilder gedacht.
Die FT81x unterstützen auch .png, man muss aber etwas mit dem Format aufpassen.
Je nach verwendetem Format belegen die Bilder unterschiedlich viel Speicher im FT8xx. Beim "L1" Format sind es 1 Bit pro Pixel, "L8" benötigt ein Byte pro Pixel, "RGB565" kommt auf zwei Byte pro Pixel.
Ein 100x100 Pixel großes Vollfarb-Icon belegt also entpackt 20.000 Bytes im FT8xx.
Ein Bild in 800x480 würde 768.000 Bytes belegen, in 800x600 gar 960.000 Bytes, es passt also überhaupt nur ein Bild in voller Auflösung in den Speicher der FT81x - dafür sind die klar nicht ausgelegt.
Ich kann grundsätzlich empfehlen, eine Memory-Map zu erstellen, dafür habe ich in der tft.c in meinem Test-Projekt am Anfang Defines für die Start-Adressen.
Ein neues Feature der BT81x ist die Möglichkeit ein externes NOR SPI-FLASH mit bis zu 2GBit zu benutzen. Die RiTFT Module von Riverdi werden mit 64MBit geliefert, für die EVE3 Module von Matrix Orbital sind die Optionen ohne Flash, mit 32MBit und 128MBit gelistet.
Das FLASH hängt dabei per QSPI an dem BT81x und kann mit einem USB-SPI Interface mit Daten beschrieben werden, etwa dem EVE2-USB2SPI-KIT-A. Dafür gibt es von Bridgetek den EVE Asset Builder. Bilder, Zeichendsätze und Animationen in das ASTC Format konvertiert und können dann direkt aus dem FLASH heraus benutzt werden.

Unter FT800 / FT810 Library habe ich unter anderem mehrere Beispiel-Projekte veröffentlicht. Der derzeit aktuelle Stand ist das "FT8xx_Test" Projekt dessen Verzeichnis ich passend zur verwendeten Hardware benennen. Etwa "FT8xx_Test_90CAN_FT813_RVT70UQFNWC0x" oder auch "FT8xx_Test_90CAN_FT810_HY50B". Das ist ein vollständiges Projekt für Atmel-Studio 7.
Zusätzlich zu den Funktionen der "Library" gibt es Plattform-spezifische Dinge zu erledigen, etwa das Initialisieren des SPI, sowie der benötigten Pins für die Chip-Select und Powerdown Ausgänge. Zusätzlich empfiehlt sich grundsätzlich eine Art Scheduler zu benutzen der in einem Ereignis-gesteuerten Programmablauf für ein festes Zeitintervall sorgt.
Die main.c im Test-Projekt implementiert das soweit, Plattform-spezifisch für AVR Controller.
Die nächsten Dateien sind dann wieder Plattform unabhängig:
tft.c - drei Funktionen, initStaticBackground(), TFT_init() und TFT_loop() tft.h - exportiert TFT_init() und TFT_loop()
tft_data.c - test-Bilder
tft_data.h

TFT_init():
Wie der Name schon sagt wird mit dieser Funktion das Display initialisiert. Dazu erfolgt zunächst ein Aufruf von FT8_init(). Ist das Display nicht oder nicht richtig angeschlossen kehrt die Funktion nach einem 200ms Timeout mit einer Null zurück.
Dann erfolgt die Übermittlung der Touch-Kalibrier-Werte, es reicht völlig aus diese einmal zu ermitteln und in den Quelltext zu schreiben, Code zum Kalibrieren des Displays und zum Anzeigen der Werte ist per "#if 0" Statement auskommentiert.
Schliesslich werden noch die benötigten Bilder an den FT8xx übertragen und die Funktion initStaticBackground() aufgerufen.

initStaticBackground():
Mein Beispiel teilt die Display-Liste in einen statischen und einen dynamischen Teil auf. Der statische Teil wird nur einmal erzeugt, im FT8xx gespeichert und beim aktualisieren der Liste immer wieder mit einkopiert. Das hat den Vorteil, dass pro aktualisierung der Liste erheblich weniger über den SPI übertragen werden muss als wenn man das komplett übertragen würde.
Wichtig ist dabei, dass die Objekte in der Reihenfolge auf das Display gebracht werden in der sie in der Liste stehen.

TFT_loop():
Diese Funktion ist dafür ausgelegt alle 10ms aufgerufen zu werden. Es gibt zwei Teile die sich abwechseln, die Auswertung von Touch-Events und entsprechend Akualisierung von Variablen, sowie das eigentliche neu Schreiben der Display-Liste. In meinem Beispiel wird die Liste also alle 20ms neu geschrieben, die Darstellung erfolgt mit 50 neuen Bilder pro Sekunde. Das geht sowohl langsamer als auch schneller, macht man das langsamer wird allerdings die Reaktion auf den Touch träger, macht man das schneller muss man aufpassen, dass man unterhalb der 60 Hz vom TFT bleibt, die FT8xx mögen es nicht, wenn man die Liste schneller aktualisiert als sie dargestellt wird.
Die Auswertung von Touch-Events erfolgt mit TAG-Werten, über "FT8_cmd_dl(TAG(n));" kann einzelnen Objekten oder auch ganzen Gruppen von Objekten ein Wert von 1 bis 255 zugewiesen werden. Ein Wert von Null bedeutet dabei, das kein Touch-Event erfolgt ist und wenn man Objekten diesen Wert zuweist sind sie von der Erkennung ausgenommen.
Im Beispiel gibt es nur einen Toggle-Button und einen Slider die ausgewertet werden, wie man daran aber sehen kann erfolgt zwar die Erkennung von Touch-Events über den FT8xx, darauf reagieren muss aber die eigene Software indem ein Toggle-Button auch wirklich optisch umgeschaltet oder auch ein normaler Button als gedrückt dargestellt wird.
Das eigentliche Aktualisieren der Display-Liste ist recht kurz weil der größere Teil der Anzeige statisch ist und über FT8_cmd_append() mit eingehängt wird.
Die mit FT8_cmd_number() dargestellten Zahlen sind im Beispiel zwar weitgehend statisch, aber mit dem einen Wert der sich in Abhängigkeit des Sliders ändert sieht man sicher was die Idee dahinter ist das in den dynamischen Teil zu packen.

Download

https://github.com/RudolphRiedel/FT800-FT813