Elektroda.pl
Elektroda.pl
X
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

[C][ATmega8]Komunikacja mikrokontrolerów - konfiguracja TWI

kirby 27 Apr 2009 16:29 6443 6
  • #1
    kirby
    Level 10  
    Mam problem z połączeniem dwóch ATmeg8 za pomocą sprzętowego TWI.

    Otóż chciałbym połączyć dwie ATmegi, z których pierwsza - SLAVE TRANSMITTER - obsługuje akcelerometr poprzez ADC i przekazuje dane drugiej - MASTER RECEIVER - która to z kolei wyświetla dane na wyświetlaczu LCD.

    Przewertowałem elektrodę, google, datasheeta ATmegi i znalazłem wydawałoby się wszystko co potrzebne do napisania obsługi TWI a nawet więcej. Jednak już nie wiem, gdzie szukać problemu. Sprawdzałem program wiele razy i nie mogę dopatrzeć się gdzie leży błąd(y).
    Jeżeli jest ktoś, kto jest na bieżąco z TWI to bardzo proszę o pomoc.

    Być może problemem jest typ zmiennej, która jest przesyłana ze slave'a do mastera - zmienna typu int. Wiem, że w paczkach danych jest przesyłana zmienna 8bitowa, a więc typu char, ale nie mogłem znaleźć algorytmu/ funkcji konwertującej zmienną typu char na typ string, który jest wyświetlany na LCD, dlatego próbowałem z typem int - możliwe, że właśnie tu leży błąd - w złym typie przesyłanej zmiennej.

    Obsługa TWI w slave'ie działa na przerwaniu.

    Linie TWI są podciągnięte poprzez rezystory 4,7k.

    W kodzie slave'a pomijam obsługę ADC.

    Kod mastera:
    Code:
    #include <avr/io.h>
    
    #include "LCD.h"
    #include <stdlib.h>
    #include <util/delay.h>

    int pomiar;
    char pomiar_bufor [1];
    char adres = (0x02<<1);

    unsigned char ACK    = 1;   //gotowosc odbioru
    unsigned char NACK  = 0;   //brak gotowosci

    unsigned char READ   = 1;   //odczyt ze slave'a
    unsigned char WRITE  = 0;   //zapis do slave'a


    void inicjalizacja (void)
    {
     DDRC  |=  _BV(PC4) | _BV(PC5);                 //wyprowadzenia TWI jako wyjscia

     TWSR = (0<<TWPS1) | (1<<TWPS0);                //prskaler =  4
     TWBR = 0x64;                                   //wartosc TWBR = 100

     LCD_Init();                                    //inicjalizacja LCD
    }

    //=============================================
    //(transmisja w trybie Master Receiver) - sekwencja START

    void TWI_start(void)
    {
     TWCR |= _BV(TWINT) | _BV(TWSTA) | _BV(TWEN);

     while (!(TWCR & (1<<TWINT)));{}               //oczekiwanie na ustawienie flagi
     
     if ((TWSR&0xF8) != 0x08)                     //sprawdzenie poprawnosci startu
      {
       LCD_Go_To(0,1);
       LCD_Write_String("blad startu");
      }         
    }

    //=============================================
    //sekwencja STOP

    void TWI_stop(void)
    {
     TWCR |= _BV(TWINT) | _BV(TWSTO) | _BV(TWEN);

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

    //=============================================
    //wyslanie adresu slave'a z bitem R/W (Read/Write)

    void TWI_sla_rw (unsigned char adres, unsigned char rw)
    {
     TWDR = ((adres<<1) | rw);
     TWCR |= _BV(TWINT) | _BV(TWEN);

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

     if ((TWSR&0xF8)!=0x40)                       //sprawdzenie odebrania ACK
      {
       LCD_Go_To(0,2);
       LCD_Write_String("blad adresu");
      } 
    }

    //=============================================
    //zapis danych

    void TWI_write (float dane)
    {
     TWDR = dane;
     TWCR |= _BV(TWINT) | _BV(TWEN);

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

    //=============================================
    //odczyt danych

    int TWI_read (unsigned char ack)
    {



     if(ack)
      {
       TWCR |= _BV(TWINT) | _BV(TWEN) | _BV(TWEA);
      }

     else
      {
       TWCR |= _BV(TWINT) | _BV(1TWEN);
      }

     while (!(TWCR & (1<<TWINT)));{}
     
     if ((TWSR&0xF8)!=0x58)                      //sprawdzenie wyslania ACK
      {
       LCD_Go_To(0,3);
       LCD_Write_String("blad odczytu");     
      }     

     return TWDR;
    }






    int main(void)
    {

      inicjalizacja();

      TWI_start();

      TWI_sla_rw(adres,READ);

      pomiar = TWI_read(NACK);

      TWI_stop();

    //=============================================

      itoa(pomiar,pomiar_bufor,10);           //zamiana int na string

      LCD_Go_To(0,0);
      LCD_Write_String(pomiar_bufor);
     
    while(1){}
     
    return 0;

    }





    Kod slave'a:
    Code:
    #include <avr/io.h>
    
    #include <stdlib.h>
    #include <util/delay.h>
    #include <avr/interrupt.h>
    #include <util/twi.h>

    volatile int dane = (4<<8);               //przesunięcie na pozycję osmiu starszych botow


    void inicjalizacja (void)
    {
     DDRC |= _BV(PC4) | _BV(PC5);            //wyprowadzenia TWI jako wyjscia

     TWAR = (0x02<<1);                       //adres slave'a
    }



    SIGNAL (SIG_2WIRE_SERIAL)
    {
     
     switch (TW_STATUS)
      {
        case TW_ST_SLA_ACK:            // odpowidz ACK na wywolanie
                TWCR |= _BV(TWEA);     // wyslanie ACK
           break;
        case TW_ST_DATA_ACK:           //wyslanie danych, odbior ACK
        case TW_ST_DATA_NACK:          //wyslanie danych, odbior NACK
                TWDR = dane;
           break;
      }
        TWCR |= _BV(TWINT);
    }




    int main(void)
    {
     inicjalizacja();

     sei();

     while(1){}

     return 0;
    }





    Jednym z błędów, których nie zauważyłem było brak ustawionego bitu TWIE w rejestrze TWCR w kodzie slave'a, który odblokowuje przerwania. Jednak TWI nadal nie chce działać.

    Sprawdzając w którym miejscu kod działa błędnie, dodałem na końcu kodu mastera wyświetlanie napisu - "koniec programu". Jednak napis nie jest wyświetlany. Po odłączeniu magistrali TWI program w masterze biegnie do końca - napis jest wyświetlany. Jednak po drodze nie są wyświetlane żadne komunikaty o błędnej procedurze startu czy błędzie adresu slve'a. Czy ktoś może przypuszcza dlaczego tak jest ?
  • #2
    kirby
    Level 10  
    Witam ponownie,

    Zmieniłem zarówno program mastera jak i slave'a. Sprawdzałem mnóstwo razy kody i wszystkie ustawienia rejestrów w poszczególnych częściach komunikacji TWI wydają się być prawidłowe, jednak nadal TWI nie chce działać.

    W kodzie slave'a dodałem ustawienie bitu TWIE w rejestrze TWCR, czyli odblokowanie przerwań. Zmieniłem również ustawienia rejestrów w wyborach instrukcji switch:

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


    volatile char dane = 4;               //przesunięcie na pozycję osmiu starszych botow


    void inicjalizacja (void)
    {
     DDRC |= _BV(PC4) | _BV(PC5);            //wyprowadzenia TWI jako wyjscia

     TWAR = (0x02<<1);                       //adres slave'a

     TWCR &= ~_BV(TWINT);
     TWCR |= _BV(TWIE) | _BV(TWEA);
    }



    SIGNAL (SIG_2WIRE_SERIAL)
    {
     
     switch (TWSR)
      {
        case TW_ST_SLA_ACK:            // odpowidz ACK na wywolanie
              TWCR |= _BV(TWEN) | _BV(TWIE)|_BV(TWINT)| _BV(TWEA);     // wlaczenie TWI, odblokowanie przerwan, wyczyszczenie flagi, wyslanie ACK
           break;
        case TW_ST_DATA_ACK:           //wyslanie danych, odbior ACK
                TWDR = dane;
             TWCR |= _BV(TWEN) | _BV(TWINT);
           break;
        case TW_ST_DATA_NACK:          //wyslanie danych, odbior NACK
                TWDR = dane;
             TWCR = _BV(TWEN);               
           break;
      }
        TWCR |= _BV(TWINT) | _BV(TWIE);
    }


    int main(void)
    {
     inicjalizacja();

     sei();

     while(1){}

     return 0;
    }





    W kodzie mastera dodałem na końcu funkcji main wyświetlanie napisu sygnalizującego zakończenie programu. Napis końcowy jednak nie jest wyświetlany. Nie są również wyświetlane napisy po sprawdzaniu rejestru TWSR sygnalizujące błąd startu, brak ack po wysłaniu SLA+R. Tak jakby program wchodził w nieskończoną pętlę już na samym początku.
    Natomiast po odłączeniu magistrali TWI kod mastera dochodzi do końca, ponieważ jest wyświetlany napis końcowy, jednak - co najdziwniejsze - po drodze nie wyświetla żadnych napisów o błędzie startu, błędzie zgłoszenia odbioru adresu przez slave'a.
    Gdzie może być tego przyczyna ?

    Mam jeszcze jedno pytanie - czy odpowiedzi slave'a polegają na odczytywaniu statusu z rejestru TWSR i wywoływaniu na tej podstawie konkretnej odpowiedzi ?
  • #3
    marti_ql
    Level 10  
    NIe wiem czy już rozwiązałeś problem, ale może to coś pomoże: w pierwszym poście masz adres slave
    Code:
    TWAR=(0x02<<1), 

    a w programie dla mastera zdefiniowaną zmienną
    Code:
    adres=(0x02<<1)

    Przy transmisji adresu dajesz ponowne przesunięcie
    Code:
    TWDR = ((adres<<1) | rw)

    Może tutaj tkwi problem i wystarczyłoby tylko:
    Code:
    TWDR = (adres | rw)


    Aha teraz zauważyłem, że zmienna
    Code:
    adres
    jest zdefiniowana jako globalna i jako lokalna dla funkcji TWI_sla_rw.

    A tutaj:
    Code:
    void TWI_write (float dane)

    dlaczego float a nie char (lub unsigned char)?
  • #4
    flapo213
    Level 21  
    Witam

    Zaczynam czytać dokumentację do Atmeg i

    konfiguracji portów robić nie musisz ponieważ w dokumentacji jest napisane że w przypadku TWEN w rejestrze TWCR to piny komunikacyjne zostaną automatycznie przekonfigurowane do pracy z TWI i odłączone od standardowego GPIO. Można o tym przeczytać w datasheet w sekcji Alternate function GPIO.

    cyt:

    [quote]• SCL/ADC5 – Port C, Bit 5
    SCL, Two-wire Serial Interface Clock: When the TWEN bit in TWCR is set (one) to
    enable the Two-wire Serial Interface, pin PC5 is disconnected from the port and
    becomes the Serial Clock I/O pin for the Two-wire Serial Interface. In this mode, there is
    a spike filter on the pin to suppress spikes shorter than 50 ns on the input signal, and the
    pin is driven by an open drain driver with slew-rate limitation.
    PC5 can also be used as ADC input Channel 5. Note that ADC input channel 5 uses digital
    power.
    • SDA/ADC4 – Port C, Bit 4
    SDA, Two-wire Serial Interface Data: When the TWEN bit in TWCR is set (one) to
    enable the Two-wire Serial Interface, pin PC4 is disconnected from the port and
    becomes the Serial Data I/O pin for the Two-wire Serial Interface. In this mode, there is
    a spike filter on the pin to suppress spikes shorter than 50 ns on the input signal, and the
    pin is driven by an open drain driver with slew-rate limitation.
  • #5
    Wilku
    Level 17  
    Witam. Czy udało się komuś uruchomić tryb slave w TWI? Siedzę drugi dzień nad tym i nic. Bazuję na nocie AVR310, jednak nie udało mi się odebrać ramki z softwarowego mastera. Może ktoś opanował już ten temat? Pozdrawiam.
  • #6
    rpal
    Level 27  
    Jesli kolega nie ma za wielkich odległości między prockami to radzę ze zdecydowanie lepszym skutkiem przejść na transmisję po SPI. Obsługa łatwiejsza i zdecydowanie szybsza, Jeśli z kolei odległości są spore to lepiej pogonić to po RS232 gdzie można osiągnąć nawet 230400 bodów. Także zdecydowanie prostsze niż w TWI.
  • #7
    Wilku
    Level 17  
    Wiem że są łatwiejsze sposoby komunikacji, jednak ja muszę mieć I2C slave. Trochę już pchnąłem do przodu temat, ale jeszcze nie działa to do końca jak powinno.