Asembler mikrokontrolerów AVR     Obsługa wyświetlacza OLED (I2C)         Kompilacja




Powyższy schemat przedstawia sposób podłączenia monochromatycznego wyświetlacza OLED
(ze sterownikiem SSD1306 i magistralą I2C), o rozdzielczości 128x32 (DISP1A, adres $78/$3C)
lub 128x64 pikseli (DISP1B, adres $7A/$3D). Wyświetlacz 128x64 jest traktowany jako 128x32,
dlatego wyświetlany na nim obraz ma 1-liniowe poziome przerwy (co druga pozioma linia jest
pusta, co powoduje rozciągnięcie obrazu w pionie). Magistrala I2C jest obsługiwana sprzętowo
przez moduł TWI z częstotliwością 100 kHz.
Działanie zworki J1 oraz J2 jest ściśle związane z poniższym programem. Dokładny opis pozo-
stałych elementów schematu, prawidłowej konfiguracji mikrokontrolera, magistrali I2C oraz
sposobu transmisji danych, znajduje się na tej stronie.

Poniżej znajduje się kod programu przeznaczonego dla mikrokontrolerów serii ATmega48/88/168/328,
który służy do inicjalizacji i obsługi wyświetlacza OLED na magistrali I2C (sprzętowo).
.include  "m48def.inc"    ;definicje dla ATmega48
;.include  "m88def.inc"    ;definicje dla ATmega88
;.include  "m168def.inc"   ;definicje dla ATmega168
;.include  "m328def.inc"   ;definicje dla ATmega328

#define   CLK     8       ;częstotliwość pracy mikrokontrolera (CLK): 1-20 [MHz]

.cseg             ;pamięć programu (FLASH)
.org      $0000   ;wektory przerwań (dla ATmega48/88: 26 wektorów 2-bajtowych, o adresach $0000-$0019,
                  ;dla ATmega168/328: 26 wektorów 4-bajtowych, o adresach $0000-$0032).

;Jeśli żadne przerwania nie będą używane, to w tym miejscu można bezpośrednio umieścić kod programu

;Wektor nr 1 (Reset)
          ;rjmp    RESET   ;dla ATmega48/88 musi to być instrukcja RJMP o rozmiarze 2 bajtów,
          ;jmp     RESET   ;dla ATmega168/328 musi to być instrukcja JMP o rozmiarze 4 bajtów,
                          ;ponieważ wszystkie wektory w tych mikrokontrolerach mają taki rozmiar.

;RESET:
;Inicjalizacja wskaźnika stosu (zbędna dla ATmega48/88/168/328)
          ;ldi     R16,LOW(RAMEND)
          ;out     SPL,R16
          ;ldi     R16,HIGH(RAMEND)
          ;out     SPH,R16

;Konfiguracja mikrokontrolera
          ldi     R16,CLK   ;częstotliwość pracy mikrokontrolera (CLK): 1-20 [MHz]
          mov     R15,R16
          ldi     R16,$6F   ;zatrzymanie zbędnych modułów (ADC, SPI, USART, TC0, TC1, TC2)
          sts     $64,R16   ;zapis rejestru PRR
          ldi     R16,$80   ;wyłączenie zasilania komparatora analogowego
          sts     $50,R16   ;zapis rejestru ACSR
          sbi     PORTB,$00 ;ustawienie pinu PB0 jako wejścia z wysokim stanem (pull-up)
          sbi     PORTB,$02 ;ustawienie pinu PB2 jako wejścia z wysokim stanem (pull-up)

;Konfiguracja modułu TWI (I2C)
          ldi     R16,$20   ;podział częstotliwości CLK dla sygnału SCL
          sts     $B8,R16   ;zapis rejestru TWBR (SCL = CLK/16+2*TWBR = 100 kHz)
          ldi     R16,$00   ;podział częstotliwości sygnału SCL ($00=1, $01=4, $02=16, $03=64)
          sts     $B9,R16   ;zapis rejestru TWSR (SCL = SCL/1 = 100 kHz)
Aby zmniejszyć pobór prądu, zostaje wyłączony komparator analogowy oraz zatrzymane taktowanie
nieużywanych modułów mikrokontrolera (ADC, SPI, USART, TC0, TC1, TC2). Za pomocą rejestru
TWBR jest ustawiana częstotliwość 100 kHz dla sygnału zegarowego SCL, uzyskiwana z głównej
częstotliwości CLK taktującej mikrokontroler, według wzoru: SCL = CLK/16+2*TWBR. Następnie
częstotliwość SCL przechodzi przez 6-bitowy prescaler, którego stopień podziału (4/16/64) zależy
od wartości 2-najmłodszych bitów TWPS1-0 w rejestrze TWSR (pozostałe bity są tylko do odczytu).
Oba bity mają wartość 0, więc częstotliwość SCL nie jest dzielona i dalej wynosi 100 kHz.
;Obsługa monochromatycznego wyświetlacza OLED o rozdzielczości 128x32 (adres $78/$3C) lub 128x64
;(adres $7A/$3D) pikseli (SSD1306) z magistralą I2C i wyprowadzeniami podłączonymi w następujący
;sposób: SCL=PC5, SDA=PC4.
;Wyświetlacz 128x64 jest traktowany jako 128x32, dlatego wyświetlany na nim obraz ma 1-liniowe
;poziome przerwy (co druga pozioma linia jest pusta, co powoduje rozciągnięcie obrazu w pionie).
;Magistrala I2C jest obsługiwana sprzętowo przez moduł TWI z częstotliwością 100 kHz.
;Po włączeniu zasilania wyświetlacza OLED zostaje: włączone poziome (horizontal) adresowanie pamięci
;GDDRAM, ustawiona liczba skanowanych linii obrazu, odwrócone mapowanie kolumn i wierszy, włączone
;rozciąganie wierszy, ustawiony czas pracy przetwornicy napięcia, włączona przetwornica napięcia
;i matryca. Po włączeniu matrycy na wyświetlaczu widnieją przypadkowe piksele, dlatego zostaje on
;wyczyszczony (wskaźnik na pozycji 0,0).
          rcall   OLEDini   ;inicjalizacja OLED
Inicjalizacja, ustawienie parametrów pracy i włączenie wyświetlacza OLED.
;Obsługa zworki J1 (FUSE) - wyświetlanie wartości fuse/lock bitów
          sbic    PINB,$00  ;odczyt stanu pinu PB0
          rjmp    Boot1     ;skok, jeśli na pinie PB0 jest stan wysoki (zworka J1=OFF)
;Zworka J1=ON (zwarta)
          rcall   OLEDclr   ;czyszczenie OLED
          rcall   FuseBit   ;odczyt i wyświetlenie na OLED wartości fuse/lock bitów
Boot0:    sbis    PINB,$00  ;odczyt stanu pinu PB0
          rjmp    Boot0     ;skok, jeśli na pinie PB0 jest stan niski (zworka J1=ON)
;Zworka J1=OFF (rozwarta)
Boot1:    rcall   OLEDclr   ;czyszczenie OLED
          ldi     R18,0     ;kolumna startowa wskaźnika (0-127)
          ldi     R19,0     ;wiersz startowy wskaźnika (0-3)
          ldi     ZL,LOW(Text1<<1)
          ldi     ZH,HIGH(Text1<<1) ;adres ciągu ASCIIZ
          rcall   OLEDwf    ;wyświetlenie na OLED ciągu ASCIIZ z pamięci FLASH
          ldi     R18,0     ;kolumna startowa wskaźnika (0-127)
          ldi     R19,1     ;wiersz startowy wskaźnika (0-3)
          rcall   OLEDwf    ;wyświetlenie na OLED ciągu ASCIIZ z pamięci FLASH
Pin PB0 mikrokontrolera jest skonfigurowany jako wejście z wysokim stanem (pull-up). Jeśli po włą-
czeniu/resecie systemu zworka J1 jest rozwarta (OFF), to na pinie PB0 panuje stan wysoki i program
przechodzi do wyświetlania zwykłych napisów testu wyświetlacza (rysunek 2). Natomiast jeśli zworka
J1 będzie wtedy zwarta (ON), to na pinie PB0 będzie stan niski i program wyświetli szesnastkowe
wartości fuse/lock bitów, aktualnie ustawionych w mikrokontrolerze (rysunek 1). Będą one pokazane
w formacie "FL:LB:FE:FH", gdzie: FL - młodszy bajt fuse bitów (Fuse Low), LB - lock bity (Lock Bits),
FE - dodatkowe fuse bity (Fuse Extended), FH - starszy bajt fuse bitów (Fuse High). Wartości te będą
widoczne aż do momentu rozwarcia (OFF) zworki J1. Wtedy zostaną już wyświetlone zwykłe napisy
testu wyświetlacza. Dodatkowo, jeśli po włączeniu/resecie systemu zworka J2 będzie zwarta (ON),
to wyświetlany obraz zostanie obrócony o 180 stopni (rysunek 3 i 4).



Rysunek 1
(J1=ON, J2=OFF)


Rysunek 2
(J1=OFF, J2=OFF)


Rysunek 3
(J1=ON, J2=ON)


Rysunek 4
(J1=OFF, J2=ON)
;Inicjalizacja rejestrów
          clr     XL
          clr     XH
          movw    R22,XL
          movw    R24,XL
          inc     XL
Ustawienie początkowych wartości rejestrów, które będą używane w głównej pętli programu.
;Główna pętla programu
Loop:     rcall   Add3216   ;dodawanie słowa do wartości 32-bitowej
          ldi     ZL,LOW(sbuf)
          ldi     ZH,HIGH(sbuf) ;adres bufora wyjściowego w pamięci SRAM
          mov     R16,R22   ;najstarszy bajt 32-bitowej wartości
          rcall   ConBH     ;konwersja bajtu do systemu HEX-ASCIIZ
          mov     R16,R23
          rcall   ConBH     ;konwersja bajtu do systemu HEX-ASCIIZ
          mov     R16,R24
          rcall   ConBH     ;konwersja bajtu do systemu HEX-ASCIIZ
          mov     R16,R25   ;najmłodszy bajt 32-bitowej wartości
          rcall   ConBH     ;konwersja bajtu do systemu HEX-ASCIIZ
          sbiw    ZL,$08
          ldi     R18,55    ;kolumna startowa wskaźnika (0-127)
          ldi     R19,1     ;wiersz startowy wskaźnika (0-3)
          rcall   OLEDws    ;wyświetlenie na OLED ciągu ASCIIZ z pamięci SRAM
          rjmp    Loop
Główna pętla programu, która jest wykonywana bez przerwy. Z każdym obiegiem pętli, 32-bitowa war-
tość zapisana w rejestrach [R22:R23:R24:R25] jest zwiększana o 16-bitową wartość $0001 z rejestrów
[XH:XL]. Następnie każdy bajt tej zwiększonej wartości jest zamieniany na szesnastkowy ciąg ASCIIZ
i zapisywany do bufora w pamięci SRAM mikrokontrolera. Uzyskany w ten sposób 8-znakowy ciąg
ASCIIZ jest wyświetlany, począwszy od kolumny nr 55 w 2 wierszu. Instrukcja skoku RJMP zamyka
pętlę. Dzięki temu podczas pracy programu na wyświetlaczu OLED można zaobserwować, szybko
zwiększającą się 32-bitową wartość szesnastkową. Przykładowa zawartość ekranu wyświetlacza
OLED, podczas wykonywania głównej pętli programu jest przedstawiona na rysunku 5 i 6.



Rysunek 5
(J1=OFF, J2=OFF)


Rysunek 6
(J1=OFF, J2=ON)
;Inicjalizacja wyświetlacza OLED
OLEDini:  ldi     R16,$0A   ;opóźnienie 100ms
          rcall   WaitM     ;czekanie na ustabilizowanie się napięcia zasilania (100ms)
          rcall   TWIsta    ;rozpoczęcie transmisji I2C
;Wysłanie bajtu kontrolnego oraz sekwencji rozkazów
          ldi     ZL,LOW(OLEDcoms<<1)
          ldi     ZH,HIGH(OLEDcoms<<1) ;adres sekwencji rozkazów
;Obsługa zworki J2 (FLIP) - obracanie obrazu o 180 stopni
          sbic    PINB,$02  ;odczyt stanu pinu PB2
          rjmp    OLEDini1  ;skok, jeśli na pinie PB2 jest stan wysoki (zworka J2=OFF)
;Zworka J2=ON (zwarta)
          ldi     ZL,LOW(OLEDflip<<1)
          ldi     ZH,HIGH(OLEDflip<<1) ;adres sekwencji rozkazów (obrót o 180 stopni)
          rjmp    OLEDini1
OLEDini0: rcall   TWIwri    ;wysłanie rozkazu do OLED
OLEDini1: lpm     R17,Z+    ;odczyt rozkazu
          cpi     R17,$FF   ;znacznik końca sekwencji rozkazów
          brne    OLEDini0
;Zakończenie wysyłania sekwencji rozkazów
          rcall   TWIsto    ;zakończenie transmisji I2C
          ldi     R16,$0A   ;opóźnienie 100ms
          rcall   WaitM     ;czekanie na włączenie się matrycy (100ms)
          ret
Inicjalizacja i włączenie wyświetlacza OLED. Na magistralę I2C jest wysyłany warunek START, a nas-
tępnie adres wyświetlacza $78 (128x32). Jeśli wyświetlacz nie odpowie na ten adres, to zostanie wys-
łany powtórzony warunek START (Repeated START) oraz drugi adres $7A (128x64). Jeśli nie uda się
wysłać warunku START/Repeated START lub wyświetlacz nie odpowie bitem potwierdzenia (ACK=1)
na żaden adres, to wtedy dioda LED1 zostanie zaświecona, a program zatrzymany w pętli.
Jeśli wyświetlacz odpowie bitem potwierdzenia (ACK=0) na któryś adres, to zostanie do niego wysła-
ny bajt kontrolny ciągu rozkazów oraz sekwencja rozkazów, które ustawiają parametry jego pracy. Pin
PB2 mikrokontrolera jest skonfigurowany jako wejście z wysokim stanem (pull-up). Jeśli po włączeniu/
resecie systemu zworka J2 jest rozwarta (OFF), to na pinie PB2 panuje stan wysoki i program wyśle
dane spod etykiety "OLEDcoms". Natomiast jeśli zworka J2 będzie wtedy zwarta (ON), to na pinie PB2
będzie stan niski i program wyśle dane spod etykiety "OLEDflip", co spowoduje że wyświetlany obraz
zostanie obrócony o 180 stopni. Po wysłaniu każdego bajtu danych, nie jest sprawdzana poprawność
jego transmisji. Transmisja zostaje zakończona wysłaniem warunku STOP na magistralę I2C.
;Czyszczenie wyświetlacza OLED i powrót wskaźnika na pozycję 0,0
OLEDclr:  rcall   TWIsta    ;rozpoczęcie transmisji I2C
          ldi     R17,$00   ;bajt kontrolny ciągu rozkazów
          rcall   TWIwri    ;wysłanie bajtu kontrolnego
;Ustawienie wskaźnika na pozycji 0,0
          ldi     R17,$21   ;pozycja wskaźnika w kolumnie (Column address)
          rcall   TWIwri
          ldi     R17,$00   ;kolumna startowa wskaźnika (0-127)
          rcall   TWIwri
          ldi     R17,$7F   ;kolumna końcowa wskaźnika (0-127)
          rcall   TWIwri
          ldi     R17,$22   ;pozycja wskaźnika w wierszu (Page address)
          rcall   TWIwri
          ldi     R17,$00   ;wiersz startowy wskaźnika (0-3)
          rcall   TWIwri
          ldi     R17,$03   ;wiersz końcowy wskaźnika (0-3)
          rcall   TWIwri
;Zapis pustych pikseli w pamięci GDDRAM
          rcall   TWIsta    ;ponowienie transmisji I2C
          ldi     R17,$40   ;bajt kontrolny ciągu danych
          rcall   TWIwri    ;wysłanie bajtu kontrolnego
;Wysłanie 512 bajtów danych (128x32 = 4096 bitów/pikseli)
          ldi     YH,$02
          ldi     YL,$00
          ldi     R17,$00   ;bajt danych (pionowa kolumna 8 pustych pikseli)
OLEDclr0: rcall   TWIwri    ;wysłanie bajtu danych
          sbiw    YL,$01
          brne    OLEDclr0
;Zakończenie wysyłania bajtów danych
          rcall   TWIsto    ;zakończenie transmisji I2C
          ret
Czyszczenie zawartości ekranu wyświetlacza OLED. Po włączeniu matrycy na wyświetlaczu widnieją
przypadkowe piksele, dlatego musi on zostać wyczyszczony. Najpierw do sterownika jest wysyłana
informacja o zakresie skanowanych kolumn (pikseli) i wierszy (po 8 pikseli) obrazu. Następnie zawa-
rtość pamięci GDDRAM (128x32 = 4096 bitów/pikseli) jest zerowana przez wysłanie 512 bajtów da-
nych o wartości $00.
Ponieważ zostało włączone poziome (horizontal) adresowanie pamięci GDDRAM, to każdy wysłany
bajt danych odpowiada pionowej kolumnie 8 pikseli obrazu (najmłodszy bit to górny piksel kolumny).
Po odebraniu każdego bajtu danych, sterownik automatycznie zwiększa wartość wskaźnika kolumny
o 1. Jeśli wskaźnik kolumny przekroczy wartość kolumny końcowej (127), to automatycznie zostanie
mu przypisana wartość kolumny startowej (0), a wartość wskaźnika wiersza zostanie zwiększona o 1.
Jeśli obydwa wskaźniki kolumny i wiersza jednocześnie przekroczą wartość kolumny (127) i wiersza (3)
końcowego, to automatycznie zostanie im przypisana wartość kolumny (0) i wiersza (0) startowego.
Sposób zapisu i wyświetlania bajtów przykładowej bitmapy 8x8 pikseli, przedstawia poniższy rysunek.



Rysunek 7

;Zapis ciągu ASCIIZ z pamięci SRAM do wyświetlacza OLED
OLEDws:   rcall   OLEDwr0   ;rozpoczęcie transmisji I2C
          rjmp    OLEDws1
OLEDws0:  rcall   OLEDwr1   ;zapis znaku do OLED
OLEDws1:  ld      R17,Z+    ;odczyt znaku z pamięci SRAM
          tst     R17
          brne    OLEDws0
          rcall   TWIsto    ;zakończenie transmisji I2C
          ret
Procedura wyświetla na OLED ciąg ASCIIZ, zapisany w pamięci SRAM mikrokontrolera.
;Zapis ciągu ASCIIZ z pamięci FLASH do wyświetlacza OLED
OLEDwf:   rcall   OLEDwr0   ;rozpoczęcie transmisji I2C
          rjmp    OLEDwf1
OLEDwf0:  rcall   OLEDwr1   ;zapis znaku do OLED
OLEDwf1:  lpm     R17,Z+    ;odczyt znaku z pamięci FLASH
          tst     R17
          brne    OLEDwf0
          rcall   TWIsto    ;zakończenie transmisji I2C
          ret
Procedura wyświetla na OLED ciąg ASCIIZ, zapisany w pamięci FLASH mikrokontrolera.
;Podprocedura #1 zapisu ciągu ASCIIZ z pamięci SRAM/FLASH do OLED
OLEDwr0:  rcall   TWIsta    ;rozpoczęcie transmisji I2C
          ldi     R17,$00   ;bajt kontrolny ciągu rozkazów
          rcall   TWIwri
          ldi     R17,$21   ;pozycja wskaźnika w kolumnie (Column address)
          rcall   TWIwri
          mov     R17,R18   ;kolumna startowa wskaźnika (0-127)
          rcall   TWIwri
          ldi     R17,$7F   ;kolumna końcowa wskaźnika (0-127)
          rcall   TWIwri
          ldi     R17,$22   ;pozycja wskaźnika w wierszu (Page address)
          rcall   TWIwri
          mov     R17,R19   ;wiersz startowy wskaźnika (0-3)
          rcall   TWIwri
          ldi     R17,$03   ;wiersz końcowy wskaźnika (0-3)
          rcall   TWIwri
          rcall   TWIsta    ;ponowienie transmisji I2C
          ldi     R17,$40   ;bajt kontrolny ciągu danych
          rcall   TWIwri
          ret

;Podprocedura #2 zapisu ciągu ASCIIZ z pamięci SRAM/FLASH do OLED
OLEDwr1:  movw    R4,ZL     ;zachowanie adresu aktualnego znaku w ciągu ASCIIZ
          ldi     ZL,LOW(OLEDfont<<1)
          ldi     ZH,HIGH(OLEDfont<<1) ;adres danych opisujących budowę znaków czcionki (8x8 pikseli)
          dec     R17
          ldi     R18,$08
;Mnożenie wartości odczytanego znaku z ciągu ASCIIZ przez 8
          mul     R17,R18
;Dodanie wyniku mnożenia do wartości adresu, wskazującego na początek danych w pamięci FLASH,
;które opisują budowę poszczególnych znaków czcionki o rozmiarze 8x8 pikseli.
          add     ZL,R0
          adc     ZH,R1
;Wyświetlanie znaku 8x8 pikseli na OLED
OLEDwr2:  lpm     R17,Z+    ;odczyt pionowej kolumny 8 pikseli znaku z pamięci FLASH
          rcall   TWIwri    ;zapis kolumny do OLED
          dec     R18
          brne    OLEDwr2
          movw    ZL,R4     ;przywrócenie adresu aktualnego znaku w ciągu ASCIIZ
          ret
Podprocedury używane do wyświetlania na OLED ciągu ASCIIZ z pamięci SRAM/FLASH.
;Wysłanie warunku START/Repeated START i adresu OLED na magistralę I2C
TWIsta:   rcall   TWIsta1   ;wysłanie warunku START
;Wysłanie adresu OLED 128x32 pikseli na magistralę I2C
          ldi     R17,$78   ;adres wyświetlacza 128x32 pikseli
          rcall   TWIwri    ;wysłanie adresu
;Sprawdzenie poprawności transmisji adresu
          cpi     R16,$18   ;wartość po udanym wysłaniu adresu z potwierdzeniem (ACK=0)
          breq    TWIsta0   ;skok, jeśli OLED odpowiedział (ACK=0)
;Ponowienie transmisji I2C
          rcall   TWIsta1   ;wysłanie warunku Repeated START
;Wysłanie adresu OLED 128x64 pikseli na magistralę I2C
          ldi     R17,$7A   ;adres wyświetlacza 128x64 pikseli
          rcall   TWIwri    ;wysłanie adresu
;Sprawdzenie poprawności transmisji adresu
          cpi     R16,$18   ;wartość po udanym wysłaniu adresu z potwierdzeniem (ACK=0)
          brne    TWIerr    ;skok, jeśli wystąpił błąd - OLED nie odpowiada (ACK=1)
TWIsta0:  ret

;Podprocedura wysłania warunku START/Repeated START na magistralę I2C
TWIsta1:  ldi     R16,$A4   ;ustawienie bitów TWINT, TWSTA i TWEN
          rcall   TWIwri0   ;wysłanie warunku START/Repeated START
;Sprawdzenie poprawności transmisji warunku START
          cpi     R16,$08   ;wartość po udanym wysłaniu warunku START
          breq    TWIsta2   ;skok, jeśli warunek został wysłany
;Sprawdzenie poprawności transmisji warunku Repeated START
          cpi     R16,$10   ;wartość po udanym wysłaniu warunku Repeated START
          brne    TWIerr0   ;skok, jeśli wystąpił błąd - nie wysłano warunku
TWIsta2:  ret

;Błąd transmisji I2C
TWIerr:   rcall   TWIsto    ;zakończenie transmisji I2C
TWIerr0:  sbi     DDRB,$01  ;ustawienie niskiego stanu na wyjściu PB1 (zaświecenie diody LED1)
TWIerr1:  rjmp    TWIerr1   ;zatrzymanie wykonywania programu

;Wysłanie warunku STOP na magistralę I2C
TWIsto:   ldi     R16,$94   ;ustawienie bitów TWINT, TWSTO i TWEN
          sts     $BC,R16   ;zapis rejestru TWCR
;Czekanie na zakończenie transmisji warunku STOP
TWIsto0:  lds     R16,$BC   ;odczyt rejestru TWCR
          andi    R16,$10   ;testowanie bitu TWSTO
          brne    TWIsto0   ;skok, jeśli bit ma wartość 1
          ret

;Wysłanie bajtu adresu/rozkazu/danych do OLED na magistrali I2C
TWIwri:   sts     $BB,R17   ;zapis rejestru TWDR
          ldi     R16,$84   ;ustawienie bitów TWINT i TWEN
TWIwri0:  sts     $BC,R16   ;zapis rejestru TWCR
;Czekanie na zakończenie transmisji warunku START/bajtu
TWIwri1:  lds     R16,$BC   ;odczyt rejestru TWCR
          andi    R16,$80   ;testowanie bitu TWINT
          breq    TWIwri1   ;skok, jeśli bit ma wartość 0
          lds     R16,$B9   ;odczyt rejestru TWSR
          andi    R16,$F8   ;wyodrębnienie bitów TWS7-3
;Rejestr R16 może mieć następujące wartości:
; $00 - błąd magistrali po wystąpieniu niedozwolonego warunku START/STOP
; $08 - warunek START został wysłany
; $10 - powtórzony warunek START (Repeated START) został wysłany
; $18 - adres (SLA+W) został wysłany z potwierdzeniem (ACK=0)
; $20 - adres (SLA+W) został wysłany bez potwierdzenia (ACK=1)
; $28 - bajt został wysłany z potwierdzeniem (ACK=0)
; $30 - bajt został wysłany bez potwierdzenia (ACK=1)
; $38 - arbitraż został przegrany podczas wysyłania adresu/bajtu
; $F8 - moduł TWI nie wykonuje żadnej transmisji lub jest w jej trakcie (bit TWINT=0)
          ret
Procedury do sprzętowej obsługi magistrali I2C.
;Dodawanie słowa do wartości 32-bitowej: [R22:R23:R24:R25] + [XH:XL] = [R22:R23:R24:R25]
Add3216:  clr     R2
          add     R25,XL    ;najmłodszy bajt wartości/wyniku, młodszy bajt słowa
          adc     R24,XH    ;starszy bajt słowa
          adc     R23,R2
          adc     R22,R2    ;najstarszy bajt wartości/wyniku
          ret               ;C=1 jeśli wynik >4294967295
Procedura dodaje 16-bitowe słowo z rejestrów [XH:XL] do 32-bitowej wartości,
zapisanej w rejestrach [R22:R23:R24:R25].
;Zapis szesnastkowej wartości bajtu do pamięci SRAM w postaci ciągu ASCIIZ: "NN"
ConBH:    ldi     R17,$02
ConBH0:   swap    R16       ;bajt do konwersji
          mov     R18,R16
          andi    R18,$0F
          subi    R18,$D0
          cpi     R18,$3A
          brlt    ConBH1
          subi    R18,$F9   ;subi R18,$D9 - małe litery (a-f)
ConBH1:   st      Z+,R18
          dec     R17
          brne    ConBH0
          st      Z,R17     ;wyjście danych
          ret
Procedura konwertuje bajt z rejestru R16 do postaci szesnastkowego 2-znakowego ciągu ASCIIZ,
zapisanego do bufora w pamięci SRAM mikrokontrolera.
;Zapis szesnastkowych wartości fuse/lock bitów do pamięci SRAM w postaci
;ciągu ASCIIZ: "FL:LB:FE:FH" i wyświetlenie go na wyświetlaczu OLED.
FuseBit:  ldi     YL,LOW(sbuf)
          ldi     YH,HIGH(sbuf) ;adres wyjścia danych
          clr     ZL
          clr     ZH
          ldi     R18,$3A
FuseBit0: lds     R16,$57       ;odczyt rejestru SPMCSR
          ori     R16,$09       ;ustawienie bitów BLBSET i SELFPRGEN
          sts     $57,R16       ;zapis rejestru SPMCSR
          lpm     R2,Z+         ;odczyt fuse/lock bitów
;W zależności od wartości adresu [ZH:ZL] są odczytywane:
; $0000 - Fuse bits Low byte (FL)
; $0001 - Lock Bits (LB)
; $0002 - Fuse bits Extended byte (FE)
; $0003 - Fuse bits High byte (FH)
          ldi     R16,$02
FuseBit1: swap    R2
          mov     R17,R2
          andi    R17,$0F
          subi    R17,$D0
          cpi     R17,$3A
          brlt    FuseBit2
          subi    R17,$F9
FuseBit2: st      Y+,R17
          dec     R16
          brne    FuseBit1
          st      Y+,R18
          cpi     ZL,$04
          brne    FuseBit0
          st      -Y,R16
;Wyświetlanie wartości fuse/lock bitów na OLED
          ldi     R18,0         ;kolumna startowa wskaźnika (0-127)
          ldi     R19,0         ;wiersz startowy wskaźnika (0-3)
          ldi     ZL,LOW(Text0<<1)
          ldi     ZH,HIGH(Text0<<1) ;adres ciągu ASCIIZ
          rcall   OLEDwf        ;wyświetlenie na OLED ciągu ASCIIZ z pamięci FLASH
          ldi     R18,0         ;kolumna startowa wskaźnika (0-127)
          ldi     R19,1         ;wiersz startowy wskaźnika (0-3)
          ldi     ZL,LOW(sbuf)
          ldi     ZH,HIGH(sbuf) ;adres ciągu ASCIIZ
          rcall   OLEDws        ;wyświetlenie na OLED ciągu ASCIIZ z pamięci SRAM
          ret
Procedura odczytuje aktualnie ustawione wartości fuse/lock bitów mikrokontrolera, zapisuje je do
pamięci SRAM w postaci 11-znakowego ciągu ASCIIZ, zawierającego cztery szesnastkowe wartości
oddzielone dwukropkami, a następnie wyświetla ten ciąg na OLED.
;Pętla opóźniająca o zadany czas: 100u - 25.5m [s]
Wait:     mov     R2,R15    ;R15 (CLK) = częstotliwość pracy mikrokontrolera: $01-$14 (1-20 MHz)
          ldi     R17,$19
          mul     R16,R17   ;R16 (DLY) = wartość opóźnienia: $01-$FF (100us-25.5ms)
Wait0:    movw    YL,R0
Wait1:    sbiw    YL,$01
          brne    Wait1
          dec     R2
          brne    Wait0
          ret
Procedura generuje opóźnienie czasowe (100us - 25.5ms), zależne od wartości rejestru R16.
;Pętla opóźniająca o zadany czas: 10m - 2.55 [s]
WaitM:    mov     R14,R16   ;R16 (DLY) = wartość opóźnienia: $01-$FF (10ms-2.55s)
          ldi     R16,$64
WaitM0:   rcall   Wait
          dec     R14
          brne    WaitM0
          ret
Procedura generuje opóźnienie czasowe (10ms - 2.55s), zależne od wartości rejestru R16.
;Napisy dla wyświetlacza OLED
Text0:    .db     "Fuse/lock bits:",0
Text1:    .db     "*** OLEDtest ***",0,"Count:",0
Napisy pokazywane na wyświetlaczu OLED, umieszczone w pamięci FLASH mikrokontrolera.
;Rozkazy inicjujące dla wyświetlacza OLED
OLEDcoms: .db     $00,$20,$00,$A8,$1F,$A1,$C8,$DA,$02,$D9,$F1,$8D,$14,$AF,$FF,$FF
OLEDflip: .db     $00,$20,$00,$A8,$1F,$A0,$C0,$DA,$02,$D9,$F1,$8D,$14,$AF,$FF,$FF ;obrót o 180 stopni

;Zaraz po adresie pierwszym bajtem wysyłanym do wyświetlacza OLED jest bajt kontrolny,
;który informuje go o liczbie i rodzaju przesyłanych do niego bajtów:
; $00 - ciąg N-bajtów rozkazów ($00+N*$xx) + STOP/Repeated START
; $40 - ciąg N-bajtów danych ($40+N*$xx) + STOP/Repeated START
; $80 - pojedynczy bajt rozkazu ($80xx) [+ STOP/Repeated START]
; $C0 - pojedynczy bajt danych ($C0xx) [+ STOP/Repeated START]

;Opis poszczególnych bajtów/rozkazów [w nawiasie kwadratowym podano domyślną wartość po
;włączeniu/resecie sterownika SSD1306, która jest zbędna (Z) i nie musi być do niego wysyłana]:
; $00 - bajt kontrolny ciągu rozkazów
; $AE (Z) - wyłączenie matrycy (Display off) [$AE = matryca wyłączona]
; $2000 - poziome (horizontal) adresowanie pamięci GDDRAM (Memory addressing mode) [$2002]
; $D580 (Z) - częstotliwość wyświetlania obrazu/sterownika (Display clock divide ratio/
;             Oscillator frequency) [$D580]
; $A81F - liczba skanowanych linii obrazu (Multiplex ratio) [$A83F = 128x64]
; $D300 (Z) - pionowe przesunięcie pikseli (Display offset) [$D300]
; $40 (Z) - numer linii startowej obrazu (Display start line) [$40]
; $A1 - mapowanie kolumn (odwraca obraz w poziomie) (Segment re-map) [$A0]
; $C8 - mapowanie wierszy (odwraca obraz w pionie) (COM output scan direction) [$C0]
; $DA02 - rozciąganie wierszy (COM pins hardware configuration) [$DA12 = 128x64]
; $817F (Z) - kontrast matrycy (Contrast control) [$817F]
; $D9F1 - czas pracy przetwornicy napięcia (Pre-charge period) [$D922 = przetwornica wyłączona]
; $DB20 (Z) - napięcie wysokiego stanu logicznego (Vcomh deselect level) [$DB20]
; $A4 (Z) - wyświetlenie zawartości pamięci GDDRAM (Entire display on) [$A4]
; $A6 (Z) - normalne wyświetlanie (bit 0 = piksel wyłączony) (Normal/inverse display) [$A6]
; $8D14 - włączenie przetwornicy napięcia (Charge pump) [$8D10 = przetwornica wyłączona]
; $AF - włączenie matrycy (Display on) [$AE = matryca wyłączona]
; $FF - znacznik końca sekwencji rozkazów
Tablice umieszczone w pamięci FLASH mikrokontrolera, zawierające bajt kontrolny ciągu rozkazów
oraz sekwencję rozkazów, które są wysyłane do sterownika wyświetlacza OLED i ustawiają para-
metry jego pracy. W zależności od stanu zworki J2 po włączeniu/resecie systemu, do sterownika
są wysyłane dane z jednej lub drugiej tablicy.
;Czcionka 8x8 pikseli (255 znaków = 2040 bajtów, polskie znaki zmapowane według Windows-1250)
OLEDfont: .db     $7E,$81,$95,$B1,$B1,$95,$81,$7E ;znak $01
          .db     $7E,$FF,$EB,$CF,$CF,$EB,$FF,$7E ;znak $02
          .db     $0E,$1F,$3F,$7E,$3F,$1F,$0E,$00 ;znak $03
          .db     $08,$1C,$3E,$7F,$3E,$1C,$08,$00 ;znak $04
          .db     $18,$BA,$FF,$FF,$FF,$BA,$18,$00 ;znak $05
          .db     $10,$B8,$FC,$FF,$FC,$B8,$10,$00 ;znak $06
          .db     $00,$00,$18,$3C,$3C,$18,$00,$00 ;znak $07
          .db     $FF,$FF,$E7,$C3,$C3,$E7,$FF,$FF ;znak $08
          .db     $00,$3C,$66,$42,$42,$66,$3C,$00 ;znak $09
          .db     $FF,$C3,$99,$BD,$BD,$99,$C3,$FF ;znak $0A
          .db     $70,$F8,$88,$88,$FD,$7F,$07,$0F ;znak $0B
          .db     $00,$4E,$5F,$F1,$F1,$5F,$4E,$00 ;znak $0C
          .db     $C0,$E0,$FF,$7F,$05,$05,$07,$07 ;znak $0D
          .db     $C0,$FF,$7F,$05,$05,$65,$7F,$3F ;znak $0E
          .db     $99,$5A,$3C,$E7,$E7,$3C,$5A,$99 ;znak $0F
          .db     $00,$00,$FF,$FF,$7E,$3C,$18,$00 ;znak $10
          .db     $00,$00,$18,$3C,$7E,$FF,$FF,$00 ;znak $11
          .db     $00,$24,$66,$FF,$FF,$66,$24,$00 ;znak $12
          .db     $00,$5F,$5F,$00,$00,$5F,$5F,$00 ;znak $13
          .db     $06,$0F,$09,$7F,$01,$7F,$01,$00 ;znak $14
          .db     $20,$4A,$5F,$55,$55,$7D,$29,$02 ;znak $15
          .db     $00,$70,$70,$70,$70,$70,$70,$00 ;znak $16
          .db     $80,$94,$B6,$FF,$FF,$B6,$94,$80 ;znak $17
          .db     $00,$04,$06,$7F,$7F,$06,$04,$00 ;znak $18
          .db     $00,$10,$30,$7F,$7F,$30,$10,$00 ;znak $19
          .db     $00,$08,$08,$08,$2A,$3E,$1C,$08 ;znak $1A
          .db     $00,$08,$1C,$3E,$2A,$08,$08,$08 ;znak $1B
          .db     $3C,$3C,$20,$20,$20,$20,$20,$00 ;znak $1C
          .db     $08,$1C,$3E,$08,$08,$3E,$1C,$08 ;znak $1D
          .db     $30,$38,$3C,$3E,$3E,$3C,$38,$30 ;znak $1E
          .db     $06,$0E,$1E,$3E,$3E,$1E,$0E,$06 ;znak $1F
          .db     $00,$00,$00,$00,$00,$00,$00,$00 ;znak " "
          .db     $00,$00,$06,$5F,$5F,$06,$00,$00 ;znak "!"
          .db     $00,$07,$07,$00,$07,$07,$00,$00 ;znak """
          .db     $14,$7F,$7F,$14,$7F,$7F,$14,$00 ;znak "#"
          .db     $00,$24,$2E,$6B,$6B,$3A,$12,$00 ;znak "$"
          .db     $46,$66,$30,$18,$0C,$66,$62,$00 ;znak "%"
          .db     $30,$7A,$4F,$5D,$37,$7A,$48,$00 ;znak "&"
          .db     $00,$00,$04,$07,$03,$00,$00,$00 ;znak "'"
          .db     $00,$00,$1C,$3E,$63,$41,$00,$00 ;znak "("
          .db     $00,$00,$41,$63,$3E,$1C,$00,$00 ;znak ")"
          .db     $00,$2A,$3E,$1C,$1C,$3E,$2A,$00 ;znak "*"
          .db     $00,$08,$08,$3E,$3E,$08,$08,$00 ;znak "+"
          .db     $00,$00,$80,$E0,$60,$00,$00,$00 ;znak ","
          .db     $00,$08,$08,$08,$08,$08,$08,$00 ;znak "-"
          .db     $00,$00,$00,$60,$60,$00,$00,$00 ;znak "."
          .db     $00,$60,$30,$18,$0C,$06,$03,$00 ;znak "/"
          .db     $00,$3E,$7F,$59,$4D,$7F,$3E,$00 ;znak "0"
          .db     $00,$00,$04,$06,$7F,$7F,$00,$00 ;znak "1"
          .db     $00,$42,$63,$71,$59,$4F,$46,$00 ;znak "2"
          .db     $00,$22,$63,$49,$49,$7F,$36,$00 ;znak "3"
          .db     $18,$1C,$16,$13,$7F,$7F,$10,$00 ;znak "4"
          .db     $00,$27,$67,$45,$45,$7D,$39,$00 ;znak "5"
          .db     $00,$3C,$7E,$4B,$49,$79,$30,$00 ;znak "6"
          .db     $00,$03,$03,$71,$79,$0F,$07,$00 ;znak "7"
          .db     $00,$36,$7F,$49,$49,$7F,$36,$00 ;znak "8"
          .db     $00,$06,$4F,$49,$69,$3F,$1E,$00 ;znak "9"
          .db     $00,$00,$00,$66,$66,$00,$00,$00 ;znak ":"
          .db     $00,$00,$80,$E6,$66,$00,$00,$00 ;znak ";"
          .db     $00,$08,$1C,$36,$63,$41,$00,$00 ;znak "<"
          .db     $00,$14,$14,$14,$14,$14,$14,$00 ;znak "="
          .db     $00,$00,$41,$63,$36,$1C,$08,$00 ;znak ">"
          .db     $00,$02,$03,$51,$59,$0F,$06,$00 ;znak "?"
          .db     $3E,$7F,$41,$5D,$55,$5F,$1E,$00 ;znak "@"
          .db     $00,$7C,$7E,$13,$13,$7E,$7C,$00 ;znak "A"
          .db     $00,$7F,$7F,$49,$49,$7F,$36,$00 ;znak "B"
          .db     $00,$3E,$7F,$41,$41,$63,$22,$00 ;znak "C"
          .db     $00,$7F,$7F,$41,$41,$7F,$3E,$00 ;znak "D"
          .db     $00,$7F,$7F,$49,$49,$49,$41,$00 ;znak "E"
          .db     $00,$7F,$7F,$09,$09,$09,$01,$00 ;znak "F"
          .db     $00,$3E,$7F,$41,$49,$7B,$7A,$00 ;znak "G"
          .db     $00,$7F,$7F,$08,$08,$7F,$7F,$00 ;znak "H"
          .db     $00,$00,$41,$7F,$7F,$41,$00,$00 ;znak "I"
          .db     $00,$30,$70,$40,$40,$7F,$3F,$00 ;znak "J"
          .db     $00,$7F,$7F,$1C,$36,$63,$41,$00 ;znak "K"
          .db     $00,$7F,$7F,$40,$40,$40,$40,$00 ;znak "L"
          .db     $7F,$7F,$06,$0C,$06,$7F,$7F,$00 ;znak "M"
          .db     $00,$7F,$7F,$06,$0C,$7F,$7F,$00 ;znak "N"
          .db     $00,$3E,$7F,$41,$41,$7F,$3E,$00 ;znak "O"
          .db     $00,$7F,$7F,$09,$09,$0F,$06,$00 ;znak "P"
          .db     $00,$3E,$7F,$41,$61,$FF,$BE,$00 ;znak "Q"
          .db     $00,$7F,$7F,$19,$39,$6F,$46,$00 ;znak "R"
          .db     $00,$26,$6F,$4D,$59,$7B,$32,$00 ;znak "S"
          .db     $00,$01,$01,$7F,$7F,$01,$01,$00 ;znak "T"
          .db     $00,$3F,$7F,$40,$40,$7F,$3F,$00 ;znak "U"
          .db     $00,$1F,$3F,$60,$60,$3F,$1F,$00 ;znak "V"
          .db     $7F,$7F,$30,$18,$30,$7F,$7F,$00 ;znak "W"
          .db     $00,$63,$77,$1C,$1C,$77,$63,$00 ;znak "X"
          .db     $00,$07,$0F,$78,$78,$0F,$07,$00 ;znak "Y"
          .db     $00,$61,$71,$59,$4D,$47,$43,$00 ;znak "Z"
          .db     $00,$00,$7F,$7F,$41,$41,$00,$00 ;znak "["
          .db     $00,$03,$06,$0C,$18,$30,$60,$00 ;znak "\"
          .db     $00,$00,$41,$41,$7F,$7F,$00,$00 ;znak "]"
          .db     $08,$0C,$06,$03,$06,$0C,$08,$00 ;znak "^"
          .db     $00,$80,$80,$80,$80,$80,$80,$00 ;znak "_"
          .db     $00,$00,$00,$03,$07,$04,$00,$00 ;znak "`"
          .db     $00,$20,$74,$54,$54,$7C,$78,$00 ;znak "a"
          .db     $00,$7F,$7F,$44,$44,$7C,$38,$00 ;znak "b"
          .db     $00,$38,$7C,$44,$44,$6C,$28,$00 ;znak "c"
          .db     $00,$38,$7C,$44,$44,$7F,$7F,$00 ;znak "d"
          .db     $00,$38,$7C,$54,$54,$5C,$18,$00 ;znak "e"
          .db     $00,$08,$7E,$7F,$09,$03,$02,$00 ;znak "f"
          .db     $00,$18,$BC,$A4,$A4,$FC,$7C,$00 ;znak "g"
          .db     $00,$7F,$7F,$04,$04,$7C,$78,$00 ;znak "h"
          .db     $00,$00,$44,$7D,$7D,$40,$00,$00 ;znak "i"
          .db     $00,$60,$E0,$80,$80,$FD,$7D,$00 ;znak "j"
          .db     $00,$7F,$7F,$10,$38,$6C,$44,$00 ;znak "k"
          .db     $00,$00,$41,$7F,$7F,$40,$00,$00 ;znak "l"
          .db     $7C,$7C,$0C,$18,$0C,$7C,$78,$00 ;znak "m"
          .db     $00,$7C,$7C,$04,$04,$7C,$78,$00 ;znak "n"
          .db     $00,$38,$7C,$44,$44,$7C,$38,$00 ;znak "o"
          .db     $00,$FC,$FC,$24,$24,$3C,$18,$00 ;znak "p"
          .db     $00,$18,$3C,$24,$24,$FC,$FC,$00 ;znak "q"
          .db     $00,$7C,$7C,$04,$04,$0C,$08,$00 ;znak "r"
          .db     $00,$48,$5C,$54,$54,$74,$24,$00 ;znak "s"
          .db     $00,$04,$3F,$7F,$44,$64,$20,$00 ;znak "t"
          .db     $00,$3C,$7C,$40,$40,$7C,$7C,$00 ;znak "u"
          .db     $00,$1C,$3C,$60,$60,$3C,$1C,$00 ;znak "v"
          .db     $3C,$7C,$60,$30,$60,$7C,$3C,$00 ;znak "w"
          .db     $00,$44,$6C,$38,$38,$6C,$44,$00 ;znak "x"
          .db     $00,$1C,$BC,$A0,$A0,$FC,$7C,$00 ;znak "y"
          .db     $00,$44,$64,$74,$5C,$4C,$44,$00 ;znak "z"
          .db     $00,$08,$08,$3E,$77,$41,$41,$00 ;znak "{"
          .db     $00,$00,$00,$7F,$7F,$00,$00,$00 ;znak "|"
          .db     $00,$41,$41,$77,$3E,$08,$08,$00 ;znak "}"
          .db     $06,$03,$03,$06,$0C,$0C,$06,$00 ;znak "~"
          .db     $18,$18,$18,$18,$18,$18,$18,$18 ;znak $7F
          .db     $00,$00,$00,$FF,$FF,$00,$00,$00 ;znak $80
          .db     $00,$00,$00,$F8,$F8,$18,$18,$18 ;znak $81
          .db     $18,$18,$18,$F8,$F8,$00,$00,$00 ;znak $82
          .db     $00,$00,$00,$1F,$1F,$18,$18,$18 ;znak $83
          .db     $18,$18,$18,$1F,$1F,$00,$00,$00 ;znak $84
          .db     $18,$18,$18,$F8,$F8,$18,$18,$18 ;znak $85
          .db     $18,$18,$18,$1F,$1F,$18,$18,$18 ;znak $86
          .db     $00,$00,$00,$FF,$FF,$18,$18,$18 ;znak $87
          .db     $18,$18,$18,$FF,$FF,$00,$00,$00 ;znak $88
          .db     $18,$18,$18,$FF,$FF,$18,$18,$18 ;znak $89
          .db     $AA,$55,$AA,$55,$AA,$55,$AA,$55 ;znak $8A
          .db     $AA,$00,$55,$00,$AA,$00,$55,$00 ;znak $8B
          .db     $00,$24,$6E,$4B,$53,$76,$24,$00 ;znak "Ś"
          .db     $55,$55,$AA,$AA,$55,$55,$AA,$AA ;znak $8D
          .db     $33,$CC,$33,$CC,$33,$CC,$33,$CC ;znak $8E
          .db     $00,$42,$66,$77,$5B,$4E,$46,$00 ;znak "Ź"
          .db     $55,$55,$55,$55,$55,$55,$55,$55 ;znak $90
          .db     $FF,$00,$FF,$00,$FF,$00,$FF,$00 ;znak $91
          .db     $FF,$55,$FF,$55,$FF,$55,$FF,$55 ;znak $92
          .db     $4A,$91,$24,$49,$92,$24,$89,$52 ;znak $93
          .db     $52,$89,$24,$92,$49,$24,$91,$4A ;znak $94
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $95
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $96
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $97
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $98
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $99
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $9A
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $9B
          .db     $00,$48,$5C,$56,$57,$75,$24,$00 ;znak "ś"
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $9D
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $9E
          .db     $00,$44,$64,$76,$5F,$4D,$44,$00 ;znak "ź"
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $A0
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $A1
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $A2
          .db     $00,$7F,$7F,$4C,$44,$40,$40,$00 ;znak "Ł"
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $A4
          .db     $00,$7C,$7E,$13,$93,$FE,$7C,$00 ;znak "Ą"
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $A6
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $A7
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $A8
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $A9
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $AA
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $AB
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $AC
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $AD
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $AE
          .db     $00,$42,$62,$73,$5B,$4E,$46,$00 ;znak "Ż"
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $B0
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $B1
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $B2
          .db     $00,$00,$49,$7F,$7F,$44,$00,$00 ;znak "ł"
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $B4
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $B5
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $B6
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $B7
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $B8
          .db     $00,$20,$74,$54,$D4,$FC,$78,$00 ;znak "ą"
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $BA
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $BB
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $BC
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $BD
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $BE
          .db     $00,$44,$64,$75,$5D,$4C,$44,$00 ;znak "ż"
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $C0
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $C1
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $C2
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $C3
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $C4
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $C5
          .db     $00,$3C,$7E,$43,$43,$66,$24,$00 ;znak "Ć"
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $C7
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $C8
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $C9
          .db     $00,$7F,$7F,$49,$C9,$C9,$41,$00 ;znak "Ę"
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $CB
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $CC
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $CD
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $CE
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $CF
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $D0
          .db     $00,$7E,$7E,$0D,$19,$7E,$7E,$00 ;znak "Ń"
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $D2
          .db     $00,$3C,$7E,$43,$43,$7E,$3C,$00 ;znak "Ó"
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $D4
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $D5
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $D6
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $D7
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $D8
          .db     $00,$44,$44,$5F,$5F,$44,$44,$00 ;znak $D9
          .db     $64,$74,$5C,$4C,$10,$7F,$7F,$04 ;znak $DA
          .db     $00,$48,$7E,$7F,$49,$43,$66,$20 ;znak $DB
          .db     $00,$14,$3E,$7F,$55,$55,$41,$22 ;znak $DC
          .db     $02,$4F,$60,$30,$18,$4C,$66,$F2 ;znak $DD
          .db     $02,$4F,$60,$30,$18,$8C,$D6,$B2 ;znak $DE
          .db     $11,$55,$6E,$30,$18,$4C,$66,$F2 ;znak $DF
          .db     $1C,$3E,$22,$36,$1C,$36,$22,$00 ;znak $E0
          .db     $00,$FE,$FE,$2A,$2A,$3E,$14,$00 ;znak $E1
          .db     $00,$7E,$7E,$02,$02,$06,$06,$00 ;znak $E2
          .db     $02,$7E,$7E,$02,$7E,$7E,$02,$00 ;znak $E3
          .db     $00,$63,$77,$5D,$49,$41,$63,$00 ;znak $E4
          .db     $80,$FE,$7E,$20,$3E,$3E,$20,$00 ;znak $E5
          .db     $00,$38,$7C,$46,$47,$6D,$28,$00 ;znak "ć"
          .db     $04,$06,$02,$7E,$7C,$06,$02,$00 ;znak $E7
          .db     $00,$99,$BD,$E7,$E7,$BD,$99,$00 ;znak $E8
          .db     $1C,$3E,$6B,$49,$6B,$3E,$1C,$00 ;znak $E9
          .db     $00,$38,$7C,$54,$D4,$DC,$18,$00 ;znak "ę"
          .db     $4C,$5E,$73,$01,$73,$5E,$4C,$00 ;znak $EB
          .db     $00,$30,$78,$4A,$4F,$7D,$39,$00 ;znak $EC
          .db     $18,$3C,$24,$3C,$3C,$24,$3C,$18 ;znak $ED
          .db     $00,$5C,$3E,$32,$2A,$26,$3E,$1D ;znak $EE
          .db     $00,$40,$51,$5B,$4E,$44,$40,$00 ;znak $EF
          .db     $00,$40,$44,$4E,$5B,$51,$40,$00 ;znak $F0
          .db     $00,$7C,$7C,$06,$07,$7D,$78,$00 ;znak "ń"
          .db     $00,$2A,$2A,$2A,$2A,$2A,$2A,$00 ;znak $F2
          .db     $00,$38,$7C,$46,$47,$7D,$38,$00 ;znak "ó"
          .db     $08,$1C,$36,$22,$08,$1C,$36,$22 ;znak $F4
          .db     $22,$36,$1C,$08,$22,$36,$1C,$08 ;znak $F5
          .db     $00,$08,$08,$6B,$6B,$08,$08,$00 ;znak $F6
          .db     $66,$33,$33,$66,$CC,$CC,$66,$00 ;znak $F7
          .db     $00,$06,$0F,$09,$0F,$06,$00,$00 ;znak $F8
          .db     $7E,$81,$99,$BD,$A5,$A5,$81,$7E ;znak $F9
          .db     $7E,$81,$BD,$95,$B5,$AD,$81,$7E ;znak $FA
          .db     $30,$60,$FF,$01,$01,$01,$01,$03 ;znak $FB
          .db     $00,$1F,$1F,$01,$1F,$1E,$00,$00 ;znak $FC
          .db     $00,$19,$1D,$17,$12,$00,$00,$00 ;znak $FD
          .db     $00,$00,$3C,$3C,$3C,$3C,$00,$00 ;znak $FE
          .db     $FF,$FF,$FF,$FF,$FF,$FF,$FF,$FF ;znak $FF
Tablica umieszczona w pamięci FLASH mikrokontrolera, która zawiera dane opisujące budowę 255
znaków czcionki o rozmiarze 8x8 pikseli (2040 bajtów). Każdy bajt to kolejna pionowa kolumna 8 pik-
seli znaku (najmłodszy bit to górny piksel kolumny).
Jeśli żadne specjalne/polskie znaki nie wystąpią w wyświetlanych ciągach tekstowych, to z danych
czcionki można usunąć ostatnie 129 znaków (wszystkie za tyldą "~", począwszy od znaku o kodzie
$7F). Dzięki temu użycie pamięci FLASH zmniejszy się o 1032 bajty.
Czcionka została stworzona w programie "Fony 1.4.7" i wyeksportowana do pliku binarnego, który
następnie został przekonwertowany na kod źródłowy asemblera AVR, specjalnym skryptem języka
VBS systemu Windows. Nieużywane znaki są całkowicie zapełnione (wszystkie bity mają wartość 1),
aby ich przypadkowe wyświetlenie było widoczne na OLED (ogranicza to również zużycie pamięci
FLASH mikrokontrolera).
.dseg                ;pamięć danych (SRAM)
.org      $0100
sbuf:     .byte   12 ;rezerwuje N-bajtów w pamięci SRAM
Dyrektywy rezerwują 12 bajtów w pamięci SRAM mikrokontrolera.

W przypadku wyświetlacza I2C z 4 pinami, nie ma możliwości zresetowania sterownika SSD1306
inaczej, niż przez wyłączenie i ponowne włączenie jego napięcia zasilania (pin RESET nie jest
wyprowadzony, a nie można tego zrobić programowo).
Sterownik SSD1306 posiada dużo więcej rozkazów i zaawansowanych funkcji (np. sprzętowe
przesuwanie zawartości ekranu - scrolling), które nie zostały tu opisane.