Elektroda.pl
Elektroda.pl
X

Search our partners

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

Problem z komunikacją I2C na ATMega [SOLVED]

valarian 10 Sep 2007 16:54 2442 8
  • #1
    valarian
    Level 22  
    Witam,

    Próbuję dogadać ze sobą dwie ATMegi za pomocą I2C [TWI]. Przeczytałem o tym sporo w dokumentacji, przejrzałem sporo postów na elektrodzie i dalej nie wiem, czemu to nie chce mi działać. Wg symulacji w VMLab program wchodzi w nieskończoną pętlę w zaznaczonym miejscu drugiej funkji (pierwsza przechodzi bez zarzutu).
    Czy ktoś może mi pomoc, byłbym bardzo wdzięczny.

    To działa OK:

    Code:
    void i2c_init(void)
    
    {
      TWSR = 0;                         //brak preskalera
      TWBR = ((F_CPU/SCL_CLOCK)-16)/2;
    }


    To już nie:



    Code:
    unsigned char i2c_start(unsigned char address)
    
    {
        uint8_t   twst;
       //START
       TWCR = (1<<TWINT) | (1<<TWSTA) | (1<<TWEN);
       // czekaj na zakonczenie transmisji
       while(!(TWCR & (1<<TWINT)));  <----TUTAJ WCHODZI W PĘTLĘ

       twst = TW_STATUS & 0xF8;
       //wystapil blad, zwroc 1
       if ( (twst != TW_START) && (twst != TW_REP_START)) {return 1;}
       // wyslij adres urzadzenia
       TWDR = address;
       TWCR = (1<<TWINT) | (1<<TWEN);

       while(!(TWCR & (1<<TWINT)));

       twst = TW_STATUS & 0xF8;

       if ( (twst != TW_MT_SLA_ACK) && (twst != TW_MR_SLA_ACK) ){return 1;}

       return 0;
    }
  • #2
    rmajda
    Level 20  
    Czy ustawiłeś jedną atmege jako master a drugą jako slave?
  • #3
    valarian
    Level 22  
    Tak, powyższy kod dotyczy mastera, slave natomiast jest ustawiany kodem:
    Code:

    sei();
    TWAR=address;
    TWCR = (1<<TWEN) | (1<<TWEA) | (1<<TWIE);


    Odbiór natomiast przeprowadzam w przerwaniu:

    Code:

    char rec='0';

    SIGNAL(SIG_2WIRE_SERIAL)
    {
     rec=TWDR;
     TWCR = (1<<TWINT);
    }
  • #4
    Ch.M.
    Level 27  
    Przyjacielu wydaje mi się że jednak nie czytałeś zbyt wiele o TWI Atmeli, a przynajmniej nie na elektrodzie ani nie w datasheecie...
    Jakbyś przeczytał to byś wiedział ,że trzeba wyzerować bit STARTu przed oczekiwaniem na ACK
    przykład ASM
    Code:

    i2c_START:
    ldi R16, 0b10100100      //(1<<TWINT)|(1<<TWSTA)|(1<<TWEN)
    sts TWCR,R16            ; envoi condition de start sur le bus

    ldi R16,0b10000100      // zeruje komendę start
    sts TWCR,R16

    rcall i2c_WAIT
    ret

    i2c_WAIT:                  ; Attente extinction de twint (twint a 1 voir doc)
    lds R16,TWCR              ; et si ack ou nack
    sbrs R16,TWINT           ;
    rjmp i2c_WAIT
    ret


    Inna sprawa to użyte komendy do nastawy bitów... operuj zmieniając cały bajt a nie tylko stany poszczególnych bitów, dlaczego?
    pozdrawiam
  • #5
    valarian
    Level 22  
    Nadal przeglądam dokumentację i nigdzie nie widzę informacji o zerowaniu bitu startu przed oczekiwaniem.

    I dlaczego należy operować całym bajtem, a niezalecane są operacje na bitach?
  • #6
    Ch.M.
    Level 27  
    AD1:
    M128 patrz koniec strony 207
    M32 patrz koniec strony 175
    M88 patrz strona 237
    M8 patrz strona 172
    Code:
    • Bit 5 – TWSTA: TWI START Condition Bit
    
    The application writes the TWSTA bit to one when it desires to become a master on the
    Two-wire Serial Bus. The TWI hardware checks if the bus is available, and generates a
    START condition on the bus if it is free. However, if the bus is not free, the TWI waits
    until a STOP condition is detected, and then generates a new START condition to claim the bus Master status. TWSTA must be cleared by software when the START condition
    has been transmitted.

    Ad2:
    Ponieważ operując na wybranych bitach, nie wiesz co się dzieje z innymi (np nie zerujesz TWINT)
  • #7
    valarian
    Level 22  
    Doszedłem do czegoś takiego (uproszczone do minimum):

    Program wysyłający:

    Code:

    int main(void)
    {
     sei();
     TWSR = 0;
     TWBR = ((F_CPU/SCL_CLOCK)-16)/2;
     while(1)
     {
      TWCR = 0b10100100; //(1<<TWSTA) | (1<<TWEN) | (1<<TWINT);
      TWCR = 0b10000100;
      while( !(TWCR & (1<<TWINT)) );
      TWDR=address & 0xFE;
      TWCR = (1<<TWINT) | (1<<TWEN);
      while( !(TWCR & (1<<TWINT)) );
      TWDR='X';
      TWCR = (1<<TWINT) | (1<<TWEN);
      while( !(TWCR & (1<<TWINT)) );
      TWCR = (1<<TWSTO) | (1<<TWEN) | (1<<TWINT);
     }
    }


    A po stronie programu odbierającego:

    Code:

    char rec='0';

    SIGNAL(SIG_2WIRE_SERIAL)
    {
      status=TWSR & 0xF8;

      switch(status)
      {
       case TW_SR_SLA_ACK:            
       case TW_SR_ARB_LOST_SLA_ACK:      
       case TW_SR_GCALL_ACK:            
       case TW_SR_ARB_LOST_GCALL_ACK:            
        
          TWCR = (1<<TWINT) | (1<<TWEA)| (1<<TWIE) | (1<<TWEN);      
          break;
       case TW_SR_DATA_ACK:              
       case TW_SR_GCALL_DATA_ACK:         
             rec = TWDR;   
       USART_Transmit(rec);
       TWCR = (1<<TWINT) | (1<<TWEA)| (1<<TWIE) | (1<<TWEN);
          break;
       case TW_SR_DATA_NACK:            
       case TW_SR_GCALL_DATA_NACK:         
       TWCR = (1<<TWINT) | (1<<TWIE) | (1<<TWEN);
          break;
       case TW_SR_STOP:      
       TWCR = (1<<TWINT) | (1<<TWEA) | (1<<TWIE) | (1<<TWEN);
          break;
      }
    }

    int main(void)
    {
     sei();
     TWAR=4;
     TWCR=(1<<TWEA) | (1<<TWEN) | (1<<TWIE);
     while(1)
     {
     }
    }


    Jak widać, program powinien wysyłać przez USART otrzymane znaki ('X' w tym wypadku), a nie wysyła nic. RS232 sprawne, każdy moduł sprawdzony osobno w symulatorze VMLab DZIAŁA, tzn. znak wysyłany przez pierwszy program jest odbierany przez wewętrzny monitor i2c VMLab'a, a wysyłanie znaku do drugiego programu powoduje jego wypisanie na terminalu. Wszystko chodzi w obrębie PC'ta.
    Sprzętowo już nie...
  • #9
    valarian
    Level 22  
    Funkcje tam użyte w sumie niczym tam nie różnią się od moich, tym bardziej nie wiem, co jest nie tak. Podejrzewam coś ze sprzętem, skoro symulacje są OK. Ale co może być nie tak w dwóch uC połączonych kablem (+ dwa rezystory 10k)?

    VMLab posiada też opcję odpalenia dwóch kopii programu, każdej z inną aplikacją i interakcji między nimi (przez ustawienie, które piny są połączone). Sprawdzałem tą opcję i komunikacja działa (tylko gubi znaki).

    Dodano po 4 [godziny] 59 [minuty]:

    Męczę się z tym cały dzień, wszystko jest sprawdzone 10 razy i działa w VMLabie... A sprzętowo dalej nie.

    Postanowiłem wymienić rezystory podciągające z 10kOhm na 4.7kOhm i dostałem ciekawy efekt:
    Po włączeniu zasilania całego układu nic się nie dzieje, terminal jest czysty, żadne znaki nie są wysyłane. Po wyłączeniu zasilania do terminalu orzesyłanych jest prawidłowo ok. 20 znaków! :-)
    W chwili przełączenia włącznika i odłączeniu napięcia w układzie dzieje się "coś" i przez ułamek sekundy wszystko działa :-)
    Nie było tak przed wymianą rezystorów na mniejsze.

    Co to może być?

    Dodano po 4 [godziny] 31 [minuty]:

    Uff, w końcu sobie poradziłem. Wina nie tkwiła jednak w niezerowaniu bitu startu.
    Po zmianie rezystorów na jeszcze mniejsze (1.5kOhm) wszystko śmiga bez problemów :-)

    Dziękuję wszystkim za pomoc, temat do zamknięcia.