詳細介紹I2C子系統驅動框架與協議

I2C子系統分析

1.基本工作原理:
以啓動信號START來掌管總線,以停止信號STOP來釋放總線;
每次通訊以START開始,以STOP結束;
啓動信號START後緊接着發送一個地址字節,其中7位爲被控器件的地址碼,一位爲讀/寫控制位R/W,R. /W位爲0表示由主控向被控器件寫數據,R/W爲1表示由主控向被控器件讀數據;
當被控器件檢測到收到的地址與自己的地址相同時,在第9個時鐘期間反饋應答信號;
每個數據字節在傳送時都是高位(MSB)在前。
1.1. I2C簡介
(1) I2C一般支持工作速率爲100K,200K和400K,3.4Mbps。
(2)I2C接口採用Open Drain機制,器件本身只能輸出低電平,無法主動輸出高電平, 需外置上拉(1K~10K).
(3)外設數量限制:等效電容 < 400pf.
(4)SMBUS 時序 電壓 速率(10K~100Kbps)only 7bit addr
2.I2C總線協議介紹
2.1.I2C物理拓撲圖

在這裏插入圖片描述
I2C總線在物理連接上非常簡單,分別由SDA(串行數據線)和SCL(串行時鐘線)及上拉電阻組成。通信原理是通過對SCL和SDA線高低電平時序的控制,來產生I2C總線協議所需要的信號進行數據的傳遞。在總線空閒狀態時,這兩根線一般被上面所接的上拉電阻拉高,保持着高電平。

2.2. I2C總線特徵
I2C總線上的每一個設備都可以作爲主設備或者從設備,而且每一個設備都會對應一個唯一的地址(可以從I2C器件的數據手冊得知),主從設備之間就通過這個地址來確定與哪個器件進行通信,在通常的應用中,我們把CPU帶I2C總線接口的模塊作爲主設備,把掛接在總線上的其他設備都作爲從設備。
I2C總線上可掛接的設備數量受總線的最大電容400pF 限制,如果所掛接的是相同型號的器件,則還受器件地址位的限制。
I2C總線數據傳輸速率在標準模式下可達100kbit/s,快速模式下可達400kbit/s,高速模式下可達3.4Mbit/s。一般通過I2C總線接口可編程時鐘來實現傳輸速率的調整,同時也跟所接的上拉電阻的阻值有關。
I2C總線上的主設備與從設備之間以字節(8位)爲單位進行雙向的數據傳輸。

I2C協議規定,總線上數據的傳輸必須以一個起始信號作爲開始條件,以一個結束信號作爲傳輸的停止條件。起始和結束信號總是由主設備產生。總線在空閒狀態時,SCL和SDA都保持着高電平,當SCL爲高電平而SDA由高到低的跳變,表示產生一個起始條件;當SCL爲高而SDA由低到高的跳變,表示產生一個 停止條件。在起始條件產生後,總線處於忙狀態,由本次數據傳輸的主從設備獨佔,其他I2C器件無法訪問總線;而在停止條件產生後,本次數據傳輸的主從設備 將釋放總線,總線再次處於空閒狀態。

不是在數據有效性中規定在SDA只能在SCL的低電平的時候變化,爲何STAR,STOP不一樣?首先STAR和STOP不是數據,所以可以不遵守數據有效性中的規定,其它數據都遵守,而STAR和STOP“不遵守”導致STAR和STOP更容易被識別。這樣不是不遵守而是更有優勢
如圖所示:

在這裏插入圖片描述在瞭解起始條件和停止條件後,我們再來看看在這個過程中數據的傳輸是如何進行的。前面我們已經提到過,數據傳輸以字節爲單位。主設備在SCL線上產生每個時鐘脈衝的過程中將在SDA線上傳輸一個數據位,當一個字節按數據位從高位到低位的順序傳輸完後,緊接着從設備將拉低SDA線,回傳給主設備一個應答位,此時才認爲一個字節真正的被傳輸完成。當然,並不是所有的字節傳輸都必須有一個應答位,比如:當從設備不能再接收主設備發送的數據時,從設備將回傳一個否定應答位,應答信號爲低電平。數據位的傳輸是邊沿觸發。數據傳輸的過程如圖所示:
在這裏插入圖片描述

在前面我們還提到過,I2C總線上的每一個設備都對應一個唯一的地址,主從設備之間的數據傳輸是建立在地址的基礎上,也就是說,主設備在傳輸有效數據之前 要先指定從設備的地址,地址指定的過程和上面數據傳輸的過程一樣,只不過大多數從設備的地址是7位的,然後協議規定再給地址添加一個最低位用來表示接下來 數據傳輸的方向,0表示主設備向從設備寫數據,1表示主設備向從設備讀數據。如圖所示:

在這裏插入圖片描述

應答信號
  I2C總線上的所有數據都是以8位字節傳送的,發送器每發送一個字節,就在時鐘脈衝9期間釋放數據線,由接收器反饋一個應答信號。 應答信號爲低電平時,規定爲有效應答位(ACK簡稱應答位),表示接收器已經成功地接收了該字節;應答信號爲高電平時,規定爲非應答位(NACK),一般表示接收器接收該字節沒有成功。 對於反饋有效應答位ACK的要求是,接收器在第9個時鐘脈衝之前的低電平期間將SDA線拉低,並且確保在該時鐘的高電平期間爲穩定的低電平。 如果接收器是主控器,則在它收到最後一個字節後,發送一個NACK信號,以通知被控發送器結束數據發送,並釋放SDA線,以便主控接收器發送一個停止信號P。

3.3.I2C總線操作
對I2C總線的操作實際就是主從設備之間的讀寫操作。大致可分爲以下三種操作情況:
第一,主設備往從設備中寫數據。數據傳輸格式如下:
在這裏插入圖片描述
第二,主設備從從設備中讀數據。數據傳輸格式如下:
在這裏插入圖片描述
第三,主設備往從設備中寫數據,然後重啓起始條件,緊接着從從設備中讀取數據;或者是主設備從從設備中讀數據,然後重啓起始條件,緊接着主設備往從設備中寫數據。數據傳輸格式如下:
在這裏插入圖片描述
第三種操作在單個主設備系統中,重複的開啓起始條件機制要比用STOP終止傳輸後又再次開啓總線更有效率。

3.I2C模塊介紹
Linux 中I2C體系結構圖1.1所示,圖中的分割線分爲三個層次:
1.用戶空間,包括所有使用I2C設備的應用者
2.內核空間,也就是設備驅動
3.硬件,指實際物理設備,I2C控制器和I2C設備
在這裏插入圖片描述

                                    圖1.1

Linux 內核中I2C體系結構邏輯上可以分3個部分:I2C核心、I2C總線驅動、I2C設備驅動。

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

2.I2C總線驅動(I2C Adapter Driver):是對I2C硬件體系結構中適配器端的實現,適配器可由CPU控制,甚至可以直接集成在CPU內部。包含了i2c_adapter、 i2c_algorithm和控制I2C適配器產生通信信號的函數。通過I2C總線驅動的代碼,我們可以控制I2C適配器以主控方式產生開始位、停止位讀寫週期,以及以從設備方式被讀寫,產生ACK等。

  1. I2C設備驅動(I2C Client Driver):是對I2C硬件體系結構中設備端的實現,設備一般掛接在受CPU控制的I2C適配器上,通過I2C適配器與CPU交換數據。包含了i2c_driver和i2c_client。

所有的I2C設備和驅動名字都在sysfs文件系統中顯示,存於/sys/bus/i2c/目錄下,以適配器地址和芯片地址的形式列出。

3.1.I2C Core核心
I2C核心(drivers/i2c/i2c-core.c)主要提供一組不依賴於硬件平臺的接口函數,這個文件一般不需要被工程師修改,但是理解其中的主要函數非常關鍵,因爲I2C總線驅動和設備驅動之間依賴於I2C核心作爲紐帶。I2C核心中的主要函數包括:
(1)增加/刪除i2c_adapter
int i2c_add_adapter(struct i2c_adapter *adap);
int i2c_del_adapter(struct i2c_adapter *adap);
(2)增加/刪除i2c_driver
int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
int i2c_del_driver(struct i2c_driver *driver);
inline int i2c_add_driver(struct i2c_driver *driver);
(3)i2c_client依附/脫離
int i2c_attach_client(struct i2c_client *client);
int i2c_detach_client(struct i2c_client *client);
(4)i2c傳輸、發送和接收
int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num);
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);
i2c_transfer ()函數用於進行I2C適配器和I2C設備之間的一組消息交互,i2c_master_send()函數和i2c_master_recv()函數內部會 調用i2c_transfer()函數分別完成一條寫消息和一條讀消息。
i2c_transfer()函數本身不具備驅動適配器物理硬件完成消息交互的能力,它只是尋找到i2c_adapter對應的i2c_algorithm,並使用i2c_algorithm的master_xfer()函數真正驅動硬件流程

(5)I2C控制命令分派
下面函數有助於將發給I2C適配器設備文件ioctl的命令分派給對應適配器的algorithm的algo_control()函數或i2c_driver的command()函數:
int i2c_control(struct i2c_client *client, unsigned int cmd, unsigned long arg);
void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg);

3.2.I2C 總線驅動
I2C適配器驅動加載與卸載
I2C總線驅動模塊的加載函數要完成兩個工作:
• 初始化I2C適配器所使用的硬件資源,申請I/O地址、中斷號等。
• 通過i2c_add_adapter()添加i2c_adapter的數據結構,當然這個i2c_adapter數據結構的成員已經被xxx適配器的相應函數指針所初始化。
I2C總線驅動模塊的卸載函數要完成的工作與加載函數的相反:
• 釋放I2C適配器所使用的硬件資源,釋放I/O地址、中斷號等。
• 通過i2c_del_adapter()刪除i2c_adapter的數據結構。

3.2.1.I2C傳輸控制
主要是指註冊適配器時需要提供的i2c_algorithm結構,其成員.master_xfer是每次觸發I2C控制器讀寫的啓動接口,就相當於給I2C總線驅動增加了讀寫“算法”。通常情況下每個I2C總線驅動都定義一個自己的讀寫算法,但也有一些總線使用相同的算法,就可以共用同一套接口函數。
Sunxi I2C控制器的.master_xfer處理流程如下(代碼實現見sunxi_i2c_xfer()):
在這裏插入圖片描述
圖2.1 傳輸控制的處理流程

3.2.2.中斷處理
圖2.1中有“使能TWI中斷”,該中斷信號就會觸發TWI的中斷處理函數,除圖2.1中“發送start”外的I2C通信過程都是由此中斷處理函數完成。
I2C通信可以分爲讀、寫兩種操作。通過判斷TWI控制器的通信狀態來實現完整的讀寫操作,而當前的通信狀態則由TWI寄存器TWI Status Register(0x10)提供,中斷處理函數首先讀取此寄存器,然後做相應的處理。
1、I2C寫操作
S + (SADDR+W+A) + (REGADDR+A)m + (DATA+A)n + P
狀態碼:0x08 + 0x18 + 0x28
m + 0x28
n + stop
2、I2C讀操作(有restart):
S + (SADDR+W+A) + (REGADDR+A)m + RS + (SADDR+R+A) + (DATA+A)(n-1) + (DATA+) + P
狀態碼:0x08 + 0x18 + 0x28m + 0x10 + 0x40 + 0x50(n-1) + 0x58 + stop

3.2.3.I2C傳輸過程中的信號狀態說明
3.2.3.1.無具體模式的狀態
0x00 總線錯誤(進入不可尋址從模式並釋放總線)
0x08 已經發送一個start條件,將要發送從設備地址和讀寫位,並接受ACK
0x10 已發送一個重複傳輸開始
3.2.3.2.主發送狀態
0x18 完成從地址+寫位傳輸,並收到ACK響應(將要發送第一個字節數據,並接受ACK)
0x20 完成從地址+寫位傳輸,沒有收到ACK響應(將要發送一個STOP信號)
0x28 在主控模式數據被傳輸完成,收到ACK響應。(如果是最後一個數據字節,將傳 輸一個STOP信號,否則傳輸下一個數據)
0x30 主模式下數據傳輸完成,沒有收到ACK,將傳輸STOP信號
0x38 在傳輸地址或數據是仲裁丟失,總線已經釋放並進入不可尋址狀態的從模式
3.2.3.3.主接收狀態
0x40 完成從地址+讀位傳輸,收到ACK
0x48 完成從地址+讀位傳輸,沒有收到ACK,將傳輸一個STOP信號
0x50 已接收到數據,已返回ACK。(將從I2CDAT讀出數據,將接收到額外的數據,如果是最後一個字節將返回NACK否則返回ACK。)
0x58 已收到數據,已返回NACK。(將從I2CDAT讀出數據,並傳輸一個STOP信號)
3.2.3.4.從接收狀態
0x60 已接受到自身從地址和寫位,並返回ACK
0x68 作爲總線主控時,在自身從地址階段仲裁丟失;已收到從地址和寫位,返回ACK。 (當總線再次空閒時,設置STA位以重新啓動主模式)
0x70 已收到廣播通知,返回ACK
0x78 作爲總線主控時,在自身從地址傳輸階段仲裁丟失;已收到廣播地址,並返回ACK。 (當總線再次空閒時,設置STA位以重新啓動主模式))
0x80 前一次使用自身從地址尋址,數據已收到,ACK返回
0x88 前一次使用自身從地址尋址,數據已收到,NACK返回(不保存接收到的數據, 進入不可尋址從模式)
0x90 廣播地址接收到之後,數據已接受,ACK返回(保存接收到的數據,只有第一個數據字節返回ACK,其他數據返回NACK)
0x98 廣播地址接收到之後,數據已接受,NACK返回。(不保存接收數據,進入不可尋址從模式)
0xA0 仍然爲從模式時,接收到一個STOP信號或者重複START信號
3.2.3.5.從發送狀態
0xA8 已接收到自身從地址和讀位,並已返回ACK(將要發出數據並接收到ACK)
0xB0 作爲總線主控時,在自身從地址傳輸階段仲裁丟失;已收到從地址和讀位,並已返回ACK。 (當總線再次空閒時,設置STA位以重新啓動主模式)
0xB8 數據已傳輸完成,已經返回ACK。(將要發送數據並接收ACK)
0xC0 數據已傳輸完成,已經返回NACK。(進入不可尋址從模式)
0xC8 最後一個字節數據已傳輸完成,並返回ACK。(將進入不可尋址從模式)
0xD0 完成第二個地址字節和寫位傳輸,並返回ACK。
0xD8 完成第二個地址字節和寫位傳輸,並返回NACK。
0xF8 沒有相關的狀態信息,INT_FLAG = 0.

3.3.I2C設備驅動一般結構
Linux 系統中,對I2C總線有很好的支持,已經實現了I2C core。適配器驅動爲平臺中訪問I2C總線的具體方法。這兩個部分爲確定的,不需要設備驅動開發人員關心。對於一個設備來說,只需要按照I2C設備的架構,實現i2c_driver結構體中的接口,即可以完成i2c的設備驅動與總線的連接。
一個具體的I2C設備驅動需要實現兩個方面的接口,一方面是對I2C core層的接口,用以掛接I2C adapter來實現對I2C總線及I2C設備具體的訪問方法,包括要實現probe,remove,detect等接口函數;另一方面是對用戶應用層的 接口,提供用戶程序訪問I2C設備的接口,包括實現open,release,read,write以及最重要的ioctl等標準文件操作的接口函數。

對I2C core層的接口函數的具體功能解釋如下:
probe:I2C driver進行設備綁定的回調函數。
remove:I2C driver解除設備綁定的回調函數。
detect:I2C設備探測回調函數,它會識別所支持的設備(返回0表示支持,否則返回-ENODEV);此外,需要定義一個供探測的 地址列表(address_list)和一個設備類型(class),這樣使那些僅匹配設備類型的i2c總線被探測到。例如,對於一個自動監測硬件芯片的 驅動將會設置它的class域爲I2C_CLASS_HWMON,只有那些class域爲I2C_CLASS_HWMON的適配器能夠被驅動探測。

3.4.常用數據結構
3.4.1.i2c_adapter
該結構體定義於:lichee\linux-3.x\include\linux\i2c.h頭文件中,i2c_adapter對應於物理上的一個控制器。一個I2C控制器需要i2c_algorithm中提供的通信函數來控制控制器上產生特定的訪問週期。
struct i2c_adapter {
struct module owner; / 所屬模塊 /
unsigned int id; /
algorithm的類型,定義於i2c-id.h,以I2C_ALGO_開始 */
unsigned int class;
const struct i2c_algorithm algo; / 總線通信方法結構體指針 */
void algo_data; / algorithm數據 /
struct rt_mutex bus_lock;
int timeout; /
超時時間,以jiffies爲單位 /
int retries; /
重試次數 /
struct device dev; /
適配器設備 /
int nr;
char name[48]; /
適配器名稱 /
struct completion dev_released; /
用於同步 */
struct mutex userspace_clients_lock;
struct list_head userspace_clients;
};

3.4.2.i2c_algorithm
該結構體定義於:lichee\linux-3.x\include\linux\i2c.h頭文件中,i2c_algorithm中的關鍵函數master_xfer()用於產生I2C訪問週期需要的信號,以i2c_msg(即I2C消息)爲單位。

struct i2c_algorithm {
/* I2C傳輸函數指針 */
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);

/* smbus傳輸函數指針 */
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr,
		   unsigned short flags, char read_write,
		   u8 command, int size, union i2c_smbus_data *data);

/* 返回適配器支持的功能 */
u32 (*functionality) (struct i2c_adapter *);

};

3.4.3.i2c_msg
該結構體定義於:lichee\linux-3.x\include\linux\i2c.h頭文件中,i2c_msg是I2C傳輸的基本單位,它包含了從設備的具體地址,消息的類型以及要傳輸的具體數據信息。每個I2C消息傳輸前,都會產生一個開始位,緊接着傳送從設備地址,然後開始數據的發送或接收,對最後的消息還需產生一個停止位。

struct i2c_msg {
__u16 addr; /* 從設備地址 /
__u16 flags; /
消息類型 /
__u16 len; /
消息長度 */
__u8 buf; / 消息數據 */
};

3.4.4.i2c_client
該結構體定義於:lichee\linux-3.x\include\linux\i2c.h頭文件中,i2c_client對應於真實的物理設備,每個I2C設備都需要一個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;
};

3.4.5.i2c_driver
該結構體定義於:lichee\linux-3.x\include\linux\i2c.h頭文件中,i2c_driver對應一套驅動方法,其主要成員函數是probe()、remove()、suspend()、resume()等,另外id_table是該驅動所支持的I2C設備的ID表。i2c_driver與i2c_client的關係是一對多,一個i2c_driver上可以支持多個同等類型的i2c_client。

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; /
掛接探測到的支持的設備 */
};

3.5.常用接口函數

3.5.1.i2c_add_driver
函數原型:static inline int i2c_add_driver(struct i2c_driver *driver);
功能描述:註冊一個I2c設備驅動到i2c子系統
參數說明:driver,i2c_driver 類型的指針,其中包含了 I2C 設備的名稱、probe、detect
等接口信息。
返回值:0:,成功;其它值,失敗。

3.5.2.i2c_del_driver
函數原型:void i2c_del_driver(struct i2c_driver *driver);
功能描述:從I2C子系統中註銷一個I2C設備驅動
參數說明:driver,i2c_driver 類型的指針
返回值:無

3.5.3.i2c_set_clientdata
函數原型:static inline void i2c_set_clientdata(struct i2c_client *dev, void *data);
功能描述:設置I2C私有數據到client
參數說明:dev 指向從設備
data 設置到client的私有數據
返回值:無

3.5.4.i2c_get_clientdata
函數原型:static inline void *i2c_get_clientdata(const struct i2c_client *dev);
功能描述:從I2C client中獲取私有數據
參數說明:dev 指向從設備
返回值:從client獲取到的私有數據

3.5.5.i2c_register_board_info
函數原型:int __init i2c_register_board_info(int busnum, struct i2c_board_info const *info,
unsigned len)
功能描述:向某個I2C總線註冊I2C設備信息。I2C子系統通過該接口保存I2C總線和 I2C設備的適配關係
參數說明:busnum I2C控制器編號
info 提供I2C設備名稱,地址信息
len 要註冊的設備數
返回值:0 成功
其它值,失敗

3.5.6.i2c_master_send
函數原型:int i2c_master_send(struct i2c_client *client, const char *buf, int count);
功能描述:在主控發送模式執行一次I2C發送操作
參數說明:client 指向當前I2C從設備的實例
buf 要發送的數據
count 要發送的數據長度
返回值:大於 0 成功發送的數據長度
小於0 發送失敗

3.5.7.i2c_master_resv
函數原型:int i2c_master_recv(struct i2c_client *client, char *buf, int count);
功能描述:執行一次I2C接受操作
參數說明:client 指向當前I2C從設備的實例
buf 用於保存數據的緩存
count 數據緩存buf的長度
返回值:大於0 成功接受的數據字節數
小於 0 接受失敗

3.5.8.i2c_transfer
函數原型:int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
功能描述:完成I2C總線與I2C設備之間的一定數目的I2C message交互
參數說明:adap 指向所屬的I2C總線控制器
msgs i2c_msg 類型的指針
num 表示一次需要處理I2C message的數量
返回值: 大於0 表示已經處理message的數量
小於0 失敗

3.5.9.i2c_smbus_read_byte
函數原型:s32 i2c_smbus_read_byte(const struct i2c_client *client)
功能描述:從I2C總線讀一個字節
參數說明:client 指向當前的I2C從設備
返回值:大於 0 讀到的數據
小於0 失敗

3.5.10.i2c_smbus_write_byte
函數原型:s32 i2c_smbus_write_byte(const struct i2c_client *client, u8 value)
功能描述:從I2C總線寫一個字節數據
參數說明:client 指向當前的I2C從設備
value 要寫入的數據
返回值:0 成功
小於0 失敗

3.5.11.i2c_smbus_read_byte_data
函數原型:s32 i2c_smbus_read_byte_data(const struct i2c_client *client, u8 command)
功能描述:從I2C設備指定的偏移處讀取一個字節數據
參數說明:client 指向當前的I2C從設備
command I2C協議數據的第0字節命令碼(即偏移值)
返回值:大於 0 讀到的數據
小於0 失敗

3.5.12.i2c_smbus_write_byte_data
函數原型:s32 i2c_smbus_write_byte_data(const struct i2c_client *client, u8 command, u8 value)
功能描述:往I2C設備指定的偏移處寫入一個字節數據

參數說明:client 指向當前的I2C從設備
command I2C協議數據的第0字節命令碼(即偏移值)
value 要寫入的字節數據
返回值:0 成功
小於0 失敗

3.5.13.i2c_smbus_read_word_data
函數原型:s32 i2c_smbus_read_word_data(const struct i2c_client *client, u8 command)
功能描述:從I2C設備指定的偏移處讀取一個word數據(兩個字節,適用於I2C設備寄存器16bit的情況)
參數說明:client 指向當前的I2C從設備
command I2C協議數據的第0字節命令碼(即偏移值)
返回值:大於 0 讀到的數據
小於0 失敗

3.5.14.i2c_smbus_write_word_data
函數原型:s32 i2c_smbus_write_word_data(const struct i2c_client *client, u8 command,
u16 value)
功能描述:往I2C設備指定的偏移處寫入一個word數據
參數說明:client 指向當前的I2C從設備
返回值:0 成功
小於0 失敗

3.5.15.i2c_smbus_read_block_data
函數原型:s32 i2c_smbus_read_block_data(const struct i2c_client *client, u8 command,
u8 *values)
功能描述:從I2C設備指定的偏移處讀取一塊數據
參數說明:client 指向當前的I2C從設備
command I2C協議數據的第0字節命令碼(即偏移值)
values 用於保存讀取數據的緩存
返回值:大於 0 讀到的數據長度
小於0 失敗

3.5.16.i2c_smbus_write_block_data
函數原型:s32 i2c_smbus_write_block_data(const struct i2c_client *client, u8 command,
u8 length, const u8 *values)
功能描述:往I2C設備指定的偏移處寫入一塊數據
參數說明:client 指向當前的I2C從設備
command I2C協議數據的第0字節命令碼(即偏移值)
length 要寫入的數據長度
values 要寫入的數據
返回值:0 成功
小於0 失敗

4.I2C設備驅動DEMO
下面給出一個最簡單的I2C設備驅動demo,例如i2c_driver_demo.c,文件具體內容如下:
#include <linux/i2c.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
static int i2c_driver_demo_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
return 0;
}

static int __devexit i2c_driver_demo_remove(struct i2c_client *client)
{
return 0;
}
static const struct i2c_device_id i2c_driver_demo_id[] = {
{ “XXXX”, 0 },
{}};
MODULE_DEVICE_TABLE(i2c, i2c_driver_demo_id);
int i2c_driver_demo_detect(struct i2c_client *client, struct i2c_board_info *info){
struct i2c_adapter *adapter = client->adapter;
int vendor, device, revision;

if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA))
    return -ENODEV;

/* 方法1:獲取設備特定寄存器的值,該值要能反映出設備的信息,判斷設備,例如如下代碼段 */
vendor = i2c_smbus_read_byte_data(client, XXXX_REG_VENDOR);
if (vendor != XXXX_VENDOR)
    return -ENODEV;

device = i2c_smbus_read_byte_data(client, XXXX_REG_DEVICE);
if (device != XXXX_DEVICE)
    return -ENODEV;

revision = i2c_smbus_read_byte_data(client, XXXX_REG_REVISION);
if (revision != XXXX_REVISION)
return -ENODEV;

/* 方法2:獲取設備的CHIP_ID,判斷設備,例如如下代碼 */
if (i2c_smbus_read_byte_data(client, XXXX_CHIP_ID_REG) != XXXX_CHIP_ID)
    return -ENODEV;

const char *type_name = "XXXX";
strlcpy(info->type, type_name, I2C_NAME_SIZE);

return 0;
}
/* 0x60爲I2C設備地址 */
static const unsigned short normal_i2c[] = {0x60, I2C_CLIENT_END};
static struct i2c_driver i2c_driver_demo = {
.class = I2C_CLASS_HWMON,
.probe = i2c_driver_demo_probe,
.remove = __devexit_p(i2c_driver_demo_remove),
.id_table = i2c_driver_demo_id,
.driver = {
.name = “XXXX”,
.owner = THIS_MODULE,
},
.detect = i2c_driver_demo_detect,
.address_list = normal_i2c,};
static int __init i2c_driver_demo_init(void){
return i2c_add_driver(&i2c_driver_demo);
}
static void __exit i2c_driver_demo_exit(void){
i2c_del_driver(&i2c_driver_demo);
}

module_init(i2c_driver_demo_init);
module_exit(i2c_driver_demo_exit);

MODULE_AUTHOR(“author”);
MODULE_DESCRIPTION(“I2C device driver demo”);
MODULE_LICENSE(“GPL”);
補充說明:若I2C設備驅動不能在detect回調函數裏訪問硬件,可採用如下形式解決,例如:
int i2c_driver_demo_detect(struct i2c_client *client, struct i2c_board_info *info){
struct i2c_adapter *adapter = client->adapter;
if(adapter->nr == 2)
{
const char *type_name = “XXXX”;
strlcpy(info->type, type_name, I2C_NAME_SIZE);
return 0;
}else
{
return -ENODEV;
}
}
在detect函數裏,需要判斷I2C adapter與I2C client是否進行綁定,若當前adapter是該驅動要綁定的adapter,例如if判斷中的2爲從配置信息中讀出的I2C設備依附的 adapter編號,則進行相應的I2C設備信息註冊;否則,不予註冊。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章