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

[AT91SAM7S] [AT91SAM7S][C] Konfiguracja rejestrów do uruchomienia TWI krok po kroku

kleki 22 Maj 2007 10:38 6731 28
REKLAMA
  • #1 3909083
    kleki
    Poziom 28  
    Posty: 1350
    Pomógł: 95
    Ocena: 142
    Mam drobna prośbę - potrzebne mi są instrukcje krok po kroku, które rejestry tego klocka muszą być zapisane (czym, to już jakoś "wyniucham") do uruchomienia TWI. Wiem, że interesują mnie wszystkie rejestry w rozdziale TWI, PWM_PCER (wklepuję 1<<9), PIOA_ASR, PIOA_BSR, PIOA_MDER. Podejżewam, że coś jeszcze powinienem poustawiać w PIOA, ale nie jestem pewien. Może PIOA_OER, lub PIOA_ODR? Objawy mam takie, że SDA i SCL są ciągle w stanie wysokim, nie mam sygnału TXCOMP.

    A może ktoś już to odpalił? Może źródełko?
    PWM, ADC - bez takich schodów, myślałem że mam już jakiś rozpęd, a tutaj... :-)

    Pozdrawiam
  • REKLAMA
  • #2 3909245
    GrzesGrz71
    Poziom 19  
    Posty: 197
    Pomógł: 32
    Ocena: 4
    Cześć

    Ja używam tego (działa) :

    libtwi.h
    
    // Global declarations 
    #define AT91C_TWI_CLOCK            400000      //400kHz 
    #define AT91C_FRAM_I2C_ADDRESS     (0xA0<<15)  //adresse fram 
    
    void AT91F_TWI_Open(void); 
    int AT91F_TWI_WriteByte (int, char *, int); 
    int AT91F_TWI_ReadByte  (int, char *, int );
    



    libtwi.c

    
    ///*---------------------------------------------------------------------------- 
    //* \fn    AT91F_TWI_ReadByte 
    //* \brief Read a byte from a slave device 
    //*---------------------------------------------------------------------------- 
    int AT91F_TWI_ReadByte(int int_address, char *data, int nb) 
    { 
       unsigned int status,counter=0,error=0,mode; 
            AT91PS_TWI pTwi; 
            pTwi = AT91C_BASE_TWI; 
            mode = AT91C_FRAM_I2C_ADDRESS | AT91C_TWI_IADRSZ_2_BYTE; 
    
            // Set TWI Internal Address Register 
       if ((mode & AT91C_TWI_IADRSZ) != 0) pTwi->TWI_IADR = int_address; 
    
       // Set the TWI Master Mode Register 
       pTwi->TWI_MMR = mode | AT91C_TWI_MREAD; 
    
       // Start transfer 
       if (nb == 1){ 
          pTwi->TWI_CR = AT91C_TWI_START | AT91C_TWI_STOP; 
          status = pTwi->TWI_SR; 
              if ((status & ERROR) == ERROR) error++; 
          while (!(status & AT91C_TWI_TXCOMP)){ 
                 status = pTwi->TWI_SR; 
                  if ((status & ERROR) == ERROR) error++; 
              } 
          *(data) = pTwi->TWI_RHR; 
       } 
        else{ 
           pTwi->TWI_CR = AT91C_TWI_START | AT91C_TWI_MSEN; 
          status = pTwi->TWI_SR; 
          if ((status & ERROR) == ERROR) error++; 
    
       // Wait transfer is finished 
               while (!(status & AT91C_TWI_TXCOMP)){ 
             status = pTwi->TWI_SR; 
             if ((status & ERROR )== ERROR) error++; 
              if(status & AT91C_TWI_RXRDY){ 
             *(data+counter++) = pTwi->TWI_RHR; 
             if (counter == (nb - 1)) pTwi->TWI_CR = AT91C_TWI_STOP; 
          } 
          } 
       } 
       return 0; 
    } 
    //*---------------------------------------------------------------------------- 
    //* \fn    AT91F_TWI_Open 
    //* \brief Initializes TWI device 
    //*---------------------------------------------------------------------------- 
    void AT91F_TWI_Open(void) 
    { 
       // Configure TWI PIOs 
       AT91F_TWI_CfgPIO (); 
       // Configure PMC by enabling TWI clock 
       AT91F_TWI_CfgPMC (); 
    
            //disable the pull-up-resistor for twi-lines 
            *AT91C_PIOA_PPUDR = AT91C_PA4_TWCK | AT91C_PA3_TWD; 
    
       // Configure TWI in master mode 
       AT91F_TWI_Configure (AT91C_BASE_TWI); 
    
       // Set TWI Clock Waveform Generator Register 
       AT91F_SetTwiClock(); 
    }
    


    przyda się też to :
    http://svn.openpcd.org/trunk/firmware/include/lib_AT91SAM7.h

    powodzenia
  • REKLAMA
  • #3 3910637
    Sowa
    Poziom 12  
    Posty: 40
    Pomógł: 3
    Tez właśnie uruchamiam różne rzeczy na tym ARMie. Poniżej zamieszczam kod do TWI (I2C). Odpaliłeś już może USB? A może wiesz gdzie znaleźć jakieś info na ten temat?

    *** twi.c ****
    #include <AT91SAM7S256.h>
    #include <lib_AT91SAM7S256.h>
    
    void twi_init(void)
    {
      // PIOA-pin setup:
      AT91F_TWI_CfgPIO();
      // clk enable:
      AT91F_TWI_CfgPMC();
      // TWI setup:
      AT91F_TWI_Configure(AT91C_BASE_TWI);
      
      // set TWI clk:
      *AT91C_TWI_CWGR = (7<<16)|(255<<8)|(255); // CKDIV | CHDIV | CLDIV = 111 1111 1111
      // set dev.adr to 7-bits, adr.space to 16-bits:
      *AT91C_TWI_MMR = 0x550200;
      
      // and roll ...
    } 
    void i2cMasterSend(unsigned char dev_adr, unsigned short mem_adr, unsigned char data)
    {
      unsigned int status;
      
      *AT91C_TWI_IADR = mem_adr;
      //
      *AT91C_TWI_MMR &= ( 0xFF00EFFF | (dev_adr<<16) );
      //
      *AT91C_TWI_CR = AT91C_TWI_START | AT91C_TWI_MSEN | AT91C_TWI_STOP;
      //
      *AT91C_TWI_THR = data;
      //
      status = *AT91C_TWI_SR;
      while ( !(status & AT91C_TWI_TXCOMP) )
      {
        status = *AT91C_TWI_SR;
      }
    }
    unsigned char i2cMasterReceive(unsigned char dev_adr, unsigned short mem_adr)
    {
      unsigned int status;
      
      *AT91C_TWI_IADR = mem_adr;
      //
      *AT91C_TWI_MMR |= ( 0x00001000 | (dev_adr<<16) );
      //
      *AT91C_TWI_CR = AT91C_TWI_START | AT91C_TWI_MSEN | AT91C_TWI_STOP;
      //
      status = *AT91C_TWI_SR;
      while ( !(status & AT91C_TWI_TXCOMP) )
      {
        status = *AT91C_TWI_SR;
      }
      //
      return (unsigned char)*AT91C_TWI_RHR;
    } 
    
    *** main.c *** fragment
    
     while (1)
     {
        for (mem_addr=0; mem_addr<256; mem_addr++)
        {
          i2cMasterSend(EEPROM_DADR, mem_addr, i);
        }
       for (mem_addr=0; mem_addr<256; mem_addr++)
        {
          data = i2cMasterReceive(EEPROM_DADR, mem_addr);
       }
    }
    
    

    Pozdro
  • #4 3911235
    _Matik_
    Poziom 19  
    Posty: 390
    Pomógł: 25
    Ocena: 7
    Hmmm, w SAM7 TWI jest podobno uwalone. Pisze podobno bo:
    a) mi sie nie udalo go uruchomic tak zeby dzialalo za kazdym razem,
    b) nie udalo mi sie zastosowac gotowej biblioteki opartej na sprzetowym TWI, bo dzialala "losowo",
    c) wiele osob na forach internetowych potwierdzilo problemy i napisalo programowa obsluge TWI,

    W pracy korzystam w softwarowego TWI przeportowanego chyba z AVR, nie pamietam juz zabardzo szczegolow ale gotowe kawalki kodu sa dostepne w internecie.
  • #5 3911313
    Sowa
    Poziom 12  
    Posty: 40
    Pomógł: 3
    Mi działa sprzętowo jw. Na początku miałem problem bo rzeczywiście działało co któryś raz, zasilałem wtedy pamięć napięciem 3,3V, aż w końcu przestało działać całkiem. Wtedy podpiąłem pod 5V i śmiga. Myślę ze słabo działało na 3,3V bo były za duże rezystory podciągające do + . W układzie mam takie jakich używałem dla 5V w '51.

    Pozdrawiam
  • #6 3911580
    GrzesGrz71
    Poziom 19  
    Posty: 197
    Pomógł: 32
    Ocena: 4
    U mnie działa bez problemu z trzema układami na magistrali (wliczając SAM7S64) na kodzie zamieszczonym powyżej - podciągane 2k2 do 3.3V i jakoś nie odnotowałem problemów z transmisją.

    pozdro
  • REKLAMA
  • #7 3911623
    Sowa
    Poziom 12  
    Posty: 40
    Pomógł: 3
    Czyli wszystko sie zgadza. Ja mam rezystory 5k1 dla 5V i przez to mi nie działało na 3,3V.
  • #8 3912166
    kleki
    Poziom 28  
    Posty: 1350
    Pomógł: 95
    Ocena: 142
    Też czytałem, że jest pokićkane. Pamiętam jeszcze taką wypowiedź, że dokumentacja jest do bani i dopóki nie uruchomisz "na Franka wyczuwacza", to nie masz szans na zrozumienie tematu HW-TWI. USB - nie miałem jeszcze okazji.






    Kuchnia, nie mogę coś sobie poradzić. Możecie mi zajrzeć do kodu? Jeszcze taka prośba, żeby opisać mi cały tok rozumowania - może ja czegoś tu nie pojmuję. :-/

    
    #include <targets\AT91SAM7.h>
    #include <user\lcd.c>   //zawiera f-cje obslugi HD44780 i delay-a
    #define LED 1<<9        //mapowanie diodki swiecacej
    #define SDA 1<<3        //mapowanie SDA
    #define SCL 1<<4        //mapowanie SCL
    
    int main (void)
    {
    PIOA_OER = LED;         //zalaczamy wyjscie na LED-ke
    PIOA_MDER = LED;        //open collector dla diodki
    PIOA_SODR = LED;        //i gasimy LED-ka
    delay(50000);           //oczekiwanie na ustabilizowanie sie zasilania
    LcdInit();              //inicjalizacja LCD, czyszczenie wyswietlacza - zwrocic 
                            //uwage na mapowanie sygnalow w pliku lcd.c
    LcdPrint(" Test dzialania ");   //sprawdzamy dzialanie LCD
    LCD_xy (0, 1);          
    LcdPrint(" magistrali I2C ") ;  //takie sobie wodotryski
    delay(2000000);         //czekamy zanim zczyscimy ekran
    LcdClear();             //i czyscimy ekran po tescie
    LCD_xy (0, 1);          //ustawiam kursorek w drugim wierszu
    PMC_PCER = 1<<9;        //ID dla TWI jest rowne 9, zalaczamy taktowanie TWI
    TWI_CR = 0x80;          //reset dla TWI
    PIOA_PDR = (SDA|SCL);   //wyłączam (dobrze myślę?) PIO dla linii magistrali
    PIOA_MDER = (SDA|SCL);  //konfiguruję jako open collector
    PIOA_PUDR = (SDA|SCL);  //wyłączam pullupy - mam zewnętrzne
    PIOA_ASR = ((SDA|SCL)); //i wybieram peryferie A (TWI) dla tych pinów
    
    /********************************************************/
    //próbuję obsłużyć I2C - z miernym efektem...
    //chociaż coś "drgnie" na liniach - sprawdzane oscyloskopem, gubi na chwilkę
    //synchronizację, więc na moje oko nóżki są dobrze skonfigurowane
    TWI_CR = (1<<7);        //znowu reset TWI
    TWI_IDR = ((1<<8)|7);   //na wszelki wielki wyłączam IRQ
    unsigned int SR = TWI_SR; //czytam status register, żeby nie pozostały śmieci
    TWI_CR = 0x4;           //ustawiam bit MSEN
    TWI_CWGR = (7<<16)|(0xFF<<8)|(0xFF);  //największe możliwe opóźnienia
    TWI_MMR = ((0x50<<16)|(1<<12)|(1<<8));  //adres 24C04 - 1010000 (trzy piny 
                            //podałem na masę), ustawiam MREAD, rozmiar adresu - 1B
    TWI_IADR = 0x2;         //pod 0x2 powinienem mieć literę "małe o"
    TWI_CR = 0x5;           //zapisuję w command register START i MSEN
    delay(5000);            //czekam chwilkę
    while(!(TWI_SR&0x2));      //i czekam, dopóki nie ustawi się RXRDY
                              //cały wic polega na tym, że RXRDY się nie ustawia
                              //chociaż jak czekam na TXCOMP, to program leci dalej
                              //ale a RHR mam dokłądnie zero... W tej chwili nie
                              //mam komunikatu na LCD, ani zaświeconego LED-a
    unsigned char var = TWI_RHR;  //tutaj chciałbym wyciągnąć z bufora daną
    TWI_CR = 0x2;                 //i wysłać na magistralę STOP
    LcdPrintChar (var + 0x30);    //wypisuję na LCD - to na pewno działa dobrze
    PIOA_CODR = LED;              //dla pewności zaświecam diodkę
    while(1);                     //THE END :-)
    }
    /********************************************************/
    

    Na LCD-ku zawsze wypisuje mi zero...

    Poratujcie, bo osiwieję....

    Z góry dzięki za oświecenie. :-)
  • #9 3923056
    _Matik_
    Poziom 19  
    Posty: 390
    Pomógł: 25
    Ocena: 7
    Nie przekonujecie mnie z tymi pullupami bo jak dobrze rozumiem, wielkosc rezystorow jest uzalezniona tylko i wylacznie od ustawionej predkosci a ja ustawialem juz bardzo male (swoja droga jezeli dobrze pamietam do na koncu datasheeta, w erracie jest informacja o bledach i ograniczeniach rejestru odpowiedzialnego za taktowanie transmisji TWI).

    Kleki: ja jedynie bede mogl zaoferowac wersje programowa TWI, ale to dopiero w srode jak bede w pracy ;/. Dziala mi bardzo ladnie na EEPROMACH I2C/TWI. Dla mnie ona w zupelnosci wystarcza bo nie zalezy mi na predkosci ale przy LCD pewnie mozesz miec inne wymagania.

    Jezeli chodzi o USB to dosyc fajnie jest ono zrobione w przykladzie lwip_demo_rowley (czy jakos tak) dostarczanym z FreeRTOSEM. Mozna wiele rzeczy podpatrzec i potem ewentualnie przerobic to na wersje bez systemu operacyjnego. Jest tez dosyc swiezy dokument USB Framework od Atmela ktory stanowi wzor obslugi USB, ale jedynie go powierzchownie przejrzalem i nie zaglebialem sie w temat (bo dziala mi to z FreeRTOSA)
  • #10 3930839
    kleki
    Poziom 28  
    Posty: 1350
    Pomógł: 95
    Ocena: 142
    _Matik_ - czekam z zaciekawieniem na źródełka. W razie wielkiej "bidy" programowo zaemuluję I2C.

    Sowa - a mógłbyś sklecić coś na zasadzie flowcharta, kiedy i co ustawiasz i czytasz, bo jakoś nie mogę ujażmić sprzętu... :-/ Mnemoników nauczyłem się już przy okazji na pamięć i nawet nie potrzeba zaglądać do TDS. ;-)
    A, jeszcze taka sprawa - na klocku jest oznaczenie - rok i kolejny tydzień produkcji - zapodałbyś? Może masz świerzszy egzemplarz i coś jest poprawione? Ja mam bodaj 29 tydzień 2005r, więc może jakieś pierwsze partie? W TDS jest dokładnie co oznaczają cyfry nadruku - tydzień/rok jest chyba w drugim wierszu od dołu, po prawej stronie.

    Do tego stopnia mi to zabiło ćwieka, że spojżałem do starszej TDS. Najnowsza wersja to 6175G, zajżałem do 6175D. Porównanie :
    Wszystko mi się zgadza, z wyjątkiem TWI_SR. W starszej wersji pdf-a są tam bity UNRE i OVRE. Nie jarzę, ale sprawdzę. Flowchart na stronie 283 - czy na pewno jest dobrze? Po mojemu bit MRED powinien byc ustawiony na 1, a tam jest jako 0... Wiecie coś bliżej?

    _Matik_ - odnośnie erraty, to jest tam zapis, że CLDIV*(2^CKDIV) nie może być większe niż 8191. Dokłądnie tak samo jest dla CHDIV.


    Sowa -> jaki układek wieszasz na tym I2C? Czy to jest pamiątka 24Cxx? Wychodzi mi adres 1010101x binarnie, ale dziwi mnie że w TWI_MMR masz ustawione pole IADRSZ jako 10 (albo pitoli mi się w oczach, bo jestem na 3 zmianie), czyli rozmiar adresu - 2 bajty. Wychodzi mi spora ta pamiątka. :-) 24C512? Nie ustawiasz bitu MREAD przy odczycie? :?:
  • #11 3943113
    gufiak
    Poziom 21  
    Posty: 444
    Pomógł: 15
    Ocena: 2
    Sowa, napewno działa Ci ten kod co zamieściłeś. Możesz wyjaśnić dlaczego stosujesz
    *AT91C_TWI_MMR &= ( 0xFF00EFFF | (dev_adr<<16) );
    zamiast normalnego wpisania do rejestru MMR? Bo to co robisz, to jest jedynie wyzerowanie niektórych bitów w tym rejestrze. I choć w niektórych przypadkach może to działać, ale generalnie na mój gust, zapis jest błędny. Podobnie masz w przypadku odczytu z TWI. Tam ten OR też może psuć.
    Tak samo to:
    status = *AT91C_TWI_SR;
      while ( !(status & AT91C_TWI_TXCOMP) )
      {
        status = *AT91C_TWI_SR;
      } 
    lepiej zapisać tak:
    
      while ( !(*AT91C_TWI_SR & AT91C_TWI_TXCOMP) );
    Poza tym jeśli to jest eeprom, to lepiej wysyłać dane sekwencyjnie, a nie po jednym bajcie, za każdym razem wysyłając pełną ramkę (bit startu, adres urządzenia, adres w pamięci, bajt danych i bit stopu). Będzie dużo szybciej. A wysyłanie sekwencyjne realizuje się dosyć łatwo - wystarczy w pętli wpisywać dane do rejestru TWI_THR zaraz po ustawieniu bitu TXRDY w rejestrze TWI_SR. Trzeba tylko wziąc pod uwagę, że zapis sekwencyjny realizowany jest w obrębie jednej strony i po dojściu do jej końca, nie przechodzi na następną, lecz zapisuje tą samą stronę od początku. No i jeszcze nie widzę przy odczycie ustawionego bitu MREAD.
    _Matik_, w SAM7S dużo jest uwalone, niestety; ale TWI działa. Najczęstszą przyczyną są albo źle dobrane pullupy, albo źle napisany kod. Datasheet dosyć niejasno opisuje obsługę TWI i niestety brak źródła, które bezsprzecznie potwierdzi, że taki i taki sposób napewno jest dobry i będzie na 100% działać. Np. w kodzie umieszczonym przez GrzesGrz71 wszystko wygląda w miarę ok, ale w przypadku wysyłania 1 bajtu, nie ustawia w rejestrze TWI_CR bitu MSEN:
    pTwi->TWI_CR = AT91C_TWI_START | AT91C_TWI_STOP;
    To może działać, ale nie musi. Jeśli wcześniej ten bit nie został ustawiony, to będą niespodzianki.
    kleki, uwagi co do Twojego kodu:
    1. To nie jest bascom. Inicjalizację TWI umieść w oddzielnej funkcji. Kod będzie przyjaźniejszy dla oka, a naprawdę niewiele cięższy dla procesora.
    2. załączyłeś bibliotekę "AT91SAM7.h", a w niej pewnie znajdują się definicje bitów. Proszę używaj ich. Bo "(7<<16)|(0xFF<<8)|(0xFF);" jest najlepszą drogą do tego żeby popełnić głupi błąd, którego trzeba będzie szukać w ekstremalnych przypadkach nawet przez parę godzin.

    Cytat:
    Nie przekonujecie mnie z tymi pullupami bo jak dobrze rozumiem, wielkosc rezystorow jest uzalezniona tylko i wylacznie od ustawionej predkosci a ja ustawialem juz bardzo male
    No niestety, ale nie tylko. Jest uzależniona od pojemności magistrali i układów na niej się znajdujących. Jeżeli pojemność jest większa, to stała czasowa również. Aby zmniejszyć stałą czasową trzeba zmniejszyć rezystancję. Zbocza nie mogą być płaskie nawet przy małych częstotliwościach.
    Cytat:
    Po mojemu bit MRED powinien byc ustawiony na 1, a tam jest jako 0... Wiecie coś bliżej?
    Oczywiście, jest źle, ale wystarczy przerzucić parę stron dalej i będzie to jasne.

    Dodano po 5 [godziny] 44 [minuty]:

    A tak BTW, na stronie http://hubbard.engr.scu.edu/embedded/arm/armlib/ jest do pobrania biblioteka do ARMów, są w niej również biblioteki do SAMów. Jest tam również implementacja TWI. Niezbyt optymalna, ale wygląda na działającą (niestety nie sprawdzałem).
    Poza tym właśnie piszę implementację TWI na przerwaniach, żeby nie blokować proca (nie wiem czy ktoś próbował liczyć, ale np. przy częstotliwości proca 48MHz i częstotliwości SCL I2C = 100kHz, wysłanie zwykłej ramki z bitami startu i stopu, adresem urządzenia oraz jednym bajtem danych zajmuje około 10000 cykli procesora). Może zadziała :)
  • REKLAMA
  • #12 3944711
    kleki
    Poziom 28  
    Posty: 1350
    Pomógł: 95
    Ocena: 142
    -> gufiak
    1. co do inita TWI w oddzielnej funkcji to spoko, ale przy większym sofcie. Takie podchody na piechotę jakoś bardziej do mnie przemawiają, wiem na 100% co kiedy dzieje się w programie. Właśnie po to pisałem ciurkiem, choć osobiście w wypadku gdy mam do czynienia z trudniejszym prg to wolę na początku main dać całą inicjalizację (o ile nie będzie potrzebna jeszcze gdzieś indziej), a później odwołuję się do np. f-ji zapisu, odczytu, itp. Być może obie metody są dobre, byc może jeszcze nie "dojżałem" do pakowania f-ji inicjalizujących - nie wiem. W każdym razie dzięki za poradę. :-)
    2. na razie jestem w fazie testów, więc TDS w garści i zarzucam bitami. Póki co prg pod ARM malutkie, więc wklepuję z ręki. ;-) Ciężko jakoś zapamiętuję mnemoniki... Na pewno kiedyś dojdę do pisania w tym stylu. :-)

    Apropos - mówisz że TWI działa - masz jakiś kodzik?

    pzdr
  • #13 4004804
    gufiak
    Poziom 21  
    Posty: 444
    Pomógł: 15
    Ocena: 2
    Troszkę czasu minęło, ale wcześniej miałem sporo roboty i mało czasu. Więc umieszczam teraz. Jest to kod do odczytu i zapisu sekwencyjnego EEPROMU AT24C128 (no niekoniecznie tego, ale kod pisałem do obsługi tego EEPROMU). W przypadku zapisu trzeba wziąć pod uwagę, że zapis sekwencyjny zapisuje w obrębie tylko jednej strony, więc po dojściu do końca strony trzeba na nowo zaadresować dane. Ten kod robi to automatycznie. Poza tym, używam dwóch częstotliwości zegara, jednej (18.432MHz) gdy USB nie jest podłączone oraz drugiej (48MHz) gdy USB jest podłączone, więc w moim kodzie jest ustawianie zegara TWI w funkcjach odpowiedzialnych za wysyłanie i odbiór danych, a nie w inicjalizującej. Przy jednej częstotliwości można przenieść to na koniec funkcji inicjalizującej. Poza tym, zauważyłem, że ważne jest przede wszystkim odczytywanie zawartości rejestru RHR przed rozpoczęciem transmisji. Lubią tam siedzieć jakieś śmieci i jak się tego nie odczyta, to się blokuje, albo odczytuje potem głupoty.
    // TWI error definitions
    #define TWI_WAIT_TIMEOUT	0x000FFFFF
    #define TWIOVRUN			BIT(0)
    #define TWIUNRUN			BIT(1)
    #define TWINACK				BIT(2)
    #define TWITIMEOUT			BIT(3)
    
    // Master Clock Frequency
    #define CLOCK_FREQ_SLCK		0
    #define CLOCK_FREQ_18MHz	1
    #define CLOCK_FREQ_48MHz	2
    
    extern volatile char ClockFrequency;
    
    void twi_init(void)
    {
    	AT91F_PIO_CfgOpendrain( AT91C_BASE_PIOA, AT91C_PA3_TWD | AT91C_PA4_TWCK );
    	// peripheral A enable & disable pio on TWI sda + scl, PA3 + PA4
    	AT91F_TWI_CfgPIO();
    	// Enable TWI peripheral clock
    	AT91F_TWI_CfgPMC();
    	// Disable TWI interrupts, reset periph, set master mode
    	AT91F_TWI_Configure( AT91C_BASE_TWI );
    }
    
    
    u08 twi_wait(u32 Mask)
    {
    	u32 Status;  
    	u32 Timeout = TWI_WAIT_TIMEOUT;
        u08 TwiError = 0;
        Status = AT91C_BASE_TWI->TWI_SR;
        while (!(Status & Mask))           
        {
            Status = AT91C_BASE_TWI->TWI_SR;
            if (Status & AT91C_TWI_OVRE)
                TwiError |= TWIOVRUN;
            if (Status & AT91C_TWI_UNRE)
                TwiError |= TWIUNRUN;
            if (Status & AT91C_TWI_NACK)
    		{
                TwiError |= TWINACK;
                AT91C_BASE_TWI->TWI_CR = AT91C_TWI_STOP | AT91C_TWI_MSEN;
            }
    
            if (--Timeout == 0)
    		{
               return TWITIMEOUT;
    		}
        }
    	return TwiError;
    }
    
    u08 twi_eep_write_block(u16 blockAddr, u08 * pData, u16 length)
    {
    	u08 ret = 0;
    	
    	// make sure that there are no transfers active
    	if ((ret = twi_wait( AT91C_TWI_TXCOMP )) & TWITIMEOUT)
    		return ret;
    
    	// setup clocks
    	if (ClockFrequency == CLOCK_FREQ_18MHz)
    	{
    		// configure TWI clock - 400kHz for 18.432MHz MCK
    		*AT91C_TWI_CWGR = (AT91C_TWI_CKDIV & (0 << 16)) | (AT91C_TWI_CHDIV & (16 << 8)) |
    								(AT91C_TWI_CLDIV & (27 << 0));
    		// CLDIV 27 & CHDIV 16 for 400kHz
    	}
    	else if (ClockFrequency == CLOCK_FREQ_48MHz)
    	{
    		// configure TWI clock - 400kHz for 48MHz MCK
    		*AT91C_TWI_CWGR = (AT91C_TWI_CKDIV & (0 << 16)) | (AT91C_TWI_CHDIV & (45 << 8)) |
    								(AT91C_TWI_CLDIV & (74 << 0));
    		// CLDIV 74 & CHDIV 45 for 400kHz
    	}
    	
    	// dummy read TWI Status Register to clear old or false events
    	dummy = *AT91C_TWI_SR;
    	dummy = dummy;
    	
    	// deviceadr + Master write + 1 Device address
    	*AT91C_TWI_MMR = AT91C_TWI_IADRSZ_2_BYTE |
    				(((unsigned long)TWI_ADDR_EEPROM) << 16);
    	
    	// handle writing to different pages
    	u16 len, _blockAddr = blockAddr;
    	do
    	{
    		// len - number of bytes that can be written to the current page
    		len = (_blockAddr & 0xFFC0) + 0x40 - _blockAddr;
    		// correct 'len' to not write more bytes than set in the 'length' variable
    		if (_blockAddr + len - blockAddr > length)
    			len = (blockAddr + length) - _blockAddr;
    	
    		// set device internal address (eep pData addr)
    		*AT91C_TWI_IADR = _blockAddr;
    		
    		_blockAddr += len;
    		
    		// write the first byte to the Transmit Holding Register
    		if (len--)
    			*AT91C_TWI_THR = *(pData++);
    
    		// start the transmission
    	    *AT91C_TWI_CR = AT91C_TWI_START | (len ? 0 : AT91C_TWI_STOP) ;
    
        	while (len)
    	    {
    			// transmit complete?
        	    ret |= twi_wait( AT91C_TWI_TXRDY );
    	        if (!ret)
        	    {
    				// stop on last byte
            	    if (len-- == 1)
                	    *AT91C_TWI_CR = AT91C_TWI_STOP;
    	            *AT91C_TWI_THR = *(pData++);
        	    }
    			else // break on error
    				return ret;
    	    }
    		
    		// transmit complete?
    		ret |= twi_wait( AT91C_TWI_TXRDY );
    		// transfer complete?
    		ret |= twi_wait( AT91C_TWI_TXCOMP );
    		// break on error
    		if (ret)
    			return ret;
    	}
    	while (((blockAddr + length) & 0xFFC0) - ((_blockAddr) & 0xFFC0) > 0); // while different pages
        return ret;
    }
    
    
    u08 twi_eep_read_block(u16 blockAddr, u08 * pData, u16 length)
    {
    	u08 ret = 0;
    	
    	// make sure that there are no transfers active
    	if ((ret = twi_wait( AT91C_TWI_TXCOMP )) & TWITIMEOUT)
    		return ret;
    
    	// setup clocks
    	if (ClockFrequency == CLOCK_FREQ_18MHz)
    	{
    		// configure TWI clock - 400kHz for 18.432MHz MCK
    		*AT91C_TWI_CWGR = (AT91C_TWI_CKDIV & (0 << 16)) | (AT91C_TWI_CHDIV & (16 << 8)) |
    								(AT91C_TWI_CLDIV & (27 << 0));
    		// CLDIV 27 & CHDIV 16 for 400kHz
    	}
    	else if (ClockFrequency == CLOCK_FREQ_48MHz)
    	{
    		// configure TWI clock - 400kHz for 48MHz MCK
    		*AT91C_TWI_CWGR = (AT91C_TWI_CKDIV & (0 << 16)) | (AT91C_TWI_CHDIV & (45 << 8)) |
    								(AT91C_TWI_CLDIV & (74 << 8));
    		// CLDIV 74 & CHDIV 45 for 400kHz
    	}
    	
    	// set internal block address for TWI device
        *AT91C_TWI_IADR = blockAddr;
    	// master read + 2 byte internal address + device address
        *AT91C_TWI_MMR = AT91C_TWI_MREAD | AT91C_TWI_IADRSZ_2_BYTE |
    						((unsigned long)TWI_ADDR_EEPROM) << 16;
    	
    	// dummy read TWI Status Register to clear old or false events
    	dummy = *AT91C_TWI_SR;
    	dummy = dummy;
    	// dummy read the receive holding register
    	dummy = *AT91C_TWI_RHR;
    	dummy = dummy;
    	// initiate the transmission
        *AT91C_TWI_CR = AT91C_TWI_START | AT91C_TWI_MSEN;
    
        while (length)
        {
    		// receive complete?
            ret |= twi_wait( AT91C_TWI_RXRDY );
    		device_red_led_toggle();
            *(pData++) = *AT91C_TWI_RHR;
    		// last byte to transfer?
            if (length-- == 1)
    			// set stop condition
                *AT91C_TWI_CR = AT91C_TWI_STOP | AT91C_TWI_MSEN;
        }
    	
    	// receive complete?
    	ret |= twi_wait( AT91C_TWI_RXRDY );
    	// dummy read the receive holding register
    	dummy = *AT91C_TWI_RHR;
    	dummy = dummy;
    	// transfer complete?
        ret |= twi_wait( AT91C_TWI_TXCOMP );
        return ret;
    }


    Poza tym udało mi się zaimplementować TWI na przerwaniach. Dużo roboty, ale efekt końcowy bardzo dobry. Trzeba przede wszystkim pamiętać aby włączać przerwania, których w danej chwili się spodziewamy. Bo jak włączymy przerwanie na TX oraz RX, to nie doczekamy się żadnego przerwania. W swoim kodzie w każdej funkcji wywoływanej przez główny program włączam przerwanie na TXCOMP jeśli wysyłam dane, zaś na RXRDY gdy chcę je odczytać. A w funkcji obsługi przerwania po odczytaniu lub zapisaniu danych wyłączam w ogóle przerwania TWI.
  • #14 5114698
    paw789
    Poziom 18  
    Posty: 298
    Pomógł: 20
    Ocena: 68
    Aby nie zakaldać nowego tematu opiszę swój problem z TWI tutaj.
    TWI używam do przesylania jednego bajtu z kilku AVR'ów( zasilanie 5V ). Master to AT91SAM7S256 ( zasilanie 3.3V) , początkowo mialem problemy z uuchomieniem, okazalo się że przy rezystorach podciągających 4,7K wogóle transmisja na odleglość kilkunastu/kilkudziesiąciu cm nie dziala - dużo blędów, aby zniwelować blędy w transmisji zszedlem do rezystorów o wartość 1,2K do +3.3V.
    Mój kod obslugi:
    
    void TWI_Open ( void )
    {	
    	AT91F_TWI_CfgPIO();
    	AT91F_PIO_CfgOpendrain(AT91C_BASE_PIOA, ((unsigned int) AT91C_PA3_TWD ) | ((unsigned int) AT91C_PA4_TWCK )); 
    	
    	AT91C_BASE_PIOA->PIO_PPUDR = AT91C_PA3_TWD | AT91C_PA4_TWCK; //wylączenie pull-up
    	
    	AT91F_TWI_CfgPMC(); // wlączenie zegara dla bloku TWI
    	
    	//TWI Configure
    	//Disable interrupts
    	AT91C_BASE_TWI->TWI_IDR = (unsigned int) -1;
    	AT91C_BASE_TWI->TWI_CR = AT91C_TWI_SWRST;
    	AT91C_BASE_TWI->TWI_CR = AT91C_TWI_MSEN; 
    	
    	//AT91F_SetTwiClock();
    	
    	AT91C_BASE_TWI->TWI_CWGR = ( AT91C_TWI_CLDIV_5 | AT91C_TWI_CHDIV_5 | AT91C_TWI_CKDIV_4 ); // okolo 100kHz
    
    	DBGU_AsyncWriteString("\n\rTWI_Open");
    	DBGU_FlushWrite();
    }
    


    
    int TWI_ReadByte( void )
    {
    	int smieci;
    	// oczekiwanie na zapelnienie bufora odczytu jednym bajtem
    	while( !((AT91C_BASE_TWI->TWI_SR) & AT91C_TWI_RXRDY ))
    	{
    		// wyjście z pętli oczekiwania po 50ms
    		if( PIT_ReadyToAnalize == 1 )
    		{
    			DBGU_AsyncWriteString("\n\r TWI ERROR !!!");	DBGU_FlushWrite(); 
    			PIT_ReadyToAnalize = 0;
    		 	return 0;
    		}
    	} // sprawdzaje czy nie minelo 50ms
    	return( AT91C_BASE_TWI->TWI_RHR );
    }
    


    
    
    void TWI_Read ( int adres )
    {
    	int smieci, miernik_id ;
    	static unsigned int otrzymany_bajt, pomiar_sharp, pomiar_sonar, pomiar_ir;
    	unsigned char otrzymany_bajt_z_myszki_1;// otrzymany_bajt_z_myszki_2 ;
    	// ustawienie adresu slave'a
    	 AT91C_BASE_TWI->TWI_MMR = (AT91C_TWI_DADR & (adres << 15)) | AT91C_TWI_IADRSZ_NO  | AT91C_TWI_MREAD; 
    	//AT91C_BASE_TWI->TWI_MMR = AT91C_TWI_IADRSZ_NO | AT91C_TWI_MREAD | adres<<16;
    	
    	smieci = AT91C_BASE_TWI->TWI_RHR;
    	smieci = AT91C_BASE_TWI->TWI_SR;
    	// start transmisji
    	AT91C_BASE_TWI->TWI_CR = AT91C_TWI_START;
    	cr_register = AT91C_BASE_TWI->TWI_CR;
    	
    	otrzymany_bajt_z_myszki_1 = TWI_ReadByte();
    
                                        LCD_Cls();
      			LCD_put_text("X=");
      			LCD_put_dec(otrzymany_bajt_z_myszki_1);
      			LCD_Update();
    
    	AT91C_BASE_TWI->TWI_CR = AT91C_TWI_STOP; // bit stopu
    	
    	while( !((AT91C_BASE_TWI->TWI_SR) & AT91C_TWI_TXCOMP) ) {
    		if( PIT_ReadyToAnalize == 1 ) break; // wyjście z pętli oczekiwania po 50ms
    	} // oczekiwanie na koniec transmisji
    }
    




    Problem polega na tym, że gdy uruchomię najpierw master'a a następnie slave'a to nie otrzymuję bajtu z sleav'a (mam zrobione wyjście z pętli oczekiwania na otrzymanie bajtu po 50ms aby program glówny się nie wieszal ), musę najpierw uruchomić sleav'a.

    Poza tym gdy już TWI dziala w pewnym urządzeniu w którym są wlączone silniki prądu stalego po czasie kilkunastu/kilkudziesięciu sekund nie otrzymuję bajtu z sleav'a. Bląd lezy po stronie master'a - pomaga reset master'a, po resecie przy wlączonych silnikach dziala poprawnie przez pewien czas, po pewnym czasie w pętli while w funkcji TWI_ReadByte znów nie doczekuję się odbioru bajtu w ciągu 50ms i następuje wyjście z pętli oczekiwania ( return 0; ) - wylączenie silników nie pomaga.

    Podsumowując - gdy nastąpi tylko jedno wyjście z pętli oczekiwania - transmisja nie zachodzi poprawnie nawet gdy źródlo zaklóceń jest już wylączone.
    Czy przy wyjściu z pętli oczekiwania na otrzymanie bajtu powinienem robić coś jeszcze ? Czy jest sposób na zresetowanie samego TWI ?
  • #15 5121695
    upanie
    Poziom 22  
    Posty: 434
    Pomógł: 33
    Ocena: 7
    Witam.
    I ja dołożę swoje grosze.
    Nie używajcie sprzętowego TWI/I2c w SAM7S. Jak najbardziej działa ale nie zawsze prawidłowo. Dokumentacja nie jest powalona i korzystając tylko z niej można uruchomić TWI. Tak czy siak TWI jest tu kiepskie. Największy babol, który dyskwalifikuje go z użycia ogromna wrażliwość na zakłócenia. Automat TWI potrafi generować dodatkowe krótkie zbocza zegara pod wpływem zakłóceń.
    Lepiej jest napisać softową wersję i się nie przejmować.

    Pozdrawiam.
  • #16 5122555
    don diego
    Poziom 32  
    Posty: 1557
    Pomógł: 165
    Ocena: 63
    Czy problemy są tylko w SAM7S? Jak jest w SAM7X?
  • #17 5122602
    Ch.M.
    Poziom 27  
    Posty: 1009
    Pomógł: 62
    Ocena: 15
    Czy nie możesz dodać do swojego programu obsługi błędów? Wyłaczyć TWI jak najbardziej można. Do zresetowania TWI możesz użyć rejestru TWI Control Register -->TWI_CR ustawiając bit Software Reset -->SWRST.
    Nie znam się jeszcze na ARMach, ale pdf umiem czytać i zachęcam do lektóry :>
    Pozdrawiam
    Punkt za reflex dla Szopena :)
  • #18 5146321
    gufiak
    Poziom 21  
    Posty: 444
    Pomógł: 15
    Ocena: 2
    upanie, zgodzę się, że TWI w tym uC nie jest pozbawione błędów. Ale nie pisz głupot w stylu "nie używajcie sprzętowego TWI". Idąc dalej tym tropem, to może przestaniemy używać w ogóle mikroprocesorów, przerzucimy się spowrotem na bramki i przerzutniki. Są one bardziej odporne na zakłócenia, a błędy można popełniać jedynie samemu i tak samo je naprawiać. Jest wiele sytuacji gdzie użycie programowego I2C/TWI nie jest możliwe albo jest bardzo kłopotliwe. Przyznam, że pomęczyłem się trochę z uruchomieniem TWI, ale od tamtego czasu nie miałem z nim problemów. Najgorsze jest pierwsze uruchomienie i trzeba sporo kombinować, zamieniać miejscami różne odwołania do rejestrów żeby wszystko grało. A dodatkowo trzeba brać pod uwagę optymalizację kompilatora, która potrafi dodatkowo namieszać.
    Co do nieodporności TWI na zakłócenia, to wiele peryferiów w różnych uC jest dużo mniej odpornych na zakłócenia niż ich implementacja programowa. Lecz jeśli spodziewamy się zakłóceń, to niestety, ale nawet w przypadku implementacji programowych trzeba brać pod uwagę błędy transmisji.
  • #19 5153918
    adamusx
    Poziom 27  
    Posty: 977
    Pomógł: 94
    Ocena: 28
    Z TWI w SAMie są problemy, gdyż nie działa do końca tak jak jest opisane to w PDFie. U mnie w firmie problem został rozwiązany.. Obsługa TWI napisana bez opierania się o istniejące atmelowskie przykłady i ruszyło sprzętowe TWI z prędkością 400KHz.
  • #20 5163245
    upanie
    Poziom 22  
    Posty: 434
    Pomógł: 33
    Ocena: 7
    gufiak:
    To nie są głupoty. Źle mnie zrozumiałeś. Ja nie odradzam używania sprzętowego TWI zawsze i wszędzie ale tylko w SAM7S. W AVRkach działa idealnie i jak najbardziej można go używać.
    Co do zakłóceń to dyskwalifikuję sprzętowy TWI z użycia w SAM7S bezapelacyjnie. Piszesz, że pierwsze uruchomienie jest problematyczny a później już działa. No to ja nie wiem bo uruchomiłem TWI bardzo szybko bez żadnych problemów. Wystarczy dokładnie czytać dokumentację i erratę. Niemniej uwierz mi, że straciliśmy 1dzień na znalezienie przyczyny wykrzaczania się transmisji TWI po zamknięciu obudowy urządzenia. Tak się składa, że to urządzenie w środku generuje mnóstwo zakłóceń wszelakiej maści ale ze wszystkich elementów i ich peryferiów tylko TWI w SAM7S sprawiał problemy. Jeszcze raz powtarzam, że automat TWI potrafi wygenerować raz na jakiś (bardzo długi) czas dodatkowe, bardzo krótkie zbocze zegara, a tego nie ma w dokumentacji. To potrafi rozpieprzyć całą komunikację. Znalezienie tego nie jest proste ale się udało. Efekt jest taki, że napisałem programową wersję TWI i nie ma żadnych problemów.
  • #21 5172043
    gufiak
    Poziom 21  
    Posty: 444
    Pomógł: 15
    Ocena: 2
    upanie, może i faktycznie źle Ciebie zrozumiałem. W każdym bądź razie dzięki za poinformowanie o tym dodatkowym impulsie na zegarze. Warto o tym wiedzieć jak coś będzie szwankowało. Chociaż może nie będzie mi ta informacja już potrzebna, bo STM32 wydaje się być ciekawą alternatywą dla SAM7S (podobna cena, podobny pobór prądu, lepsza wydajność) i jak narazie errata dla STM32 nie straszy tak jak errata dla SAM7S (co wcale mnie tak zupełnie nie uspokaja). Zaniedługo się okaże czy to faktycznie jest ciekawa alternatywa.
  • #22 5657041
    grzeg_w
    Poziom 10  
    Posty: 28
    Programuje mikrokontroler AT91SAM7X. Mam problem z odczytem flagi NACK informującej o braku odpowiedzi urządzenia slave na wysyłany adres urządzenia.
    Nota aplikacyjna napisał:
    The read sequence begins by setting the START bit. After the start condition has been sent, the
    master sends a 7-bit slave address to notify the slave device. The bit following the slave address
    indicates the transfer direction, 1 in this case (MREAD = 1 in TWI_MMR). During the acknowledge
    clock pulse (9th pulse), the master releases the data line (HIGH), enabling the slave to pull
    it down in order to generate the acknowledge. The master polls the data line during this clock
    pulse and sets the NACK bit in the status register if the slave does not acknowledge the byte.


    Użyłem następującego kodu:
    BYTE TWI_MasterReceive( BYTE deviceAddr, ULONG internalAddr, BYTE *data, ULONG len )
    {
         volatile ULONG status;
    
         // prepare read transmission
         AT91C_BASE_TWI->TWI_MMR = ( deviceAddr << 16 ) | AT91C_TWI_MREAD | AT91C_TWI_IADRSZ_2_BYTE;
         // dummy read the receive holding register
         AT91C_BASE_TWI->TWI_RHR;
         // set slave device internal address
         AT91C_BASE_TWI->TWI_IADR = internalAddr;
         // send start condition
         AT91C_BASE_TWI->TWI_CR = AT91C_TWI_START;
    
         // wait for complete (or no device)
         do
         {
        	 // check status
             status = AT91C_BASE_TWI->TWI_SR;
             // watch for address NACK
             if( status & AT91C_TWI_NACK )
            	 return TWI_ERROR_NODEV;
         }
         while(!( status & AT91C_TWI_RXRDY ));
    
         // receive data
    

    Program blokuje się w pętli while, a wartość rejestru statusowego wynosi 0b1101. Maska AT91C_TWI_NACK = (0x1 << 8 ), a AT91C_TWI_RXRDY = (0x1 << 1). Funkcja powinna prawidłowo zwracać TWI_ERROR_NODEV.
  • #23 5658389
    gufiak
    Poziom 21  
    Posty: 444
    Pomógł: 15
    Ocena: 2
    Lektura erraty często jest ważniejsza niż lektura samej dokumentacji uC. Polecam zapoznać się z punktem 41.2.9.3 erraty: "TWI: NACK Status Bit Lost".
    Chodzi o to, że nie można odczytywać rejestru TWI_SR przed ustawieniem flagi TXCOMP, w przeciwnym razie flaga NACK może nie zostać ustawiona. Rozwiązaniem jest skonfigurowanie przerwania dla flagi TXCOMP, w którym będzie ustawiana własna flaga (np. "volatile BYTE TxComp_flag"). W powyższym kodzie trzeba dodać przed pętlą while następujący kod:
    while (!TxComp_flag) {}
    TxComp_flag = 0;
    Flagę też trzeba zerować, jeśli przerwanie dla TXCOMP jest włączone w momencie ustawiania flagi MSEN, czyli podczas włączania TWI. Ustawienie flagi MSEN powoduje również ustawienie flagi TXCOMP.
    Trzeba również pamiętać, że flaga NACK jest ustawiana wraz z TXCOMP, czyli już po zakończeniu transmisji wszystkich danych. Zatem powyższy kod jest poprawny przy odbiorze jednego bajtu danych podczas jednej ramki. Jeśli ma być odczytane kilka bajtów w jednej ramce, to ten kod nie będzie działać. W takim przypadku trzeba skorzystać dodatkowo z przerwania RXRDY. I wtedy jeśli najpierw zostanie wykonane przerwanie dla flagi RXRDY, to jest komunikacja z danym slavem, a jak pierwsze zostanie wykonane przerwanie dla TXCOMP i będzie ustawiona flaga NACK, to oznacza, że nie ma z nim komunikacji. Użycie przerwania dla flagi RXRDY ma dodatkową zaletę. Czekanie na ustawienie flagi RXRDY to w zależności od prędkości transmisji i częstotliwości zegara PCK od ok. 100 do ok. 10000 "pustych" cykli zegara. I to wszystko dla jednego przesłanego bajta. Za ten czas można zrobić wiele innych rzeczy albo uśpić procesor dla zaoszczędzenia odrobiny prądu.
  • #24 8287664
    Menuet
    Poziom 19  
    Posty: 162
    Pomógł: 39
    Ocena: 122
    Witam,

    Odświeżę troszkę temat, bo nie udało mi się znaleźć rozwiązania po paru dniach... Jestem jak na razie początkujący w ARMach i mam pewien poważny problem z TWI, mianowicie korzystam z "gotowca" do wysyłania danych po I2C a dokładniej do przedwzmacniacza TDA7313. Mój problem polega na tym, że zaraz po wysłaniu "START" i pierwszego bajtu danych sprzętowe TWI nie chce zgłosić, że wysłało już dane i programik sobie "wisi" a na dodatek na SCL jest cały czas częstotliwość taktująca magistrale (ok 26kHz). Na magistrali mam tylko ten przedwzmacniacz i dwa oporniki podciągające po 2,2k, ten TDA jest na pewno sprawny, bo jak podłączę pod AVRa to wszystko działa jak należy. Poniżej zamieszczam swój kod, może ktoś go przejrzy sprawnym okiem i zauważy jakiś banalny błąd.
    Pozdrawiam, Menuet.

    
    void TWI_Init()
    {
    	AT91C_BASE_PMC->PMC_PCER = 1 << AT91C_ID_TWI;
    	AT91C_BASE_PIOA->PIO_ASR = AT91C_PA3_TWD | AT91C_PA4_TWCK;
    	AT91C_BASE_PIOA->PIO_PDR = AT91C_PA3_TWD | AT91C_PA4_TWCK;
    	AT91C_BASE_PIOA->PIO_MDER = AT91C_PA3_TWD | AT91C_PA4_TWCK;
    	AT91C_BASE_PIOA->PIO_PPUDR = AT91C_PA3_TWD | AT91C_PA4_TWCK;
    	AT91C_BASE_TWI->TWI_CR = AT91C_TWI_SWRST;
    	AT91C_BASE_TWI->TWI_CWGR = 	(2 << 16) |		// CKDIV
    								(255 << 8) |	// CHDIV
    								(255 << 0);		// CLDIV
    	AT91C_BASE_TWI->TWI_CR = AT91C_TWI_MSEN;
    	
    	
    }
    
    void TWI_Transmit(char * datatosend, unsigned int datalength, unsigned int address, unsigned int intaddress)
    {//transmit (datalength) bytes from slave at (address) writing to (datatoreceive)
     
     AT91C_BASE_TWI->TWI_CR = AT91C_TWI_START; //send start condition
     AT91C_BASE_TWI->TWI_IADR = intaddress; 
      AT91C_BASE_TWI->TWI_MMR = (AT91C_TWI_DADR & (address << 16))| AT91C_TWI_IADRSZ_1_BYTE; //set slave address and set data direction for transmit
      AT91C_BASE_TWI->TWI_THR = *datatosend++; //load in first byte and increment data pointer
      datalength--;
      
      while (datalength)
     {
        datalength--;
       while (!(AT91C_BASE_TWI->TWI_SR & AT91C_TWI_TXRDY)) ; //wait for last tx to finish
        AT91C_BASE_TWI->TWI_THR = *datatosend++; //load in new byte
      }
      while (!(AT91C_BASE_TWI->TWI_SR & AT91C_TWI_TXRDY)) ; //wait for last tx to finish
      AT91C_BASE_TWI->TWI_CR = AT91C_TWI_STOP; //send stop condition
     while (!(AT91C_BASE_TWI->TWI_SR & AT91C_TWI_TXCOMP)) ; //wait for TWI tx to finish
      
    }
    
  • #25 9398512
    007mirek
    Poziom 11  
    Posty: 17
    Ocena: 4
    Witam

    Od kilku dni próbuje uruchomić programowe TWI dla AT91SAM7S64 oraz PCF8583 na nóżkach PA5 i PA6 nie chce mi to ciągle działać, czy ma może ktoś jakiegoś sampla juz pisałem w poście https://www.elektroda.pl/rtvforum/topic1651785.html#9280342 ale jakoś nikt mi nic nie doradził.
    Z góry dziękuję
  • #26 9400615
    Menuet
    Poziom 19  
    Posty: 162
    Pomógł: 39
    Ocena: 122
    Witam,
    Ja skleciłem dosyć dawno już procedurkę programowego wysyłania danych na tą magistrale (tylko wysyłanie, odbiór nie był mi potrzebny...). Wszystko działa jak należy i można podłączyć magistralę pod dowolne piny procesora.
    Poniżej zamieszczam te procedurki.
    Pozdrawiam, Menuet.

    Kod: C / C++
    Zaloguj się, aby zobaczyć kod
  • #27 9401711
    007mirek
    Poziom 11  
    Posty: 17
    Ocena: 4
    Wielki dzięki

    zaimplementuje i zobaczę
  • #28 9435456
    baldur
    Poziom 1  
    Posty: 1
    Mógłby ktoś poratować w temacie?

    Powyższy kod do zapisu działa, ale kiedy próbuję odpalić odczyt z pamięci eeprom (at24c128) nieustannie otrzymuje brak odpowiedzi tj. wygląda jakby pamięć nie reagowała na pulsy zegara i ustawiała nieustannie zero.
    Wykorzystuje at91sam7x256 na płytce rozwojowej, ale to chyba nie ma wielkiej zmiany w temacie w stosunku do sam7s

    Mój kod:
    Kod: text
    Zaloguj się, aby zobaczyć kod


    reszta jak w kodzie powyżej, z wyjątkiem tego, że poprawiłem z SCl_IS_HIGH na SCL_IS_HIGH.

    wykorzystanie w kodzie:

    char adresWew[2];
    adresWew[0] = 0x00;
    adresWew[1] = 0x00;
    odbior = odb(adres,2,adresWew);

    Jakikolwiek działający kod też byłby pomocny.
    Z góry dzięki za jakąkolwiek pomoc,
    pozdrawiam.
  • #29 9441225
    007mirek
    Poziom 11  
    Posty: 17
    Ocena: 4
    Witam troszkę przerobiłem kod choć nie wiem czy będzie działał

    void czytaj_bajt(unsigned char* byte) {
    unsigned char i;

    //SCL_SET_LOW;
    SDA_SET_HIGH;
    delay_twi(CZAS_I2C);
    for (i=0;i<8;i++)
    {
    SCL_SET_HIGH;
    delay_twi(CZAS_I2C);
    byte <<= 0x01;
    byte |= (I2C_PIO->PIO_PDSR & SDA_MASK)
    SCL_SET_LOW;
    delay_twi(CZAS_I2C);

    }
    //9th cycle
    jesli ack to :

    SDA_SET_LOW
    Delay_twi(CZAS_I2C);
    SCL_SET_H();
    Delay_twi(CZAS_I2C);
    SCL_SET_LOW;

    jesli nak to :

    SDA_SET_HIGH;
    Delay_twi(CZAS_I2C);
    SCL_SET_H();
    Delay_twi(CZAS_I2C);
    SCL_SET_LOW;

    }

    #define SCL_SET_HI I2C_PIO->PIO_ODR = I2C_SCL
    #define SCL_GET (I2C_PIO->PIO_PDSR & I2C_SCL)

    static void SCL_SET_H(void)
    {
    SCL_SET_HI;
    while (!SCL_GET) {
    Delay_uS(2);

    }
    }

    wysyłasz do urządzenia zapytanie :

    U16 I2C_Gotowe(U8 adres)
    {
    U8 Wynik = 0;
    Start();
    Wynik = !SendByte(adres & 0xFE);
    Stop();
    return Wynik;
    }

    gdy zwroci jeden to urzadzenie jest dostepne

Podsumowanie tematu

✨ Dyskusja dotyczy problemów i metod konfiguracji rejestrów mikrokontrolera AT91SAM7S w celu uruchomienia sprzętowego interfejsu TWI (I2C). Użytkownicy wymieniają się fragmentami kodu inicjalizującego TWI, wskazując na konieczność konfiguracji rejestrów takich jak PWM_PCER (włączenie zegara dla TWI), PIOA_ASR, PIOA_BSR, PIOA_MDER, PIOA_OER oraz PIOA_ODR dla odpowiedniego ustawienia pinów SDA i SCL. Podkreślają znaczenie poprawnego ustawienia rezystorów podciągających (np. 2,2kΩ do 3,3V) oraz wpływ napięcia zasilania na stabilność działania magistrali. Wskazano, że dokumentacja i errata mikrokontrolera zawierają istotne informacje, m.in. o problemach z flagą NACK i konieczności odpowiedniego odczytu rejestru statusu TWI_SR po zakończeniu transmisji (flaga TXCOMP). Część użytkowników zgłasza niestabilność sprzętowego TWI w AT91SAM7S, sugerując alternatywę w postaci programowej emulacji I2C, zwłaszcza w środowiskach z zakłóceniami elektromagnetycznymi. Przykłady kodu obejmują konfigurację rejestrów TWI_MMR, TWI_CWGR, TWI_IADR oraz obsługę przerwań i flag statusowych. Dyskutowano także o problemach z odczytem i zapisem do pamięci EEPROM AT24C128 oraz o różnicach w działaniu TWI między modelami SAM7S i SAM7X. Wskazano, że poprawne działanie wymaga dokładnego czytania dokumentacji, errat oraz testów sprzętowych, a w niektórych przypadkach lepszym rozwiązaniem jest implementacja programowego TWI. Poruszono również temat alternatywnych mikrokontrolerów, takich jak STM32, jako potencjalnie bardziej stabilnych w obsłudze I2C.
Podsumowanie wygenerowane przez AI na podstawie treści dyskusji.
REKLAMA