PHILIPS公司開發的兩線式串列匯流排
GPIO類比i2c驅動中有自己的一套傳輸演算法。GPIO類比I2C是要佔用CPU資源的,而用I2C晶片是不佔CPU資源的
特點
介面線少,控制方式簡單,器件封裝形式小,通訊速率較高
特徵 一條串列資料線SDA,一條串列時鐘線SCL 它是一個真正的多主機匯流排,如果兩個或更多主機同時初始化,資料轉送可以通過衝突檢測和仲裁防止資料被破壞 串列的8 位雙向資料轉送位速率在標準模式下可達100kbit/s,快速模式下可達400kbit/s,高速模式下可達3.4Mbit/s 串連到相同匯流排的IC 數量只受到匯流排的最大電容400pF 限制
架構 I2C core架構
提供了核心資料結構的定義和相關介面函數,用來實現I2C適配器。驅動和裝置驅動的註冊、登出管理
實現在/drivers/i2c目錄下的i2c-core.c和i2c-dev.c I2C匯流排驅動
定義描述具體I2C匯流排適配器的i2c_adapter資料結構、實現在具體I2C適配器上的I2C匯流排通訊方法,並由i2c_algorithm資料結構進行描述。 經過I2C匯流排驅動的的代碼,可以為我們控制I2C產生開始位、停止位、讀寫周期以及從裝置的讀寫、產生ACK等
實現在/drivers/i2c目錄下busses檔案夾。例如:Linux I2C GPIO匯流排驅動為i2c_gpio.c;I2C匯流排演算法在/drivers/i2c目錄下algos檔案夾。例如:Linux I2C GPIO匯流排驅動演算法實現在i2c_algo_bit.c I2C裝置驅動
對具體I2C硬體驅動的實現。I2C 裝置驅動通過I2C適配器與CPU通訊。其中主要包含i2c_driver和i2c_client資料結構,i2c_driver結構對應一套具體的驅動方法,例如:probe、remove、suspend等,需要自己申明;i2c_client資料結構由核心根據具體的裝置註冊資訊自動產生
實現在/drivers/i2c目錄下chips檔案夾
裝置串連圖
波形圖
開始訊號:當SCL為高電平時,SDA由高電平向低電平跳變,表示將要開始傳輸資料
結束訊號:當SCL為高電平時,SDA由低電平向高電平跳變,表示結束傳輸資料
i2c_client
struct i2c_client{ unsigned short flags; //標誌位 unsigned short addr; //裝置的地址,低7位為晶片地址 char name[I2C_NAME_SIZE]; //裝置的名稱,最大為20個位元組 struct i2c_adapter *adapter; //依附的適配器i2c_adapter,適配器指明所屬的匯流排 struct i2c_driver *driver; //指向裝置對應的驅動程式 struct device dev; //裝置結構體 int irq; //裝置申請的中斷號 struct list_head list; //串連到匯流排上的所有裝置 struct list_head detected; //已經被發現的裝置鏈表 struct completion released; //是否已經釋放的完成量};
地址碼
1101000x讀地址:11010001 = 0xd1寫地址:11010000 = 0xd0裝置地址:01101000 = 0x68 //高位補0 地址碼
i2c_driver
struct i2c_driver{ int id; //驅動標識ID unsigned int class; //驅動的類型 int (*attach_adapter)(struct i2c_adapter *); //當檢測到適配器時調用的函數 int (*detach_adapter)(struct i2c_adapter *); //卸載適配器時調用的函數 int (*detach_client)(struct i2c_client *) __deprecated; //卸載裝置時調用的函數/*以下是一種新類型驅動需要的函數,這些函數支援IIC裝置動態插入和拔出。如果不想支援只實現上面3個。要不實現上面3個。要麼實現下面5個。不能同時定義*/ int (*probe)(struct i2c_client *, const struct i2c_device_id *); //新類型裝置探測函數 int (*remove)(struct i2c_client *); //新類型裝置的移除函數 void (*shutdown)(struct i2c_client *); //關閉IIC裝置 int (*suspend)(struct i2c_client *, pm_messge_t mesg); //掛起IIC裝置 int (*resume)(struct i2c_client *); //恢複IIC裝置 int (*command)(struct i2c_client *client, unsigned int cmd, void *arg); //使用命令使裝置完成特殊的功能。類似ioctl()函數 struct devcie_driver driver; //裝置驅動結構體 const struct i2c_device_id *id_table; //裝置ID表 int (*detect)(struct i2c_client *, int kind, struct i2c_board_info *); //自動探測裝置的回呼函數 const struct i2c_client_address_data *address_data; //裝置所在的位址範圍 struct list_head clients; //指向驅動支援的裝置};
i2c_adapter
struct i2c_adapter{ struct module *owner; //模組計數 unsigned int id; //alogorithm的類型,定義於i2c_id.h中 unsigned int class; //允許探測的驅動類型 const struct i2c_algorithm *algo; //指向適配器的驅動程式 void *algo_data; //指向適配器的私人資料,根據不同的情況使用方法不同 int (*client_register)(struct i2c_client *); //裝置client註冊時調用 int (*client_unregister(struct i2c_client *); //裝置client登出時調用 u8 level; struct mutex bus_lock; //對匯流排進行操作時,將獲得匯流排鎖 struct mutex clist_lock; //鏈表操作的互斥鎖 int timeout; //逾時 int retries; //重試次數 struct device dev; //指向適配器的裝置結構體 int nr; struct list_head clients; //串連匯流排上的裝置的鏈表 char name[48]; //適配器名稱 struct completion dev_released; //用於同步的完成量};
i2c_algorithm
struct i2c_algorithm{ int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msg, int num); //傳輸函數指標,指向實現IIC匯流排通訊協定的函數,用來確定適配器支援那些傳輸類型 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方式傳輸函數指標,指向實現SMBus匯流排通訊協定的函數。 SMBus和IIC之間可以通過軟體方式相容,所以這裡提供了一個函數,但是一般都賦值為NULL*/ u32 (*functionality)(struct i2c_adapter *); //返回適配器支援的功能};
i2c_msg
struct i2c_msg{ __u16 addr; //IIC裝置地址。這個欄位說明一個適配器在獲得匯流排控制權後,可以與多個IIC裝置進行互動 __u16 flags; //訊息類型標誌 __u16 len; //訊息位元組長度 __u8 *buf; //指向訊息資料的緩衝區};
i2c_adapter 與 i2c_algorithm
i2c_adapter 對應於物理上的一個適配器,而 i2c_algorithm 對應一套通訊方法。一個 I2C 適配器需要 i2c_algorithm 中提供的通訊函數來控制適配器上產生特定的訪問周期。缺少 i2c_algorithm 的 i2c_adapter 什麼也做不了,因此 i2c_adapter 中包含其使用的 i2c_algorithm 的指標
i2c_algorithm 中的關鍵函數 master_xfer() 用於產生 I2C 訪問周期需要的訊號,以 i2c_msg為單位
i2c_driver 與 i2c_client
i2c_driver 對應一套驅動方法,是純粹的用於輔助作用的資料結構,它不對應於任何的物理實體。 i2c_client 對應於真實的物理裝置,每個 I2C 裝置都需要一個 i2c_client 來描述。 i2c_client 一般被包含在 i2c 字元裝置的私人資訊結構體中
i2c_driver 與 i2c_client 發生關聯的時刻在 i2c_driver 的 attach_adapter() 函數被運行時。 attach_adapter() 會探測物理裝置,當確定一個 client 存在時,把該 client 使用的 i2c_client 資料結構的 adapter 指標指向對應的 i2c_adapter
driver 指標指向該 i2c_driver ,並會調用 i2c_adapter 的 client_register() 函數。相反的過程發生在 i2c_driver 的 detach_client() 函數被調用的時候
i2c_adpater 與 i2c_client
i2c_adpater 與 i2c_client 的關係與 I2C 硬體體系中適配器和裝置的關係一致,即 i2c_client 依附於 i2c_adpater 。由於一個適配器上可以串連多個 I2C 裝置,所以一個 i2c_adpater 也可以被多個 i2c_client 依附, i2c_adpater 中包括依附於它的 i2c_client 的鏈表
SMBus
SMBus 是 I2C 的子集 i2c_smbus_read_byte
從裝置讀取一個位元組(不定義位置位移,使用以前發起的命令的位移)。意義不大,無基地址 i2c_smbus_write_byte
從裝置寫入一個位元組(使用以前發起的命令的位移) i2c_smbus_write_quick
向裝置發送一個位元 i2c_smbus_read_byte_data
從裝置指定位移處讀取一個位元組。第一個msg用來傳送讀的基地址,第二個msg用來讀取資料 i2c_smbus_write_byte_data
向裝置指定位移處寫入一個位元組。傳送兩個msg i2c_smbus_read_word_data
從裝置指定位移處讀取二個位元組 i2c_smbus_write_word_data
向裝置指定位移處寫入二個位元組 i2c_smbus_read_block_data
從裝置指定位移處讀取一塊資料 i2c_smbus_write_block_data
向裝置指定位移處寫入一塊資料(<= 32 位元組)
上面的一系列函數最終都是調用的i2c_smbus_xfer()函數
/** * i2c_smbus_xfer - execute SMBus protocol operations * @adapter: Handle to I2C bus * @addr: Address of SMBus slave on that bus * @flags: I2C_CLIENT_* flags (usually zero or I2C_CLIENT_PEC) * @read_write: I2C_SMBUS_READ or I2C_SMBUS_WRITE * @command: Byte interpreted by slave, for protocols which use such bytes * @protocol: SMBus protocol operation to execute, such as I2C_SMBUS_PROC_CALL * @data: Data to be read or written * * This executes an SMBus protocol operation, and returns a negative * errno code else zero on success. */s32 i2c_smbus_xfer(struct i2c_adapter *adapter, u16 addr, unsigned short flags, char read_write, u8 command, int protocol, union i2c_smbus_data *data){ unsigned long orig_jiffies; int try; s32 res; /* If enabled, the following two tracepoints are conditional on * read_write and protocol. */ trace_smbus_write(adapter, addr, flags, read_write, command, protocol, data); trace_smbus_read(adapter, addr, flags, read_write, command, protocol); flags &= I2C_M_TEN | I2C_CLIENT_PEC | I2C_CLIENT_SCCB; if (adapter->algo->smbus_xfer) { i2c_lock_adapter(adapter); /* Retry automatically on arbitration loss */ orig_jiffies = jiffies; for (res = 0, try = 0; try <= adapter->retries; try++) { res = adapter->algo->smbus_xfer(adapter, addr, flags, read_write, command, protocol, data); if (res != -EAGAIN) break; if (time_after(jiffies, orig_jiffies + adapter->timeout)) break; } i2c_unlock_adapter(adapter); if (res != -EOPNOTSUPP || !adapter->algo->master_xfer) goto trace; /* * Fall back to i2c_smbus_xfer_emulated if the adapter doesn't * implement native support for the SMBus operation. */ } res = i2c_smbus_xfer_emulated(adapter, addr, flags, read_write, command, protocol, data);trace: /* If enabled, the reply tracepoint is conditional on read_write. */ trace_smbus_reply(adapter, addr, flags, read_write, command, protocol, data); trace_smbus_result(adapter, addr, flags, read_write, command, protocol, res); return res;}EXPORT_SYMBOL(i2c_smbus_xfer);
首先在判斷主控制器是否支援smbus_xfer傳輸,但是通常i2c主控制器都是不支援的,所以直接調用i2c_smbus_xfer_emulated()函數
參考:http://blog.chinaunix.net/uid-27041925-id-3631304.html