I2C總線驅動源碼位置:linux-3.4.2\drivers\i2c\i2c-core.c
1. 框架
1.1 硬件協議簡介
IIC硬件原理
通信過程:
由主機開始發送S(start)信號後,發送7位設備地址加一位W/R標誌,之後將SDA置低,如果IIC線上有對應設備地址的設備時,該設備會將SDA拉低,代表此設備存在。之後如果是寫就可以直接發送數據,讀就直接接收數據。
1.2 驅動框架
驅動框架圖
i2c_client 與i2c_driver通過name進行匹配。
1.3 bus-drv-dev模型介紹
文檔位置linux-3.4.2\Documentation\i2c\instantiating-devices
所有構造方法源碼地址:https://github.com/yogach/linux-drive/tree/master/i2c
a.1 使用i2c_board_info創建i2c設備
定義一個i2c_board_info結構體,裏面有設備名字, 設備地址,然後i2c_register_board_info(busnum(總線ID), ...) (把它們放入__i2c_board_list鏈表)
i2c_register_board_info()
list_add_tail(&devinfo->list, &__i2c_board_list);
鏈表在i2c_scan_static_board_info()函數內被調用。
調用關係:
i2c_register_adapter() > i2c_scan_static_board_info() > i2c_new_device()
使用限制:必須在 i2c_register_adapter()執行之前 註冊 i2c_register_board_info()
所以:不適合我們動態加載insmod
a.2 直接使用i2c_new_device(), i2c_new_probed_device()
a.2.1 i2c_new_device() : 認爲設備肯定存在(不判斷設備是否存在)
a.2.2 i2c_new_probed_device() :需要先判斷設備是否存在,纔會創建新設備
i2c_new_probed_device()
if (probe(adap, addr_list[i])) /* 確定設備是否真實存在 */
break;//如果設備不存在,返回
info->addr = addr_list[i];
i2c_new_device(adap, info);
a.3 從用戶空間創建設備
創建設備
echo at24c08 0x50 > /sys/class/i2c-adapter/i2c-0/new_device
導致i2c_new_device被調用
刪除設備
echo 0x50 > /sys/class/i2c-adapter/i2c-0/delete_device
導致i2c_unregister_device
a.4 如果事先不知道i2c設備接在哪個適配器上可以使用此方法
1、定義一個i2c_driver結構體,結構體中需要實現以下幾個參數
static struct i2c_driver at24cxx_driver = {
.class = I2C_CLASS_HWMON, /* 表示去哪些適配器上找設備 */
.driver = {
.name = "100ask",
.owner = THIS_MODULE,
},
.probe = at24cxx_probe,
.remove = __devexit_p(at24cxx_remove),
.id_table = at24cxx_id_table,
.detect = at24cxx_detect, /* 用這個函數來檢測設備確實存在 */
.address_list = addr_list, /* 這些設備的地址 */
};
"class"表示在這一類I2C適配器中找,"detect函數"來分辨設備地址相同的設備哪個纔是需要的設備。
i2c_add_driver()
i2c_register_driver()
a. at24cxx_driver放入i2c_bus_type的drv鏈表
driver_register()會在總線上查找該設備,如果沒有則在總線上註冊設備。會從dev鏈表裏取出能匹配的i2c_client並調用probe()
res = driver_register(&driver->driver);
if (res)
return res;
b. 對於每一個適配器,調用__process_new_driver()
對於每一個適配器,調用它的函數確定address_list裏的設備是否存在
如果存在,再調用detect進一步確定、設置,然後i2c_new_device()
/* Walk the adapters that are already present */
i2c_for_each_dev(driver, __process_new_driver);
__process_new_driver()
i2c_do_add_adapter()
/* 檢測該總線上支持的設備,並實例化它們 */
i2c_detect(adap, driver)
//address_list 等於我們自己定義的address_list 如果不存在直接返回
address_list = driver->address_list;
if (!driver->detect || !address_list)
return 0;
for (i = 0; address_list[i] != I2C_CLIENT_END; i += 1) {
//對傳入的地址表中的地址進行判斷
temp_client->addr = address_list[i];
/* 判斷這個設備是否存在:簡單的發出S信號確定有ACK */
err = i2c_detect_address(temp_client, driver);
/* Make sure the address is valid */
err = i2c_check_addr_validity(addr);
/* Make sure there is something at this address */
if (!i2c_default_probe(adapter, addr))
return 0;
/* Finally call the custom detection function */
// 設置info.type
memset(&info, 0, sizeof(struct i2c_board_info));
info.addr = addr;
//調用i2c_driver結構體內的detect()進一步判斷設備
err = driver->detect(temp_client, &info);
/* Detection succeeded, instantiate the device */
i2c_new_device();
b. 驅動的寫法
使用SMBus --- system manager bus對i2c設備進行操作 驅動裏面用的是這種 這也是官方文檔中推薦使用的
文檔位置linux-3.4.2\Documentation\i2c\smbus-protocol
裏面有庫函數介紹
圖上標記部分代表使用i2c_smbus_write_byte_data()函數進行I2C通信時發送的數據順序,中括號內表示設備返回給主機。
對一個外部存儲設備的讀寫順序是:START信號,設備地址,寫操作 [設備回ACK] 寫地址 [設備回ACK] 寫數據 [設備回ACK] PAUSE信號
2. 完善設備驅動程序
實現的源碼地址https://github.com/yogach/linux-drive/tree/master/i2c/4th_device_driver_test
3. 不自己寫驅動直接訪問
直接使用內核中已經寫好的驅動
介紹文檔位置linux-3.4.2\Documentation\i2c\dev-interface
實現的源碼https://github.com/yogach/linux-drive/tree/master/i2c/5th_user_space_test
封裝後的i2c-dev.h文件內實際上就是調用i2c-dev.c內的函數
使用時需要將I2C device interface編譯進內核 也可以編譯成模塊之後加載
Device Drivers
I2C support
<*> I2C device interface
使用此方法時,有個特殊點需要注意
當i2c的設備地址被註冊(已經有驅動註冊了)之後,就無法使用此方法進行訪問
具體代碼如下:
i2cdev_ioctl()
case I2C_SLAVE:
//i2cdev_check_addr()函數會在總線進行查找設備是否存在 如果存在返回失敗
if (cmd == I2C_SLAVE && i2cdev_check_addr(client->adapter, arg))
4. 編寫"總線(適配器adapter)"驅動
內核自帶i2c總線源碼位置linux-3.4.2\drivers\i2c\busses\i2c-s3c2410.c
已實現的源碼位置https://github.com/yogach/linux-drive/tree/master/i2c/6th_i2c_bus
流程:
1、分配/設置i2c_adapter
2、使用i2c_add_adapter()註冊i2c_adapter
3、初始化s3c2440 i2c控制器相關寄存器
4、申請i2c中斷函數,並在中斷實現數據的發送與讀取。
以讀AT24C08的100地址爲例,下面劃紅線部分會產生中斷。
發出start信號的同時,會緊接着發送設備地址與讀寫狀態位,之後會產生一個i2c中斷(判斷是否收到ack,無返回錯誤,收到進入下一階段)。
如果之後是發送數據,每發送一個字節數據會產生一個中斷,每次進入中斷後可以判斷是否收到ack。
如果是接收數據,每接收一個字節數據會產生一個中斷,如果接下來還有數據需要接收,則設置接收完之後產生一個ack信號,反之則不發送ack信號。
在讀寫結束之後,需要發送一個pause信號。
所以在使用request_irq()申請中斷時,需要注意中斷觸發方式選擇IRQF_TRIGGER_NONE,只要能產生中斷就產生。
做實驗之前先去除內核配置內的
Device Drivers
I2C support
I2C Hardware Bus support
< > S3C2410 I2C Driver