linux-3.4.2 IIC驅動使用介紹

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模型介紹

設備(device)一共有4種構建方法(文檔有介紹)

文檔位置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

 

 

 

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