Elektroda.pl
Elektroda.pl
X

Search our partners

Find the latest content on electronic components. Datasheets.com
Elektroda.pl
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

[ATMega8][TWI][24C128][c] I2C na przerwaniach + EEPROM

dondu 19 Dec 2010 02:40 8436 43
  • #1
    dondu
    Moderator on vacation ...
    Przyszedł czas na nauczenie się I2C na przykładzie zewnętrznego EEPROM-u 24C128N w wersji SMD:
    http://www.atmel.com/atmel/acrobat/doc0670.pdf

    Napisy na układzie:
    ATMEL018
    24C128N
    SI27 A

    Wylutowałem ją ze starej NOKIA 3210

    Schemacik układu:
    [ATMega8][TWI][24C128][c] I2C na przerwaniach + EEPROM

    Na początek chciałbym upewnić się że prawidłowo podłączyłem.
    Na razie nie dawałem rezystorów szeregowo pomiędzy ATMega8 a EEPROM ponieważ chciałbym oba układy zasilać 5V.

    I tutaj pierwszy problem:
    1. Jak rozpoznać czy to wersja układu na 5V czy na 3,6V?
    Telefon zasilany był akumulatorem 2,4V, a w dokumentacji pamięci pisze:



    Quote:
    Low-voltage and Standard-voltage Operation
    - 2.7 (VCC = 2.7V to 5.5V)
    - 1.8 (VCC = 1.8V to 3.6V


    Niestety nie znalazłem w dokumentacji informacji w jakiej wersji mam tę pamięć.

    2. Czy oznacza to, że w zależności jak zasilę układ tak pracuje na parametrach określonych w dokumentacji?

    3. Jeżeli tak, to jeżeli zasilę go 5V to rezystory szeregowe na SCL i SDA są niepotrzebne?




    Dodano po 54 [minuty]:

    Czekając na Wasze odpowiedzi na powyższe pytania, które otrzymam zapewne gdy wstaniecie w niedzielny poranek, zaczynam studiować część softwarową:
    http://www.nongnu.org/avr-libc/user-manual/group__twi__demo.html

    I tutaj pytanie:
    4. Czy <util/twi.h> to właściwa biblioteka którą powinienem stosować?
    [28-30.06.2022, targi] PowerUP EXPO 2022 - zasilanie w elektronice. Zarejestruj się za darmo
  • Helpful post
    #2
    skalsky5000
    Level 21  
    Jeśli uczysz się proponuje samemu napisać obsługę I2C wbrew pozorom jest to proste jak budowa cepa. TWI to odpowiednik I2c.
  • #3
    dondu
    Moderator on vacation ...
    OK, napiszę swoją, ale nadal nie wiem czy mam 3,6 czy 5V układ czy schemat jest ok itd. (pytania powyżej)?





    Dodano po 2 [godziny] 12 [minuty]:

    Czekając na odpowiedzi piszę pierwszą funkcję swojej biblioteki I2C: TWI_Init()

    Zakładam na razie transmisję z zegarem 100kHz.

    Code:

    #define F_CPU 12000000UL

    void TWI_Init(void){

       //Ustawiam prędkość transmisji 100kHz ze wzoru: Prędkość = F_CPU / (16 + 2 * TWBR * 4^TWPS)
       //czyli: 12MHz / (16 + 2 * 52 * 4^0) = 100kHz
       TWSR &=   ~((1<<TWPS1) | (1<<TWPS1));      // TWPS = 0
       TWBR =   52;                        // TWBR = 52

       //pozostałe ustawienia
       TWCR   |=   (1<<TWIE);   //włączam przerwania TWI
       TWCR   |=   (1<<TWEA);   //włączam bit potwierdzenia ACK
       
       //włączam TWI
       TWCR   |=   (1<<TWEN);
       
       //włączam przerwania
       sei();
    }



    I tutaj pytania:

    5. Czy powinienem włączać linie SCL i SDA jako wyjścia lub wejścia poprzez ustawieniea w DDRC?

    6. Czy coś powinienem jeszcze dołożyć do tej funkcji?







    Dodano po 1 [godziny] 19 [minuty]:

    Teraz czas na procedurę obsługi przerwania. Na początek mam problem ze zrozumieniem kiedy TWINT jest 0 a kiedy 1.


    Dokumentacja ATMega8 str. 171 wrote:
     Bit 7 – TWINT: TWI Interrupt Flag
    This bit is set by hardware when the TWI has finished its current job and expects application
    software response.

    Czyli TWINT ustawiany sprzętowo na 1 po zakończeniu aktualnej operacji na TWI.


    Dokumentacja ATMega8 str. 171 wrote:
    The TWINT Flag must be cleared by software by writing a logic one to it.

    TWINT ma być zerowane programowo np. przez procedurę obsługi przerwania na samym jej końcu. Zerowanie ma być zrealizowane poprzez wpisanie 1 (!) - i tego nie rozumie - tam już jest jedynka :(

    7. Jak to jest z TWINT? Czy mam rozumieć, że TWINT ustawiany jest sprzętowo na 0 a nie na 1?
  • Helpful post
    #4
    tymon_x
    Level 30  
    dondu wrote:
    1. Jak rozpoznać czy to wersja układu na 5V czy na 3,6V?
    Nie ma tam jakiś charakterystycznych oznaczeń na obudowie? Ciąg literek i cyferek, żeby można było ustalić itp. Jeśli wylutowałeś to z telefonu, obstawiam na niższy zakres napięć.

    dondu wrote:
    TWINT ma być zerowane programowo np. przez procedurę obsługi przerwania na samym jej końcu. Zerowanie ma być zrealizowane poprzez wpisanie 1 (!) - i tego nie rozumie - tam już jest jedynka :(

    Z czego niby rejestr się składa, z przerzutników. Zakładam, że zastosowano na tym bicie przynajmniej fdc (D Flip-Flop with clear). Zerujesz wyjście Q[n] (stan n-bitu rejestru), wpisując 1 na C. Wejście D[n] jest wewnętrznym sygnałem. Ale to tylko najprostsza teoria z możliwych...
    dondu wrote:
    7. Jak to jest z TWINT? Czy mam rozumieć, że TWINT ustawiany jest sprzętowo na 0 a nie na 1?
    Patrz odpowiedź wyżej, sprzętowo ustawia na 1, zerujesz wpisując 1 (;
  • #5
    dondu
    Moderator on vacation ...
    tymon_x wrote:
    Nie ma tam jakiś charakterystycznych oznaczeń na obudowie? Ciąg literek i cyferek, żeby można było ustalić itp.

    Na początku napisałem co jest na obudowie. Literka A (jak sądzę) w prawym dolnym rogu oznacza zakres temperatury ponieważ znalazłem w dokumentacji informację, że B oznacza większy zakres temperatur więc przez analogię ... A mniejszy.
    Zostaje więć tajemniczy napis SI27 - czy to on sugeruje 2,7V? Ale jeżeli tak to oznaczałoby że to układ na wyższe napięcie bo:
    Quote:
    Low-voltage and Standard-voltage Operation
    - 2.7 (VCC = 2.7V to 5.5V)
    - 1.8 (VCC = 1.8V to 3.6V



    Dzięki za odpowiedzi, czyli odpowiedzi uzyskałem na pytania
    1, 2, 3 ... przyjmę że układ pracuje z niższym napięciem i zastosuję rezystory szeregowe + dzielnik napięcia do zasilania pamięci.

    7. ... TWINT należy sprawdzać czy ma wartość 1 i zerować przez wpisanie 1 (ot taki dziwny, ale sprzętowo prosty sposób).

    Na pytanie 4 odpowiadam sobie sam: TAK!

    Pozostały dwa pytania: 5 i 6.
  • Helpful post
    #6
    mirekk36
    Level 42  
    dondu wrote:

    5. Czy powinienem włączać linie SCL i SDA jako wyjścia lub wejścia poprzez ustawieniea w DDRC?


    Przy sprzętowej obsłudze TWI dzieje się podobnie jak przy sprzętowej obsłudze USART. Czyli włączenie mechanizmu sprzętowego powoduje automatycznie zmianę funkcji odpowiednich pinów transmisyjnych i wymusza ich działanie.

    Tylko przy programowej realizacji I2C trzeba samemu o to zadbać - co wydaje się oczywiste chyba.
  • Helpful post
    #7
    User removed account
    User removed account  
  • #8
    dondu
    Moderator on vacation ...
    _marek wrote:
    Tak SI27 sugeruje 2,7V?

    czyli wyższy zakres napięć zgodnie z ????:
    Quote:
    Low-voltage and Standard-voltage Operation
    - 2.7 (VCC = 2.7V to 5.5V)
    - 1.8 (VCC = 1.8V to 3.6V

    ... czyli mogę go zasilać z 5V bez rezystorów szeregowo na SCL i SDA?

    Ale jak to się ma do tego że w NOKIA 310 była zasilana 2,4V ?? :cry:




    Dodano po 7 [minuty]:

    Ale chyba jednak tak jest jak pisze _marek bo znalazłem w sieci 2 rodzaje tej pamięci SI27 i SI18. Czyli SI można próbować tłumaczyć Supply Input.




    Dodano po 1 [minuty]:

    mirekk36 wrote:
    Przy sprzętowej obsłudze TWI ... włączenie mechanizmu sprzętowego powoduje automatycznie zmianę funkcji odpowiednich pinów transmisyjnych i wymusza ich działanie.

    To bardzo logiczne i wygodne rozwiązanie :)





    Dodano po 1 [minuty]:

    Czyli zostało na razie tylko pytanie nr 6 dot. funkcji inicjującej TWI_Init()
    Zakładam na razie transmisję z zegarem 100kHz.

    Code:

    #define F_CPU 12000000UL

    void TWI_Init(void){

       //Ustawiam prędkość transmisji 100kHz ze wzoru: Prędkość = F_CPU / (16 + 2 * TWBR * 4^TWPS)
       //czyli: 12MHz / (16 + 2 * 52 * 4^0) = 100kHz
       TWSR &=   ~((1<<TWPS1) | (1<<TWPS1));      // TWPS = 0
       TWBR =   52;                        // TWBR = 52

       //pozostałe ustawienia
       TWCR   |=   (1<<TWIE);   //włączam przerwania TWI
       TWCR   |=   (1<<TWEA);   //włączam bit potwierdzenia ACK
       
       //włączam TWI
       TWCR   |=   (1<<TWEN);
       
       //włączam przerwania
       sei();
    }


    6. Czy coś powinienem jeszcze dołożyć do tej funkcji?
  • Helpful post
    #9
    User removed account
    User removed account  
  • #11
    User removed account
    User removed account  
  • Helpful post
    #13
    User removed account
    User removed account  
  • #14
    dondu
    Moderator on vacation ...
    _marek wrote:
    Wprowadzenie do twi z linka o tym mówi .

    Ale tam tylko pisze o pullup-ach i te zostawię bo wymaga tego standard IC2.

    Czy Ty miałeś na myśli, abym zostawił te pomiędzy Atmega, a eepromem szeregowo na liniach SCL i SDA? Ich zadaniem jak rozumie jest dostosowywanie poziomu sygnału jeżeli ATmega pracuje na 5V a eeprom na 3,6V. W moim przypadku oba układy będą pracować na 5V więc te rezystorki chyba nie są potrzebne?
  • Helpful post
    #15
    User removed account
    User removed account  
  • #16
    dondu
    Moderator on vacation ...
    _marek wrote:
    Myślałem że chcesz usunąć pullup'y. Na 5V będzie ok.

    ok, dzięki :)

    Dodano po 49 [minuty]:

    PIERWSZE PODEJŚCIE

    Na razie zadanie polega na:
    - włącz diodę (dodałem ją na porcie PB0 podłączona przez rezystor do 5V.
    - odczekaj 5sek.
    - zgaś diodę + inicjuj TWI
    - odczekaj 5 sekund
    - rozpocznij transmisję
    - po poprawnym rozpoczęciu transmisji wygenerować powinno się przerwanie, a w nim tylko zapalam diodę (by sprawdzić czy przerwanie ma miejsce).

    Niestety efekt jest taki:
    - dioda się zaświeca
    - 5 sek przerwy
    - dioda gaśnie ( linie SCL i SDA cały czas stan wysoki)
    - 5 sek przerwy ( linie SCL i SDA spadają na stan niski około 0,03V)
    - i nic się nie zmienia ani na liniach ani dioda nie zapala się.

    Czyli nie dochodzi do przerwania.
    Efekt jest taki sam niezależnie czy eeprom jest włożony w podstawkę czy nie.





    Code:

    #define F_CPU 12000000UL

    #include <avr/io.h>
    #include <avr/interrupt.h>
    #include <util/twi.h>
    #include <util/delay.h>

     
    #define   eeprom24C128_adres  0;   //mam tylko jedną pamięć z pinami adresowymi ustawionymi na 0


    //-------------------------------------------------------------------------------------


    void TWI_Init(void){

       //Ustawiam prędkość transmisji 100kHz ze wzoru: Prędkość = F_CPU / (16 + 2 * TWBR * 4^TWPS)
       //czyli: 12MHz / (16 + 2 * 52 * 4^0) = 100KHz
       TWSR   &=   ~((1<<TWPS1) | (1<<TWPS1));      // TWPS = 0
       TWBR   =   52;                        // TWBR = 52

       //pozostałe ustawienia
       TWCR   |=   (1<<TWIE);   //włączam przerwania TWI
       TWCR   |=   (1<<TWEA);   //włączam bit potwierdzenia ACK
       
       //włączam TWI
       //TWCR   |=   (1<<TWEN);   <---- TWEN włączam w poszczególnych rozkazach np, TWI_START()
       
       //włączam przerwania
       sei();
    }





    //-------------------------------------------------------------------------------------

    void TWI_START(void){
       //rozpocznij transmisję TWI
       TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTA);
    }












    /*****************************************************************************
    * Obsługa przerwania z TWI
    *
    */
    SIGNAL(SIG_2WIRE_SERIAL){

       //na razie zapal diodę przy pierwszym przerwaniu
       PORTB   &=   ~(1<<PB0);


       //zeruj flagę przerwania
       TWCR   |=   (1<<TWINT);

    }




    //-------------------------------------------------------------------------------------




    int main(void){


       //ustaw port LED sygnalizacyjnego
       DDRB   |=   (1<<PB0);
       PORTB   &=   ~(1<<PB0);    //zapal diodę


       _delay_ms(5000);

       PORTB   |=   (1<<PB0);    //zgaś diodę
       TWI_Init();   //inicjuj TWI
       
       _delay_ms(5000);

       TWI_START();    //rozpocznij transmisję
       
       while(1){
          //nudzimy się
       }
    }


    Dodano po 2 [godziny] 14 [minuty]:

    NIestety nie udało mi się dojść co może być przyczyną ... jakieś pomysły?





    Dodano po 4 [godziny] 11 [minuty]:

    Pierwsza poprawka kodu to zmiana adresu EEPROM.
    Przeoczyłem, że dokumentacji pisze iż adres jest następujący:

    [ATMega8][TWI][24C128][c] I2C na przerwaniach + EEPROM

    Czyli w moim przypadku gdy A1-A0 są zwarte do masy daje to adres 0b10100000 czyli 0xA0.

    Code:
    #define   eeprom24C128_adres  0xA0;


    Niemniej jednak nie zmienia to faktu, że problem który opisałem wcześniej nadal istnieje czyli po wywołaniu funkcji TWI_START() linie SCL i SDA przechodzą w stan niski, a przerwanie się nie generuje po zakończeniu wysyłania START (a może nic nie wysyła bo zegar nie pracuje?).

    Proszę o pomoc bo stanąłem i nie potrafię tego ugryźć :(
  • #17
    dondu
    Moderator on vacation ...
    Zgodnie z dokumentację na stronie 4:
    http://www.atmel.com/dyn/resources/prod_documents/doc2564.pdf

    Przerwanie generowane jest gdy:
    The TWINT Flag is set in the following situations:
     After the TWI has transmitted a START/REPEATED START condition
     After the TWI has transmitted SLA+R/W
     After the TWI has transmitted an address byte
     After the TWI has lost arbitration
     After the TWI has been addressed by own Slave address or general call
     After the TWI has received a data byte
     After a STOP or REPEATED START has been received while still addressed as a
    Slave.
     When a bus error has occurred due to an illegal START or STOP condition


    Czyli po wywołaniu funkcji:
    Code:
    void TWI_START(void){ 
    
       //rozpocznij transmisję TWI
       TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTA);
    }


    A tak się nie dzieje :( a to że SCL ma stan niski wynika z:
    Quote:
    As long as the TWINT Flag is set, the SCL line is held low

    co sprawdziłem dodając w main() zerowanie flagi po 5 sek od TWI_START(). Efekt jest taki że SCL wraca do poziomu wysokiego, ale przerwanie oczywiście się nie wygenerowało (w obsłudze przerwania od początku mam zerowanie flagi TWINT na jego końcu).



    No to może drobnymi kroczkami:

    Czy jeżeli do linii I2C nie mam podpiętego żadnego zewnętrznego urządzenia tylko rezystorki podciągające, to czy po zainicjowaniu TWI i wysłaniu START powinno zostać wygenerowane przerwanie?
    Sądzę że tak.
  • #20
    User removed account
    User removed account  
  • #22
    User removed account
    User removed account  
  • #23
    dondu
    Moderator on vacation ...
    _marek wrote:
    A w TWI_Init ?

    Chodzi ci o to?
    Code:
    //włączam TWI 
    
       //TWCR   |=   (1<<TWEN);   <---- TWEN włączam w poszczególnych rozkazach np, TWI_START()

    Jest zamarkowana tak jak na powyższym listingu. Początkowo tam było ale przeniosłem do TWI_START.

    W dokumentacji wyczytałem, że START to:
    Code:
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTA); 

    i tak właśnie robię tylko 1 raz.
  • #24
    User removed account
    User removed account  
  • #26
    User removed account
    User removed account  
  • #27
    dondu
    Moderator on vacation ...
    Nie bardzo wiem z czego się śmiejesz ... ale z chęcią się także pośmieję :D:D:D

    Zadałem ten temat na forum AVRFreaks
    http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=101394

    i otrzymałem odpowiedź:

    david.prentice wrote:
    Code:
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTA); 
    

    You have removed the TWIE bit with that assignment.

    If you are a TWI master, simply download the Fleury libs.
    They work fine.

    There are app notes for IRQ driven TWI. Essential for a Slave, but not particularly advantageous for a Master.

    David.


    Czyli sugeruje, że ustawiając tę sekwencję zeruję TWIE - czy to możliwe?

    W dokumentacji ATMega8 na str 175 jest pokazane, że tak właśnie mam inicjować START.


    Sugeruje bym korzystał z tych bibliotek: http://homepage.hispeed.ch/peterfleury




    Dodano po 14 [minuty]:


    ROZWIĄZANIE PROBLEMU
    David ma rację bo gdy dodałem TWIE do rozkazu startu to przerwanie nastąpiło :|
    Code:
    TWCR = (1<<TWINT)|(1<<TWEN)|(1<<TWSTA) | (1<<TWIE);

    Tylko nie znalazłem nic na ten temat w dokumentacji :cry::cry:
  • #28
    User removed account
    User removed account  
  • #29
    dondu
    Moderator on vacation ...
    To mój angielski jest chyba nieco do ... pośladków smoka, bo ja to zrozumiałem tak, że
    - ustawiam TWIE
    - włączam przerwania sei()
    - wysyłam start
    - i następuje przerwanie

    i tak właśnie robiłem.
    Tutaj nie pisze, że muszę ustawiać TWIE w czasie wysyłania START bo jest zerowane po wyzerowaniu TWINT ... no ale jak na wstępie tylna część smoka chyba bierze górę.