Asembler mikrokontrolerów AVR     Obsługa magistrali I2C         Kompilacja




Powyższy schemat przedstawia prawidłowy sposób podłączenia zewnętrznych elementów podczas
korzystania z modułu TWI (Two-Wire Interface), który całkowicie sprzętowo obsługuje magistralę I2C
i jest wbudowany w mikrokontroler ATmega48/88/168/328. Moduł ten ma dość rozbudowane funkcje
i wspiera: 7-bitowe adresy, szybkość magistrali do 400 kHz, arbitraż przy podłączeniu wielu nadzorców
(multi-master), możliwość pracy jako nadzorca/nadajnik (master/transmitter) lub urządzenie podrzędne/
odbiornik (slave/receiver) z dowolnie ustawianym adresem wywoławczym i obsługą adresu rozgłosze-
niowego.
Kondensator C1 filtruje napięcie zasilania Vcc=4.75-5.25V i powinien być umieszczony, jak najbliżej
mikrokontrolera. Rezystor R1 utrzymuje wysoki stan logiczny na pinie RESET, przez co zapobiega
przypadkowemu i samoistnemu resetowaniu się mikrokontrolera (np. podczas pracy w środowisku,
gdzie występują duże zakłócenia). Złącze CON1 służy do programowania mikrokontrolera w systemie
(ISP - In-System Programming) i zawiera sygnały rozmieszczone w standardowy sposób, zalecany
przez firmę Atmel. Programowanie w systemie oznacza możliwość zaprogramowania mikrokontrolera
w układzie elektronicznym, w którym pracuje bez potrzeby wyjmowania/wylutowywania go z niego.
Rezystory R2 i R3 podciągają linie PC4/SDA i PC5/SCL magistrali I2C do plusa napięcia zasilania
Vcc i są niezbędne do jej działania. Złącze CON2 służy do opcjonalnego podłączenia urządzenia do
magistrali I2C. Rezystor R4 ogranicza prąd diody LED1, która zaświeci się, jeśli wystąpi błąd trans-
misji (nieudane wysłanie warunku START lub brak odpowiedzi od urządzenia).
Rezonator kwarcowy X1 o częstotliwości 8 MHz taktuje pracę mikrokontrolera, dzięki czemu przebiegi
sygnałów SCL/SDA na magistrali I2C są dokładne i stabilne. Istnieje też możliwość taktowania mikro-
kontrolera jego wewnętrznym oscylatorem RC, o nominalnej częstotliwości 8 MHz. Zaletą tego rozwią-
zania jest to, że nie trzeba wtedy montować rezonatora X1 i kondensatorów C2/C3, ale dużą wadą jest
bardzo niedokładna i niestabilna częstotliwość pracy.
Aby niżej opisany program zadziałał, konieczne jest ustawienie odpowiednich wartości fuse/lock bitów.
Należy bardzo uważać, aby nie dokonać zmian w fuse/lock bitach, które uniemożliwią dalsze progra-
mowanie mikrokontrolera.
W przypadku, gdy mikrokontroler będzie taktowany rezonatorem kwarcowym X1: FL (Fuse Low) = $F7,
FH (Fuse High) = $DF ($D9 dla ATmega328), FE (Fuse Extended) = $F9 ($FF dla ATmega48/328),
LB (Lock Bits) = $FF. Takie ustawienia powodują, że mikrokontroler korzysta z zewnętrznego rezona-
tora kwarcowego (bity CKSEL3-0=0111); wyłączony jest dzielnik częstotliwości przez 8, co powoduje
taktowanie mikrokontrolera pełną częstotliwością rezonatora (CKDIV8=1); wydłużony jest czas jego
startu do ok. 65ms po włączeniu napięcia zasilania (SUT1-0=11); wyłączone jest generowanie często-
tliwości taktującej mikrokontroler na pinie PB0 (CKOUT=1); włączona jest możliwość jego resetowania
przez pin PC6 (RSTDISBL=1) oraz programowania przez interfejs szeregowy (SPIEN=0).
W przypadku, gdy mikrokontroler będzie taktowany wewnętrznym oscylatorem RC: FL (Fuse Low) =
$E2, FH (Fuse High) = $DF ($D9 dla ATmega328), FE (Fuse Extended) = $F9 ($FF dla ATmega48/
328), LB (Lock Bits) = $FF. Powodują one, że mikrokontroler korzysta z wewnętrznego oscylatora RC,
o nominalnej częstotliwości 8 MHz (bity CKSEL3-0=0010); wyłączony jest dzielnik częstotliwości przez
8, co powoduje taktowanie mikrokontrolera pełną częstotliwością oscylatora (CKDIV8=1); wydłużony
jest czas jego startu do ok. 65ms po włączeniu napięcia zasilania (SUT1-0=10); wyłączone jest gene-
rowanie częstotliwości taktującej mikrokontroler na pinie PB0 (CKOUT=1); włączona jest możliwość
jego resetowania przez pin PC6 (RSTDISBL=1) oraz programowania przez interfejs szeregowy
(SPIEN=0).

1. Opis magistrali
2. Transmisja danych
3. Program sterujący

1. Opis magistrali

Synchroniczna, dwukierunkowa magistrala szeregowa I2C (IIC - Inter-Integrated Circuit) została opracowana w 1982 ro-
ku przez firmę Philips (aktualnie NXP). Do października 2006 roku firmy musiały wykupić licencję na implementowanie
i używanie tego protokołu w swoich urządzeniach (nadal muszą płacić za przyznanie unikalnego adresu). Dlatego wiele
firm opracowało własny, kompatybilny standard i dzięki temu uniknęło opłat licencyjnych. Jedną z tych firm był Atmel
(aktualnie Microchip), który wprowadził nieznaczne zmiany do protokołu I2C i nazwał go TWI (Two-Wire Interface).
Magistrala I2C używa dwóch 2-kierunkowych linii: sygnału taktującego/zegarowego SCL (Serial CLock) i przesyłu dany-
ch SDA (Serial DAta). Umożliwia jednoczesne podłączenie do 127 różnych urządzeń podrzędnych (slave) - każde o uni-
kalnym 7-bitowym adresie. Pole 7-bitów zawiera 128 kombinacji, ale adres 0 jest wykorzystywany jako rozgłoszeniowy
i kieruje transmisję do wszystkich podłączonych urządzeń. Według specyfikacji 16 adresów (2 grupy po 8) jest zarezer-
wowanych i przeznaczonych do pełnienia specjalnych funkcji: %0000xxx oraz %1111xxx. Jeśli te funkcje nigdy nie będą
wykorzystywane na magistrali, to zarezerwowane adresy można przydzielić urządzeniom.
Urządzenie nadzorcze (master) wysyłające dane na magistralę to nadajnik (transmitter), a urządzenie podrzędne (slave)
odczytujące dane z magistrali to odbiornik (receiver). Nadzorca rozpoczyna/kończy transmisję danych i generuje sygnał
SCL podczas jej trwania. Do magistrali I2C może być podłączonych wielu nadzorców (multi-master). Wtedy tylko jeden
z nich może sterować transmisją w danym momencie. Łatwo wyobrazić sobie sytuację, np. gdy kilku nadzorców jedno-
cześnie rozpoczyna transmisję. Do wykrywania takich konfliktów służą techniki synchronizacji zegara (clock synchroniza-
tion) i arbitrażu (arbitration), które rozwiązują prawie wszystkie problemy mogące występować przy wielu nadzorcach.
Maksymalna szybkość przesyłu danych wynosi 100 kbit/s (standard mode, SCL=100 kHz), ale może być dowolnie niska
(np. 10 kbit/s low-speed mode). W nowszej/szybszej wersji standardu z 1992 roku, szybkość została zwiększona do 400
kbit/s (fast mode, SCL=400 kHz), a adres rozszerzony do 10-bitów (umożliwia podłączenie do 1008 urządzeń, 16 adre-
sów jest zarezerwowanych). Nowsza wersja jest całkowicie kompatybilna z oryginalną (adresowanie 10-bitowe nie zys-
kało dużej popularności).
W przesyłanych danych wyróżnia się: bajt zawierający 7-bitowy adres urządzenia (SLA) z bitem kierunku danych RW
(0 = zapis do urządzenia, 1 = odczyt), bajty rozkazów/danych oraz bit potwierdzenia ACK (ACKnowledge) po każdym
przesłanym bajcie. Dlatego każdy bajt zajmuje 9-bitów, a rzeczywista szybkość przesyłu użytecznych danych jest niższa,
niż ta podana w specyfikacji standardu i wynikająca z częstotliwości sygnału SCL.
Urządzenia na magistrali I2C mają wyjścia z otwartym kolektorem/drenem, dlatego linie SCL/SDA muszą posiadać po
jednym rezystorze podciągającym Rp (pull-up, zwykle 2.2-10k), podłączonym do plusa napięcia zasilania Vcc. Rezysto-
ry Rp wymuszają na tych liniach wysoki stan logiczny (H), gdy wyjścia wszystkich podłączonych urządzeń są w stanie wy-
sokiej impedancji (Hi-Z) - oznacza to, że magistrala nie jest używana przez żadne urządzenie. Linie SCL/SDA przyjmują
niski stan logiczny (L), gdy wyjście któregoś (jednego lub więcej) urządzenia ma taki stan - oznacza to, że trwa transmi-
sja i magistrala jest zajęta (busy). Jak widać magistrala I2C opiera swoje działanie na realizacji funkcji logicznej AND.
Każde z podłączonych urządzeń musi mieć włączone zasilanie i być sprawne, aby magistrala mogła działać. Jest to wa-
runek konieczny, ponieważ wewnętrzne obwody wyłączonego/uszkodzonego urządzenia mogą wymuszać niski stan lo-
giczny na liniach SCL/SDA (np. diody zabezpieczające na pinach mikrokontrolera), co uniemożliwia rozpoczęcie trans-
misji przez nadzorcę, który wykrywa zajętą magistralę.
Minimalną wartość rezystancji Rp można obliczyć ze wzoru: Rpmin = Vcc-Vol/Iol [Ohm], gdzie: Vcc - napięcie zasilania
magistrali [V], Vol = 0.4V - maksymalne napięcie wyjściowe niskiego stanu logicznego [V], Iol = 3mA - minimalny prąd
wyjściowy niskiego stanu logicznego [A]. Maksymalną wartość rezystancji Rp można obliczyć ze wzoru: Rpmax = Tr/
0.847298*Cb [Ohm], gdzie: Tr = 300/1000ns dla Fmax=400/100kHz - maksymalny czas narastania zbocza sygnału
na linii [s], Cb - całkowita pojemność linii [F]. Dla rezystora podciągającego Rp zalecana jest wartość rezystancji ze
środka zakresu Rpmin-Rpmax.
Liczba urządzeń, które można podłączyć do magistrali I2C jest ograniczona do 127 przez 7-bitowy adres oraz przez
maksymalną dopuszczalną, całkowitą pojemność linii SCL/SDA wynoszącą Cb=400pF (długość linii do kilku metrów).
Pojemność linii Cb i jej rezystancja podciągająca Rp tworzą filtr o stałej czasowej RC, który decyduje o minimalnym
czasie narastania zbocza sygnału, a więc o maksymalnej częstotliwości pracy Fmax linii (100/400kHz dla oryginalnej/
szybkiej wersji standardu). Im większa jest pojemność Cb, tym mniejsza musi być rezystancja podciągająca Rp, aby
zapewnić działanie linii przy danej częstotliwości Fmax.
Wartości wejściowych napięć Vil/Vih niskiego/wysokiego stanu logicznego na magistrali I2C, są zależne od wartości jej
napięcia zasilania Vcc (zwykle 3.3-5.0V) i wynoszą: Vil = 0.3*Vcc (maksymalnie), Vih = 0.7*Vcc (minimalnie). Niektóre
starsze urządzenia mają ustawione stałe wartości tych napięć: Vil = 1.5V, Vih = 3.0V.
Protokół I2C jest powszechnie stosowany do komunikacji z urządzeniami, które nie wymagają dużej szybkości trans-
misji danych na większe odległości. Można tu wymienić: pamięci EEPROM 24xxx/PCF85xxx, przetworniki ADC/DAC,
zegary czasu rzeczywistego RTC, czujniki temperatury/przyspieszenia/odległości/prędkości obrotowej wentylatorów,
wyświetlacze LCD/OLED, specjalizowane układy scalone w sprzęcie RTV (cyfrowe potencjometry, procesory audio/
wideo, dekodery).


2. Transmisja danych

Gdy do magistrali I2C jest podłączonych wielu nadzorców (multi-master), to przed rozpoczęciem transmisji każdy nad-
zorca (master) musi sprawdzić, czy magistrala jest zajęta (busy). W przypadku jednego nadzorcy nie jest to wymagane.
Jeśli magistrala nie jest używana (SCL=H, SDA=H), to nadzorca rozpoczyna transmisję wysyłając na magistralę waru-
nek START (SCL=H, SDA=H-L). Od tego momentu magistrala jest zajęta i żaden inny nadzorca nie może nią sterować.
Po wysłaniu warunku START, nadzorca zaczyna generować sygnał taktujący/zegarowy SCL, podczas trwania którego
wysyła minimum 1 bajt danych (START + $xx), który jest adresem urządzenia (SLA+RW).
Urządzenie (slave) podłączone do magistrali, odczytuje wartości kolejnych wysyłanych bitów (od najstarszego MSB do
najmłodszego LSB) ze stanu linii SDA (L=0, H=1), podczas trwania wysokiego stanu na linii SCL. Nadzorca może zm-
ieniać stan linii SDA tylko wtedy, gdy linia SCL ma niski stan logiczny. Po wysłaniu 8-bitów danych nadzorca uwalnia
linie SDA i SCL.
Jeśli w tym momencie urządzenie nie jest gotowe (np. potrzebuje więcej czasu na przetworzenie danych), to może
wstrzymać transmisję przez wymuszenie niskiego stanu na linii SCL (clock stretching). Wtedy nadzorca musi czekać
z wznowieniem transmisji do momentu, gdy urządzenie uwolni linię SCL (L-H). Takie wstrzymywanie transmisji jest
opcjonalne, a większość urządzeń go nie wykorzystuje.
Pojawia się 9 wysoki stan na linii SCL (9 cykl zegarowy), podczas trwania którego urządzenie potwierdza (ACK)/nie
potwierdza (NACK) odebranie 8-bitów danych, ustawiając niski (ACK=0)/wysoki (ACK=1) stan na linii SDA. Jest to
9-bit potwierdzenia ACK, którego stan logiczny urządzenie utrzymuje do momentu wystąpienia niskiego stanu na
linii SCL.
Pierwszy wysłany bajt zawiera 7-bitowy adres wywoławczy urządzenia (SLA), z którym nadzorca chce nawiązać komu-
nikację (najstarsze bity 7-1) oraz bit RW (najmłodszy bit 0), który określa czy nadzorca zapisuje (RW=0)/odczytuje (RW=
1) dane do/z urządzenia. Jeśli urządzenie o danym adresie jest dostępne na magistrali, to podczas trwania 9 wysokie-
go stanu na linii SCL (9 cykl zegarowy), ustawia niski stan na linii SDA potwierdzając swoją obecność (ACK=0).
Jeśli urządzenie nie potwierdzi swojej obecności (ACK=1), to nadzorca musi wysłać na magistralę warunek STOP
(SCL=H, SDA=L-H) kończący transmisję (START + $xx + STOP) lub powtórzony warunek START (Repeated START)
z innym adresem (START + $xx + RepSTART + $xx). Brak odpowiedzi od urządzenia (NACK - Not ACKnowledge)
może oznaczać, że urządzenie: nie jest podłączone do magistrali; nie jest gotowe na komunikację (wykonuje inne ope-
racje); podczas transmisji otrzymało niezrozumiałe rozkazy/dane; podczas transmisji nie może przyjąć więcej bajtów;
nadzorca-odbiornik sygnalizuje koniec transferu danych urządzeniu podrzędnemu-nadajnikowi.
Jeśli urządzenie potwierdzi swoją obecność (ACK=0), to nadzorca po wysłaniu jego adresu może od razu zakończyć
transmisję warunkiem STOP (START + $xx + STOP) lub wysyłać kolejne bajty danych, zakończone warunkiem STOP
(START + N*$xx + STOP). Może też wielokrotnie wysyłać sekwencje powtórzonego warunku START (Repeated STA-
RT) z minimum 1 bajtem danych, a następnie zakończyć warunkiem STOP (START + N*$xx + N*[RepSTART + N*$xx]
+ STOP).
Nie jest dozwolona transmisja, składająca się z samych warunków (START+START, START+STOP, STOP+START,
STOP+STOP). Powtórzony warunek START (Repeated START) od zwykłego warunku START różni się tylko tym, że
występuje przed pojawieniem się warunku STOP (gdy magistrala jest zajęta). Nadzorca za pomocą powtórzonego
warunku START, nawiązuje komunikację z nowym lub ponawia ją z tym samym urządzeniem, bez konieczności uwol-
nienia magistrali. Po wystąpieniu warunku STOP transmisja nadzorcy zostaje zakończona, a uwolniona magistrala
staje się dostępna dla innych nadzorców.

Dozwolona kolejność wysyłania warunków i danych (START / STOP - warunek, DATA - adres/bajt):
START + DATA; DATA + START, DATA + DATA, DATA + STOP; STOP + START.


3. Program sterujący

Poniżej znajduje się kod programu przeznaczonego dla mikrokontrolerów serii ATmega48/88/168/328,
który konfiguruje moduł TWI (Two-Wire Interface) i wysyła za jego pomocą dane do urządzenia podrzę-
dnego (slave) 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

;Inicjalizacja linii SCL/SDA (włączenie wewnętrznych rezystorów podciągających)
          ;sbi     PORTC,$05 ;ustawienie wysokiego stanu na linii SCL (pull-up)
          ;sbi     PORTC,$04 ;ustawienie wysokiego stanu na linii SDA (pull-up)

;Konfiguracja mikrokontrolera
          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

;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)
Teoretycznie można włączyć wewnętrzne rezystory podciągające (pull-up) mikrokontrolera na liniach
PC5/SCL i PC4/SDA magistrali I2C i użyć ich zamiast rezystorów zewnętrznych. Jednak sprawdzą się
one tylko w niewielu przypadkach, bo ich rezystancja (20-50k) jest zbyt duża. 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.
Poniższa tabela zawiera wybrane częstotliwości sygnału SCL przy danej częstotliwości taktowania
CLK mikrokontrolera oraz wartości rejestru TWBR (bity TWPS1-0 = 0).

CLK
[MHz]
TWBRSCL
[kHz]
1$0062.5
$0250
$0C25
$2A10
$F22
2$00125
$02100
$0C50
$5C10
$F24
3$00187.5
$02150
$07100
$1650
$F26
4$00250
$02200
$0C100
$2050
$F28
CLK
[MHz]
TWBRSCL
[kHz]
5$00312.5
$04208.3
$11100
$2A50
$F210
6$00375
$07200
$16100
$3450
$F212
7$01388.8
$09205.8
$1B100
$3E50
$F214
8$02400
$0C200
$20100
$4850
$F216
CLK
[MHz]
TWBRSCL
[kHz]
9$04375
$0E204.5
$25100
$5250
$F218
10$05384.6
$11200
$2A100
$5C50
$F220
11$06392.8
$13203.7
$2F100
$6650
$F222
12$07400
$16200
$34100
$7050
$F224
CLK
[MHz]
TWBRSCL
[kHz]
13$09382.3
$18203.1
$39100
$7A50
$FC25
14$0A388.8
$1B200
$3E100
$8450
$F228
15$0B394.7
$1D202.7
$43100
$8E50
$F230
16$0C400
$20200
$48100
$9850
$F232
CLK
[MHz]
TWBRSCL
[kHz]
17$0E386.3
$22202.3
$4D100
$A250
$F234
18$0F391.3
$25200
$52100
$AC50
$F236
19$10395.8
$27202.1
$57100
$B650
$F238
20$11400
$2A200
$5C100
$C050
$F240

Dla zapewnienia prawidłowej komunikacji, częstotliwość pracy sterownika/kontrolera (uC/CPU) magi-
strali I2C w urządzeniu podrzędnym (slave), powinna być minimum 16x wyższa od częstotliwości syg-
nału SCL.
;Poniższe procedury umożliwiają obsługę magistrali I2C i wysyłanie danych do urządzenia podrzędnego
;(slave) ze stałą szybkością, taktowaną zegarem o częstotliwości 100 kHz (standard mode). Magistrala
;I2C jest obsługiwana sprzętowo przez moduł TWI (Two-Wire Interface) z liniami podłączonymi w nastę-
;pujący sposób: SCL=PC5, SDA=PC4.

;Wysłanie warunku START na magistralę I2C
          rcall   TWIsta    ;rozpoczęcie transmisji I2C
;Sprawdzenie poprawności transmisji warunku START
          cpi     R16,$08   ;wartość po udanym wysłaniu warunku START
          brne    TWIerr0   ;skok, jeśli wystąpił błąd
;Wysłanie adresu urządzenia na magistralę I2C
          ldi     R16,$78   ;7-bitowy adres (najmłodszy bit RW określa kierunek danych:
                            ;0 = zapis do urządzenia, 1 = odczyt z urządzenia)
          rcall   TWIwri    ;wysłanie adresu (w kolejności od najstarszego do najmłodszego bitu)
;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 - urządzenie nie odpowiada (ACK=1)
;Wysłanie bajtu do urządzenia na magistrali I2C
          ldi     R16,$55   ;bajt danych
          rcall   TWIwri    ;wysłanie bajtu
;Sprawdzenie poprawności transmisji bajtu
          cpi     R16,$28   ;wartość po udanym wysłaniu bajtu z potwierdzeniem (ACK=0)
          brne    TWIerr    ;skok, jeśli wystąpił błąd - urządzenie nie odpowiada (ACK=1)
;Wysłanie warunku STOP na magistralę I2C
          rcall   TWIsto    ;zakończenie transmisji I2C

;Po wysłaniu warunku START musi wystąpić minimum 1 bajt danych (START + $xx), który jest adresem
;urządzenia. Jeśli urządzenie nie odpowiedziało na adres (ACK=1), to nadzorca musi wysłać warunek
;STOP kończący transmisję (START + $xx + STOP) lub powtórzony warunek START (Repeated START) z innym
;adresem (START + $xx + RepSTART + $xx). Jeśli urządzenie odpowiedziało (ACK=0), to nadzorca może
;zakończyć transmisję (START + $xx + STOP) lub wysyłać kolejne bajty danych, zakończone warunkiem STOP
;(START + N*$xx + STOP). Może też wielokrotnie wysyłać sekwencje powtórzonego warunku START (Repeated
;START) z minimum 1 bajtem danych, a następnie zakończyć warunkiem STOP (START + N*$xx + N*[RepSTART +
;N*$xx] + STOP). Nie jest dozwolona transmisja, składająca się z samych warunków (START+START, START+
;STOP, STOP+START, STOP+STOP).

;Główna pętla programu
Loop:     rjmp    Loop      ;zatrzymanie wykonywania programu

;Wysłanie warunku START/Repeated START na magistralę I2C
TWIsta:   ldi     R16,$A4   ;ustawienie bitów TWINT, TWSTA i TWEN
          rcall   TWIwri0   ;transmisja warunku START
          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)
          rjmp    Loop      ;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 urządzenia na magistrali I2C
TWIwri:   sts     $BB,R16   ;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
Po zakończeniu każdej operacji moduł TWI zwraca kod w 5-najstarszych bitach TWS7-3 rejestru
TWSR, który informuje o jej powodzeniu lub błędzie transmisji. Wartość tych bitów zostaje wyodrę-
bniona (bity TWPS1-0 są maskowane) i zapisana w rejestrze R16. Poniższa tabela zawiera wszystkie
możliwe wartości kodów, zwracanych przez moduł TWI.

 KOD OPIS
$00Błąd magistrali po wystąpieniu niedozwolonego warunku START/STOP.
$08Warunek START został wysłany.
$10Powtórzony warunek START (Repeated START) został wysłany.
$18Adres (SLA+W) został wysłany z potwierdzeniem (ACK=0).
$20Adres (SLA+W) został wysłany bez potwierdzenia (ACK=1).
$28Bajt został wysłany z potwierdzeniem (ACK=0).
$30Bajt został wysłany bez potwierdzenia (ACK=1).
$38Arbitraż został przegrany podczas wysyłania adresu/bajtu.
$F8  Moduł TWI nie wykonuje żadnej transmisji lub jest w jej trakcie (bit TWINT=0).  

Na magistralę I2C jest wysyłany warunek START - moduł TWI czeka, aż magistrala będzie dostępna
i dopiero wtedy wysyła warunek START. W tym czasie jest wykonywana pętla między etykietą "TWIwri1"
i instrukcją "breq TWIwri1". Po zakończeniu operacji wysyłania warunku START program sprawdza,
czy się to udało weryfikując kod zwrócony przez moduł TWI w rejestrze R16.
Jeśli zwrócony kod nie ma prawidłowej wartości, to warunek START nie został wysłany na magistralę.
Może to oznaczać awarię modułu TWI lub linii SCL/SDA. Wtedy na wyjściu PB1 jest ustawiany niski
stan logiczny, co powoduje zaświecenie czerwonej diody LED1 sygnalizującej wystąpienie błędu,
a wykonywanie programu zostaje zatrzymane w głównej pętli.
Jeśli warunek START został wysłany, to rozpoczęła się transmisja nadzorcy i następnie na magistralę
jest wysyłany adres urządzenia podrzędnego. W tym przypadku jest to adres $78, należący np. do
modułu monochromatycznego wyświetlacza OLED o rozdzielczości 128x32 pikseli.
Jeśli urządzenie o takim adresie nie jest podłączone do magistrali (złącze CON2), to po jego wysłaniu
nie odpowie bitem potwierdzenia (ACK=1). Wtedy transmisja zostaje zakończona wysłaniem warunku
STOP, dioda LED1 zostaje zaświecona, a program zatrzymany w głównej pętli. Przebiegi czasowe
sygnałów SCL/SDA w takiej sytuacji, są przedstawione na poniższym rysunku.



Jeśli urządzenie o takim adresie jest podłączone do magistrali, to po jego wysłaniu rozpozna go
i odpowie bitem potwierdzenia (ACK=0). Następnie do urządzenia jest wysyłany bajt $55, na który
powinno ono odpowiedzieć bitem potwierdzenia (ACK=0). Wtedy transmisja zostaje zakończona
wysłaniem warunku STOP, dioda LED1 pozostaje zgaszona, a program zatrzymany w głównej pętli.
Przebiegi czasowe sygnałów SCL/SDA w takiej sytuacji, są przedstawione na poniższym rysunku.



Jeśli z jakiegoś powodu urządzenie nie odpowie bitem potwierdzenia (ACK=1) na wysłany bajt, to
wtedy transmisja zostaje zakończona wysłaniem warunku STOP, dioda LED1 zostaje zaświecona,
a program zatrzymany w głównej pętli.
Jak łatwo zauważyć dioda LED1 zostanie zaświecona zawsze, gdy wystąpi błąd transmisji: nieudane
wysłanie warunku START lub brak odpowiedzi urządzenia na wysłany adres/bajt. Wtedy zostanie też
wysłany warunek STOP, oprócz błędu związanego z nieudanym wysłaniem warunku START. Dioda
LED1 pozostanie zgaszona, gdy transmisja adresu i bajtu do urządzenia powiodła się, ale również gdy
transmisja w ogóle się nie rozpoczęła, bo magistrala jest zajęta i moduł TWI czeka, aż stanie się dostę-
pna (dopiero po wystąpieniu warunku STOP).

Po zakończeniu operacji wysyłania warunku START/Repeated START lub adresu/bajtu, najstarszy bit
TWINT w rejestrze TWCR zostaje ustawiony (TWINT=1). Natomiast po zakończeniu operacji wysyłania
warunku STOP, bit TWINT pozostaje wyzerowany (TWINT=0). Zmienia się jedynie bit TWSTO, który
zostaje wyzerowany (TWSTO=0), co pozwala wykryć moment wysłania warunku STOP na magistralę
i zakończenia transmisji.

Przedstawione przebiegi czasowe sygnałów SCL/SDA, zostały zarejestrowane analizatorem logicznym
Kingst LA2016 z próbkowaniem o częstotliwości 200 MHz. Przebiegi znajdują się też w archiwum, jako
pliki "TWI ACK=0.kvdat" i "TWI ACK=1.kvdat" w formacie programu sterującego "Kingst VIS 3.5.4".

Na przebiegach czasowych sygnału SDA, są widoczne krótkie impulsy (szpilki) wysokiego stanu logi-
cznego o szerokości ok. 400ns, które występują ok. 200ns po zaniku wysokiego stanu w 9 cyklu zega-
rowym sygnału SCL. Jest to normalne zjawisko wynikające ze sposobu działania modułu TWI, które nie
zaburza pracy magistrali (stan linii SDA zmienia się, gdy linia SCL ma stan niski, co jest dozwolone).