I2C驅動

一:I2C 概述
I2C是philips提出的外設總線.I2C只有兩條線,一條串行數據線:SDA,一條是時鐘線SCL ,使用SCL,SDA這兩根信號線就實現了設備之間的數據交互,它方便了工程師的佈線。因此,I2C總線被非常廣泛地應用在EEPROM,實時鐘,小型LCD等設備與CPU的接口中。
二:在linux下的驅動思路

談到在linux系統下編寫I2C驅動,目前主要有兩種方法,一種是把I2C設備當作一個普通的字符設備來處理,另一種是利用linux下I2C驅動體系結構來完成。下面比較下這兩種方法: 
第一種方法: 
優點:思路比較直接,不需要花很多時間去了解linux中複雜的I2C子系統的操作方法。 
第一種方法的缺點: 
要求工程師不僅要對I2C設備的操作熟悉,而且要熟悉I2C的適配器(I2C控制器)操作。 
要求工程師對I2C的設備器及I2C的設備操作方法都比較熟悉,最重要的是寫出的程序可以移植性差。 
對內核的資源無法直接使用,因爲內核提供的所有I2C設備器以及設備驅動都是基於I2C子系統的格式。
第一種方法的優點就是第二種方法的缺點, 
第一種方法的缺點就是第二種方法的優點。
三.I2C架構概述

1.Linux的I2C體系結構分爲3個組成部分:

I2C核心

I2C核心提供了I2C總線驅動和設備驅動的註冊,註銷方法,I2C通信方法(”algorithm”)上層的,與具體適配器無關的代碼以及探測設備,檢測設備地址的上層代碼等。

I2C總線驅動

I2C總線驅動是對I2C硬件體系結構中適配器端的實現,適配器可由CPU控制,甚至可以直接集成在CPU內部。

I2C設備驅動

I2C設備驅動(也稱爲客戶驅動)是對I2C硬件體系結構中設備端的實現,設備一般掛接在受CPU控制的I2C適配器上,通過I2C適配器與CPU交換數據。

2.Linux I2C體系結構

Linux I2C體系結構如下圖所示
四. I2C設備驅動程序編寫
首先要明確適配器驅動的作用是讓我們能夠通過它發出符合I2C標準協議的時序。 
在linux內核源代碼中drivers/i2c/busses目錄下包含着一些適配器的驅動。如S3C2440的驅動i2c-s3c2410.c。適配器加載到內核後,接下來的工作就是要針對具體的設備編寫驅動了。
編寫I2C設備驅動也有兩種方法:一種是利用系統給我們提供的i2c-dev.c來實現一個i2c適配器的設備文件。然後通過在應用層操作I2C適配器來控制i2c設備。另一種是爲i2c設備,獨立編寫一個設備驅動。注意:在後面一種情況下,是不需要使用i2c-dev.c的。
五:Linux下I2C體系文件構架
在Linux內核源代碼中的driver目錄下包含一個i2c目錄,而在i2c目錄下又包含如下文件和文件夾:

 
i2c-core.c這個文件實現了I2C核心的功能以及/proc/bus/i2c*接口。 
i2c-dev.c實現了I2C適配器設備文件的功能,每一個I2C適配器都被分配一個設備。通過適配器訪設備時的主設備號都爲89,次設備號爲0-255。I2c-dev.c並沒有針對特定的設備而設計,只是提供了通用的read(),write(),和ioctl()等接口,應用層可以借用這些接口訪問掛接在適配器上的I2C設備的存儲空間或寄存器,並控制I2C設備的工作方式。 
Chips這個目錄包含了一些特定的I2C設備驅動。在具體的I2C設備驅動中,調用的都是I2C核心提供的API,因此,這使得具體的I2C設備驅動不依賴於CPU的類型和I2C適配器的硬件特性。 
busses文件夾這個文件中包含了一些I2C總線的驅動,如針對S3C2410,S3C2440,S3C6410等處理器的I2C控制器驅動爲i2c-s3c2410.c. 
algos文件夾實現了一些I2C總線適配器的algorithm.

六:重要的結構體

1.各個結構體

在內核中的i2c.h這個頭文件中對i2c_driver,i2c_client,i2c_adapter和i2c_algorithm這個四個結構體進行了定義。理解這4個結構體的作用十分關鍵。 
i2c_adapter結構體 
struct i2c_adapter { 
struct module *owner;//所屬模塊 
unsigned int id;//algorithm的類型,定義於i2c-id.h, 
unsigned int class; 
const struct i2c_algorithm *algo; //總線通信方法結構體指針 
void *algo_data;//algorithm數據 
struct rt_mutex bus_lock;//控制併發訪問的自旋鎖 
int timeout; 
int retries;//重試次數 
struct device dev; //適配器設備 
int nr; 
char name[48];//適配器名稱 
struct completion dev_released;//用於同步 
struct list_head userspace_clients;//client鏈表頭 
};
I2c_algorithm結構體 
struct i2c_algorithm { 
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);//I2C傳輸函數指針 
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,unsigned short flags, char read_write,u8 command, int size, union 
i2c_smbus_data *data);//smbus傳輸函數指針 
u32 (*functionality) (struct i2c_adapter *);//返回適配器支持的功能 
}; 
SMbus大部分基於I2C總線規範,SMbus不需要增加額外引腳。與I2C總線相比,SMbus增加了一些新的功能特性,在訪問時序也有 
一定的差異。 
i2c_driver結構體 
struct i2c_driver { 
unsigned int class; 
int (*attach_adapter)(struct i2c_adapter *);//依附i2c_adapter函數指針 
int (*detach_adapter)(struct i2c_adapter *);//脫離i2c_adapter函數指針
int (*probe)(struct i2c_client *, const struct i2c_device_id *); 
int (*remove)(struct i2c_client *); 
void (*shutdown)(struct i2c_client *); 
int (*suspend)(struct i2c_client *, pm_message_t mesg); 
int (*resume)(struct i2c_client *); 
void (*alert)(struct i2c_client *, unsigned int data); 
int (*command)(struct i2c_client *client, unsigned int cmd, void*arg);//命令列表 
struct device_driver driver; 
const struct i2c_device_id *id_table;//該驅動所支持的設備ID表 
int (*detect)(struct i2c_client *, struct i2c_board_info *); 
const unsigned short *address_list; 
struct list_head clients; 
}; 
i2c_client結構體 
struct i2c_client { 
unsigned short flags;//標誌 
unsigned short addr; //低7位爲芯片地址 
char name[I2C_NAME_SIZE];//設備名稱 
struct i2c_adapter *adapter;//依附的i2c_adapter 
struct i2c_driver *driver;//依附的i2c_driver 
struct device dev;//設備結構體 
int irq;//設備所使用的結構體 
struct list_head detected;//鏈表頭 
};

2:各結構體的作用與它們之間的關係

i2c_adapter對應於物理上的一個適配器,而i2c_algorithm對應一套通信方法。一個i2c適配器需要i2c_algorithm中提供的通信函數來控制適配器上產生特定的訪問週期。缺少i2c_algorithm的i2c_adapter什麼也做不了,因此i2c_adapter中包含其使用的i2c_algorithm的指針。i2c_algorithm中的關鍵函數master_xfer()用於產生I2C訪問週期需要的信號,以i2c_msg(即I2C消息)爲單位。i2c_msg也很重要,代碼清單如下: 
struct i2c_msg { 
__u16 addr;//設備地址 
__u16 flags;//標誌 
__u16 len;//消息長度 
__u8 *buf;//消息數據 
}; 
i2c_driver與i2c_clienti2c_driver對應一套驅動方法,其主要成員函數是probe(),remove(),suspend(),resume()等,另外id_table是該驅動所支持的I2C設備的ID表。i2c_client對應於真實的物理設備,每個I2C設備都需要一個i2c_client來描述。i2c_driver與i2c_client的關係是一對多,一個i2c_driver上可以支持多個同等類型的i2c_client。i2c_client信息通常在BSP的板文件中通過i2c_board_info填充。一般在arch/arm目錄下的板文件中。在I2C總線驅動i2c_bus_type的match()函數i2c_device_match()中,會調用i2c_match_id()函數匹配板文件中的ID和i2c_driver所支持的ID表。 
i2c_adpater與i2c_client i2c_adpater與i2c_client的關係與I2C硬件體系中適配器和設備的關係一致,即i2c_client依附與i2c_adpater.由於一個適配器上可以連接多個I2C設備,所以就一個i2c_adpter也可以被多個i2c_client依附,i2c_adpter中包括依附與它的i2c_client的鏈表。

3.編寫驅動需要完成的工作

編寫具體的I2C驅動時,工程師需要處理的主要工作如下: 
1).提供I2C適配器的硬件驅動,探測,初始化I2C適配器(如申請I2C的I/O地址和中斷號),驅動CPU控制的I2C適配器從硬件上產生。

2).提供I2C控制的algorithm, 用具體適配器的xxx_xfer()函數填充i2c_algorithm的master_xfer指針,並把i2c_algorithm指針賦給i2c_adapter的algo指針。

3).實現I2C設備驅動中的i2c_driver接口,用具體yyy的yyy_probe(),
yyy_remove(),yyy_suspend(),yyy_resume()函數指針和i2c_device_id設備ID表賦給i2c_driver的probe,remove,suspend,resume和id_table指針。 
4)實現I2C設備所對應類型的具體驅動,i2c_driver只是實現設備與總線的掛接。 
上面的工作中前兩個屬於I2C總線驅動,後面兩個屬於I2C設備驅動。
七:I2C協議

數據傳送:SCL線呈現高電平期間,SDA線上的電平必須保持穩定,低電平表示0(此時的線電壓爲地電壓),高電平表示1(此時的電壓由元器件的VDD決定)。只有在SCL線爲低電平期間,SDA上的電平允許變化。 
應答信號ACK:I2C總線的數據都是以字節(8位)的方式傳送的,發送器件每發送一個字節之後,在時鐘的第9個脈衝期間釋放數據總線,由接收器發送一個ACK(把數據總線的電平拉低)來表示數據成功接收。 
無應答信號NACK: 在時鐘的第9個脈衝期間發送器釋放數據總線,接收器不拉低數據總線表示一個NACK,NACK有兩種用途: 
a. 一般表示接收器未成功接收數據字節; 
b. 當接收器是主控器時,它收到最後一個字節後,應發送一個NACK信號,以通知被控發送器結束數據發送,並釋放總線,以便主控接收器發送一個停止信號STOP。 
開始與停止信號的時序圖

I2C的讀寫時序 
讀過程

寫過程
整個數據發送與接收時序
ACK時序
八.適配器驅動程序分析

在linux系統中,適配器驅動位於linux目錄下的\drivers\i2c\busses下,不同的處理器的適配器驅動程序設計有差異,但是總體思路不變,在適配器的驅動中,實現兩個結構體非常關鍵,也是整個適配器驅動的靈魂。下面以某個適配器的驅動程序爲例進行說明: 
static struct platform_driver tcc_i2c_driver = { 
.probe = tcc_i2c_probe, 
.remove = tcc_i2c_remove, 
.suspend = tcc_i2c_suspend_late, 
.resume = tcc_i2c_resume_early, 
.driver = { 
.owner = THIS_MODULE, 
.name = "tcc-i2c", 
}, 
}; 
看見這個結構體應該不會陌生,說明這個驅動是基於平臺總線的,這樣實現的目的是與CPU緊緊聯繫起來。 
static const struct i2c_algorithm tcc_i2c_algorithm = { 
.master_xfer = tcc_i2c_xfer, 
.functionality = tcc_i2c_func, 
}; 
這個結構體也是非常的關鍵,這個結構體裏面的函數tcc_i2c_xfer是適配器算法的實現,這個函數實現了適配器與I2C CORE的連接。 
tcc_i2c_func是指該適配器所支持的功能。tcc_i2c_xfer這個函數實質是實現I2C數據的發送與接收的處理過程。不同的處理器實
現的方法不同,主要表現在寄存器的設置與中斷的處理方法上。把握上面的兩點去分析適配器程序就簡單多了。更深一步的分析見代碼註釋。
九.I2C-core驅動程序分析 
在I2C-core.c這個函數中,把握下面的幾個關鍵函數就可以了。 
增加/刪除i2c_adapter 
int i2c_add_adapter(struct i2c_adapter *adapter) 
int i2c_del_adapter(struct i2c_adapter *adap)

增加/刪除i2c_driver 
int i2c_register_driver(struct module *owner, struct i2c_driver *driver) 
void i2c_del_driver(struct i2c_driver *driver)

i2c_client依附/脫離 
int i2c_attach_client(struct i2c_client *client)

增加/刪除i2c_driver 
int i2c_register_driver(struct module *owner, struct i2c_driver *driver) 
void i2c_del_driver(struct i2c_driver *driver)

i2c_client依附/脫離 
int i2c_attach_client(struct i2c_client *client)
int i2c_detach_client(struct i2c_client *client)

I2C傳輸,發送和接收 
int i2c_master_send(struct i2c_client *client,const char *buf ,int count) 
int i2c_master_recv(struct i2c_client *client, char *buf ,int count) 
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num) 
I2c_transfer這個函數實現了core與adapter的聯繫。更深一步的分析見代碼註釋。
十:AT24XXEEPROM驅動

在linux目錄下的\drivers\misc\eeprom中實現了大部分EEPROM的驅動。對於EEPROM而言,設備本身的驅動以bin_attribute二進制sysfs結點形式呈現。分析這個驅動首先看關鍵的結構體 
static struct i2c_driver at24_driver = { 
.driver = { 
.name = "at24", 
.owner = THIS_MODULE, 
}, 
.probe = at24_probe, 
.remove = __devexit_p(at24_remove), 
.id_table = at24_ids, 
}; 
從上面看說明這個驅動又是基於平臺總線的。再進一步看at24_bin_read()與at24_bin_write()這兩個函數,這兩個函數 
會調用I2C_core.c中的i2c_transfer()函數,從而實現了設備,core,適配器這三者的聯繫。 
更深一步的分析見代碼註釋。 
轉載於http://www.cnblogs.com/cute/archive/2011/08/30/2159326.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章