Huhu! Ich möchte hier jetzt auch mal was gutes tun und meine TFT-Klasse mit euch teilen, vielleicht kann sie der ein oder andere gebrauchen. Ich habe sie ziemlich grob aus meinem Projekt rauskopiert, falls ihr sie nicht zum laufen bekommt helfe ich gerne. Mein Ziel war es, eine gut lesbare Textausgabe am TFT zu bekommen, deshalb ist auch kaum Grafikspaß in der Klasse enthalten. Laufen tut sie auf einem STM32F429IGT6 (Waveshare Core und Open4X9I-C Mainboard) mit AT070TN92 Bildschirm und SSD1963er Controller. Auf dem F4 Discovery läuft sie mit ziemlicher Sicherheit auch. Implementiert ist nur die Initialisierung der Hardware und Textausgabe. Es werden 2 Font-Formate unterstützt: 1. "The Dot Factory" 2. "FontCvt" von Segger - Hier nur die 8Bit 4xAA Formate Es sind geringfügige Änderungen am Font-Structure notwendig, geht aber ruckzuck, siehe fonts.h /.c . Getestet habe ich ASCII 0x20 - 0x7E, da passt alles. Grüße Reggie
mal einen Screenshot vom TFT in Betrieb zur Hand ? Wieviel kB werden denn im Flash belegt ? Die Fonts sehen groß aus. Gruß, dasrotemopped.
Reginald L. schrieb: > Huhu! > > Ich möchte hier jetzt auch mal was gutes tun und meine TFT-Klasse mit > euch teilen, Ja, danke für das freundliche Ansinnen, aber ich kann mir beim besten Willen nicht vorstellen, daß man diese Fonts wirklich vernünftig benutzen kann. Und was du da in den " _TFT::WriteChar" geschrieben hast, erscheint mir grauselig. Abgesehen davon: Deine Fonts scheinen ja alle feste Breite zu haben und sind ansonsten auch ausgesprochen platzverschwenderisch codiert. Mein Tip: Schau dir mal die Fonts und das GDI in der Lernbetty an. Nochwas: Du scheinst bie den Zeichenausgaben dich darum zu bemühen, an den Stellen, wo das Zeichen keinen gesetzten Pixel hat, einen Pixel in Hintergrundfarbe setzen zu wollen. Das ist blöd, denn damit verplemperst du ne Menge Zeit und du verbaust dir auch noch die Möglichkeit, einen Text auf eine nicht unifarbene Fläche zu setzen. W.S.
Hi W.S., danke für die Hinweise! Du kennst den LCD-Controller? Bei mir läuft das recht gut bisher, für Tipps bin ich aber offen da dies mein erster TFT ist. Wenn du Verbesserungsvorschläge hast, immer her damit! Die Fonts haben keine feste Breite, weder die AA noch die non-AA. Das Problem bei diesem Controller ist, dass man für jede neue Speicheraddressierung 8 Befehle benötigt. Deshalb erschien es mir sinnvoll, WriteChar() so auszuführen. Was ist denn die Lernbetty? @dasrotemopped: Mein komplettes Projekt auf dem MCU schlägt mit 112KB auf dem Flash und 7KB im RAM zu buche. Kannst ja nachrechnen, je nachdem wieviele Buchstaben und Schriftarten du verwendest. Meine Idee war das TFT Zeug auf den externen Flash zu legen, beim Start in den externen RAM zu schmeißen und dann zu verwenden.
:
Bearbeitet durch User
Reginald L. schrieb: > Was ist denn die Lernbetty? Beitrag "die Betty-Fernbedienung von Pollin als ARM-Eval Board"
Rufus Τ. F. schrieb: > Reginald L. schrieb: >> Was ist denn die Lernbetty? > > Beitrag "die Betty-Fernbedienung von Pollin als ARM-Eval Board" So wie ich den Code verstehe, setzt du für jedes zu schreibende Pixel eine neue Koordinate?
Reginald L. schrieb: > Ich möchte hier jetzt auch mal was gutes tun und meine TFT-Klasse mit > euch teilen, vielleicht kann sie der ein oder andere gebrauchen. Selber Hubhu! danke erst einmal dafür ;-) Welches Protokoll verwendest du zur Ansteuerung: 8080 oder 6800 ? Woher hast du dein Display bezogen ? Danke für die Informationen. Gruß
Marc schrieb: > Reginald L. schrieb: >> Ich möchte hier jetzt auch mal was gutes tun und meine TFT-Klasse mit >> euch teilen, vielleicht kann sie der ein oder andere gebrauchen. > > Selber Hubhu! > > danke erst einmal dafür ;-) > > Welches Protokoll verwendest du zur Ansteuerung: 8080 oder 6800 ? > Woher hast du dein Display bezogen ? > > Danke für die Informationen. > > Gruß Ich benutze 8080, aber eher unbewusst ;) Hier habe ich es gekauft: http://www.amazon.de/gp/product/B00IYHZQUU?psc=1&redirect=true&ref_=oh_aui_detailpage_o05_s00 Mit Jumper-Kabeln von ca. 15-20cm Länge komme ich auf eine komplette Framefüllung in etwa 90ms. Denke für bewegte Bilder eignet sich das Display, auch bei kurzer Anbindung, eher weniger.
>@dasrotemopped: Mein komplettes Projekt auf dem MCU schlägt mit 112KB >auf dem Flash und 7KB im RAM zu buche. Einen Font 12x16(BxH)habe ich in 6kB gequetscht, mit allen 256 ASCII Zeichen. Der Initialisierungcode für SSD1963 (Pin Wackler) hat ca. 7kB. RAM werden ca 200 Byte gebraucht, jeder Buchstabe wird on the Fly auf 16bit Color aufgebläht. Auch Cortex-M0 geeignet war das Ziel. i80/FSMC soll noch kommen, so bald meine neuen Displays und Nucleos mit FSMC da sind. Bis jetzt mit 240x320 und 480x272 getestet. Gruß, dasrotemopped. PS: die SSD1963 Initialisierung habe ich natürlich aus den I-Net zusammengeklaubt, nicht mein Werk, nur die Anpassung an CubeMX ist meins.
hier an einem Nucleo-F091RC rechts ein ILI9341 mit 240x320Pixel über SPI links am 2. Nucleo-F091RC ein SSD1963 mit 480x272Pixel über GPIO. Die Bilder und die Fonts sind im Flash gespeichert, beide Displays laufen mit 16bit Farbtiefe. Gruß, dasrotemopped.
Wollte dich nicht angreifen, aber kann deinen Text nicht richtig einordnen. 7KB für die Initialisierung? Pin-Wackler? 16Bit Buchstaben? Was hat CubeMX mit SSD1963 zu tun?
:
Bearbeitet durch User
Den Code teile ich natürlich auch gerne. Ist noch etwas unaufgeräumt, aber wie man sieht Cortex-Mx übergreifend. Hier mal auf dem STM32F4-disco board, immmer noch nur Pin-Wackeln. Ich benutze Keil oder SW4STM32 mit CubeMX für die Grundstruktur. Alles in C programmiert. Der Font ist aus Platzgründen auf der Seite liegend, damit keine Bits verschwendet werden. 12x uint16_t werden auf 192x uint16_t aufgeblasen und in den Grafikspeicher geschoben. Dadurch kann Hintergrundfarbe und Schriftfarbe beliebig geändert werden. Ist aber noch viel am Code zu optimieren. Noch Fragen ? Gruß, dasrotemopped. PS: hab nur die wichtigsten ASCII Zeichen implementiert, aber alle belegen schon Speicherplatz, der Textscreen im Video wird in Laufzeit generiert, das Bild ist ein Bitmap im Flash und frisst natürlich Mengen an Speicher. Lässt man das weg, reichen 13kB Flash und ca 200 Byte RAM. Kann ja jetzt jeder ausprobieren.
dasrotemopped schrieb: > Einen Font 12x16(BxH)habe ich in 6kB gequetscht, Ich habe mich schon über das eigenartige Format gewundert, sehe jetzt aber, daß die Breite von 12 Pixeln weder genutzt wird noch irgendeine Verbesserung der Lesbarkeit bringt. Zum Vergleich ein 8x16 Zeichensatz, der 4K belegt und auf größeren TFTs einfach 'aufgepustet' werden kann (zoom). @Reginald Proportionalschrift ist zum Lesen von Texten sehr angenehm; für technische Anwendungen Meßwertanzeige/Tabellen ist eine Schrift mit festem Zeichenabstand erheblich besser. Ich finde es immer wieder 'schade', wenn Zeichensätze zu finden sind, die lediglich den Bereich 0x20 - 0x7f abdecken. Auch bei einem Grafikdisplay können Sonderzeichen und Blockgrafik sehr nützlich sein.
m.n. schrieb: > Proportionalschrift ist zum Lesen von Texten sehr angenehm; für > technische Anwendungen Meßwertanzeige/Tabellen ist eine Schrift mit > festem Zeichenabstand erheblich besser. Recht hast du. Habe ich mir gerade eben in meine TODO.h geschrieben :> m.n. schrieb: > Ich finde es immer wieder 'schade', wenn Zeichensätze zu finden sind, > die lediglich den Bereich 0x20 - 0x7f abdecken. Auch bei einem > Grafikdisplay können Sonderzeichen und Blockgrafik sehr nützlich sein. Man muss halt wissen was man braucht. Jeder gute Ingenieur lebt getreu dem Motto: So einfach wie möglich, so kompliziert wie nötig. Hängt halt eben auch von den eigenen Vorstellungen ab. Ich benötige beispielsweise keine ö's und ü's ;)
>Zeichensätze,die lediglich den Bereich 0x20 - 0x7f abdecken. Macht auch das Programmieren umständlicher, ein voller Font bedeutet ja immer, ASCII Code = Speicheradresse des Fonts im Datenarray*Fontgröße. Die Fontgröße 12x16 ergibt bei 240x320 Pixel genau 20x20 Zeichen, bei 480*272 genau 40x17 Zeichen. Leider geht es bei 800x480 nicht genau auf, 66KommaIrgendwas x30 lasse ich aber mal durchgehen. Ich mag es, wenn man ohne Verrenkungen seinen Code schreiben kann. Ein M0 hat ja heute schon genug Reserven, das man nicht wie in den 80ern um jedes Bit kämpfen muss. Aber Embedded bedeutet schon noch, Ressourcen zu schonen. >Man muss halt wissen was man braucht. Genau. Alles den eigenen Bedürfnissen anpassen. Keine Lösung kann universell sein. Gruß, dasrotemopped.
Reginald L. schrieb: > Man muss halt wissen was man braucht. Jeder gute Ingenieur lebt getreu > dem Motto: So einfach wie möglich, so kompliziert wie nötig. Hängt halt > eben auch von den eigenen Vorstellungen ab. Ich benötige beispielsweise > keine ö's und ü's ;) Man muß das Rad ja nicht jedesmal neu erfinden. http://v3.sk/~lkundrak/fonts/kbd/
m.n. schrieb: > Reginald L. schrieb: >> Man muss halt wissen was man braucht. Jeder gute Ingenieur lebt getreu >> dem Motto: So einfach wie möglich, so kompliziert wie nötig. Hängt halt >> eben auch von den eigenen Vorstellungen ab. Ich benötige beispielsweise >> keine ö's und ü's ;) > > Man muß das Rad ja nicht jedesmal neu erfinden. > http://v3.sk/~lkundrak/fonts/kbd/ Achja, vergessen: Verwende Normelemente, wann immer möglich. Auf den ersten Blick (Vermutung meinerseits) gibt es hier wesentliche Unterschiede, zwischen unseren beiden Herangehensweisen bezüglich der Fonts: Die Auflösung, AntiAliasing-Fonts und die Möglichkeit einen FontCreator zu nutzen.
Markus H. schrieb: > hier an einem Nucleo-F091RC rechts ein ILI9341 mit 240x320Pixel über SPI > links am 2. Nucleo-F091RC ein SSD1963 mit 480x272Pixel über GPIO. Kannst Du dazu mal den Code bereitstellen? Ich suche dringend eine Möglichkeit Text+Grafik auf mehr als diesen winzigen 320x240 px Displays darzustellen. Das Display mapped sich doch ins Ram des F0 über den FMSC rein, oder?
>Kannst Du dazu mal den Code bereitstellen? < für Nucleo-F091RC ein SSD1963 mit 480x272Pixel über GPIO < schaust du hier - Src.zip Beitrag "Re: STM32F4 SSD1963 TFT FONTS" >Das Display mapped sich doch ins Ram des F0 über den FMSC FSMC gibt's nicht bei F0, darum GPIO Gruß, dasrotemopped.
Jetzt weiß ich, was du mit unelegant (du hast es anders ausgedrückt), in Bezug auf den WriteChar() gemeint hast. Allerdings lässt sich das bei einem SSD1963 kaum arg anders lösen. Weswegen ich hier bin, dies ist mein derzeitiger Schnipsel, sobald ich mit der Klasse fertig bin, lade ich sie auch hoch:
1 | /////////////////// Schrift Schwarz, Hintergrund Weiß:
|
2 | uint16_t _TFT::WriteChar(uint16_t x_start, uint16_t y_start, const FONT_AA * ft, char chr) |
3 | {
|
4 | while ((DMA2D->CR & DMA2D_CR_START) == SET) {}; |
5 | // Char width
|
6 | uint8_t width = ft->charInfo[chr - ft->startChar].widthpixel; |
7 | // Char height
|
8 | uint8_t height = ft->charHeight; |
9 | // Char data
|
10 | uint8_t* data = (uint8_t*)ft->charInfo[chr - ft->startChar].data; |
11 | // Char size in bytes
|
12 | uint16_t blk_width = ft->charInfo[chr - ft->startChar].datawidth; |
13 | |
14 | // Write char
|
15 | uint32_t blk_pos; |
16 | for (int y_pxl = 0; y_pxl < height; y_pxl++) |
17 | {
|
18 | for (int x_byte = 0; x_byte < blk_width; x_byte++) |
19 | {
|
20 | blk_pos = (y_pxl * blk_width) + x_byte; |
21 | // Don't touch pixel if CharData == 0
|
22 | if (data[blk_pos] != 0x00) |
23 | {
|
24 | // Check first 4bits
|
25 | if ((data[blk_pos] >> 4) != 0x00) |
26 | SetPixel(x_start + (x_byte * 2), y_start + y_pxl, Mono4To16LookUp[(data[blk_pos] >> 4)]); |
27 | // Check last 4bits
|
28 | if ((data[blk_pos] & 0x0F) != 0x00) |
29 | SetPixel(x_start + (x_byte * 2) + 1, y_start + y_pxl, Mono4To16LookUp[(data[blk_pos] & 0x0F)]); |
30 | }
|
31 | }
|
32 | }
|
33 | return width; |
34 | }
|
35 | |
36 | /////////////////// Schrift schwarz, Hintergrund einheitlich:
|
37 | uint16_t _TFT::WriteChar(uint16_t x_start, uint16_t y_start, uint32_t backcol, const FONT_AA* ft, char chr) |
38 | {
|
39 | // Return immediately if space character
|
40 | if (chr == 0x20) |
41 | return ft->charInfo[chr - ft->startChar].widthpixel; |
42 | |
43 | while ((DMA2D->CR & DMA2D_CR_START) == SET) {}; |
44 | // Char width
|
45 | uint8_t width = ft->charInfo[chr - ft->startChar].widthpixel; |
46 | // Char height
|
47 | uint8_t height = ft->charHeight; |
48 | // Char data
|
49 | uint8_t* data = (uint8_t*)ft->charInfo[chr - ft->startChar].data; |
50 | // Char size in bytes
|
51 | uint16_t blk_width = ft->charInfo[chr - ft->startChar].datawidth; |
52 | |
53 | // Write char
|
54 | uint32_t blk_pos; |
55 | uint8_t R_bkgrnd = 0; |
56 | uint8_t G_bkgrnd = 0; |
57 | uint8_t B_bkgrnd = 0; |
58 | uint8_t R_new = 0; |
59 | uint8_t G_new = 0; |
60 | uint8_t B_new = 0; |
61 | float shade = 0; |
62 | for (int y_pxl = 0; y_pxl < height; y_pxl++) |
63 | {
|
64 | for (int x_byte = 0; x_byte < blk_width; x_byte++) |
65 | {
|
66 | blk_pos = (y_pxl * blk_width) + x_byte; |
67 | // Don't touch pixel if CharData == 0
|
68 | if (data[blk_pos] != 0x00) |
69 | {
|
70 | // Check first 4bits
|
71 | if ((data[blk_pos] >> 4) != 0x00) |
72 | {
|
73 | shade = (float)1 - (((float)(data[blk_pos] >> 4) * 17) / 255); |
74 | |
75 | R_bkgrnd = backcol >> 16; |
76 | G_bkgrnd = backcol >> 8; |
77 | B_bkgrnd = backcol; |
78 | |
79 | R_new = ((float)R_bkgrnd * (float)shade); |
80 | G_new = ((float)G_bkgrnd * (float)shade); |
81 | B_new = ((float)B_bkgrnd * (float)shade); |
82 | |
83 | SetPixel(x_start + (x_byte * 2), y_start + y_pxl, ((R_new << 16) | (G_new << 8) | B_new)); |
84 | }
|
85 | // Check last 4bits
|
86 | if ((data[blk_pos] & 0x0F) != 0x00) |
87 | {
|
88 | shade = (float)1 - (((float)(data[blk_pos] & 0x0F) * 17) / 255); |
89 | |
90 | R_bkgrnd = backcol >> 16; |
91 | G_bkgrnd = backcol >> 8; |
92 | B_bkgrnd = backcol; |
93 | |
94 | R_new = ((float)R_bkgrnd * (float)shade); |
95 | G_new = ((float)G_bkgrnd * (float)shade); |
96 | B_new = ((float)B_bkgrnd * (float)shade); |
97 | |
98 | SetPixel(x_start + (x_byte * 2) + 1, y_start + y_pxl, ((R_new << 16) | (G_new << 8) | B_new)); |
99 | }
|
100 | }
|
101 | }
|
102 | }
|
103 | return width; |
104 | }
|
Hast du oder jemand anders eine Ahnung ob ich das eleganter lösen / optimieren kann? Für etwa 100 Chars (56px hoch) komme ich so auf knapp 60ms. Die Funktion, die die Hintergrundfarbe nicht berücksichtigt kommt auf etwa 45ms, Hier noch meine Framebufferkopierfunktion™:
1 | void _TFT::SetPixel(uint16_t x, uint16_t y, uint32_t col) |
2 | {
|
3 | while ((DMA2D->CR & DMA2D_CR_START) == SET) {}; |
4 | (*(uint16_t*)(BufferInfo.WriteBuffer + (3 * (x + (TFT_X * y))))) = (uint16_t)(((uint16_t)col << 8) | ((uint16_t)col >> 8)); |
5 | (*(uint8_t*)(BufferInfo.WriteBuffer + (3 * (x + (TFT_X * y))) + 2)) = (uint8_t)(col >> 16); |
6 | }
|
Wenn ich 32bit kopiere, quetscht er mir ja 8bits zu viel in den Framebuffer, vllt lohnt sich da ein Workaround ala "allerletztes Byte auslesen und gesondert kopieren"? Ich möchte unbedingt beim AA bleiben und, aufgrund der Flexibilität, bei den von FontCvt konvertierten Fonts. Wenn du mir sagst, aus dem Code ist nicht mehr viel rauszuholen (was ich nicht glaube, ganz im Gegenteil), dann kann ich auch prima damit leben. Danke schonmal!
Da ich gerade an was ähnlichem bin hier eine Skizze, wie ich das gelöst habe. Bei mir ist das System viel langsamer (PIC18F44K20 mit 16MHz, mit ILI9341-basiertem Display, 16bit parallel angeschlossen). Trotzdem kann ich einen 320x240-Bildschirm schnell füllen (Aufbau kaum sichtbar, bei 64MHz [PLL] unsichtbar). Um ein Zeichen zu schreiben gehe ich wie folgt vor: Erst setze ich mit "page address set" und "column address set" das Fenster, in welches das Zeichen zu liegen kommt. Das gibts beim SSD1289 auch, soweit ich das jetzt überblicke sind das Register 44h-46h. Dann spule ich die Pixel mit "memory write" raus. Der Trick ist dabei, dass ich ein "run length encoding" für die Font-Definition verwende, und zwar so, dass ich in ein Byte in Bits 3..7 die Anzahl "Hintergrundpixel" (0..31) und in Bits 0..2 die "Vordergrundpixel" (0..7) schreibe. 0b01000011 heisst dann z.B. 8 Pixel Hintergrund schreiben, gefolgt von 3 Pixeln Vordergrund. So lassen sich max. 31 Hintergrundpixeln gefolgt von 7 Vordergrundpixeln in ein einziges Byte packen. Das spart gerade bei grösseren Fonts viel Speicherplatz. Bei Parallel-Interface ist der Schreibablauf dann so: 1) Hintergrundfarbe setzen (2 Port-Zugriffe) 2) Byte holen und erste 5 Bits maskieren (-> n) 3) /WR n x pulsen 4) Vordergrundfarbe setzen 5) Untere 3 Bits maskieren (-> m) 6) /WR m x pulsen 7) Schritte 2-6 wiederholen bis alle Pixel geschrieben sind Diesen Teil habe ich in Assembler geschrieben, mit "Hardware-nahem" C sollte das aber auch gehen. Vielleicht ist das ja auch für Dein Projekt nützlich.
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.