Forum: Mikrocontroller und Digitale Elektronik Arrays kompakter im Flash speichern


von Stefan F. (Gast)


Lesenswert?

Hallo Leute,
vielleicht hat jemand spontan eine Idee, wie ich mit die Füll-Byte bei 
folgendem 5x8 Pixel-Font sparen kann.
1
static const uint8_t font [] PROGMEM = {
2
    2, 0x00, 0x00, 0x00, 0x00, 0x00, // space
3
    2, 0x5f, 0x00, 0x00, 0x00, 0x00, // !
4
    3, 0x07, 0x00, 0x07, 0x00, 0x00, // "
5
    5, 0x14, 0x7f, 0x14, 0x7f, 0x14, // #
6
    5, 0x24, 0x2a, 0x7f, 0x2a, 0x12, // $
7
    5, 0x62, 0x64, 0x08, 0x13, 0x23, // %
8
    5, 0x36, 0x49, 0x55, 0x22, 0x50, // &
9
    ... }

Das erste Byte jeder Zeile gibt an, wie breit das Zeichen ist. Danach 
folgen entsprechend viele sichtbare Bytes, und dann ggf. Füllbytes. Um 
ein char c darzustellen, gehe ich so vor:
1
size_t index = ((c - 32)*6) & 0xFFFF;
2
uint_fast8_t n = pgm_read_byte(&font[index]);

Danach gebe ich n Bytes ab font[index+1] aus.

Nun würde ich mir gerne die Füllbytes sparen, wenn das mit vertretbarem 
Aufwand machbar ist. Allerdings habe ich keinen Plan, wie ich dann den 
Index zügig ermitteln kann.

Irgendwelche Ideen?

von Falk B. (falk)


Lesenswert?

Stefan F. schrieb:
> Hallo Leute,
> vielleicht hat jemand spontan eine Idee, wie ich mit die Füll-Byte bei
> folgendem 5x8 Pixel-Font sparen kann.

Und wozu? Wieviele GB willst du damit sparen?

> Nun würde ich mir gerne die Füllbytes sparen, wenn das mit vertretbarem
> Aufwand machbar ist. Allerdings habe ich keinen Plan, wie ich dann den
> Index zügig ermitteln kann.

Dann musst du die Infos in ein getrenntes Array legen. Und die 
Startpunkte der Zeichen mit variabler Länge in ein getrenntes Array, 
quasi ein Index.

von Stefan F. (Gast)


Lesenswert?

Falk B. schrieb:
> Dann musst du die Infos in ein getrenntes Array legen.

Dachte ich mir schon. Dann brauche ich aber ca. 100 Pointer. Die belegen 
allerdings mehr Platz, als die paar Füllbytes, die ich dabei einspare.

Also lasse ich besser, wie es ist?

von C-hater (c-hater)


Lesenswert?

Stefan F. schrieb:
> Nun würde ich mir gerne die Füllbytes sparen, wenn das mit vertretbarem
> Aufwand machbar ist.

Ist es nicht. Du bräuchtest eine zusätzliche Indextabelle. Die kostet 
mit einiger Wahrscheinlichkeit mehr Speicher, als du durch das Wegfallen 
des Padding einsparst, gerade bei so winzigen Fonts, wo die meisten 
Zeichen eh' die ganze Breite ausnutzen (müssen).

Aaaber: Bei dem Umfang der Texte, die man üblicherweise auf µC 
verwendet, kann man den Index auch einsparen. Alle nötige Information 
steckt ja bereits in den Längenbytes. Statt also einfach per Index 
zuzugreifen, muss man dann für jedes Zeichen über die Tabelle iterieren. 
Das kostet natürlich Rechenzeit.

Insgesamt also der übliche tradeoff: Speicherbedarf vs. Geschwindigkeit.

von Falk B. (falk)


Lesenswert?

Stefan F. schrieb:
> Dachte ich mir schon. Dann brauche ich aber ca. 100 Pointer. Die belegen
> allerdings mehr Platz, als die paar Füllbytes, die ich dabei einspare.
>
> Also lasse ich besser, wie es ist?

Was machst du mit 100 gesparten BYTEs im Flash? Dafür gibt es keine Geld 
zurück.

von Stefan F. (Gast)


Lesenswert?

Falk B. schrieb:
> Was machst du mit 100 gesparten BYTEs im Flash?
> Dafür gibt es keine Geld zurück.

War nur so eine fixe Idee, weil es AVR mit nur 1 kB Flash gibt. 
Allerdings würde man daran wohl eh kein solches Grafikdisplay 
anschließen, solange man noch alle Tassen im Schrank hat.

von Gerhard O. (gerhard_)


Lesenswert?

Hallo Stefan,

Wenn fast alle Zeichen eine Breite von 5 Pixel haben, würde ich fast 
dazu neigen, einfach die Ausnahmen mit entsprechendem Test isolieren und 
die Zeichenbreiten Bytes wegzulassen, rät mir mein "einfach denkendes 
Gehirn". Wie viele nicht "5er" Zeichen gibt es? Wahrscheinlich nur ein 
paar wie "Ii.," u.ä. Ein einfacher Test jann dann die Breiten Daten 
liefern.

so eben fiel mir aber noch eine Möglichkeit ein. Wie wäre es die Ascii 
Längen Information der Zeichen in 32bit long ints zu speichern und dann 
als Ascii Nummer auswerten. Allerdings brauchst Du dann sprintf oder ä.

Z.B. in Deiner Tabelle fängt es mit 22355... an. Wenn Du jetzt diese 
Sequenz als long int speicherst, z.B. ascii 
"23555555355"(0x8C66ECCF)dann komprimierst Du die Nummer der notwendigen 
Bytes. Mit einem pointer kann man dann bequem auf jede Ascii Nummer 
zugreifen und durch Subtraktion in einen uint8_t verwandeln. Wäre nicht 
zu aufwendig. Mit dieser Methode lässt sich also viel Platz sparen. In 
dem obigen Beispiel können 11 Zeichen mit 4Bytes Speicher auskommen. 
Wenn Du dann die Zeichen Codierung in einem Array speicherst, dann 
kannst Du mit einem Pointer sofort auf die Bewertung zugreifen, was 
schnell geht.
Mit 6 Long ints könntest Du 64 Zeichen unterstützen.

Gruß,
Gerhard

von Falk B. (falk)


Lesenswert?


von Stefan F. (Gast)


Lesenswert?

Gerhard O. schrieb:
> Wenn fast alle Zeichen eine Breite von 5 Pixel haben

Das ist der Fall, nur wenige Zeichen sind schmaler.

von Gerhard O. (gerhard_)


Lesenswert?

Stefan F. schrieb:
> Gerhard O. schrieb:
>> Wenn fast alle Zeichen eine Breite von 5 Pixel haben
>
> Das ist der Fall, nur wenige Zeichen sind schmaler.

Dann könnte ein einfacher Test der betreffenden Zeichen ausreichen.

von C-hater (c-hater)


Lesenswert?

Gerhard O. schrieb:

> Stefan F. schrieb:
>> Gerhard O. schrieb:
>>> Wenn fast alle Zeichen eine Breite von 5 Pixel haben
>>
>> Das ist der Fall, nur wenige Zeichen sind schmaler.
>
> Dann könnte ein einfacher Test der betreffenden Zeichen ausreichen.

Nein. Dieser Ansatz löst natürlich garnix. Es stimmen dann auch alle 
Indizes für die "höheren" Zeichen nicht mehr. Eine Abwandlung davon 
würde allerdings funktionieren: eine zusätzliche Tabelle mit den 
Ausnahmen. Auch über die müsste für jedes auszugebende Zeichen iteriert 
werden, aber sie wäre immerhin sehr viel kürzer als die "Haupttabelle", 
die Iteration wäre also rechenzeitmäßig deutlich billiger.

von Stefan F. (Gast)


Lesenswert?

Ich habe nachgezählt, es geht um 59 Füllbytes und 102 Längenbytes.

von Gerhard O. (gerhard_)


Lesenswert?

Hmmm...

Man könnte aber jeweils das unterste bit in jeder Spalte als 
Längenkodierung mißbrauchen, wenn man sich auf 5x7 Matrix beschränkt und 
das letzte Bit einfach ANDen.

von Stefan F. (Gast)


Lesenswert?

Gerhard O. schrieb:
> Man könnte aber jeweils das unterste bit in jeder Spalte als
> Längenkodierung mißbrauchen, wenn man sich auf 5x7 Matrix beschränkt und
> das letzte Bit einfach ANDen.

Ich dachte gerade an etwas ähnliches. Ich könnte anstelle der 
Längenbytes einen reservierten Wert (der sonst nicht vorkommt) als 
Ende-Markierung nutzen.

von Gerhard O. (gerhard_)


Lesenswert?

Stefan F. schrieb:
> Gerhard O. schrieb:
>> Man könnte aber jeweils das unterste bit in jeder Spalte als
>> Längenkodierung mißbrauchen, wenn man sich auf 5x7 Matrix beschränkt und
>> das letzte Bit einfach ANDen.
>
> Ich dachte gerade an etwas ähnliches. Ich könnte anstelle der
> Längenbytes einen reservierten Wert (der sonst nicht vorkommt) als
> Ende-Markierung nutzen.

Es sieht so aus, daß diese Richtung sich praktisch bewähren könnte.

Muß jetzt weg frühstücken gehen:-)

von MaWin O. (mawin_original)


Lesenswert?

Stefan F. schrieb:
> es geht um 59 Füllbytes

Und die brauchst du ganz dringend für etwas?
Oder wären die gewonnenen Bytes dann einfach unbenutzt?

von Falk B. (falk)


Lesenswert?

MaWin O. schrieb:
> Und die brauchst du ganz dringend für etwas?

Man könnte ja seinen Kontostand dort speichern. Oder Hochzeitstag. Oder 
Tage bis zur Rente . . .

> Oder wären die gewonnenen Bytes dann einfach unbenutzt?

First World Problems

von MaWin O. (mawin_original)


Lesenswert?

Falk B. schrieb:
> Man könnte ja seinen Kontostand dort speichern

59 Bytes reichen leider nur für arme Schlucker.

> First World Problems

Vielleicht könnte man bei Microchip Geld zurück verlangen. Oder eine 
Ehrenurkunde für den besten Speichersparer bekommen. Die bekommt man 
aber nur, wenn der notwendige zusätzliche Code den Speichergewinn nicht 
wieder auffrisst.

von C-hater (c-hater)


Lesenswert?

Hallodri schrieb:
> eine zusätzliche Tabelle mit den
> Ausnahmen. Auch über die müsste für jedes auszugebende Zeichen iteriert
> werden, aber sie wäre immerhin sehr viel kürzer als die "Haupttabelle",
> die Iteration wäre also rechenzeitmäßig deutlich billiger.

Dieser Ansatz ist auch insofern geil, weil in der "Haupttabelle" das 
Längenbyte entfallen könnte. Da könnte man also tatsächlich deutlich 
Speicher sparen und auch der Mehraufwand an Rechenzeit wäre relativ 
gering.

von C-hater (c-hater)


Lesenswert?

Stefan F. schrieb:

> Ich habe nachgezählt, es geht um 59 Füllbytes und 102 Längenbytes.

Du hast den eigentlichen Knackpunkt nicht erkannt, nämlich:

Wie viele von deinen 102 unterstützten Zeichen haben die volle Breite 
und wie viele wären dementsprechend Ausnahmen. DAS sind die 
entscheidenden Kenngrößen. Damit kann man nämlich unmittelbar den 
Speicherbedarf und den (mittleren) Rechenzeitbedarf einer jeden 
denkbaren (einfachen *) Lösung ermitteln.

(*) Sachen wie Komprimierung sind bei dieser Sache natürlich aussen vor. 
Der Codeaufwand würde den Speicherplatzbedarf für die 
Konstanten-Tabellen sicher mehr als auffressen.

von Gerhard O. (gerhard_)


Lesenswert?

Wenn die unterste Pixelreihe (MSb) ohnehin unbenutzt ist, dann lohnt es 
sich vielleicht doch noch, diese Ressource für die Zeichen 
Breitenerkennung auszunutzen. Der Code Overhead würde sich in Grenzen 
halten und ist einfachst machbar. Je weniger kompliziert, desto besser.

von C-hater (c-hater)


Lesenswert?

Gerhard O. schrieb:
> Wenn die unterste Pixelreihe (MSb) ohnehin unbenutzt ist, dann lohnt es
> sich vielleicht doch noch, diese Ressource für die Zeichen
> Breitenerkennung auszunutzen. Der Code Overhead würde sich in Grenzen
> halten und ist einfachst machbar. Je weniger kompliziert, desto besser.

Nein. Es ist selten bis nie sinnvoll, Metadaten in potentiellen Inhalten 
unterzubringen. Spätestens der erste Zeichensatz mit Unterlängen zeigt 
das dann...

Beitrag #7358896 wurde vom Autor gelöscht.
von MaWin O. (mawin_original)


Lesenswert?

Gustl B. schrieb im Beitrag #7358896:
> Es funktioniert nicht

Natürlich kann das je nach Situation funktionieren.
Das kommt ganz auf die Umstände und Randbedingungen an.

von MaWin O. (mawin_original)


Lesenswert?

Hallodri schrieb:
> Es ist selten bis nie sinnvoll, Metadaten in potentiellen Inhalten
> unterzubringen

außer, wenn man den freien Platz in den Inhalten nutzen will. Und darum 
geht es hier.

von Stefan F. (Gast)


Lesenswert?

Ich habe mir einen (sonst ungenutzten) Wert als Füllbyte festgelegt. Die 
Längenangaben entfallen dadurch, was immerhin 100 Bytes ausmacht. Für 
jedes Zeichen werden nun immer 5 Bytes ausgegeben, oder weniger wenn er 
dabei auf ein Füllbyte stößt.
1
// 5x8 font with variable width 
2
// 0x88 are invisible filler bytes
3
static const uint8_t oled_font [] PROGMEM = {
4
    0x00, 0x00, 0x88, 0x88, 0x88, // space
5
    0x5F, 0x88, 0x88, 0x88, 0x88, // !
6
    0x07, 0x00, 0x07, 0x88, 0x88, // "
7
    0x14, 0x7F, 0x14, 0x7F, 0x14, // #
8
    0x24, 0x2A, 0x7F, 0x2A, 0x12, // $
9
    0x62, 0x64, 0x08, 0x13, 0x23, // %
10
    0x36, 0x49, 0x55, 0x22, 0x50, // &
11
    ...

So wird jetzt ein Zeichen c ausgegeben:
1
    size_t font_index = ((c - 32)*5) & 0xFFFF;
2
    for (uint_fast8_t i=0; i<5; i++)
3
    {
4
        uint8_t b = pgm_read_byte(&oled_font[font_index++]);
5
6
        // Skip filler bytes
7
        if (b == 0x88)
8
        {
9
            break;
10
        }
11
     
12
        draw_byte(x, y, b);
13
        x++;
14
    }

War doch ganz einfach :-)

von Falk B. (falk)


Lesenswert?


von Stefan F. (Gast)


Lesenswert?

Jetzt habe ich zwar immer noch die Füllbytes im Zeichensatz, aber dafür 
brauche ich keine Längen-Bytes mehr, was viel Platz spart.

Danke für eure Anregungen.

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.