Individální projekty MPOA

Mikroprocesory s architekturou ARM

Uživatelské nástroje

Nástroje pro tento web


2017:fontx-glcd

Fonty FONTX pro grafické displeje

Zadání

Rozšiřte knihovnu pro běžné grafické displeje 128×64 o podporu fontů ve formátu FONTX. Z Unicode fontů vytvořte sadu pro kódování ISO-8859-2. Demonstrujte použití vč. diakritiky na vhodné vývojové desce s displejem 128×64.


Úvod

FONTX je bitmapový formát sloužící k vykreslování znaků, jehož struktura je naznačena v následující tabulce. Soubor se skládá z hlavičky, která obsahuje Podpis souboru o velikosti 6 B, kde jsou uloženy znaky „FONTX2“, Název fontu, Šířku fontu, Výšku fontu a Kódový příznak, který slouží k rozlišení mezi Single Byte a Double Byte formátem. V tomto projektu je řešen pouze Single Byte format. Po hlavičce následují už jen data všech znaků, která jsou řazena za sebou po bytech. Editor pro tento formát je dostupný zde.

Offset Velikost [B] Popis
0 6 Podpis souboru „FONTX2“
6 8 Název fontu
14 1 Šířka fontu [pixel]
15 1 Výška fontu [pixel]
16 1 Kódový příznak
17 1 Obrazec fontu, první byte
18 1 Obrazec fontu, druhý byte
1

Řazení bytů v poli obrazce fontů je znázorněno na následujícím obrázku:
Znaky uložené v souboru ve formátu FontX by měli být číslovány podle následující tabulky znaků ISO-8859-2:


Řešení

K řešení projektu bylo využito vývojové desky STM32F411E-DISCOVERY a LCD displeje 128×64 s řadičem ST7920. Grafický displej s tímto řadičem umožňuje vykreslování na displej v textovém nebo grafickém módu. Textový mód obsahuje základní znakovou sadu písmen bez diakritiky, číslice a některé další znaky. Komunikace s tímto řadičem může být paralelní 4-bitová, paralelní 8-bitová nebo sériová. Pro tento projekt bude využíván pouze grafický mód, který umožňuje nakreslit jakýkoliv znak a komunikace bude probíhat po sériové lince.

Propojení desky s displejem:

Pro sériové řízení displeje byl napsán ovladač ST7920-Serial-Driver. Protože sériová komunikace neumožňuje čtení z displeje, je v tomto ovladači vytvořeno pole ST7920_LCD_bit_array[][], které kopíruje celou zobrazovací část displeje. Toto pole potom umožňuje vykreslovat pixely tak, že nedojde k přemazání okolních objektů. Zároveň lze provádět při vykreslování pixelů například logickou operaci XOR. Pomocí funkce ST7920_draw_pixel lze vykreslit jeden pixel kdekoliv na displeji (adresace x <0-127>, y <0-63>). Vzhledem k tomu, že sériová komunikace je poměrně pomalá a vykreslování objektu po jednom pixelu zabere hodně času, byla napsaná ještě funkce ST7920_serial_draw_8pixels pro vykreslení osmice pixelů (jednoho bytu), kde jsou pixely vykresleny v ose X za sebou (adresace x <0-119>, y <0-63>). Tato funkce umožňuje vykreslit osmici pixelů 4x – 8x rychleji (záleží na adrese), než funkce vykreslující osmici pixelů po jednom. Funkce umožňují rozsvítit/zhasnout/přepsat pixely nebo nad nimi provést logickou operaci xor.

Pro vykreslování znaků uložených v souboru ve formátu FONTX byl napsán ovladač FontX-Driver. Tento ovladač umožňuje napojení vykreslování znaků na jakýkoliv displej pomocí funkce FONTX_LCD_print_pixel, která je schopna vykreslit na displej jeden pixel a funkce FONTX_LCD_print_8pixels, která vykreslí osmici pixelů (jeden byte) za sebe. V tomto případě je napojení na ovladač řadiče ST7920:

/* Draw pixel to a specific address */
void FONTX_LCD_print_pixel(uint8_t AddrX, uint8_t AddrY, bool SetPixel)
{
    if(SetPixel)
        ST7920l_draw_pixel(AddrX , AddrY, set_pixel);
    else
        ST7920l_draw_pixel(AddrX , AddrY, reset_pixel);
}
 
/* Draw one byte (8pixels) to a specific address */
void FONTX_LCD_print_8pixels(uint8_t AddrX, uint8_t AddrY, uint8_t Pixels)
{
    ST7920_draw_8pixels(AddrX, AddrY, Pixels, rewrite_pixel);
}

Ovladač pracuje se strukturou, která odpovídá tabulce FontX popsané v úvodu.

typedef struct
{
    char FileSignature[6];  /* "FONTX2" */
    char FontName[8];
    uint8_t FontWidth;
    uint8_t FontHeight;
    uint8_t CodeFlag;
    uint8_t FontImage[];
} fontx_st;

K výpisu jednoho znaku jakékoliv velikosti slouží funkce FONTX_print_char. Její vstupními parametry jsou adresa levého spodního rohu znaku, ukazatel na strukturu a číslo znaku. Funkce si načte soubor FONTX pomocí ukazatele na strukturu fontx_st a vyčte šířku a výšku znaků. Dále vypočítá, z kolika bytů je složen jeden řádek znaku a jaký má znak offset v poli FontxChar→FontImage[]. Následují dva for cykly, které se starají o posouvání vykreslovaného pixelu v ose X a Y. Ve vnitřním for cyklu dochází k výpočtu pozice bytu, v kterém se nachází pixel ( který má být právě vykreslen), pomocí funkce FONTX_get_pixel_from_byte se zjistí zda má být pixel rozsvícen či zhasnut a nakonec dojde k jeho vykreslení na příslušnou adresu.

/* Draw one FONTX char to a specific address (from the bottom left corner to right upper corner) */
void FONTX_print_char(uint8_t AddrX, uint8_t AddrY, fontx_st *FontxChar, uint8_t NumberOfChar)
{
    uint8_t Height = FontxChar->FontHeight;
    uint8_t Width = FontxChar->FontWidth;
 
    /*Calculation number of bytes in one row*/
    uint8_t BytesInRow;
    if(Width % 8u)
        BytesInRow = Width / 8u + 1u;
    else
        BytesInRow = Width / 8u;
 
    /*Char offset in FontImage array*/
    uint32_t CharOffset = BytesInRow * Height * NumberOfChar;
 
    uint8_t XShifter, YShifter;
    for(YShifter = 0u; YShifter < Height; YShifter++)
    {
        for(XShifter = 0u; XShifter < Width; XShifter++)
        {
            uint32_t BytePosition = CharOffset + XShifter/8u + YShifter*BytesInRow; /*Position of byte in FontImage array*/
            bool PrintPixel = FONTX_get_pixel_from_byte(FontxChar->FontImage[BytePosition], XShifter % 8u); /*Set/Reset pixel?*/
            FONTX_LCD_print_pixel(AddrX + XShifter, AddrY - Height + 1u + YShifter, PrintPixel); /*Show pixel on display*/
        }
    }
}

Pro rychlejší vykreslování fontů o rozměrech 8×13 pixelů (na displej s řadičem ST7920) byla napsána funkce FONTX_LCD_print_8pixels, která využívá rychlejší zápis 8 pixelů (jednoho bytu) na displej. Funkce je díky známé velikosti fontu o mnoho jednodušší než univerzální funkce FONTX_print_char zmíněná výše.

/* Draw one FONTX 8x13 char to a specific address (from the bottom left corner to right upper corner)*/
/* NumberOfChar - char number in FONTX table */
void FONTX_print_char_8x13(uint8_t AddrX, uint8_t AddrY, fontx_st *FontxChar, uint8_t NumberOfChar)
{
 
    uint8_t YShifter;
    for(YShifter = 0u; YShifter < 13u; YShifter++)
    {
        FONTX_LCD_print_8pixels(AddrX, AddrY - 12u + YShifter, FontxChar->FontImage[13u * NumberOfChar + YShifter]);
    }
}

Příklad použití s několika možnostmi zápisu čísla znaku (pro zápis ve formátu 'ě' je třeba nastavit editor, aby kódoval vstupní data jako ISO-8859-2):

/* ... */
 
#include "ST7920.h"
#include "FONTX.h"
 
IMPORT_BIN("ISO-8859-2_8x13.fnt", fontx_st, Fontx8x13B);
 
/* ... */
 
int main(void)
{
 
    /* peripheral initialization ... */
 
    ST7920_init();
    ST7920_clear_graphic();
 
    FONTX_print_char(0u, 12u, &Fontx8x13B, 'ě');
    FONTX_print_char(3u, 25u, &Fontx8x13B, 0xEC);
    FONTX_print_char(6u, 38u, &Fontx8x13B, 236u);
 
    FONTX_print_char_8x13(8u, 12u, &Fontx8x13B, 'ě');
    FONTX_print_char_8x13(11u, 25u, &Fontx8x13B, 0xEC);
    FONTX_print_char_8x13(14u, 38u, &Fontx8x13B, 236u);
 
    while(1)
    {
	/* ... */
    }
}

Výsledek příkladu použití: FocusArea=001011111

K vykreslení celého řetězce znaků jsou určeny funkce FONTX_puts a FONTX_puts_8x13. Voláním těchto funkcí se docílí vykreslení řetězce na jeden řádek. Pokud však nastane situace, kdy by byl znak oříznut (nacházel se na konci displeje) nedojde k jeho vykreslení vůbec. Druhá jmenovaná funkce se liší pouze v rychlejším vykreslováním znaku na display. Následující příklad ukazuje použití těchto funkcí. Na druhém a čtvrtém řádku displeje nedošlo k vykreslení znaku 'é', jelikož by byl oříznut na velikost 7×13 pixelů.

FONTX_puts(55u, 12u, &Fontx8x13B, "ěščřžýáíé");
FONTX_puts(56u, 25u, &Fontx8x13B, "ěščřžýáíé");
 
FONTX_puts_8x13(55u, 38u, &Fontx8x13B, "ěščřžýáíé");
FONTX_puts_8x13(56u, 51u, &Fontx8x13B, "ěščřžýáíé");

Výsledek příkladu použití funkcí pro vykreslení řetězce: fontx-glcd_string.jpg

Ukázkové video se znaky definovanými ISO-8859-2:



Závěr

K realizaci tohoto projektu byl využit vývojový editor EmBitz společně s STM32CubeMX. Byl napsán ovladač pro displej s rozlišením 128×64 a řadičem ST7920, který využívá sériovou komunikaci. Dále byl vytvořen ovladač pro vykreslování znaků ve formátu FontX, který je možno napojit na jakýkoliv displej, který má funkci pro vykreslení jednoho pixelu na displej. Ovladač obsahuje funkce, které umožňují vykreslit jeden znak jakékoliv velikosti či jednořádkový řetězec těchto znaků na jakoukoliv adresu. Funkce byly „zdvojeny“ pro vykreslování znaků o velikosti 8×13 pixelů, které využívají funkci knihovny ST7920 k rychlejšímu vykreslení osmice pixelů na displej. Nakonec byla vytvořena pomocí FontX editoru sada znaků pro kódování ISO-8859-2. Nutno podotknou, že ke správnému vykreslování znaků na displej bylo třeba nastavit vývojový editor na kódování ve formátu ISO-8859-2 (viz zde). Pokračování ve vylepšování FontX ovladače by bylo například možnost rotace znaků či celého řetězce.

Zdrojové kódy ST7920-Serial-Driver + FontX-Driver + Font8x13b (ISO-8859-2)
FONTX editor


Zdroje

2017/fontx-glcd.txt · Poslední úprava: 2018/01/10 12:47 autor: Petr Skryja