logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Komunikacja na I2C ATtiny2313 z EEPROMem AT24C16

Mikon_603 11 Maj 2009 18:51 4005 15
REKLAMA
  • #1 6517462
    Mikon_603
    Poziom 11  
    Witam.
    Mam dość spory problem nad którym głowie się już od dłuższego czasu i bez żadnych wyników. Mianowicie chciałbym się połączyć z ATtiny2313 za pomocą I2C z EEPROMem AT24C16. Jest to częś grubszego projektu wyświetlacza diodowego. Schemat projektu jest w załączniku.

    Poczytałem sobie trochę w książkach o AVRach i Internecie. Przejrzałem noty katalogowe obu elementów. Z nabytą wiedzą zasiadłem do pisania programu. Muszę przyznać, że szło topornie, bo dopiero poznaję język C. Ale coś wydukałem. Efekty mojej pracy jednak nie działają. Kod wklejam poniżej. Jeżeli ktoś mógłby sprawdzić i ewentualnie pomóc to byłbym wdzięczny.

    
    #include <avr/io.h>
    
    unsigned char word = 0x01;            // Adres komórki pamięci
    unsigned char dane = 0x7A;            // Dane
    void main(void)
    {
    	while(1)
    	{
    	unsigned char dioda;
    	long int j;
    
    	bitstartu();  
    	zapis_I2C(word, dane);
    	bitstopu();
    
    	bitstartu();
    	dioda = odczyt_I2C(word);
    	bitstopu();
    	word++;
    	
    	for(j=0; j<1000; j++)   // pętla uruchamiająca rejestr przesuwny 74HC595
    	{
    		DDRD = 0x3F;     // Porty D jako wyjścia
    		DDRB = 0x7F;     // Porty B jako wyjścia
    		PORTB = dioda;    // ustawić powinno wartość 0x7A
    		PORTD = 0x14;    // Narastanie zbocza zegara SCK i sygnał na SI; wrzuca 1 a nastepnie ja przesuwa
    		PORTD = 0x24;    // Opadanie zbocza zegara SCK, narastanie zbocza RCK i sygnał na 1
    		PORTD = 0x14;    
    		PORTD = 0x24;
    		PORTD = 0x14;
    		PORTD = 0x24;
    		PORTD = 0x14;
    		PORTD = 0x24;
    		PORTD = 0x14;
    		PORTD = 0x24;
    		PORTD = 0x14;
    		PORTD = 0x24;
    		PORTD = 0x14;
    		PORTD = 0x24;
    	}
    	}
    }
    
    void bitstartu(void)
    {
    	DDRB = 0xFF;           
    	PORTB = 0x00;
    	czekaj_I2C(10);
    	DDRD = 0xFF;
    	PORTD = 0x00;
    	czekaj_I2C(10);
    	DDRB = 0xFF;
    	PORTB = 0x80;
    	czekaj_I2C(10);
    	DDRD = 0xFF;
    	PORTD = 0x40;
    	czekaj_I2C(10);
    }
    
    void bitstopu(void)
    {
    	DDRB = 0xFF;
    	PORTB = 0x80;
    	czekaj_I2C(10);
    	DDRD = 0xFF;
    	PORTD = 0x00;
    	czekaj_I2C(10);		
    	DDRB = 0xFF;
    	PORTB = 0x00;
    	czekaj_I2C(10);	
    }
    
    void zapis_I2C(unsigned char word, unsigned char dane)
    {
    	unsigned char adr_slave;
    	unsigned char adr_word;
    	unsigned char dana;
    
    	DDRB = 0xFF;
    	DDRD = 0xFF;
    	adr_slave = 0xA2;
    	przesyl(adr_slave);
    	czekaj_I2C(10);
    	if (PINB&0x80)
    		return;
    	adr_word = word;
    	przesyl(adr_word);
    	czekaj_I2C(10);
    	if (PINB&0x80)
    		return;
    	dana = dane;
    	przesyl(dana);
    	czekaj_I2C;
    	if (PINB&0x80)
    		return;
    }
    
    void odczyt_I2C(unsigned char word)
    {
    	unsigned char adr_slave;
    	unsigned char adr_word;
    	unsigned char dana;
    	unsigned char zapal;
    
    	DDRB = 0xFF;
    	DDRD = 0xFF;
    	adr_slave = 0xA2;
    	przesyl(adr_slave);
    	czekaj_I2C(10);
    	if (PINB&0x80)
    		return;
    	adr_word = word;
    	przesyl(adr_word);
    	czekaj_I2C(10);
    	if (PINB&0x80)
    		return;
    	bitstartu();
    	adr_slave = 0xA3;
    	przesyl(adr_slave);
    	czekaj_I2C(10);
    	if (PINB&0x80)
    		return;
    	zapal = przechwyt();
    	czekaj_I2C(10);
    	PORTB = 0x00;
    	return zapal;
    }
    
    void przesyl(unsigned char adress)
    {
    	unsigned char i;
    	unsigned char adress_pom;
    	for(i=0;i<8;i++)
    	{
    		adress_pom = adress;
    		adress &= 0x80;
    		if (adress==0x80)
    			{
    				PORTB = 0x80;
    				czekaj_I2C(10);
    				PORTD = 0x08;
    				czekaj_I2C(10);
    				PORTD = 0x00;
    				czekaj_I2C(10);
    			}
    		PORTB = 0x00;
    		czekaj_I2C(10);
    		PORTD = 0x08;
    		czekaj_I2C(10);
    		PORTD = 0x00;
    		czekaj_I2C(10);
    		adress = adress_pom;
    		adress<<=1;
    	}
    }
    
    void przechwyt(void)
    {
    	unsigned char diody;
    	unsigned char i;
    
    	DDRB = 0x00;
    	DDRD = 0xFF;
    	for(i=0;i<8;i++)
    	{
    		PORTD = 0x08;
    		czekaj_I2C(10);
    		if (PINB&0x80)
    		{
    			diody |= 0x01;
    			diody <<= 1;
    		}
    		diody <<= 1;
    		PORTD = 0x00;
    		czekaj_I2C(10);
    	}
    	return diody;
    }
    
    void czekaj_I2C(unsigned char tauI2C)
    {
    	do
    	{
    		asm("nop");
    	}while(--tauI2C!=0);
    }
    
  • REKLAMA
  • #2 6522325
    Konto nie istnieje
    Konto nie istnieje  
  • #3 6522639
    sąsiadZPokojuObok
    Poziom 1  
    Proponuję zacząć poprawianie kodu od deklaracji funkcji przed funkcją main. Co dokładnie nie działa, jakieś opisy błędów?
  • REKLAMA
  • #4 6522758
    Mikon_603
    Poziom 11  
    sąsiadZPokojuObok napisał:
    Proponuję zacząć poprawianie kodu od deklaracji funkcji przed funkcją main. Co dokładnie nie działa, jakieś opisy błędów?


    Wszystko ładnie mi kompiluje. Pewnie błąd tkwi w samej idei programu i działanie na I2C. Czytelność kodu potaram się do jutra w miarę poprawić.
  • REKLAMA
  • #5 6525580
    Konto nie istnieje
    Konto nie istnieje  
  • #6 6526164
    Mikon_603
    Poziom 11  
    No cóż poprawiłęm program trochę na miarę moich umiejętności. Dodałem trochę komentarzy, a efekt wrzucam w tym poście. Pomoc by mi się naprawdę przydała. Po kompilacji nie ma błędów ani warningów.

    
    #include <avr/io.h>
    
    unsigned char word = 0x01;            // Adres komórki pamięci
    unsigned char dane = 0x7A;            // Dane wysyłane i odbierane z pamięci
    
    int bitstartu(void);
    int bitstopu(void);
    int zapis_I2C(unsigned char, unsigned char);
    unsigned char odczyt_I2C(unsigned char);
    int przesyl(unsigned char);
    unsigned char przechwyt(void);
    int czekaj_I2C(unsigned char);
    
    int main(void)
    {
    	while(1)
    	{
    	unsigned char dioda;              // Zmienna przechowująca odebrane dane
    	long int j;                       // Licznik do pętli
    
    	DDRB = 0xFF;
    	DDRD = 0xFF;
    	
    	bitstartu();                      // Bit inicjalizujący operację
    	zapis_I2C(word, dane);            // funkcja realizująca zapis do EEPROMu
    	bitstopu();                       // Bit kończący operację
    
    	bitstartu();
    	dioda = odczyt_I2C(word);         // funkcja realizująca odczyt z EEPROMu
    	bitstopu();
    	word++;                           // zwiększenie adresu komórko o 1
    	
    	for(j=0; j<1000; j++)   // pętla uruchamiająca rejestr przesuwny 74HC595
    	{
    		DDRD = 0x3F;     // Porty D jako wyjścia
    		DDRB = 0x7F;     // Porty B jako wyjścia
    		PORTB = dioda;    // ustawić powinno wartość 0x7A
    		PORTD = 0x14;    // Narastanie zbocza zegara SCK i sygnał na SI; wrzuca 1 a nastepnie ja przesuwa
    		PORTD = 0x24;    // Opadanie zbocza zegara SCK, narastanie zbocza RCK i sygnał na 1
    		PORTD = 0x14;    
    		PORTD = 0x24;
    		PORTD = 0x14;
    		PORTD = 0x24;
    		PORTD = 0x14;
    		PORTD = 0x24;
    		PORTD = 0x14;
    		PORTD = 0x24;
    		PORTD = 0x14;
    		PORTD = 0x24;
    		PORTD = 0x14;
    		PORTD = 0x24;
    	}
    	}
    	return 0;
    }
    
    int bitstartu(void)
    {
    	           
    	PORTB = 0x00;           
    	czekaj_I2C(10);
    	DDRD = 0xFF;
    	PORTD = 0x00;
    	czekaj_I2C(10);
    	PORTB = 0x80;
    	czekaj_I2C(10);
    	PORTD = 0x40;
    	czekaj_I2C(10);
    	return 0;
    }
    
    int bitstopu(void)
    {
    	DDRB = 0xFF;
    	PORTB = 0x80;
    	czekaj_I2C(10);
    	DDRD = 0xFF;
    	PORTD = 0x00;
    	czekaj_I2C(10);	
    	PORTB = 0x00;
    	czekaj_I2C(10);
    	return 0;	
    }
    
    int zapis_I2C(unsigned char word, unsigned char dane)
    {
    	unsigned char adr_slave;               // Adres urządzenia działającego w trybie SLAVE (EEPROM)
    	unsigned char adr_word;                // Adres komórki w pamięci EEPROM
    	unsigned char dana;                    // Dane do zapisu
    
    	DDRB = 0xFF;
    	DDRD = 0xFF;
    	adr_slave = 0xA1;                      // Przesłanie do EEPROMU sekwencji 10100001
    	przesyl(adr_slave);                    // Funkcja wystawiająca bity na PORTy
    	czekaj_I2C(10);                        // Opóźnienie
    	if (PINB&0x80)                         // Sprawdzenie bitu ACK, jeżeli ustawiony na "1' to kończy operację
    		return;
    	adr_word = word;                       
    	przesyl(adr_word);                     // Przesłanie adresu komórki pamięci do której będzie wszystko zapisywane
    	czekaj_I2C(10);
    	if (PINB&0x80)                         // Sprawdzanie bitu ACK
    		return;
    	dana = dane;                           // Przesłanie danych do komórki pamięci
    	przesyl(dana);
    	czekaj_I2C(10);
    	if (PINB&0x80)
    		return 0;
    }
    
    unsigned char odczyt_I2C(unsigned char word)
    {
    	unsigned char adr_slave;                // Adres urządzenia działającego w trybie SLAVE (EEPROM)
    	unsigned char adr_word;                 // Adres komórki w pamięci
    	unsigned char zapal;                    // Zmienna przechowująca dane z pamięci
    
    	DDRB = 0xFF;
    	DDRD = 0xFF;
    	adr_slave = 0xA1;                       // Przesłanie do EEPROMU sekwencji 10100001
    	przesyl(adr_slave);                     // Funkcja wystawiająca bity na PORTy
    	czekaj_I2C(10);                         // Opóźnienie
    	DDRB = 0x00;                            // PORTy B jako wejście
    	if (PINB&0x80)                          // Sprawdzenie bitu ACK
    		return;
    	DDRB = 0xFF;
    	adr_word = word;                        // Adres komórki pamięci
    	przesyl(adr_word);                      
    	czekaj_I2C(10);
    	DDRB = 0x00;
    	if (PINB&0x80)                          // Sprawdzenie ACK
    		return;
    	DDRB = 0xFF;
    	bitstartu();                            // Bitstartu potrzebny do zasygnalizowania odbierania danych
    	adr_slave = 0xA1;                       
    	przesyl(adr_slave);
    	czekaj_I2C(10);
    	if (PINB&0x80)
    		return;
    	zapal = przechwyt();                    // Funkcja odbierająca dane
    	czekaj_I2C(10);
    	PORTB = 0x00;
    	return zapal;
    }
    
    int przesyl(unsigned char adress)
    {
    	unsigned char i;
    	unsigned char adress_pom;
    	for(i=0;i<8;i++)
    	{
    		adress_pom = adress;
    		adress &= 0x80;
    		if (adress==0x80)
    			{
    				PORTB = 0x80;
    				czekaj_I2C(10);
    				PORTD = 0x08;
    				czekaj_I2C(10);
    				PORTD = 0x00;
    				czekaj_I2C(10);
    			}
    		PORTB = 0x00;
    		czekaj_I2C(10);
    		PORTD = 0x08;
    		czekaj_I2C(10);
    		PORTD = 0x00;
    		czekaj_I2C(10);
    		adress = adress_pom;
    		adress<<=1;
    	}
    	return 0;
    }
    
    unsigned char przechwyt(void)
    {
    	unsigned char diody;
    	unsigned char i;
    
    	DDRB = 0x00;
    	DDRD = 0xFF;
    	for(i=0;i<8;i++)
    	{
    		PORTD = 0x08;
    		czekaj_I2C(10);
    		if (PINB&0x80)
    		{
    			diody |= 0x01;
    			diody <<= 1;
    		}
    		diody <<= 1;
    		PORTD = 0x00;
    		czekaj_I2C(10);
    	}
    	return diody;
    }
    
    int czekaj_I2C(unsigned char tauI2C)
    {
    	do
    	{
    		asm("nop");
    	}while(--tauI2C!=0);
    	return 0;
    }
    
  • REKLAMA
  • #7 6526954
    Dr.Vee
    VIP Zasłużony dla elektroda
    Przecież ten projekt jest do bani... linie SDA i SCL powinny mieć rezystory podciągające do Vcc, a nie rezystory szeregowe. Poza tym ten procesor ma sprzętową obsługę I2C przez moduł USI - zobacz datasheet.

    Tak naprawdę to powinieneś mieć domyślnie SDA i SCL w trybie wejścia (PORTx = 0 i DDRx = 0), przy nadawaniu 0 powinieneś włączać DDRx = 1, przy nadawaniu 1 DDRx = 0 i rezystor podciągający wymusza stan wysoki na linii.

    Jeśli już musisz implementować I2C programowo, to zamiast zapisywać dane do całego portu na raz wystarczy zmieniać jeden bit:
    #define set_sda_high (DDRB &= ~_BV(7))
    #define set_sda_low  (DDRB |= _BV(7))
    #define set_sck_high (DDRD &= ~_BV(6))
    #define set_sck_low  (DDRD |= _BV(6))
    #define is_sda_high  (PINB & _BV(7))
    #define is_scl_high  (PIND & _BV(6))
    #define is_sda_low   (!is_sda_high)
    #define is_scl_low   (!is_scl_high)
    Pozdrawiam,
    Dr.Vee
  • #8 6527454
    Mikon_603
    Poziom 11  
    Rzeczywiście ATtiny ma możliwość sprzętowej obsługi I2C, co próbuje już ogarnąć od kilku dni. Myślałem, że może jednak w wersji programowej będzie łatwiej.
    Zastanawiem się, czy te inne podłączenie rezystorów może mieć jakiś wpływ na to, że nie ma komunikacji, ale wydaje mi się, że chyba nie powinno nic szkodzić projektowi.
    Ten pomysł ze zmianą jednego bitu i definiowanie tego jest naprawdę świetny. Dzięki. Zaraz zabieram się do poprawy kodu.
  • #9 6643241
    Mikon_603
    Poziom 11  
    No cóż. Posiedziałem nad tematem trochę dłużej. Przeanalizowałem noty katalogowe i poszperałem w Internecie. Postanowiłem też program napisać od podstaw. Jest on czytelniejszy. Jednak nic to nie dało. Mam pewność, że EEPROM jest dobry, bo zaprogramowałem goo w BASCOMIE. Nadal jednak proszę o pomoc. Poniżej zamieszczam kod programu.

    #include <avr/io.h>
    
    //----------------/ USTAWIENIE PORTÓW /------------------------
    
    #define I2CDirD DDRD            // Ustawia Porty D na we/wyj
    #define I2COutD PORTD           // Porty D jako wyjście
    #define I2CInD  PIND            // Porty D jako wejście
    
    #define I2CDirB DDRB            // Ustawia porty B na we/wyj
    #define I2COutB PORTB           // Porty B jako wyjście
    #define I2CInB  PINB            // Porty B jako wejście
    
    //---------/ DEFINICJA NUMERÓW PORTÓW SDA I SCL /--------------
    
    #define SDA 7                   // Numer Portu pod którym jest we/wyj SDA
    #define SCL 6                   // Numer portu pod którym jest we/wyj SCL
    
    //-------------/ NADAWANIE SYGNAŁU NA SDA /--------------------
    
    #define SET_SDA I2COutB |= (1 << SDA)       // Ustawianie wysokiego poziomu na SDA
    #define CLR_SDA I2COutB &= ~(1 << SDA)      // Ustawianie niskiego poziomu na SDA
    
    //-------------/ NADAWANIE SYGNAŁU NA SCL /--------------------
    
    #define SET_SCL I2COutD |= (1 << SCL)       // Ustawianie wysokiego poziomu na SCL
    #define CLR_SCL I2COutD &= ~(1 << SCL)      // Ustawianie niskiego poziomu na SCL
    
    //-------------/ USTAWIANIE SDA JAKO WE/WYJ /------------------
    
    #define SDA_OUT I2CDirB |= (1 << SDA)        // Ustawienie SDA jako wyjście
    #define SDA_IN  I2CDirB &= ~(1 << SDA)       // Ustawienie SDA jako wejście
    
    //-------------/ USTAWIENIE SCA JAKO WE/WYJ /------------------
    
    #define SCL_OUT I2CDirD |= (1 << SCL)        // Ustawienie SCL jako wyjście
    #define SCL_IN  I2CDirD &= ~(1 << SCL)       // Ustawienie SCL jako wejście
    
    //-------------/ POBIERANIE BITU Z PAMIĘCI /-------------------
    
    #define GET_SDA (I2CInB & (1 << SDA))
    
    //--------------------/ OPÓŹNIENIE /--------------------------
    
    void czekaj_I2C(void)
    {
    char i;
    
    for(i=10;i>0;i--)
    {
    asm("nop");
    }
    }
    
    //-------------/ SPRAWDZENIE BITU ACK /------------------------
    
    unsigned char bitack(void)
    {
    	unsigned char ack;
    	SDA_IN;
    	czekaj_I2C();
    	ack = GET_SDA;
    	return ack;
    }
    
    
    
    //------------/ BIT STARTU NA MAGISTRALI I2C /-----------------
    void i2cstart(void)
    {
    SDA_OUT;                 //
    SCL_OUT;                 // Ustawienie linii SDA i SCL w tryb wyjściowy
    SET_SDA;                 //
    SET_SCL;                 // Ustawienie na liniach SDA i SCL stanu wysokiego
    czekaj_I2C();            // Opóźnienie
    CLR_SDA; 
    czekaj_I2C();
    CLR_SCL;
    }
    
    //------------/ BIT STOPU NA MAGISTRALI I2C /-----------------
    
    void i2cstop(void)
    {                 //        SCL       SDA
    CLR_SDA;          //        |         |
    czekaj_I2C();          //        |         |
    SET_SCL;          //         \        |
    czekaj_I2C();          //          |       |
    SET_SDA;          //          |        
    czekaj_I2C();          //          |         |
    }
    
    //------------/ FUNKCJA WYSYŁAJĄCA BAJT NA SZYNE I2C/---------
    
    void zapis_i2c(unsigned char x)
    {
    unsigned char count = 8;           // Licznik
    do
    {
    SDA_OUT;
    CLR_SCL;                           // Ustaw "0" na zegarze
    if(x & 0x80) SET_SDA;              // Jeżeli w danych jest "1" to ustaw SDA w wysoki poziom
    else CLR_SDA;                      // Jeżeli w danych jest "0" to ustaw SDA w niski poziom
    x <<= 1;                           // Przesuwanie bitów danych o jeden w lewo
    czekaj_I2C();                      // Opóźnienie
    SET_SCL;                           // Ustaw "1" na zegarze 
    czekaj_I2C();
    }while(--count);
    CLR_SCL;
    CLR_SDA;
    }
    
    //------------/ FUNKCJA ODCZYTUJĄCA BAJT Z SZYNY I2C/---------
    
    unsigned char odczyt_i2c()
    {
    unsigned char count = 8, bufor = 0;          // Ustawia licznik i bufor danych
    
    SET_SDA;                                     // Ustawia "1' na PORT B
    SDA_IN;                                      // Ustawia PORT B na wejście
    do
    {
    czekaj_I2C();                                // Opóźnienie
    SET_SCL;                                     // Ustawia "1" na PORT D
    czekaj_I2C();
    bufor <<= 1;                                 // Przesuwa dane buforu o 1
    if(GET_SDA) bufor++;                        // Jeżeli na szynie jest odbierana "1" to inkrementuje bufor
    CLR_SCL;                                     // Ustawia "0" na PORT D
    } while (--count);
    czekaj_I2C();                                // Opóźnienie
    SET_SCL;                                     
    czekaj_I2C();
    SET_SDA;
    CLR_SCL;
    return (bufor);                              // Zwraca wartość z pamięci
    }
    
    //-------------------/ GŁÓWNA FUNKCJA /-----------------------
    
    int main(void)
    {
    
    unsigned char diody;
    int j;
    unsigned char ack;
    
    i2cstart();                 // Inicjalizacja magistrali I2C
    ack = bitack();
    if(ack)          // Sprawdzenie bitu ACK
    	return 0;
    zapis_i2c(160);             // Adres urządzenia + bit zapisu (10100000)
    ack = bitack();
    if(ack)
    	return 0;
    zapis_i2c(1);               // Adres komórki
    ack = bitack();
    if(ack)
    	return 0;
    zapis_i2c(48);              // Wysyłanie danej 1001000 = 48
    ack = bitack();
    if(ack)
    	return 0;
    i2cstop();
    
    i2cstart();
    ack = bitack();
    if(ack)
    	return 0;
    zapis_i2c(161);
    ack = bitack();
    if(ack)
    	return 0;
    zapis_i2c(1);
    ack = bitack();
    if(ack)
    	return 0;
    diody = odczyt_i2c();  // Odczyt danych 
    i2cstop();
    return 0;
    }


    Nie mam pojęcia co może być źle. :/ Sprawdzałem szybkość transmisji danych i na pewno nie przekracza 100000 bitów/s. Powiedziałbym raczej, że jest sporo wolniejszy.
  • Pomocny post
    #10 6643493
    zumek
    Poziom 39  
    Mikon_603 napisał:
    No cóż. Posiedziałem nad tematem trochę dłużej. Przeanalizowałem noty katalogowe i poszperałem w Internecie...

    
    i2cstart();                 // Inicjalizacja magistrali I2C
    ack = bitack();
    if(ack)          // Sprawdzenie bitu ACK
    	return 0;

    Już z powyższego fragmentu Twojego kodu można wywnioskować, że Twoje wysiłki poszły na marne i nadal nie rozumiesz "zasady działania"
    tej magistrali :|

    A teraz, zastanów się dlaczego :?:
  • #11 6644126
    Mikon_603
    Poziom 11  
    Coś mi się wydaje, że chodzi tu o brak zegara SCL i przy sprawdzaniu bitu ACK oraz o to, że przy wysokim bicie ACK powinien być bit stopu. Nie sestem jednak pewien. Program poprawiłem, ale nadal nie działa.

    Najpierw zmieniłem finkcję pobierania bitu ACK:

    unsigned char bitack(void)
    {
    	unsigned char ack;
    	SET_SCL;
    	SDA_IN;
    	ack = GET_SDA;
    	CLR_SCL;
    	return ack;
    }


    Następnie dodałem warunek bitu stopu przy ACK = 1:

    start:
    i2cstart();                 // Inicjalizacja magistrali I2C
    ack = bitack();
    if(ack)          // Sprawdzenie bitu ACK
    	{
    	i2cstop();
    	goto start;
    	}


    A także poprawiłem funkcję odczytu, gdzie mikrokontroler wysyłał jako ACK jedynke (teraz jest już zero):

    unsigned char odczyt_i2c()
    {
    unsigned char count = 8, bufor = 0;          // Ustawia licznik i bufor danych
    
    SET_SDA;                                     // Ustawia "1' na PORT B
    SDA_IN;                                      // Ustawia PORT B na wejście
    do
    {
    czekaj_I2C();                                // Opóźnienie
    SET_SCL;                                     // Ustawia "1" na PORT D
    czekaj_I2C();
    bufor <<= 1;                                 // Przesuwa dane buforu o 1
    if(GET_SDA) bufor++;                        // Jeżeli na szynie jest odbierana "1" to inkrementuje bufor
    CLR_SCL;                                     // Ustawia "0" na PORT D
    } while (--count);
    czekaj_I2C();                                // Opóźnienie
    CLR_SDA;
    SET_SCL;                                     
    czekaj_I2C();
    CLR_SCL;
    return (bufor);                              // Zwraca wartość z pamięci
    }


    W dobrym kierunku szukam błędu???
  • #12 6646456
    Mikon_603
    Poziom 11  
    Dzisiaj jeszcze skłoniłem się w kierunku usunięcia funkcji bitack(). Pomyślałem sobie, że może zbyt dużo czasu ona zajmuje i lepiej będzie sprawdzać bit ACK na bieżąco, więc wstawiłem tą część kodu do funkcji odczytującej i zapisującej. Jednak wciąż nic. :/ Prosiłbym jednak o nakierowanie mnie na mój błąd. Może powinienem napisać program od nowa z uwzględnieniem jakichś rzeczy??? Może podejść do sprawy łopatologicznie i rozrysować sobie diagram przebiegów, a następnie krok po kroku zapisywać na kod C. Jak zadziała to wtedy spróbować optymalizacji.
  • #13 6647165
    janbernat
    Poziom 38  
    Wpisz w googla 2313 usi master.
    To pomaga.

    Dodano po 1 [godziny] 2 [minuty]:

    Zwłaszcza strona avrfreaks.
  • #14 6839423
    Mikon_603
    Poziom 11  
    Witam wszystkich ponownie. Przez ostatnie tgodnie byłem zmuszony odłożyć zabawę z pamięcią i ATtiny, ale w końcu do tego powróciłem.
    Zmontowałem układzik na płytce wtykowej. Podłączyłem przez rezystorki 10kOhm do VCC piny SDA i SCL. Wgrałem na ATtiny ten programik:
    
    #include <avr/io.h>
    //----------------/ USTAWIENIE PORTÓW /------------------------
    
    #define I2CDirD DDRD            // Ustawia Porty D na we/wyj
    #define I2COutD PORTD           // Porty D jako wyjście
    #define I2CInD  PIND            // Porty D jako wejście
    
    #define I2CDirB DDRB            // Ustawia porty B na we/wyj
    #define I2COutB PORTB           // Porty B jako wyjście
    #define I2CInB  PINB            // Porty B jako wejście
    
    //---------/ DEFINICJA NUMERÓW PORTÓW SDA I SCL /--------------
    
    #define SDA 7                   // Numer Portu pod którym jest we/wyj SDA
    #define SCL 6                   // Numer portu pod którym jest we/wyj SCL
    
    //-------------/ NADAWANIE SYGNAŁU NA SDA /--------------------
    
    #define SET_SDA I2COutB |= (1 << SDA)       // Ustawianie wysokiego poziomu na SDA
    #define CLR_SDA I2COutB &= ~(1 << SDA)      // Ustawianie niskiego poziomu na SDA
    
    //-------------/ NADAWANIE SYGNAŁU NA SCL /--------------------
    
    #define SET_SCL I2COutD |= (1 << SCL)       // Ustawianie wysokiego poziomu na SCL
    #define CLR_SCL I2COutD &= ~(1 << SCL)      // Ustawianie niskiego poziomu na SCL
    
    //-------------/ USTAWIANIE SDA JAKO WE/WYJ /------------------
    
    #define SDA_OUT I2CDirB |= (1 << SDA)        // Ustawienie SDA jako wyjście
    #define SDA_IN  I2CDirB &= ~(1 << SDA)       // Ustawienie SDA jako wejście
    
    //-------------/ USTAWIENIE SCA JAKO WE/WYJ /------------------
    
    #define SCL_OUT I2CDirD |= (1 << SCL)        // Ustawienie SCL jako wyjście
    #define SCL_IN  I2CDirD &= ~(1 << SCL)       // Ustawienie SCL jako wejście
    
    //-------------/ POBIERANIE BITU Z PAMIĘCI /-------------------
    
    #define GET_SDA (I2CInB & (1 << SDA))
    
    //--------------------/ OPÓŹNIENIE /--------------------------
    
    static void I2C_xdelay(void)            // Opóźnienie 10us, czyli dla prędkości przesyłu danych 100kb/sec.
    {
    	asm volatile(\
    		"delayus8_loop%=: \n\t"\
    		"dec %[ticks] \n\t"\
    		"brne delayus8_loop%= \n\t"\
    	:  :[ticks]"r"(50) );
    } 
    
    //------------/ BIT STARTU NA MAGISTRALI I2C /-----------------
    void i2cstart(void)
    {
    	SDA_OUT;                 //
    	I2C_xdelay();
    	SCL_OUT;                 // Ustawienie linii SDA i SCL w tryb wyjściowy
    	I2C_xdelay();
    	SET_SDA;                 //
    	I2C_xdelay();	
    	SET_SCL;                 // Ustawienie na liniach SDA i SCL stanu wysokiego
    	I2C_xdelay();            // Opóźnienie
    	CLR_SDA; 
    	I2C_xdelay();
    	CLR_SCL;
    }
    
    //------------/ BIT STOPU NA MAGISTRALI I2C /-----------------
    
    void i2cstop(void)
    {                 //        SCL       SDA
    	CLR_SDA;          //        |         |
    	I2C_xdelay();          //        |         |
    	SET_SCL;          //         \        |
    	I2C_xdelay();          //          |       |
    	SET_SDA;          //          |        
    	I2C_xdelay();          //          |         |
    }
    
    //------------/ FUNKCJA WYSYŁAJĄCA BAJT NA SZYNE I2C/---------
    
    char zapis_i2c(unsigned char x)
    {
    
    	unsigned char ack;
    	SDA_OUT;
    	for(char i=0;i<8;i++)
    	{
    		CLR_SCL;
    		if(x&0x80)
    		{
    			SET_SDA;
    			I2C_xdelay();
    			SET_SCL;
    			I2C_xdelay();
    			CLR_SCL;
    			I2C_xdelay();
    			CLR_SDA;
    		}
    		else
    		{
    			SET_SCL;
    			I2C_xdelay();
    			CLR_SCL;
    			I2C_xdelay();
    		}
    		x = x<<1;
    	}
    	SDA_IN;
    	I2C_xdelay();
    	SET_SCL;
    	I2C_xdelay();
    	ack = GET_SDA;
    	CLR_SCL;
    	CLR_SDA;
    	SDA_OUT;
    	if(ack)          // Sprawdzenie bitu ACK
    	{
    		return 1;
    	}
    	return 0;
    }
    
    //------------/ FUNKCJA ODCZYTUJĄCA BAJT Z SZYNY I2C/---------
    
    unsigned char odczyt_i2c()
    {
    unsigned char count = 8, bufor = 0;          // Ustawia licznik i bufor danych
    
    SDA_IN;                                      // Ustawia PORT B na wejście
    SET_SDA;                                     // Ustawia "1' na PORT B
    do
    {
    bufor = bufor << 1;                          // Przesuwa dane buforu o 1                                
    SET_SCL;                                     // Ustawia "1" na PORT D
    I2C_xdelay();                                // Opóźnienie
    if(GET_SDA) bufor++;                        // Jeżeli na szynie jest odbierana "1" to inkrementuje bufor
    CLR_SCL;                                     // Ustawia "0" na PORT D
    } while (--count);
    I2C_xdelay();                                // Opóźnienie
    SDA_OUT;
    CLR_SDA;
    SET_SCL;                                     
    I2C_xdelay();
    CLR_SCL;
    return (bufor);                              // Zwraca wartość z pamięci
    }
    
    //-------------------/ GŁÓWNA FUNKCJA /-----------------------
    
    int main(void)
    {
    
    	unsigned char diody = 0;
    	//int j;
    
    	i2cstart();                              // Inicjalizacja magistrali I2C
    
    	if(zapis_i2c(160) == 1)             // Adres urządzenia + bit zapisu (10100000)
    	{
    		I2CDirB = 0x7F;
    
    		I2COutB = 0xAA;
    		for(long int i=0; i<500000; i++)
    		{
    			asm("nop");
    		}
    		I2COutB = 0x00;
    		for(long int i=0; i<500000; i++)
    		{
    			asm("nop");
    		}
    	}
    
    	if(zapis_i2c(1) == 1)               // Adres komórki
    	{
    		I2CDirB = 0x7F;
    
    		I2COutB = 0xAA;
    		for(long int i=0; i<500000; i++)
    		{
    			asm("nop");
    		}
    			I2COutB = 0x00;
    			for(long int i=0; i<500000; i++)
    		{
    			asm("nop");
    		}
    	}
    
    	if(zapis_i2c(0x0F) == 1)            // Wysyłanie danej 00001111 16
    	{
    		I2CDirB = 0x7F;
    
    		I2COutB = 0xAA;
    		for(long int i=0; i<500000; i++)
    		{
    			asm("nop");
    		}
    		I2COutB = 0x00;
    		for(long int i=0; i<500000; i++)
    		{
    			asm("nop");
    		}
    	}
    i2cstop();
    
    i2cstart();
    	if(zapis_i2c(161) == 1)
    	{
    		I2CDirB = 0x7F;
    
    		I2COutB = 0xAA;
    		for(long int i=0; i<500000; i++)
    		{
    			asm("nop");
    		}
    		I2COutB = 0x00;
    		for(long int i=0; i<500000; i++)
    		{
    			asm("nop");
    		}
    	}
    	if(zapis_i2c(1) == 1)
    	{
    		I2CDirB = 0x7F;
    
    		I2COutB = 0xAA;
    		for(long int i=0; i<500000; i++)
    		{
    			asm("nop");
    		}
    		I2COutB = 0x00;
    		for(long int i=0; i<500000; i++)
    		{
    			asm("nop");
    		}
    	}
    	diody = odczyt_i2c();  // Odczyt danych 
    i2cstop();
    
    I2CDirB = 0x7F;
    
    I2COutB = diody;
    while(1)
    {
    asm("nop");
    }
    
    return 0;
    


    W wyniku tego dostałem taką sytuację:
    Przy 2 zapisach do pamięci otrzymałem ack = 1, a następnie na diody podpięte pod porty PB0...6 wysłało same 1.
    Zainteresowało mnie kiedy urządzenie EEPROM zgłasza problem i okazało się (po odpowiednim zakomentowaniu IFów), że podczas procedury odczytującej z pamięci, czyli po 2 sygnale START (4 i 5 IF). Nie wiem co to może być. Sam zapis danej przebiega niby dobrze. Ale jak wysyła 2 raz adres do urządzenia to to zwraca ACK =1. Może panowie będą wiedzieć o co w tym wszystkim chodzi. Wiem, że pewnie można by było z USI skorzystać tak jak podawaliści, ale ja chyba jestem za ciemny, żeby to zrozumieć. Za wszelką pomoc z góry dziękuję.
  • #15 6842646
    Mikon_603
    Poziom 11  
    DZisiaj się okazało, że zapisuje mi do komórek pamięci same 1. To znaczy jeżeli wyśle dane, które znajdują się na 4 młodszych bitach, np. 0x0F lub 0x03 to dostaje wartość 0x0F zawsze. Świecą się tylko 4 diody na porcie B. Natomiast jeżeli wyślę dane na 8 bitach, np. 0x11 lub 0x7E to zawsze dostaje wartość 0xFF. Czyli układ żyje, ale nie zapisuje, tudzież odczytuje danych jak trzeba.
  • #16 6847220
    Mikon_603
    Poziom 11  
    W końcu po kilkunastu tygodniach udało mi się przesłać po IIC bajt danych. Złapałem się na naprawdę głupim błędzie. Program mi już dawno działał. Zapisywał do pamięci, a przy odczycie wyskakiwał bit ack = 1, bo nie dawałem czasu EEPROMOWI na zapis danych tylko od razu wymagałem od niego kolejnej operacji. Zapomniałem całkiem o Time Write Cycle (około 10-15ms dla układów 24C16. Jak wstawiłem pętle to zaczęło działać. Dziękuję wszystkim za pomoc.
REKLAMA