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

[ATMEGA16] Sprzętowa obsługa TWI/I2C w GCC

gdL 01 Sie 2010 10:48 2944 3
REKLAMA
  • #1 8353246
    gdL
    Poziom 27  
    Witam. Chciałem uruchomić na własną rękę sprzętowe TWI w ATMEGA16. Napisałem prostą bibliotekę zgodną z dokumentacją. Program wykonuje "start" i wysłanie adresu, następnie podczas odczytu z zegarka zapętla się. Obsługuję zegarek PCF8583P. W/g dokumentacji PHILIPSA można od razu czytać z zegarka wysyłając [adres][1](163 = 0xA3) -> READ [auto increment]

    KOD BIBLIOTEKI -------------
    
    #include <avr/io.h>
    ///////////////////////////////////////////////////
    void twiStart(unsigned char address) {
    TWCR=((1<<TWINT) | (1<<TWSTA) | (1<<TWEN));
    	while(!(TWCR & (1 << TWINT)));
    TWDR = address;
    TWCR=((1<<TWINT) | (1<<TWEN));
    	while(!(TWCR & (1 << TWINT)));
    }
    ///////////////////////////////////////////////////
    void twiStop() {
    TWCR=((1<<TWINT) | (1<<TWSTO) | (1<<TWEN));
    	while ((TWCR & (1<<TWSTO)));
    }
    ///////////////////////////////////////////////////
    void twiSend(unsigned char data) {
    TWDR = data;
    TWCR=((1<<TWINT) | (1<<TWEN));
    	while(!(TWCR & (1 << TWINT)));
    }
    ///////////////////////////////////////////////////
    char twiRead(char ack) {
    	if (ack == 1) {
    	TWCR = ((1<<TWINT) | (1 << TWEN) | (1 <<  TWEA)); // ustawia TWCR->TWINT i TWEN
    	} else {
    	TWCR = ((1<<TWINT) | (1 << TWEN));
    	}
    	while(!(TWCR & (1 << TWINT)));
    return TWDR;
    }
    ///////////////////////////////////////////////////
    


    MAIN

    #include <avr/io.h>
    #include <avr/delay.h>
    #include <stdlib.h>
    #include <dodane/mojLCD.h>
    
    ///////////////////////////////////////////////////////
    static void lcd_print(char *str) {
    unsigned char i=0;
    
    	while(str[i]!=0) {
    	lcdData(str[i]);
    	i++;
    	}
    
    }
    ///////////////////////////////////////////////////////
    
    int main(void) {
    char address = 0xA3, data; // bajt [adres][1]
    char charTable[10]="";
    lcd_init();
    
    twiStart(address);
    lcd_print("addrOK ");
    data = twiRead(1);
    lcd_print("d1 "); <-- to nigdy sie nie pojawia...
    data = twiRead(1);
    lcd_print("d2 ");
    twiStop();
    
    
    itoa(data,charTable,10);
    lcd_print("RTC ");
    lcd_print(charTable);
    _delay_ms(1000);
    lcdCommand(HD44780_HOME);
    
    while(1) {
    }
    return 0;
    }
    


    Błąd występuje po pierwszym wywołaniu "data = twiRead(1);" zapętla się czekając na zakończenie operacji "while(!(TWCR & (1 << TWINT)));". Co należy zrobić i gdzie jest błąd ?
  • REKLAMA
  • Pomocny post
    #2 8353399
    Andrzej__S
    Poziom 28  
    Nie analizowałem dokładnie kodu, ale na pierwszy rzut oka brakuje mi ustawienia częstotliwości taktowania magistrali (rejestr TWBR i bity TWPS1:0 w rejestrze TWSR). Maksymalna częstotliwość taktowania SCL układu PCF8583 to 100kHz. Nie napisałeś jaką częstotliwością jest taktowany procesor. Po starcie procesora te rejestry są ustawione na wartość 0 i przy tym ustawieniu (jeśli nic do nich nie wpiszesz) dopuszczalna częstotliwość taktowania procesora, by nie przekroczyć tych 100kHz, wynosi 1,6MHz.

    To, że udaje się wysłać na magistralę adres nic jeszcze nie oznacza. Nie sprawdzasz poprawności transmisji w rejestrze TWSR (bity TWS7:3), więc nie wiesz nawet, czy układ odpowiada Ci bitem potwierdzenia.
  • REKLAMA
  • Pomocny post
    #3 8353472
    tmf
    VIP Zasłużony dla elektroda
    Dokładnie. Ja bym tą "bibliotekę" wzbogacił o cokolwiek związanego z diagnostyką - przynajmniej sprawdzanie ACKów i NACKów. Bez tego nie wiesz nawet czy układ coś odpowiedział. BTW, pamiętałeś o właściwych rezystorach podciągających na SDA i SCL?
  • #4 8354434
    gdL
    Poziom 27  
    Tak, dzięki, zgadza się. Chwilę po opublikowaniu postu rzuciłem okiem do datasheeta, gdzie jest sprecyzowane ustalenie dzielników prędkości magistrali. Teraz wszystko śmiga jak trzeba.

    SCL_freq = (CPU_freq) / (16 + 2(TWBR) * 4^TWPS)

    rejestr TWBR - bit rate, w 8 bitach rejestru TWBR
    w rejestrze TWSR, bity "0" i "1" w kombinacjach :

    "1"__"0"
    0__0 - 1
    0__1 - 4
    1__0 - 16
    1__1 - 64

    Przed startem w funkcji "twiStart(unsigned char address);" Na przykład tak...

    
    TWBR=0xFF;
    TWSR|=((1<<TWPS1) | (1<<TWPS0));
    


    Mój ATMEGA16 ma zewnętrzny oscylator 16Mhz w płytce AND-TECH :
    http://www.and-tech.pl/EvB4.3/Instrukcja_EvB_4.3.pdf

    Dzięki za pomoc, problem rozwiązano.
REKLAMA