2017-06-21 144 views
2

我有一個非常簡單的ATTINY85 I2C位打包庫。讀取I2C傳感器值(bit-banging)

#define PORT_SDA PB0 
#define PORT_SCL PB2 

#define SIGNAL_HIGH(PORT) PORTB |= (1 << PORT) 
#define SIGNAL_LOW(PORT) PORTB &= ~(1 << PORT) 

void LED_ON(void); 
void LED_OFF(void); 

void i2c_init(void); 
void i2c_start(void); 
void i2c_read(void); 
void i2c_stop(void); 
void i2c_write(uint8_t byte); 

void i2c_init() 
{ 
    DDRB |= (1 << PORT_SDA); 
    DDRB |= (1 << PORT_SCL); 
} 


void LED_ON(void) 
{ 
    PORTB |= 0b00000010; 
} 


void LED_OFF(void) 
{ 
    PORTB &= 0b111111101; 
} 


void i2c_start(void) 
{ 
    SIGNAL_HIGH(PORT_SCL); 
    SIGNAL_HIGH(PORT_SDA); 
    SIGNAL_LOW( PORT_SDA); 
    SIGNAL_LOW( PORT_SCL); 
} 


void i2c_stop(void) 
{ 
    SIGNAL_LOW( PORT_SCL); 
    SIGNAL_LOW( PORT_SDA); 
    SIGNAL_HIGH(PORT_SCL); 
    SIGNAL_HIGH(PORT_SDA); 
} 

void i2c_write(uint8_t byte) 
{ 
    uint8_t bit; 

    for (bit = 0; bit < 0x08; bit++) 
    { 
     if((byte << bit) & 0x80) 
      SIGNAL_HIGH(PORT_SDA); 
     else 
      SIGNAL_LOW(PORT_SDA); 

     SIGNAL_HIGH(PORT_SCL); 
     SIGNAL_LOW(PORT_SCL); 
    } 

    SIGNAL_HIGH(PORT_SDA); 
    SIGNAL_HIGH(PORT_SCL); 
    SIGNAL_LOW(PORT_SCL); 
} 

我能夠成功寫入I2C,沒有任何問題。我已經用SSD1306和LC2404B測試了這個代碼,如果VCC設置爲4.2V,即使時序也一切正常。

i2c_init(); 

i2c_start(); 
    i2c_write(0xA0); 
    i2c_write(0x01); 
    i2c_write(0x13); 
i2c_stop(); 

雖然書面方式,完美的作品,我似乎無法能夠引起我的任何I2C模塊,ATTINY85回我,我以後可以讀取值。

我連接覆盆子和GY-521傳感器(因爲即使未設置內部地址,它也會返回值)。我可以檢測傳感器,並與覆盆子從中讀取的值通過以下方式:

i2cdetect -y 1 

    0 1 2 3 4 5 6 7 8 9 a b c d e f 
00:   -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- 68 -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- -- 


i2cget -y 1 0x68 

0x02 

這是示波器上的輸出:

Raspberry I2C output

我可以看到傳感器數據改變。問題是我似乎無法從ATTINY85向傳感器複製相同的請求。傳感器根本無法用該值做出響應。 我似乎無法理解,如果第一個字節的最後一位是ACK或'READ'指示位,所以我嘗試了不同的地址。

i2c_start(); 
i2c_write( 0b11010001); // address: 0xD1 

i2c_start(); 
i2c_write( 0b11010000); // address: 0xD0 

i2c_start(); 
i2c_write( 0b10100001); // address: 0xA1 

i2c_start(); 
i2c_write( 0b10100000); // address: 0xA0 

,但無論對我所用的地址,傳感器根本不迴應(在oscioloscope)和SDA線保持高之後我發送地址字節。我也嘗試在發送地址後追加另一個start()條件,但仍然沒有運氣。任何提示我要去哪裏錯?我只是想讓傳感器響應ATTINY85讀取請求,以便稍後可以讀取該值。謝謝!

回答

2

我建議閱讀一些關於I2C的教程,以便對協議有一個大概的瞭解。例如,請參見https://learn.sparkfun.com/tutorials/i2c

簡而言之,I2C是一條帶時鐘線和數據線的雙線多主總線。在任何通信中,無論是讀還是寫,主設備都提供時鐘信號。你的i2c_write()用SCL轉換來實現這一點。

爲了讀取數值,還需要提供從機用於輸出數據的時鐘。沒有時鐘,沒有數據。所以,你需要實現一個類似於你的i2c寫的i2c_read(),它產生時鐘轉換,並且每次移動一個位。

+0

「您還需要提供從設備用於輸出數據的時鐘。」就是這樣!非常感謝你! – Kristian

2

您需要讓SDA線成爲輸入,以便您可以檢測到從站是否正在發送ACK。您沒有任何代碼將SDA線設置爲輸入,因此從設備無法將任何數據發回給您;你放在SDA線上的價值可能會壓倒奴隸想要做的任何事情。並且要讀取數據,您需要製作一個i2c_read函數,該函數在SDA爲輸入時擺動SCL線。您的I2C實現遠未完成。您可以仔細閱讀I2C spec from NXP或查找更完整的bit-banging I2C實現作爲參考。

+0

謝謝,偉大的來源!問題是我沒有把時鐘提供給從設備。現在我終於可以看到示波器中的價值了。現在唯一的問題是,從設備不能「足夠強」地降低SDA線,但只有50%左右,但據我所知,使用下拉/上拉電阻應該解決這個問題。 – Kristian

+1

如果只是將線路拉低50%,這意味着您的代碼可能會使線路變得更高,而奴隸和主人正在戰鬥。就像我在我的回答中所說的那樣,您必須*釋放SDA線,並在特定時間使其成爲輸入,以便讓奴隸與您交談。 –

+0

非常感謝您的提示。儘管我沒有將SDA設置爲輸入,但奇怪的是,我仍然可以使用PINB來讀取它,並將其與示波器讀數進行比較,看起來這個值相當。通過將它設置爲輸入,如你所建議的解決了50%的問題! – Kristian