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

Jak poprawnie użyć sprzętowego I2C (TWI) w Atmega8? Przykład biblioteki i kodu

Fantomen 25 Lut 2007 15:38 8841 16
REKLAMA
  • #1 3616223
    Fantomen
    Poziom 20  
    Posty: 482
    Pomógł: 3
    Ocena: 19
    Chce użyć sprzętowego i2c w atmedze8. Znalazlem taka biblioteke na elektrodzie:
    //*********************************************************
    // WARUNEK STARTU LUB PONOWNEGO STARTU TWI
    // funkcja zwroci 1 jezeli warunek startu lub ponownego startu nie zostal wystawiony
    // funkcja zwroci 0 jezeli warunek startu lub ponownego startu
    //*********************************************************
    unsigned char twi_start(void)
    {     
      TWCR |= 0b11100100; // wysylij warunek startu i ustaw TWEA
      while (!(TWCR & 0b10000000)); // czekaj na TWRC.TWINT=1
      TWCR &= 0b01011111; // TWCR.TWSTA=0
      if (((TWSR & 0xF8) != 0x08) && ((TWSR & 0xF8) != 0x10))
        {
          return 1; // jezeli status != kod poprawnego startu to zwroc error
        };
      return 0; // zwroc kod !error
    }
    //*********************************************************
    // WARUNEK SOPU TWI
    //*********************************************************
    void twi_stop(void)
    {   
      TWCR |= 0b10010100; // ustaw bit stopu i twint
    }
    //*********************************************************
    // ZAPIS BAJTU DANYCH NA TWI
    // funkcja zwroci 1 jezeli slave nie wystawi ACK
    // funkcja zwroci 0 jezeli slave wystawi ACK
    //*********************************************************
    unsigned char twi_wr(unsigned char data_byte)
    {     
      TWDR = data_byte; // zaladuj TWDR danymi
      TWCR |= 0b10000000; // wyzeruj flage TWCR.TWINT
      while (!(TWCR & 0b10000000)); // czekaj na TWRC.TWINT=1 oznaczajacy wyslanie TWRD i odebranie ACK/!ACK
      if (((TWSR & 0xF8) != 0x18) && ((TWSR & 0xF8) != 0x28) && ((TWSR & 0xF8) != 0x40))
        {
          return 1; // jezeli status != kod odebrania ACK to zwroc error
        };
      return 0; // zwroc kod !error   
    }
    //*********************************************************
    // ODCZYT BAJTU DANYCH Z TWI
    // funkcja zwroci 1 jezeli afery
    // funkcja zwroci bajt danych jezeli odbior poprawny
    // jezeli ack=1 master reciver wystawi na szyne ack
    // jezeli ack=0 master reciver wystawi na szyne no_ack
    //*********************************************************
    unsigned char twi_rd(unsigned char ack)
    {
      if (ack == 1)
        {
          TWCR |= 0b10000000; // wyzeruj flage TWCR.TWINT     
        }
      else
        { 
          TWCR &= 0b10111111; // wyzeruj flage TWCR.TWINT & TWCR.TWEA     
        };
      while (!(TWCR & 0b10000000)); // czekaj na TWRC.TWINT=1 oznaczajacy odebranie TWRD i odebranie ACK/!ACK 
      if (((TWSR & 0xF8) != 0x50) && ((TWSR & 0xF8) != 0x58))
        {
          return 1; // jezeli status != kod poprawnego startu to zwroc error
        };
      return TWDR; // zwroc dane z i2c
    } 


    Chce odczytac dane z przetwornika A/C.
    Domyslam sie ze trzeba to zrobic w ten sposob:

    twi_start();
    twi_rd();
    pomiar1=TWDR;
    twi_stop();

    ale gdzie podac adres przetwornika? Prosze pomozcie bo siedze juz nad tym od 2 dni i mi nic nie wychodzi.
  • REKLAMA
  • #2 3617576
    DosinskY
    Poziom 19  
    Posty: 332
    Pomógł: 24
    Ocena: 9
    Widzę, że dokopałeś się do funkcji które kiedyś skrobnąłem ;) Działać działają ale były pisane w pośpiechu...właśnie tylko po to żeby działały :D Trzeba jeszcze nad nimi popracować i byłbym ostrożny podczas korzystania z nich. Należy zmienić chociażby sposób zwracania kodów błędów.

    Sposób komunikacji z przetwornikiem A/C powinien być opisany w jego dokumentacji a i pewnie na forum też znajdziesz kilka konkretnych przykładów. O I2C było sporo. Co do adresu przetwornika... Trzeba go wpisać do rejestru TWAR, co jest opisane w dokumentacji ATmegi (dla ATmega16 str. 180).
  • #3 3618070
    johny_w
    Poziom 24  
    Posty: 674
    Pomógł: 80
    Ocena: 64
    Witam. Może to coś Ci pomoże - jest to biblioteka do obsługi zegarka PCF8583, ale myślę że uda Ci się ją zaadoptować do obsługi przetwornika.

    Adres urządzenia zdefiniowany jest na samym początku. SLA_W to adres do zapisu, a SLA_R - adres do odczytu.

    Tobie najbardziej przydadzą się funkcje Write i Read (dwie pierwsze). Pierwsza zapisuje bajt do pamięci zegarka, a druga odczytuje jeden bajt z pamięci.

    Opis poszczególnych części funkcji (tj. start, stop, itd.) znajdziesz w karcie ATmegi.
    Załączniki:
    • pcf8583.h (6.72 KB) Musisz być zalogowany, aby pobrać ten załącznik.
  • REKLAMA
  • #4 3618736
    Fantomen
    Poziom 20  
    Posty: 482
    Pomógł: 3
    Ocena: 19
    Nie można pobrać pliku. Mogłbyś to wstawić jeszcze raz albo wysłać mi na maila: fantomen(_at_)wp.pl?
  • REKLAMA
  • #5 3618899
    DosinskY
    Poziom 19  
    Posty: 332
    Pomógł: 24
    Ocena: 9
    Ale dałem plamę :D Oczywiście, że adresu slave nie wpisuje się do TWAR. Naprawiam swój błąd. Tak jak napisał Jj_Johnys adres podaje się po warunku startu. Dla odczytu będzie to wyglądało tak:

    1) start
    2) wyslij adres urzadzenia do odczytu
    3) wyslij numer pierwszego rejestru do odczytu
    4) ponowiony start
    5) odczytaj bajt danych i wyslij ack jesli kolejny rejestr ma byc odczytany
    6) odczytaj bajt danych i wyslij no ack jesli to byl ostatni rejestr do odczytania
    7) stop

    Twój A/C zapewne ma autoinkrementację rejestrów podczas odczytywania, czyli jeżeli wyślesz numer pierwszego rejestru do odczytu np. 0x00h to po odebraniu bajtu danych i wysłaniu ack adres zostanie automatycznie zwiększony o jeden na 0x01h dzięki czemu istnieje możliwość odczytu kilku kolejnych rejestrów za jednym zamachem.
  • #6 3618915
    Goompas
    Poziom 13  
    Posty: 123
    Pomógł: 3
    Ocena: 2
    Jak wroce do domu z pracy, zalacze funkcje z ktorych kiedys z powodzeniem korzystalem, programujac eeprom oraz sterujac wzmacniaczem.

    Przepraszam ze tak pozno, ale wczesniej nie mialem jak tego dodac.
    Załączniki:
    • mojtwi.h (4.42 KB) Musisz być zalogowany, aby pobrać ten załącznik.
  • #7 3624295
    Fantomen
    Poziom 20  
    Posty: 482
    Pomógł: 3
    Ocena: 19
    Dalej nie mogę użyć tych bibliotek co podalem na poczatku. Proboje to zrobic tak:

    
    int main(void)
    {
    DDRB=0xFF;
    PORTB =0xFF;
    lcd_init();
    
    twi_start();
    twi_wr(0x91);
    twi_wr(0x00);
    twi_stop();
    
        while(1)
        {
    
    twi_start();
    tmp=twi_rd(0);
    ltoa(tmp,bufor1,10); 
    write_text(bufor1);
    twi_stop();	
    waitms(40);					
    write_command(0x01); 
      
      }	
    return (0);
    }


    Co dalej źle robie? Mogby ktoś poprawić ten kod tak, by był dobrze? Zaczynam podejrzewać, że mam przetwornik rypniety.
  • REKLAMA
  • #8 3625551
    johny_w
    Poziom 24  
    Posty: 674
    Pomógł: 80
    Ocena: 64
    Witam. Przede wszystkim nie podałeś typu ADC - chciałem sprawdzić kod, ale nawet nie wiem jakie rejestry i funkcje posiada ten układ.

    Jeżeli chodzi o Twój program, procedura jest następująca:

    1. Zapis 1 bajtu:
    twi_start ();
    twi_wr (ADRES_DO_ZAPISU);
    twi_wr (NUMER_REJESTRU);
    twi_wr (DANA);
    twi_stop ();


    2. Odczyt bajtu:
    twi_start ();
    twi_wr (ADRES_DO_ZAPISU);
    twi_wr (NUMER_REJESTRU);
    twi_start ();
    twi_wr (ADRES_DO_ODCZYTU);
    DANA = twi_rd (0);     //bez ACK
    twi_stop ();


    No i podstawowe pytanie: dałeś zewnętrzne rezystory na obu liniach?
  • #9 3626098
    Fantomen
    Poziom 20  
    Posty: 482
    Pomógł: 3
    Ocena: 19
    Przetwornik to PCF8591. Rezystory dałem. Chce odczytać wartość z niego. Robię to teraz w ten sposob:
    
    int main(void)
    {
    DDRB=0xFF;
    PORTB =0xFF;
    lcd_init();
    
        while(1)
        {
    twi_start ();
    twi_wr (0x91);
    twi_wr (0x00);
    twi_start ();
    twi_wr (0x91);
    tmp=twi_rd (0);     
    twi_stop ();
    
    ltoa(tmp,bufor1,10);
    write_text(bufor1);
    waitms(40);               
    write_command(0x01);
     
      }   
    return (0);
    }
    

    Dalej nie działa. Moze cos w tych adresach zle robie? :cry:
  • Pomocny post
    #10 3627453
    DosinskY
    Poziom 19  
    Posty: 332
    Pomógł: 24
    Ocena: 9
    Sprawdz podlaczenie pinow A0, A1, A2. Dla A0, A1, A2 podlaczonych do masy adres do odczytu to 91h a do zapisu 90h. Zgodnie z:
    Cytat:

    2. Odczyt bajtu:
    Kod:
    twi_start ();
    twi_wr (ADRES_DO_ZAPISU);
    twi_wr (NUMER_REJESTRU);
    twi_start ();
    twi_wr (ADRES_DO_ODCZYTU);
    twi_rd (DANA); //bez ACK
    twi_stop ();


    Powinno być tak:
    
    twi_start ();
    twi_wr (0x90);
    twi_wr (0x00);
    twi_start ();
    twi_wr (0x91);
    tmp=twi_rd (0);     
    twi_stop (); 


    Po włączeniu zasilania Control Byte przetwornika rowny jest 00h, a wiec będziesz odczytywał wartości napięć z kanału zerowego.
    Nie korzystałem nigdy z tego przetwornika ale powinno być tak jak napisałem. Pozdrawiam.
  • #11 3628486
    Fantomen
    Poziom 20  
    Posty: 482
    Pomógł: 3
    Ocena: 19
    Działa :-) Super.. jeszcze mialem jeden blad ale to przez swoja glupote. Dziekuje wszystkim.
  • #12 8345692
    kornel_p
    Poziom 11  
    Posty: 21
    Ocena: 1
    Witam!!

    Mam problem z TWI... Programuję na uC od niedawna, więc troche idzie mi to opornie ;/

    Mam układ który odczytuje natężenie oświetlenia z czujnika TSL2561T. uC Atmega8. piny oczywiście podłączone pod sprzętowe TWI. zastosowałem konwerter napięć (ze strony philipsa), aby czujnik działał prawidłowo z procesorem o logice 5V. przejściówka sprawdzona na innym czujniku i programowej obsłudze i2c.
    Jedynie samego czujnika TSL nie jestem pewien czy chodzi dobrze....

    Teraz program...
    Korzystam z bibliotek ze strony: http://radzio.dxp.pl/twi/

    mój kod:
    #define F_CPU 8000000UL
    
    #include <avr/io.h>
    #include "HD44780.h"
    #include "twi.h"
    #include <math.h>
    
    
    int main (void)
    
    {
    	int				ch0, bufor1;
    	
    	LCD_Initalize();
    	twistart();
    	twiwrite (0x29); //adres urządzenia (zwarty pin ADR_SEL do masy)
    	twiwrite (0x03); //powinno włączać czujnik (po starcie jest w stand-by). Tu program się zatrzymuje (sprawdzałem wstawiając co linijkę wyświetlanie różnych liter na lcd i poóźnienie 1s, zebym mógł zobaczyć gdzie się wiesza...
    
    	twistart();
    	twiwrite (0xAC);
    	twiwrite (0x0C);
    	ch0 = twiread (); //bez ACK
    	twistop (); 
    LCD_WriteText("B");
    	_delay_ms(1000);
    	ltoa(ch0,bufor1,10); 
    	LCD_WriteText(bufor1);
    }


    Dostaję takie warningi:
    Build started 30.7.2010 at 17:24:38
    avr-gcc  -mmcu=atmega8 -Wall -gdwarf-2 -std=gnu99 -DF_CPU=8000000UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -MD -MP -MT czujnik_oswietlenia.o -MF dep/czujnik_oswietlenia.o.d  -c  ../czujnik_oswietlenia.c
    ../czujnik_oswietlenia.c: In function 'main':
    ../czujnik_oswietlenia.c:33: warning: implicit declaration of function 'ltoa'
    ../czujnik_oswietlenia.c:34: warning: passing argument 1 of 'LCD_WriteText' makes pointer from integer without a cast
    ../czujnik_oswietlenia.c:33: warning: 'bufor1' is used uninitialized in this function
    avr-gcc -mmcu=atmega8 -Wl,-Map=czujnik_oswietlenia.map czujnik_oswietlenia.o HD44780.o twi.o     -o czujnik_oswietlenia.elf
    avr-objcopy -O ihex -R .eeprom -R .fuse -R .lock -R .signature  czujnik_oswietlenia.elf czujnik_oswietlenia.hex
    avr-objcopy -j .eeprom --set-section-flags=.eeprom="alloc,load" --change-section-lma .eeprom=0 --no-change-warnings -O ihex czujnik_oswietlenia.elf czujnik_oswietlenia.eep || exit 0
    avr-objdump -h -S czujnik_oswietlenia.elf > czujnik_oswietlenia.lss
    
    AVR Memory Usage
    ----------------
    Device: atmega8
    
    Program:     722 bytes (8.8% Full)
    (.text + .data + .bootloader)
    
    Data:          4 bytes (0.4% Full)
    (.data + .bss + .noinit)
    
    
    Build succeeded with 3 Warnings...
    
    


    Proszę o pomoc, bo nie kumam co jest nie tak ;/
  • #13 8346608
    tmf
    VIP Zasłużony dla elektroda
    Posty: 14318
    Pomógł: 2090
    Ocena: 2206
    A zainkludowałeś plik nagłówkowy z definicjami użytych funkcji? Nie.
  • #14 8348114
    kornel_p
    Poziom 11  
    Posty: 21
    Ocena: 1
    fakt... zapomniałem dodać twi.h... poprawiłem kod powyżej, ale dalej staje w tym samym miejscu. Link do datasheet'a: TSL2561T datasheet
  • #15 8348531
    tmf
    VIP Zasłużony dla elektroda
    Posty: 14318
    Pomógł: 2090
    Ocena: 2206
    Nie staje, tylko generuje ostrzeżenia, program się kompiluje. TWI.h nie załatwia wszystkiego, bo ltoa jest definiowane w innym pliku, Pozostałe ostrzeżenia czy są istotne to już zależy od implementacji funkcji.
  • #16 8348552
    gaskoin
    Poziom 38  
    Posty: 4159
    Pomógł: 436
    Ocena: 102
    poza tym ltoa przyjmuje zupełnie inne parametry
  • #17 8362258
    kornel_p
    Poziom 11  
    Posty: 21
    Ocena: 1
    Oto działający kod na odczyt kanałów Ch0 i CH1 z TSL2561 i wyświetlenie ich na LCD:

    #define F_CPU 8000000UL
    #define SLA1_W		0x52
    #define SLA1_R		0x53
    #define data0low	0x8c
    #define data0high	0x8d
    #define data1low	0x8e
    #define data1high	0x8f
    
    #include <avr/io.h>
    #include "HD44780.h"
    #include "twi.h"
    #include <math.h>
    
    
    
    
    int main (void)
    
    {
    	int	ch0low, ch0high, ch1low, ch1high;
    
    	char array[4];
    
    	
    	LCD_Initalize();
    
    
     /*-----Inicjalozacja czujnika-----*/
    	twistart();
    	twiwrite (SLA1_W);
    	twiwrite (0x01);
    	twiwrite (0x03);
    	twistop ();
    
    
     while (1)
     { 
    
    /*-----Odczyt CH0low-----*/
    	twistart();
    	twiwrite (SLA1_W);
    	twiwrite (data0low);
    	twistart();
    	twiwrite (SLA1_R);
    	ch0low = twiread (NOACK); //bez ACK
    	twistop ();
    
    /*-----Odczyt CH0high-----*/
    	twistart();
    	twiwrite (SLA1_W);
    	twiwrite (data0high);
    	twistart();
    	twiwrite (SLA1_R);
    	ch0high = twiread (NOACK); //bez ACK
    	twistop ();
    
    /*-----Odczyt CH1low-----*/
    	twistart();
    	twiwrite (SLA1_W);
    	twiwrite (data1low);
    	twistart();
    	twiwrite (SLA1_R);
    	ch1low = twiread (NOACK); //bez ACK
    	twistop ();
    
    /*-----Odczyt CH1high-----*/
    	twistart();
    	twiwrite (SLA1_W);
    	twiwrite (data1high);
    	twistart();
    	twiwrite (SLA1_R);
    	ch1high = twiread (NOACK); //bez ACK
    	twistop ();
    
    	LCD_GoTo(0,0);
    	LCD_WriteText("ch0 0x");
    	LCD_GoTo(6,0);
    	snprintf (array, sizeof(array), "%x", ch0low);
    	LCD_WriteText(array);
    	LCD_GoTo(8,0);
    	LCD_WriteText(" 0x");
    	LCD_GoTo(11,0);
    	snprintf (array, sizeof(array), "%x", ch0high);
    	LCD_WriteText(array);
    
    	LCD_GoTo(0,1);
    	LCD_WriteText("ch1 0x");
    	LCD_GoTo(6,1);
    	snprintf (array, sizeof(array), "%x", ch1low);
    	LCD_WriteText(array);
    	LCD_GoTo(8,1);
    	LCD_WriteText(" 0x");
    	LCD_GoTo(11,1);
    	snprintf (array, sizeof(array), "%x", ch1high);
    	LCD_WriteText(array);
     }
    }

Podsumowanie tematu

LABEL_AI_GENERATED
Dyskusja dotyczy poprawnego wykorzystania sprzętowego interfejsu I2C (TWI) w mikrokontrolerze Atmega8, ze szczególnym uwzględnieniem implementacji i przykładów kodu. Użytkownicy omawiają funkcje startu, zapisu i odczytu danych, zwracając uwagę na prawidłowe adresowanie urządzeń slave oraz konieczność stosowania zewnętrznych rezystorów podciągających na liniach SDA i SCL. Przykładowo, dla przetwornika analogowo-cyfrowego PCF8591 wskazano, że adres do zapisu to 0x90, a do odczytu 0x91, z uwzględnieniem stanu pinów A0, A1, A2. Omówiono także sekwencję komunikacji: start, wysłanie adresu z bitem zapisu, numeru rejestru, ponowny start, adres z bitem odczytu, odczyt danych i stop. Wskazano błędy w pierwotnym kodzie, takie jak nieprawidłowe użycie rejestru TWAR czy błędne adresy. W dalszej części dyskusji pojawił się temat obsługi czujnika natężenia światła TSL2561T z wykorzystaniem Atmega8 i sprzętowego TWI, wraz z przykładowym kodem inicjalizacji i odczytu kanałów pomiarowych. Podkreślono znaczenie dołączenia odpowiednich plików nagłówkowych i prawidłowej obsługi funkcji konwersji danych. Całość wskazuje na konieczność dokładnego zapoznania się z dokumentacją urządzeń, prawidłowego adresowania i sekwencji komunikacji I2C oraz stosowania odpowiednich rezystorów podciągających dla stabilnej pracy magistrali TWI w Atmega8.
Podsumowanie AI na podstawie dyskusji. Może zawierać błędy.
REKLAMA