Also der Zeichengenerator (der den großen Videospeicher vollschreibt) besteht aus einem großen PROGMEM-Array, das die ganzen Pixel-Informationen der einzelnen Buchstaben enthält (damit alles schön in ein Byte passt kann bei mir ein Buchstabe nicht länger sein als 8 Spalten, da ich 7 Zeilen LEDs habe sind es dann 7 Bytes pro Buchstabe). Jeder Buchstabe enthält bei mir eine Leerspalte (gehört also fix zu den Pixeldaten), dass die Buchstaben nicht so aufeinander kleben. Ein Leerzeichen besteht dann halt aus vier oder fünf Leerspalten, glaube ich. Die perfekte Umsetzung ist auch das nicht, aber so schlecht schaut es meiner Meinung nicht aus. Bleibt noch, dass manche Buchstaben mehr Spalten brauchen als andere (z.B. ist B länger als i), deswegen gibt es ein zweites PROGMEM-Array, in dem die Längen abgespeichert sind für die einzelnen Buchstaben. Die beiden Arrays generiere ich mit einem simplen kleinen VB.net Programm, das geht schneller ein solches zu schreiben als alles per Hand reinzuklopfen.

Code:
#define     used_length       5 // d.h. der große Videospeicher ist 5*8 Spalten lang, wenn wir darüber hinaus sind fängts von vorne an

void bufcpy(char startpoint, char targetbuffer){
    /*
        So, was macht diese Funktion jetzt: 
        Sie kopiert ausgehend vom Startpoint (in Bits -> LED-Spalten im großen Videospeicher) ein Byte in den Ausgabebuffer, das in jeder Zeile
        
        d1[5][8] ist der große Videospeicher, 
              wobei [5] bedeutet dass die Länge 8*5 = 40 Spalten beträgt. 
              [8] bedeutet, dass die Matrix 8 Zeilen hat

       dm[2][8] sind die zwei Swap-Buffer 
              [2] bedeutet dass es zwei gibt, in die geschrieben werden kann (als Funktionsparameter übergeben,
              in welchen geschrieben werden soll)
              [8] bedeutet, dass die Matrix 8 Zeilen hat

    */
    
    //Funktionsinterne Variablen:
        //So viele Bits ist unterschied zwischen den Byte-Reihen der beiden Buffer
            char startblock = startpoint/8; 
            char offset = startpoint%8; 
        
        //Zählervariablen der Schleifen
            char i, j; 
            
        //Hier werden die beiden Bytes, also das halbe drüber und das halbe drunter hineinkopiert und entsprechend geshiftet
            char low, high; 
    
    //Die Zeilen durchgehen
    for(i=0; i < 8; i++){   
            //Lowbyte laden und entsprechend verschieben, gleiches für Highbyte
            low = d1[(startblock )%used_length][i]; 
            low = low >> offset; 
            high = d1[(startblock + 1)%used_length][i]; 
            high = high << (8-offset); 
            
            //Nun zusammenfügen
            dm[targetbuffer][i] = high | low; 
    }
    

}
Das ist jetzt ein wenig angepasst, dass es zu deinen Abmessungen passt, deshalb auch nicht getestet und eher nur als "Pseudocode" zu verstehen.
Aber ich hoffe, es ist ein wenig verständlich wie ich das meine

Grüße
Thegon