Forum: Mikrocontroller und Digitale Elektronik GLCD Fonts (64x128)


von Jan H. (janiiix3)


Lesenswert?

Guten Morgen,

habe hier gesucht, bin leider nur nicht fündig geworden.
Hat jemand schon fertige GLCD Fonts? Alles ab 6x8?

Würde gerne mal ausprobieren wie andere Zeichen auf dem GLCD aussehen.

Danke im vorraus!

von Horst S. (Gast)


Lesenswert?

Heute schon gegoogelt?

von Jan H. (janiiix3)


Lesenswert?

Habe gerade was passen könnte gefunden.
Mir kommen noch ein paar Verständnissfragen auf..

https://github.com/johnmccombs/arduino-libraries/blob/master/glcd/fonts/fixednums7x15.h

In der Headerdatei steht, dass die Zeichen "7x15" groß sind.

Als beispiel die '0'
1
// char '0'
2
0xfc, 0xfe, 0x03, 0xe1, 0x1b, 0xfe, 0xfc,
3
0x0f, 0x1f, 0x36, 0x21, 0x30, 0x1f, 0x0f,

Habe mir die Funkionen geschrieben die mir die Daten zum Display 
schicken.
Wenn ich das "Array für die '0'" jetzt übertragen würde, würde ja nur 
Müll im Display angezeigt werden..

Wie müssten die "for" Schleifen jetzt aussehen?
1
  glcdClear();
2
  while(1)
3
  {
4
    uint8_t i = 0;
5
    glcdGotoXY(2,0);
6
    for (; i < 7 ; i++)
7
    {
8
      glcdSendData(newArray[i]);
9
    }
10
    glcdGotoXY(3,0);
11
    for (; i < 14 ; i++)
12
    {
13
      glcdSendData(newArray[i]);
14
    }    
15
  }

: Bearbeitet durch User
von spess53 (Gast)


Lesenswert?

Hi

>Wenn ich das "Array für die '0'" jetzt übertragen würde, würde ja nur
>Müll im Display angezeigt werden..

Nö, das passt schon. Gilt aber nur für Displays mit senkrechter 
Byteausrichtung und für eine 0 mit Schrägstrich.

MfG Spess

von Jan H. (janiiix3)


Lesenswert?

Okay.

Wie kommt man am besten auf den Anfang der Zahl?
1
void glcdWidthNumb(uint8_t numb, uint8_t x, uint8_t y)
2
{
3
  uint8_t i = numb;
4
  
5
  glcdGotoXY(x,y);
6
  for (; i<(numb+8) ; i++)
7
  {
8
    glcdSendData(swapBits(fixednums8x16[i+6]));
9
  }
10
  glcdGotoXY(x+1,y);
11
  for (; i<(numb+16) ; i++)
12
  {
13
    glcdSendData(swapBits(fixednums8x16[i+6]));
14
  }
15
}

So das man die Funktion mit glcdWidthNumb('1',0,0) aufrufen kann?

von Thomas F. (igel)


Lesenswert?

Jan H. schrieb:
> habe hier gesucht, bin leider nur nicht fündig geworden.

Naja, schlecht gesucht:

Beitrag "Re: LCD Schriftarten ( Fonts in veschiedenen Größen )"

Ein 64x128 Font wie in der Überschrift braucht aber schon ordentlich 
Speicherplatz, 1024 Bytes pro Zeichen!
Da würde ich eher einen kleineren Font wählen und dann in doppelter 
Breite und Höhe ausgeben.

von Jan H. (janiiix3)


Lesenswert?

Thomas F. schrieb:
> Ein 64x128 Font wie in der Überschrift braucht aber schon ordentlich
> Speicherplatz, 1024 Bytes pro Zeichen!
> Da würde ich eher einen kleineren Font wählen und dann in doppelter
> Breite und Höhe ausgeben.

Stimmt habe mich vertan.

Es sollte 6 x 8 heißen.

von W.S. (Gast)


Lesenswert?

Jan H. schrieb:
> habe hier gesucht, bin leider nur nicht fündig geworden.

Da haste aber schlecht gesucht...


Jan H. schrieb:
> for (; i<(numb+8) ; i++)
>   {
>     glcdSendData(swapBits(fixednums8x16[i+6]));

Mir scheint, daß du nicht systematisch an die Sache herangehst.

Also, du hast ein GRAFIK Lcd, ja?
Da solltest du zu allererst dran denken, dein physisches LCD (also die 
Schnittstelle, deren Bedienung usw) vom logischen Display (also 
Bildspeicher mit XXX mal YYY Pixeln) zu trennen. Das macht dir das Leben 
deutlichst leichter, glaub's mir.

Bei deinem logischen Display brauchst du zu allererst eine Funktion zum 
Setzen von Pixeln, etwa so:
void SetzePixel(int X, int Y, byte wie);

Das "wie" gestaltet sich bei monochromen Displays recht einfach: 
schwarz, weiß, invertierend.

Auf diese Funktion zum Pixel Setzen baust du dir nun alle anderen 
Funktionen auf: Textzeichen malen, Rechtecke füllen, Linien zeichnen, 
Kreise zeichnen, Bilder/Icons malen und so weiter.

Dabei merkst du gleich, daß bereits ab der SetzePixel-Funktion das Ganze 
recht hardwareunabhängig wird. Es bleibt eigentlich nur noch die 
Dimension, also wieviele Pixel in X und in Y.

Als zweites brauchst du eine globale bool Variable, die du bei jedem 
Pixel-Schreiben setzt.

Als drittes brauchst du eine Funktion zum blockweisen Übertragen der 
Pixel zum LCD. Die wird in der Grundschleife aufgerufen, wenn die obige 
Variable true ist. Diese Funktion schaufelt den ganzen logischen 
Display-RAM in das Display und setzt obige Variable wieder auf false 
zurück.

So. Damit hast du ein relativ geräteunabhängiges GDI und einen 
"Hardware-treiber" für dein aktuelles Display.

So herum geht das viel besser.

W.S.

von Dominik (Gast)


Lesenswert?

W.S. schrieb:
> Bei deinem logischen Display brauchst du zu allererst eine Funktion zum
> Setzen von Pixeln, etwa so:
> void SetzePixel(int X, int Y, byte wie);

Hallo,
das ist sicher richtig wenn man von potenter Steuerungshardware ausgeht.
Handelt es sich um einen kleinen µC will man sicher eher nicht 6x8=48 
Funktionsaufrufe pro Zeichen, da wird man den Abstraktionslayer doch 
näher in Richtung Hardware legen wollen.
Der andere Quelltext im Thread erinnert auch schwer an einen 8bit AVR 
o.ä.

Jan H. schrieb:
> Wie müssten die "for" Schleifen jetzt aussehen?
>   glcdClear();
>   while(1)
>   {
>     uint8_t i = 0;
>     glcdGotoXY(2,0);
>     for (; i < 7 ; i++)
>     {
>       glcdSendData(newArray[i]);
>     }
>     glcdGotoXY(3,0);
>     for (; i < 14 ; i++)
>     {
>       glcdSendData(newArray[i]);
>     }
>   }

Kommt darauf an. Reine Spekulation:

Handelt es sich um das "Standard"-LCD12864 mit ST7920?
Dann (wie immer) lohnt sich ein tiefer Blick ins Datenblatt, das Teil 
verfügt über 16bit Breite Spalten im Grafikmodus. Bei den gotoxy... ist 
zu beachten, dass sich die unterer Hälfte meist linear an die obere 
anschließt, sprich logisch sind es eher 256x32 Pixel.

Die oben erwähnte 7x15er ist eigentlich 7x16 und macht in sofern Sinn,
dass man ein Zeichen ohne Umwege in einer Spalte platzieren kann.

11111100 11111110 00000011...
sieht aus wie die 3 rechten oberen Zeilen der 0, die müssen dann auch so
platziert werden
0x0F -> 00001111 0x1F -> 00011111
könnte links oben sein,
also muss jetzt 16 bit Zeilenweise der Array[zeile+0] und der 
Array[zeile+8te] Wert aufs Display geschrieben werden.
In welcher Reihenfolge müsste ich jetzt selbst erst ins Datenblatt 
gucken.

Dass man bei so einer Schriftart auch ein normales Text-LCD mit 16x02 
nehmen könnte (wenn das die einzige Grafikanzeige wird) ist denke klar.

Sollen nun aber andere Schriftgrößen angezeigt werden wird es zunehmend 
schwieriger. Bei den angedachten 6x8 passen nämlich 2 2/3 Zeichen in 
eine
16 bit breite Spalte, daher muss man entweder diese Spalte im Speicher 
vorbereiten (+weitere wenn die Zeichen überlappen) oder sich den 
Speicherinhalt vom LCD zurücklesen, anpassen und wieder zurückschreiben.

Gruß Dominik

von Nico W. (nico_w)


Lesenswert?

Dominik schrieb:
> Hallo,
> das ist sicher richtig wenn man von potenter Steuerungshardware ausgeht.
> Handelt es sich um einen kleinen µC will man sicher eher nicht 6x8=48
> Funktionsaufrufe pro Zeichen, da wird man den Abstraktionslayer doch
> näher in Richtung Hardware legen wollen.
> Der andere Quelltext im Thread erinnert auch schwer an einen 8bit AVR
> o.ä.


Das ist doch völlig Banane. Ich habe erst letztens einen Treiber für ein 
ST7735 gebaut auf nem STM mit 72MHz. Da kann es genauso zu langsam 
werden. Liegt aber nicht an der Funktion, sondern was man dem Compiler 
an Optimierungen ermöglicht.


Mit O0, ohne LTO und inlining wird es natürlich arsch lahm, wenn man 
viele Funktionen hat. Aber deshalb drauf zu verzichten? Äh, ne...

von Dominik (Gast)


Lesenswert?

Nico W. schrieb:
> Das ist doch völlig Banane. Ich habe erst letztens einen Treiber für ein
> ST7735 gebaut auf nem STM mit 72MHz. Da kann es genauso zu langsam
> werden. Liegt aber nicht an der Funktion, sondern was man dem Compiler
> an Optimierungen ermöglicht.

Hallo,
war keine Kritik, nur ein Vorschlag, sicherlich kann man das so machen,
kommt wie gesagt auf das System an und was man erreichen will.
Setzt man auf ein langsames Pferd (Atmega mit 8Mhz oder so), wird die 
Performance davon schon profitieren wenn man (bei oben spekuliertem 
Szenario) seine Grafikansteuerung auf die 16bit Felder abstimmt. Wenn 
ich einzelne Bits setze, kann der Compiler da auch nicht auf 16 bit 
Breite optimieren.
Bleiben wir mal beim Beispiel mit dem AVR und dem 128*64er Display,
wenn ich jetzt alle Pixel per Funktion setzen will (z.B. CLRSCR) brauche 
ich 8192 Aufrufe
meiner setPixel(...)-Funktion, nehmen wir mal an, diese operiert auf 
einem
internen Grafik-Speicher:
1
uint8_t pixMap[16][64];
2
void setPixel(uint8_t x, uint8_t y, uint8_t white)
3
{
4
  uint8_t column=x>>3;  //Div 8
5
  uint8_t bit=x&0x07;    //Modulo 8
6
  switch(white)
7
  {
8
    case 1:
9
      pixMap[column][y]|=(1<<bit);
10
    break;
11
    case 0:
12
      pixMap[column][y]&=(uint8_t)(~(1<<bit));
13
    break;
14
  }
15
}
Diese Beispiel-Funktion (ohne Aufrufe) benötigt auf obigem AVR etwas 
über 50 Takte, macht 410.000 Takte nur für das setzen aller Pixel, also 
etwa 1/20 Sekunde...
Kann je nach Anwendung genügen. Aber wahrscheinlich hat der Controller 
ja auch noch irgendeine Hauptaufgabe wenn es nicht unbedingt ein 
dedizierter LCD-Treiber ist.

Es führen viele Wege nach Rom.

Gruß Dominik

von W.S. (Gast)


Lesenswert?

Dominik schrieb:
> das ist sicher richtig wenn man von potenter Steuerungshardware ausgeht.
> Handelt es sich um einen kleinen µC will man sicher eher nicht 6x8=48
> Funktionsaufrufe pro Zeichen, da wird man den Abstraktionslayer doch
> näher in Richtung Hardware legen wollen.

Wenn man selber potent genug ist, um sich seine Probleme selbst zu 
lösen, dann ist dein Vorschlag richtig. Wenn man hingegen schon an den 
Präliminarien scheitert, dann nicht.

In letzterem Falle sollte man eben doch die 48 Aufrufe sich gönnen - 
einfach um wenigstens dazu in der Lage zu sein, das richtige Setzen von 
Pixeln zunächst zu lösen, bevor man sich an höhere Dinge wagt, wie z.B. 
das Verstehen und Anwenden von Fonts.

Nebenbei ist der schiere Aufruf eher kein Thema. Beim Pixelsetzen muß 
man ja noch so einiges anderes machen, z.B. Abprüfen der Bounds (if x<0 
or x>displaybreite or y<0 oe y>displayhöhe dann raus hier..) oder das 
Ausführen der Pixelmodi (if black then machblack else if white then.. 
else if invert then liespixel..xorpixel..setpixel..) - kurzum, die 48 
Aufrufe lohnen sich, damit ein bissel Qualität und Funktionssicherheit 
hineinkommt.

W.S.

von Dominik (Gast)


Lesenswert?

W.S. schrieb:
> Nebenbei ist der schiere Aufruf eher kein Thema. Beim Pixelsetzen muß
> man ja noch so einiges anderes machen, z.B. Abprüfen der Bounds (if x<0
> or x>displaybreite or y<0 oe y>displayhöhe dann raus hier..) oder das
> Ausführen der Pixelmodi (if black then machblack else if white then..
> else if invert then liespixel..xorpixel..setpixel..) - kurzum, die 48
> Aufrufe lohnen sich, damit ein bissel Qualität und Funktionssicherheit
> hineinkommt.

Wie gesagt, viele wegen führen nach Rom, aber wenn das anzunehmende 
Target ein 8bit µC ist, würde ich das nicht machen sondern bereits in 
den aufrufenden Funktionen die Bereichsüberschreitungen verhindern.
Je nach uC kommt sonst neben Geschwindigkeitsproblemen auch gerne mal 
das Ende vom Flash sehr nahe :-)
Im Falle der Programmierung einer Anwendersoftware unter einem gängigen 
Betriebssystem stimme ich dem natürlich zu, da wird man diese 
Überschreitungen meist auch zusätzlich noch zum debuggen catchen.

Dominik schrieb:
> uint8_t column=x>>3;  //Div 8
>   uint8_t bit=x&0x07;    //Modulo 8

Sowas würde ich für eine solche Anwendung dann auch in "Schönschrift" 
programmieren.

Sofern man die setPixel-Variante auf dem µC gehen will, sollte man aber 
zumindest clearScreen/setBackground/setPattern/etc. auf die logischen 
Spalten und Zeilen des Displays umsetzen, damit man dort wenigstens 
nicht jede Variable 8x anfassen muss.

Gruß Dominik

von W.S. (Gast)


Lesenswert?

Dominik schrieb:
> Wie gesagt, viele wegen führen nach Rom, aber wenn das anzunehmende
> Target ein 8bit µC ist, würde ich das nicht machen

Ich auch nicht wirklich. Also was soll das Ganze? Mit einem 
Grafik-Display ist ein kleiner 8 Bitter recht schnell an seinen Grenzen. 
Ich hatte mal aus Wißbegierde ein ALPS LSU7S1011A (von Pollin) mit einem 
PIC16F871 in Assembler angesteuert. Ja, geht, geht auch schnell genug, 
aber ist dennoch ein einziger Krampf, weil eben der µC dafür zu klein 
war.

Also, wenn wirklich Grafik ansteht, dann den Controller danach 
auswählen. Wenn die Grafik nur so etwa 128x64 oder gar nur 96x32 oder so 
beträgt, dann muß der Controller nur genügend RAM mitbringen, in diesem 
Fall so etwa 1K für den Display-RAM und nochwas obendrauf für den Rest 
des Ganzen. Die Geschwindigkeit reicht zumeist völlig aus.

W.S.

von dasrotemopped (Gast)


Lesenswert?

8 bit und Display ist ein weites Feld.
https://atmelcorporation.wordpress.com/2015/05/28/build-an-inexpensive-handheld-gaming-console-with-atmega328/
Auch ein kleiner 8-bit Arduino kann was.

Obwohl ich einen STM32 mit TFT bevorzuge würde ich das pauschale 
Ablehnen eines Displays an einen 8-bitter nicht unterstützen. Der C64 
war ja auch nicht ohne Grund trotz seiner 8-bit ein Grafikwunder zu 
seiner Zeit.

Gruß,
dasrotemopped.

von Dominik (Gast)


Lesenswert?

dasrotemopped schrieb:
> würde ich das pauschale
> Ablehnen eines Displays an einen 8-bitter nicht unterstützen.

Sehe ich auch so, kommt halt immer drauf an, was man machen will,
und wie man es dann genau macht.

Ich habe auch schon ein LCD-Display mit Schieberegister an einen Tiny45 
gefriemelt, ob man sich dann einen Gefallen getan hat oder besser was 
größeres genommen hätte, muss man von Fall zu Fall mit sich selber 
ausmachen.

Gruß Domihik

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.