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

Programowy I2C problem z kodem

maly_elektronik 08 Kwi 2009 23:45 2756 15
REKLAMA
  • #1 6391267
    maly_elektronik
    Poziom 23  
    Witam
    Już od kilku dni walczę z programowym i2c. Niestety nie bardzo mi to wychodzi :(
    Mój kod wygląda następująco:
    
    #include <avr/io.h>
    #include <util/delay.h>
    
    #define DDR DDRC
    #define PORT PORTC
    #define PIN  PINC
    //
    #define SDA 1
    #define SCL 0
    //////////////////////////////////////////////////////////////////////////////////////////
    void i2c_write(unsigned char data)
    {
    DDR |= _BV(SDA); 
    DDR |= _BV(SCL);
    
    char index;
    PORT &= ~_BV(SCL);
     for(index=0; index<8; index++)
     { 
      if(data & 0x80)
      PORT |= _BV(SDA); // ustawienie 1 jeżeli 1
      else
      PORT &= ~_BV(SDA);// ustawienie 0 jeżeli 0
      _delay_us(10); // opuźnienie dla uzyskania prędkości 100kHz
      PORT |=  _BV(SCL);// ustawienie zegara na 1 w celu odczytania danych przez odbiornik
      _delay_us(10); // opuźnienie dla uzyskania prędkości 100kHz
      PORT &= ~_BV(SCL);
      data<<=1;
     }
    }
    ///////////////////////////////////////////////////////////////////////////////////////////
    unsigned char i2c_read(unsigned char ACK)
    {
    DDR &= ~_BV(SDA); 
    DDR |= _BV(SCL);
    
    unsigned char data=0x00;
    char index;
    
    PORT &= ~_BV(SCL);
     for(index=0; index<8; index++)
     { 
    data<<=1;
      _delay_us(10); // opuźnienie dla uzyskania prędkości 100kHz
      PORT |=  _BV(SCL);
      if(bit_is_clear(PINC,SDA))
      data = data;
      else
      data = (data | 0x01);
      _delay_us(10); // opuźnienie dla uzyskania prędkości 100kHz
      PORT &= ~_BV(SCL);// ustawienie zegara na 0 w celu zmiany danych wysylanych przez odbiornik
     }
    
    
    
    
     if(ACK)
     {
      PORT |= _BV(SDA);
      _delay_us(10);
      PORT |=  _BV(SCL);
      _delay_us(10);
      PORT &= ~_BV(SCL);
     }
    return data;
    }
    ///////////////////////////////////////////////////////////////////////////////////////////
    void i2c_start(void)
    {
     DDR |= _BV(SDA);
     DDR |= _BV(SCL);
     PORT |= _BV(SDA);
     _delay_us(10);
     PORT |=  _BV(SCL);
     _delay_us(10);
     PORT &= ~_BV(SDA);
     _delay_us(10);
     PORT &= ~_BV(SCL);
    } 
    ///////////////////////////////////////////////////////////////////////////////////////////
    void i2c_stop(void)
    {
     DDR |= _BV(SDA); 
     DDR |= _BV(SCL);
     PORT &= ~_BV(SDA);
     _delay_us(10);
     PORT |=  _BV(SCL);
     _delay_us(10);
     PORT |= _BV(SDA);
     _delay_us(10);
     PORT &= ~_BV(SCL);
    } 
    ///////////////////////////////////////////////////////////////////////////////////////////
    


    Czy widzi ktoś w nim jakieś błędy :?:
    PS Program ma emulować i2c z prędkością 100kHz. Zastosowany przy procesorze kwarc to 16MHz (konfiguracja fusów jest odpowiednia dla kwarcu zewnętrznego).

    Z góry dziękuje za pomoc :)
    Pozdrawiam maly_elektronik
  • REKLAMA
  • #2 6391500
    dawid512
    Poziom 32  
    Na czym dokładnie polega twój problem? Nie możesz nic odczytać/zapisać na innym urządzeniu typu eeprom, RTC ? Po za tym jaki model uc i czy fuse bit CKOPT(niektóre modele) zaprogramowany?
  • #3 6391634
    maly_elektronik
    Poziom 23  
    Tak chce się dogadać z kostką sta015 (dekoderem MPEG). Układ sta015 jest na pewno sprawny gdyż odpowiada poprawnie gdy wysyłam komendy ze sprzętowego i2c. Na potrzeby projektu muszę mieć i2c programowy, niestety najwyraźniej mam coś źle w kodzie czego nie potrafię poprawić :(
    PS Po co ten fus CKOPT :?: Modelem uC jest atmega16 z kwarcem zewnętrznym 16MHz
  • #4 6392399
    dawid512
    Poziom 32  
    To może pokaż ustawienia fuse bitów. Fuse bit CKOPT ustawia się po to by móc podłączyć zew. kwarc > 8MHz.
  • REKLAMA
  • #5 6392466
    piti___
    Poziom 23  
    Przy wysylaniu ACK nie przestawiasz rejestru DDR na wyjscie. Masz zewnętrzne podciąganie na lini SDA ? Jeśli nie, to musisz podczas odbierania danych podciągać w procesorze.
  • REKLAMA
  • #6 6393256
    maly_elektronik
    Poziom 23  
    dawid512 -> mam włączony ten fuse bit :) Ale dzięki za wyjaśnienie o co w nim chodzi :)
    piti__ -> Mam zewnętrzne pull up'y.
    Macie może jeszcze jakieś pomysly :?:
  • #7 6394429
    zumek
    Poziom 39  
    maly_elektronik napisał:
    ...Macie może jeszcze jakieś pomysly :?:

    Napisz kod w zgodzie ze specyfikacją I2C pamiętając , że piny urządzeń podłączonych do tej magistrali, powinny być typu OC(OD).
    W skrócie:
    a)chcesz wystawić "zero" - "zwierasz" linię magistrali do GND
    b)chcesz wystawić "jeden" - "zwalniasz" linię, czyli pin podłączony do tej linii, przechodzi w stan wysokiej impedancji.
    Np. kawałek gotowca ;)
    
    #define I2CPORT	  PORTD
    #define I2CPIN		PIND
    #define I2CDDR		DDRD
    #define SCLPIN		4
    #define SDAPIN		5
    //-----------------------------
    #define SCL_H() {I2CDDR&=~(1<<SCLPIN);}
    #define SCL_L() {I2CDDR|=(1<<SCLPIN);}
    #define SDA_H() {I2CDDR&=~(1<<SDAPIN);}
    #define SDA_L() {I2CDDR|=(1<<SDAPIN);}
    #define R_SDA() ( ((I2CPIN &(1<<SDAPIN))>>SDAPIN) )
    //...
    void i2c_init(void)
    {
       SCL_H();SDA_H();
       I2CPORT&=~((1<<SDAPIN) | (1<<SCLPIN));
       I2CDELAY();
    }
    
    void i2c_start(void)
    {
       SDA_H();
       SCL_H();
       I2CDELAY();
       SDA_L();
       I2CDELAY();
       SCL_L();
       I2CDELAY();
    }
    //...
    
  • #8 6407743
    maly_elektronik
    Poziom 23  
    Czyli że niby mam sterować całością poprzez zmiany DDRx kolejno w stan wejścia czyli 0 i stan wyjścia czyli 1 (wystarczą takie male różnice w napięciu):?:

    czy taki kod może być czy nie :?: (działa ale czy jest adekwatny co do tego o czym napisałeś wyżej :?: )

    
    #include <avr/io.h>
    #define F_CPU 16000000UL
    #include <util/delay.h>
    
    
    #define DDR DDRC
    #define PORT PORTC
    #define PIN  PINC
    
    #define SDA 1
    #define SCL 0
    
    #define SCL_set() (PORT |= _BV(SCL))
    #define SCL_clr() (PORT &= ~_BV(SCL))
    #define SCL_out() (DDR  |= _BV(SCL))
    
    #define SDA_set() (PORT |= _BV(SDA))
    #define SDA_clr() (PORT &= ~_BV(SDA))
    #define SDA_read() (PIN  & _BV(SDA)) 
    #define SDA_out() (DDR  |= _BV(SDA))
    #define SDA_in()  (DDR  &= ~_BV(SDA))
    
    
    ////////////////////////////////////////////////////////////////////////////////////////////
    void delay(void)
    {
     _delay_us(10);
    }
    ////////////////////////////////////////////////////////////////////////////////////////////
    void i2c_init(void)
    {
     TWBR=0x48;
    
     TWAR=0x02;
    
     TWCR=0x44;
    }
    ////////////////////////////////////////////////////////////////////////////////////////////
    void i2c_write(unsigned char data)
    {
    SDA_out();
    SCL_out();
    char index;
    
     SCL_clr();
     delay();
     for(index=0; index<9; index++)
     { 
      if(data & 0x80)
      SDA_set();
      else
      SDA_clr();
      delay();
      SCL_set();
      delay();
      SCL_clr();
      delay();
      data<<=1;
     }
    SDA_clr();
    }
    ///////////////////////////////////////////////////////////////////////////////////////////
    unsigned char i2c_read(unsigned char ACK)
    {
    
    SDA_in();
    SCL_out();
    
    unsigned char data=0x00;
    char index;
    
    SCL_clr();
    
     for(index=0; index<8; index++)
     { 
      delay();
      SCL_set(); 
      delay();
      data<<=1;
      if(SDA_read())
      data++;
      delay();
      SCL_clr();
     }
    
     if(ACK==0x00)
     SDA_set();
     else
     SDA_clr();
     delay();
     SCL_set();
     delay();
     SCL_clr();
    
    return data;
    }
    ///////////////////////////////////////////////////////////////////////////////////////////
    void i2c_start(void)
    {
    
    SCL_out();
    SDA_out();
    
     SDA_set();
     delay();
     SCL_set();
     delay();
     SDA_clr();
     delay();
     SCL_clr();
    } 
    ///////////////////////////////////////////////////////////////////////////////////////////
    void i2c_stop(void)
    {
    SCL_out();
    SDA_out();
     
     SDA_clr();
     delay();
     SCL_set();
     delay();
     SDA_set();
     delay();
    
    } 
    ///////////////////////////////////////////////////////////////////////////////////////////
  • #9 7512092
    mc_mc
    Poziom 10  
    Ja też walczę z programową obsługą i2c i mam takie zasadnicze pytanie. Czy muszę zastosować rezystory do podciągnięcia magistrali, czy wystarczą wewnętrzne pull-up'y?
    Mój procek to ATmega32, 3.57MHz, 3.3V.
  • REKLAMA
  • #10 7512463
    asembler
    Poziom 32  
    Nie potrzeba stosowac zewnetrznyc rezystorów a przynajmniej ja tego nigdy nie robilem.
  • #11 7513123
    tmf
    VIP Zasłużony dla elektroda
    Baju baju, policz wg specyfikacji I2C wartosc tego rezystora i porownaj wynik z pull upem w AVR. Jesli ci to dziala to tylko przez przypadek, albo dla mikropredkosci.
  • #12 7513150
    asembler
    Poziom 32  
    Wiesz o tym tylko teoretycznie czy praktycznie? Nie stosuje max predkosci bo nie wszystkie kosci I2C dzialaja na max. Moja mikro predkosc to 400kHz a wystarczy wziasc oscyloskop i sprawdzic kształt przebiegu podczas transmisli i przekoanac sie czy trzeba czy nie dodac rezystory i tyle.
  • #13 7516728
    tmf
    VIP Zasłużony dla elektroda
    Nie wprowadzaj innych w blad, bo jeszcze ktos w to uwierzy.
    Chociazby PDF do ATMegi128, strona 322, pozycja Value of pull up resistor - dla fscl>100kHz Rmax[kOm]=300ns/C[pf] - to dla jednego urzadzenia I2C na magistrali daje nam 2*10pf+powiedzmy 10pF na sciezki, razem 30pF. Maksymalna wartosc pull upa to 10kOm, z tego samego pdfa - pull upy na wejsciach IO maja 20-50kOm, w najlepszym wypadku 2 razy za duzo.
    Jesli ci to dziala to wylacznie przez przypadek, chyba, ze znowu uwazasz, ze jestes madrzejszy niz inzynierowie Atmela.
  • #14 7516768
    asembler
    Poziom 32  
    Nie moze dzialac kilkase urzadzen przez przypadek? a ile ma wewnetrzne pull-up. To ty sie zachowujesz jakbys wszystkie PDF przeczytał i wszystkie programy sciagnął a sam zadnego nie napisał
    Nie bede z tobą dyskutował teoretyku.
    Do jednego urzadzenia i to jeszcze umieszczonego blisko nie potrzeba nigdy rezystorow podciagajacy.
  • #15 7517002
    tmf
    VIP Zasłużony dla elektroda
    asembler napisał:
    Nie moze dzialac kilkase urzadzen przez przypadek? a ile ma wewnetrzne pull-up. To ty sie zachowujesz jakbys wszystkie PDF przeczytał i wszystkie programy sciagnął a sam zadnego nie napisał


    Wewnetrzne pull upy wg tego samego pdfa maja tak jak podalem 20-50kOm, ty przeczytales w zyciu chociaz jednego pdfa? Dla wiekszej ilosci urzadzen ten rezystor musi byc jeszcze mniejszy, wiec tym bardziej wewnetrzne pull upy sie nie nadadza.

    asembler napisał:
    Nie bede z tobą dyskutował teoretyku.
    Do jednego urzadzenia i to jeszcze umieszczonego blisko nie potrzeba nigdy rezystorow podciagajacy.


    I bardzo dobrze, ze nie bedziesz, bo nie ma o czym, piszesz bzdury i tyle. Pokaz ten fragment datascheeta, ktory mowi, ze dla jednego urzadzenia nie trzeba tych rezystorow. Czy to kolejny twoj wymysl?
  • #16 7517161
    asembler
    Poziom 32  
    Weźmy tak dla przykladu linie SCL podpinamy ją do wyjscia AVR. Wyjscie AVR ma -20mA dla stanu H i 20mA stanu L to po jaką cholerę obciazac dodatkowo rezystorem zeby tylko prąd tracic?
    Co innego jakby to był AT89S4051 tam jes 20mA i -80uA i tu sie zgadza trzeba cos wpiac do plusa.
REKLAMA