I2C裝置應用層讀寫

2021-08-02 11:46:36 字數 4309 閱讀 5924



i2c匯流排是有philips公司開發的,它是一種比較簡單的匯流排,接線簡單:只有兩根線資料線(scl)和時鐘線(sda),控制簡單。所以一些封裝較小的器件多使用i2c匯流排,常見的使用i2c匯流排的裝置有eeprom、rtc及一些感測器。這裡我們介紹下基於linux的i2c裝置驅動的編寫。

i2c裝置驅動的編寫有多種方式:

一種是直接操作cpu的i2c控制器,正對於某乙個裝置寫乙個字元驅動,這種驅動相對來說比較直接,不需要太依賴於核心相關配置,但是這類裝置驅動依賴cpu,可移植性較差。

一種是基於linux核心i2c子系統完成裝置驅動的編寫,一般核心會繼承相關cpu的控制器驅動即使沒有也可以通過技術支援可以獲得,所以我們只需要使用linux下i2c子系統提供的相關介面來構建我們的裝置驅動就行了。這樣我們的裝置驅動並不依賴於某乙個特定的cpu,可移植性較好。

在寫驅動之前我們先了解下i2c匯流排中幾個比較重要的概念:

1、 位址

i2c匯流排上可以連線多個相同或不同的裝置,匯流排怎麼樣才能知道資料應該傳送到那個裝置呢,這裡需要乙個位址來唯一的標識乙個裝置。i2c裝置位址有7位位址和10位位址,那麼這個位址是怎麼來的呢,其實這個位址我們可以通過相關的晶元手冊獲得,這裡通過乙個eeprom和乙個溫度感測器來說明。

eeprom(at24c02/04/08/16)晶元手冊上有如下說明:

再結合原理圖

在通過晶元手冊我們可以知道eeprom的位址的前四位為1010,通過原理圖a0/a1/及nc的狀態我們可以知道後三位為000,這樣我們就知道這個eeprom在i2c匯流排上的位址為7』b1010000。

同樣我們可以通過如下內容知道溫度感測器的位址為7』b1001000

晶元手冊:

原理圖:

2、 時序

不同的i2c裝置有不同的時序,我們也可以說是不同的協議,我們需要了解一些時序相關的東西,我們傳送資料是什麼時候開始什麼時候結束,怎麼傳送都由這個時序決定。

開始/停止

完整時序

現在的cpu多數都有i2c控制器,我們不需要太關心具體時序的實現,這些都由控制器去完成,並且核心已經整合多數cpu的i2c控制器驅動,我們寫裝置驅動就是按照i2c子系統的要求,為它提供需要的資料即可。

i2c子系統下裝置驅動有兩種模式,一種是使用者模式裝置驅動這種驅動依賴i2c子系統中的i2c-dev這個驅動,我們需要在應用程式去封裝資料,這需要應用程式的開發人員具備相當的硬體基礎,另外一種是普通的裝置驅動。分別看下這兩種方法的具體實現過程。

使用者模式驅動實現:

struct i2c_msg ;

struct i2c_rdwr_ioctl_data ;

上面就是我們向底層傳遞的結構,我們需要把我們的時序封裝成這樣的結構然後傳遞下去就行了。

at24c04時序

轉化為訊息結構為:

e2prom_data.nmsgs=2;

(e2prom_data.msgs[0]).len=1; //e2prom 目標資料的位址

(e2prom_data.msgs[0]).addr=0x50; // e2prom 裝置位址

(e2prom_data.msgs[0]).flags=0; //write

(e2prom_data.msgs[0]).buf=(unsigned char*)malloc(2);

(e2prom_data.msgs[0]).buf[0]=0x0; //e2prom資料位址

(e2prom_data.msgs[1]).len=1; //讀出的資料

(e2prom_data.msgs[1]).addr=0x50; // e2prom 裝置位址

(e2prom_data.msgs[1]).flags=i2c_m_rd; //read

(e2prom_data.msgs[1]).buf=(unsigned char*)malloc(1);//存放返回值的位址。

(e2prom_data.msgs[1]).buf[0]=0; //初始化讀緩衝

這裡我們封裝了兩個訊息,在這個時序中操作模式改變了,所以我們必須封裝為兩個時序,如果操作模式不變封裝乙個訊息就可以了比如如下時序:

(e2prom_data.msgs[0]).len=1; //e2prom 目標資料的位址

(e2prom_data.msgs[0]).addr=0x50; // e2prom 裝置位址

(e2prom_data.msgs[0]).flags=0; //write

(e2prom_data.msgs[0]).buf=(unsigned char*)malloc(1);

(e2prom_data.msgs[0]).buf[0]=0x0; //e2prom資料位址

接著我們可以看看別的裝置的時序大家可以發現大同小異!

我們把剛才封裝的訊息通過ioctl發下去就能夠完成資料的讀寫了。例程如下:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

int main()

e2prom_data.nmsgs=2;

e2prom_data.msgs=(struct i2c_msg*)malloc(e2prom_data.nmsgs*sizeof(struct i2c_msg));

if(!e2prom_data.msgs)

ioctl(fd,i2c_timeout,1);/*超時時間*/

ioctl(fd,i2c_retries,2);/*重複次數*/

sleep(1);

e2prom_data.nmsgs=2;

(e2prom_data.msgs[0]).len=1; //e2prom 目標資料的位址

(e2prom_data.msgs[0]).addr=0x48; // e2prom 裝置位址

(e2prom_data.msgs[0]).flags=0; //write

(e2prom_data.msgs[0]).buf=(unsigned char*)malloc(2);

(e2prom_data.msgs[0]).buf[0]=0x0; //e2prom資料位址

(e2prom_data.msgs[1]).len=2; //讀出的資料

(e2prom_data.msgs[1]).addr=0x48; // e2prom 裝置位址

(e2prom_data.msgs[1]).flags=i2c_m_rd;//read

(e2prom_data.msgs[1]).buf=(unsigned char*)malloc(2);//存放返回值的位址。

(e2prom_data.msgs[1]).buf[0]=0; //初始化讀緩衝

(e2prom_data.msgs[1]).buf[1]=0; //初始化讀緩衝

ret=ioctl(fd,i2c_rdwr,(unsigned long)&e2prom_data);

if(ret<0)

printf("%x",(e2prom_data.msgs[1]).buf[0]);

printf("%x\n",(e2prom_data.msgs[1]).buf[1]);

close(fd);

return 0;

}

I2C讀寫問題

a.完全不能進行讀寫 1 通訊協議不正確 有很多的i2c裝置,並不支援所有的i2c協議,同時也不是乙個比較標準的i2c裝置 軟體的通訊時序不正確。2 i2c裝置位址不正確 有很多的i2c裝置的位址是可以通過硬體設定的 也有器件資料提供的資料是錯誤的。3 i2c通訊線上沒有加上拉電阻 由於i2c的從裝...

i2c裝置驅動

1,i2c 裝置註冊 static struct i2c board info i2c2 devices i2c裝置一般在板級 中註冊 static void msm8916 add i2c deivces void 2,i2c驅動註冊 include static const struct i2c...

I2C讀寫EEPROM EEPROM簡介

eeprom 是一種掉電後資料不丟失的儲存器,常用來儲存一些配置資訊,以便系統重新上電的時候載入之。eepom 晶元最常用的通訊方式就是 i 2c 協議,本小節以 eeprom 的讀寫實驗為大家講解如何使用 stm32 軟體模擬產生 i 2c 協議進行通訊。本實驗板中的 eeprom 晶元 型號 a...