======== Fonty FONTX pro grafické displeje ======== ====== Zadání ====== Rozšiřte knihovnu pro běžné grafické displeje 128x64 o podporu fontů ve [[http://elm-chan.org/docs/dosv/fontx_e.html|formátu FONTX]]. Z [[https://www.cl.cam.ac.uk/~mgk25/ucs-fonts.html|Unicode fontů]] vytvořte sadu pro kódování ISO-8859-2. Demonstrujte použití vč. diakritiky na vhodné vývojové desce s displejem 128x64. ---- ====== Ú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ý [[http://elm-chan.org/fsw/fontxedit.zip | 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: {{ 2017:fontx-glcd:fontx_ukladani_znaku.png?520x300 }} \\ 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: {{ http://ascii-table.com/img/cp912.gif?770x790 }} ---- ====== Řešení ====== K řešení projektu bylo využito vývojové desky STM32F411E-DISCOVERY a LCD displeje 128x64 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: {{ 2017:fontx-glcd:fontx-glcd_zapojeni.png?400x300 }} Pro sériové řízení displeje byl napsán ovladač [[https://gitlab.com/skryja.petr/ST7920-Serial-Driver | 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č [[https://gitlab.com/skryja.petr/FontX/tree/master/FONTX | 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 8x13 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í: {{ 2017:fontx-glcd:fontx-glcd_priklad.jpg?500x300 }} 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 7x13 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: {{ 2017:fontx-glcd:fontx-glcd_string.jpg?520x300 }} Ukázkové video se znaky definovanými ISO-8859-2: {{ youtube>vqWD3pwcxjA?medium }} \\ ---- ====== 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 128x64 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 8x13 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 {{2017:fontx-glcd:fontx-glcd_editor_setting.png?linkonly | zde}}). Pokračování ve vylepšování FontX ovladače by bylo například možnost rotace znaků či celého řetězce. \\ \\ [[https://gitlab.com/skryja.petr/FontX | Zdrojové kódy ST7920-Serial-Driver + FontX-Driver + Font8x13b (ISO-8859-2)]] \\ [[http://elm-chan.org/fsw/fontxedit.zip | FONTX editor]] ---- ===== Zdroje ===== https://github.com/alpov/PSAT-2/tree/master/STM32 \\ http://elm-chan.org/docs/dosv/fontx_e.html \\ https://www.cl.cam.ac.uk/~mgk25/ucs-fonts.html \\ http://elm-chan.org/fsw_e.html#fontxedit