Forum: Mikrocontroller und Digitale Elektronik STM32F4 SSD1963 TFT FONTS


von Reginald L. (Firma: HEGRO GmbH) (reggie)


Angehängte Dateien:

Lesenswert?

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

von dasrotemopped (Gast)


Lesenswert?

mal einen Screenshot vom TFT in Betrieb zur Hand ?
Wieviel kB werden denn im Flash belegt ?
Die Fonts sehen groß aus.

Gruß,

dasrotemopped.

von W.S. (Gast)


Lesenswert?

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.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Angehängte Dateien:

Lesenswert?

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
von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?


von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

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?

von Marc (Gast)


Lesenswert?

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ß

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

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.

von dasrotemopped (Gast)


Lesenswert?

>@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.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

Troll?

von Markus H. (dasrotemopped)


Angehängte Dateien:

Lesenswert?

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.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Angehängte Dateien:

Lesenswert?

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
von Markus H. (dasrotemopped)


Angehängte Dateien:

Lesenswert?

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.

von m.n. (Gast)


Angehängte Dateien:

Lesenswert?

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.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

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 ;)

von dasrotemopped (Gast)


Lesenswert?

>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.

von m.n. (Gast)


Lesenswert?

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/

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

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.

von Christian J. (Gast)


Lesenswert?

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?

von dasrotemopped (Gast)


Lesenswert?

>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.

von Reginald L. (Firma: HEGRO GmbH) (reggie)


Lesenswert?

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!

von Anon E. (anonelec)


Lesenswert?

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
Noch kein Account? Hier anmelden.