Skoro już koniecznie chcesz to zrobić programowo, to proponuję na początek zmianę podejścia. Magistrala I2C jest magistralą typu wired-and. Oznacza to, że stan wysoki wymuszają rezystory podciągające, a tylko stan niski jest wymuszany przez tranzystor na wyjściu mikrokontrolera. Zastanów się, co będzie w sytuacji, gdy pamięć chce wysłać bit potwierdzenia (oznacza to zwarcie linii SDA do masy w 9 takcie zegara) w momencie, gdy wyjście mikrokontrolera wymusza stan wysoki.
Dla ułatwienia pisania programu proponowałbym zastosować zewnętrzne rezystory podciągające do Vcc na liniach SDA i SCL (wykorzystanie wewnętrznych pull-up'ów nieco skomplikuje sprawę). Jeśli ustawisz odpowiedni bit rejestru PORTB jako 0, to później manipulujesz tylko rejestrem DDRB. Gdy ustawisz za pomocą rejestru DDRB ten pin jako wejście, na odpowiedniej linii magistrali otrzymasz stan wysoki (poprzez rezystor), jak ustawisz jako wyjście - uzyskasz stan niski. W momencie wysyłania przez układ slave bitu potwierdzenia, powinieneś właśnie pozostawić pin odpowiadający linii SDA w stanie wysokiej impedancji (skonfigurowany jako wejście), aby układ slave mógł tę linię zewrzeć do masy.
Widzę, że coś tam próbowałeś zrobić (makra DATA_IN i DATA_OUT, chociaż obecnie masz je zakomentowane przy odczycie bitu potwierdzenia), ale chyba jednak nie tędy droga. Zwróć uwagę na taki fragment specyfikacji:
Cytat:
If a slave can’t receive or transmit another complete byte of data until it has performed some other function, for example servicing an internal interrupt, it can hold the clock line SCL LOW to force the master into a wait state.
W Twoim przypadku to bez znaczenia, ale podobnie jest z linią SDA w systemach multi-master. Z tego wniosek, że generalnie bezpieczniej nie używać pinów sterujących liniami SDA i SCL jako wyjścia w stanie wysokim.
Druga sprawa - popracuj jeszcze nad tym kodem. Nie analizowałem wszystkiego dokładnie, ale weźmy przykładowo funkcję
eeprom_sbyte():
Zaloguj się, aby zobaczyć kod
Iterator
b pętli
for będzie miał w pierwszym przebiegu pętli wartość 16-bitową (w systemie dwójkowym) 0000 0001 0000 0000. Jak myślisz, co się stanie gdy wykonasz funkcję AND ze zmienną 8-bitową
byte?
Dlaczego przykładowo jedno opóźnienie jest uzależnione od warunku
if(byte & b)? Czy w przypadku, gdy dany bit jest zerem, to opóźnienia być nie musi? Być może przy zegarze 1MHz ten numer przejdzie (1 takt = 1us), ale jak zmienisz częstotliwość taktowania procesora, to raczej ten kod przestanie działać.
Więcej już nie sprawdzałem. Proponuję jeszcze raz poczytać
specyfikację magistrali, i krok po kroku przeanalizować program zwracając uwagę na opóźnienia w odpowiednich miejscach (rysunek 31 na stronie 33 i tabela 5 na stronie 32). Chyba, że ten kod ma być tylko dla treningu i będzie działał tylko przy częstotliwości taktowania 1MHz, wtedy jest szansa, że niektóre potrzebne opóźnienia "zrobią się same" pod wpływem wykonywanego programu. Przykładowo takie konstrukcje
CLK_OFF; i
DATA_OFF; następujące zaraz po sobie przy taktowaniu 16MHz to już raczej nie przejdą.