Powrót

Kurs Arduino – Tajemnice glcdfont.c

W poprzednim odcinku (UR024)  zajmowaliśmy się podstawowym fontem, zawartym w bibliotece Adafruit GFX, który jest zdefiniowany w pliku glcdfont.c. Biblioteka ta oferuje także znacznie ulepszone fonty, jednak my nie wyczerpaliśmy tematu fontu podstawowego.

Wiemy od dawna, że podstawowe znaki: litery, cyfry i niektóre symbole, wyświetlamy za pomocą kodów ASCII. Ściślej biorąc, te podstawowe znaki drukowalne mają kody ASCII od 32 do 126. Wiemy też, że kody ASCII 1…31 pierwotnie były kodami sterującymi dla elektromechanicznych dalekopisów. W Adafruit GFX spośród nich wykorzystujemy tylko dwa: kod numer 10 – nowa linia oraz niekiedy wraz z nim kod nr 13 – powrót karetki. Pozostałe kody sterujące można wykorzystać, by kodowały jakieś symbole. I już wiemy, że tak jest. Można też wykorzystywać kody powyżej kodu ASCII, czyli z zakresu 128…255.

I właśnie font podstawowy w bibliotece Adafruit GFX zawiera znaki strony kodowej CP437, znanej od roku 1981, zastosowanej w pierwszych komputerach IBM PC w pierwszej wersji systemu operacyjnego MS-DOS (1.0). Tematyka fontów jest skomplikowana. Na rysunku 1 znaki strony kodowej CP437 wypisane są wciąż bardzo popularnym fontem Arial. Pod symbolami podane są też numery tych symboli w Unikodzie.

Rysunek 1

Rysunek 2 pokazuje znaki strony kodowej CP437 jako prosty font bitmapowy na ekranie (konsoli) prymitywnego komputera.

Rysunek 2

Rysunek 3

Natomiast Rysunek 3 przedstawia znaki zawarte w foncie podstawowym z pliku glcdfont.c biblioteki Adafruit GFX. Wprawdzie w poszczególnych fontach te same znaki strony kodowej i Unikodu mogą różnić się i różnią wyglądem. Niemniej wyraźnie widać, że w Adafruit GFX też mamy znaki strony kodowej CP437.

Tak, ale..

Na rysunku 3 żółtymi strzałkami zaznaczone są zastępcze znaki o numerach 10 i 13. Wiemy już, że biblioteka Adafruit GFX nie chce ich drukować i nie drukuje, a na potrzeby naszych rozważań w poprzednim odcinku stworzyliśmy programik, który zamiast nich zastępczo wydrukował literkę „x”. Jednak z podanego wcześniej opisu, jak działa wyświetlanie znaków, wynika, że plik glcdfont.c też musi zawierać po pięć bajtów dla kodów 10 i 13. Okazuje się, że nie są to wcale „bajty puste”:

(...)

0x00, 0x18, 0x24, 0x18, 0x00, //9

0xFF, 0xE7, 0xDB, 0xE7, 0xFF, //10

0x30, 0x48, 0x3A, 0x06, 0x0E, //11

0x26, 0x29, 0x79, 0x29, 0x26, //12

0x40, 0x7F, 0x05, 0x05, 0x07, //13

0x40, 0x7F, 0x05, 0x25, 0x3F, //14

(...)

Rysunek 4

Rysunek 4 prezentuje wynik „ręcznego dekodowania” znaków o numerach 10 i 13. To tylko ciekawostka, bowiem znaków tych nie da się w prosty sposób wykorzystać. Ważniejszy jest inny szczegół, zaznaczony na rysunku 3 czerwonymi strzałkami.

Najpierw zwróć uwagę na prawy dolny róg. Ostatni symbol w samym rogu ma numer – kod 255 i nie jest wyświetlany żaden symbol.

Prawidłowo, bo jest to albo rozkaz skasowania poprzedniego znaku (DEL), albo spacja twarda, niełamiąca (NBSP = non-breaking space). Problem jest z poprzednim symbolem o numerze 254. Na rysunku 3 pole jest puste, a według rysunków 1 i 2 powinien tam być „kwadracik”. Ten „kwadracik” na rysunku 3 jest, ale w polu o kodzie 253. Analogicznie wcześniejsze znaki! Mamy przesunięcie o jeden numer, ale nie w całej tabeli znaków. Przesunięcie zaczyna się przy kodzie  numer 176, czyli szesnastkowo B0. Na rysunku 3 mamy tu dwie kolejne czerwone strzałki.

Jak pokazują rysunki 1 i 2, kody 176, 177 i 178 (0xB0, 0xB1, 0xB2) powinny zapełnić pole pikselami, które dadzą w sumie średnią jasność pola 25%, 50% i 75%. Na rysunku 3 brakuje symbolu o kodzie 176, a w polu o tym numerze wyświetlony jest symbol, który w CP437 ma numer 177. Przesunięcie wynika z braku symbolu 176 i dotyczy tylko kodów o większym numerze. Czyli pożytecznych symboli, w tym greckich liter, znaków matematycznych i innych, w tym często wykorzystywanego symbolu stopnia.

Nie tak miało być!

Jest to wynik, a ściślej echo ewidentnej pomyłki. Strona kodowa CP437 od roku 1981 jednoznacznie określa, jakie mają być symbole o numerach 176 i wyższych. Jednak ktoś, kto kiedyś przygotowywał zawartość pliku glcdfont.c, popełnił ewidentny błąd i nie umieścił w nim pięciu bajtów, które powinny rysować symbol o kodzie 176. Pomyłkę tę odkryto po dość długim czasie, gdy powstało mnóstwo programów (szkiców) wykorzystujących ten błędnie zdefiniowany font i wspomniane bardzo pożyteczne „wyższe” kody o błędnych numerach. Jak widać z rysunku 3, błędny kod zawierał mniej znaków.

Jednak aktualny plik  glcdfont.c zawiera już wszystkie znaki:

(...)

0x55, 0x00, 0x55, 0x00, 0x55,

//#176 (25% block) missing in old code

0xAA, 0x55, 0xAA, 0x55, 0xAA,

// #177  50% block
0xFF, 0x55, 0xFF, 0x55, 0xFF,

// #178 75% block

(...)

Tak! „Ręczne zdekodowanie” symboli o numerach 176, 177, 178 daje efekt widoczny na rysunku 5!

Rysunek 5

Kod 176 i wszystkie wyższe są więc prawidłowo zdefiniowane w aktualnym pliku glcdfont.c. Dlaczego więc są błędnie wyświetlane?

To proste: dla kompatybilności z wcześniejszymi programami – szkicami! Przewidziano natomiast prosty sposób korzystania z prawidłowych kodów CP437. Podczas kompilacji program sprawdza stan wewnętrznej zmiennej _cp437. Jeżeli ma ona wartość logiczną true, wykorzystywane są prawidłowe kody CP437. Domyślnie ma ona wartość false, więc wykorzystywany jest stary, błędny sposób z przesunięciem. W pliku Adafruit_GFX.cpp w definicji metody drawChar() sprawdzany jest stan tej zmiennej, jak pokazuje szkic 1.

Szkic 1:

void Adafruit_GFX::drawChar(x, y, c, color, bg, size_x, size_y) {

 if(!gfxFont) { // 'Classic' built-in font

  if((x >= _width)            || // Clip right

   (y >= _height)           || // Clip bottom

   ((x + 6 * size_x - 1) < 0) || // Clip left

   ((y + 8 * size_y - 1) < 0))   // Clip top

   return;

  if(!_cp437 && (c >= 176)) c++; //Handle 'classic' charset behavior

   startWrite();\

    for(int8_t i=0; i<5; i++ ) { // Char bitmap = 5 columns

    (...) // rysowanie znaku

Jeżeli zmienna _cp437 ma wartość false (domyślnie) i wyświetlany ma być symbol o kodzie 176 lub wyższym, to o 1 zwiększany jest kod – liczba w zmiennej c. Realizuje to linia

if(!_cp437 && (c >= 176)) c++;

Aby to zadziałało, w bibliotece (w pliku Adafruit_GFX.h) przewidziano funkcję-metodę (tak!) o nazwie .cp437(), która ustawi wewnętrzną zmienną (pole) _cp437, jak widać na szkicu 2.

Szkic 2:

/*! @brief  Enable (or disable) Code Page 437-compatible charset.   There was an error in glcdfont.c for the longest time -
- one character (#176, the 'light shade' block) was missing -- this threw off the index of every character that followed it.
But a TON of code has been written with the erroneous character indices. By default, the library uses the original 'wrong' behavior 
and old sketches will still work. Pass 'true' to this function to use correct CP437 character values in your code.
  @param x true = enable (new behavior),

  @param x false = disable (old behavior)

 */
/***************************************/

void cp437(boolean x=true) { _cp437 = x; }

Jeżeli więc we wcześniejszym szkicu A2301 .ino w funkcji setup() dodamy instrukcję:

wysw.cp437(1);

to wtedy na wyświetlaczu zobaczymy prawidłową „wyższą” część strony CP437, jak pokazuje fotografia 6. Zmodyfikowany szkic jest dostęny jako A2401.ino (tylko dla SH1106, ale możesz to samodzielnie zmienić, kopiując fragmenty szkicu A2301.ino).

Fotografia 6

Polskie litery

Wiedząc, jak funkcjonuje podstawowy font biblioteki Adafruit GFX, możemy zmodyfikować plik glcdfont.c i dodać tam „polskie litery”. Moglibyśmy zrobić to teraz, ale proponuję, żebyś potraktował to jako zadanie domowe. Spróbuj uzyskać na wyświetlaczu polskie litery.

Zadanie nie jest wcale trudne i można je zrealizować na wiele sposobów.

Wrócimy do tego później, a na razie spróbuj sam. A my w następnym odcinku (UR026) omawiamy ulepszone fonty Adarduit GFX.

 Piotr Górecki