Praca w trybie tekstowym

Wyświetlacz graficzny doskonale się nadaje także do wyświetlania tekstów. W klasycznych wyświetlaczach alfanumerycznych czcionki mają jednakową wielkość zależną od wielkości wyświetlacza i mogą być wyświetlane w ustalonych wierszach. W wyświetlaczu graficznym można definiować czcionki o różnych rozmiarach zależnie od potrzeb i umieszczać je w dowolnym miejscu wyświetlacza – o ile się tam zmieszczą.

Przy adresowaniu Page Addressing Mode jest bardzo łatwo zdefiniować znaki o wysokości 8 pikseli, lub wielokrotności tej wartości: 16, 24, 32 itd. Najprościej definiować znaki np. 8×6 pikseli umieszczając w pamięci generatora znaków 6 kolejnych bajtów – łatwo to sobie wyobrazić patrząc na rysunek 7. W programowych bibliotekach obsługujących wyświetlacze graficzne stosowałem z powodzeniem tę metodę. Mam zdefiniowaną tablice generatora znaków i proste procedury wyświetlania znaku na podstawie jego kodu ASCII. Niestety przy tym wyświetlaczu znaki o wysokości 8 pikseli są praktycznie nieczytelne. Wynika to po prostu z małych wymiarów (przekątnej) matrycy. W naszym przypadku wartością graniczną jest wysokość 12 pikseli, a najlepiej gdyby znaki miały 16 i więcej pikseli.

Do rysowanie znaków o dowolnej wielkości najlepiej się nadaje funkcja, która potrafi zapalić, lub zgasić jeden piksel o dowolnych współrzędnych x, y niezależnych od trybu adresowania. Taka funkcja została pokazana na listingu 7.

 

List. 7. Rysowanie punktu o ustalonych współrzędnych

Jak łatwo zauważyć funkcja void DrawPoint() z listingu 7 modyfikuje zawartość dwuwymiarowego bufora DispBuff umieszczonego w pamięci RAM mikrokontrolera hosta i nie zapisuje modyfikacji do sterownika SSD1306. Jak zobaczymy dalej wszystkie procedury wyświetlania modyfikują tylko ten bufor, i aby zobaczyć efekt tych modyfikacji, trzeba przepisać całą zawartość DispBuff do pamięci sterownika wywołując funkcję void RefreshRAM(void) pokazaną na listingu 5.

Mając do dyspozycji procedurę potrafiącą zapalać/gasić piksel o konkretnej współrzędnej możemy rysować proste, figury, okręgi, ale tez rysować znaki alfanumeryczne o dowolnych wielkościach. Żeby to robić potrzebne są wzorce znaków umieszczone w tablicy zwanej tez generatorem znaków. Ręczne tworzenie wzorców znaków jest możliwe, ale to żmudne zajęcie. Istnieje szereg programów tworzących wzorce o zadanej wielkości znaku. W Internecie można też znaleźć gotowe tablice z wzorcami. Ja skorzystałem z gotowych tablic ze zdefiniowanymi znakami o wielkości 12x6pikseli i 16x8pikseli. Tablice są tak zbudowane, ze kod ASCII znaku adresuje grupę bajtów definiujących ten znak. To znacznie upraszcza procedury wyświetlające łańcuchy znaków (napisy). Na przykład tablica dla znaków o wysokości 12 i szerokości 6 pikseli jest dwuwymiarową tablicą const char c_chFont1206[95][12] i zawiera 95 wzorców znaków. Każdy element wzorca znaków ma 12bajtów i definiuje znak o szerokości 6 pikseli, ale wykorzystuje przestrzeń 12×8 pikseli – listing 8.

 

List. 8. Fragment tablicy generatora znaków 12×6 pikseli

Dla znaków 16×8 pikseli jest zdefiniowana druga tablica const uint8_t c_chFont1608[95][16].

Mając tablice generatora znaków i procedurę DrawPoint ()potrafiącą „zapalać i gasić” bit w buforze pamięci mikrokontrolera odpowiadający zawartości pamięci RAM sterownika, a tym samym odpowiadający pikselowi na matrycy OLED można napisać procedurę „rysującą” znak w pamięci RAM mikrokontrolera. Pamiętamy, że aby ten znak się wyświetlił trzeba przepisać zawartość DispBuff do pamięci RAM wywołując funkcję void RefreshRAM(). Na listingu 9 pokazano procedurę void DisplayChar() z argumentami:

  • x i y – współrzędne początku znaku na ekranie,
  • Chr – kod ASCII wyświetlanego znaku,
  • size – wielkość znaku 12, lub 16 pikseli,
  • mode= 1 wyświetlanie normalne, mode=0 wyświetlanie w negatywie.

Zależnie od wartości argumentu size bajty wzorca są pobierane z tablicy c_chFont1206[95][12], lub z tablicy c_chFont1608[95][16]. Kiedy argument mode jest wyzerowany to dodatkowo wartość pobranego bajtu jest negowana. Potem jest analizowany każdy bit bajtu wzorca i zależnie od jego wartości DrawPoint() zapisuje do bufora DispBuff odpowiednią wartość.

 

List. 9. Wyświetlenie jednego znaku

Mając procedurę wyświetlania jednego znaku o dowolnych współrzędnych można napisać procedurę wyświetlającą łańcuch znaków od określonej pozycji. Na listingu 10 pokazano procedurę void DispTxt() z argumentami:

  • x i y – współrzędne początku znaku na ekranie,
  • *txt – wskaźnik na początek bufora z łańcuchem znaków,
  • size – wielkość znaku 12, lub 16 pikseli,
  • mode= 1 wyświetlanie normalne, mode=0 wyświetlanie w negatywie.

 

List. 10. Procedura wyświetlania ciągów znaków

Po każdym wyświetleniu jednego znaku na podstawie informacji o maksymalnych współrzędnych ekranu wyświetlacza (SSD1306_WIDTH i SSD1306_HEIGHT), oraz wartości argumentu size są wyliczane współrzędne kolejnego znaku. Jeżeli kolejny znak nie zmieści się w całości w linijce, to tekst jest automatycznie przenoszony na początek kolejnej linijki (CR, LF). Efekt działania krótkiego fragmentu programu z listingu 11 jest pokazany na rysunku 12.

 

List. 11. Wyświetlanie w trybie tekstowym

Rys. 12. Wyświetlanie tekstów o wysokości znaków 16 i 12 pikseli

 

Wyświetlanie bitmap

Każda czcionka wyświetlana w trybie tekstowym jest bitmapą rysowaną na ekranie. Te bitmapy -wzorce znaków umieszczane są w tablicy generatora znaków i mają jednakowe wymiary. Jednak często zachodzi konieczność wyświetlania bitmap o różnych rozmiarach. Pewnym problemem jest konwertowanie monochromatycznych. Ja używam prostego programu bmp2c.exe potrafiącego konwertować monochromatyczne mapy o niewielkich rozmiarach na tablicę w języku C. Kiedy już mamy skonwertowaną bitmapę, to procedura jej wyświetlania jest stosunkowo prosta – listing 12.

 

List. 12. Wyświetlenie bitmapy