linuxok6410的I2C驅動分析 -开发者知识库

linuxok6410的I2C驅動分析 -开发者知识库,第1张

3  i2c-dev

3.1 概述

之前在介紹I2C子系統時,提到過使用i2c-dev.c文件在應用程序中實現我們的I2C從設備驅動。不過,它實現的是一個虛擬,臨時的i2c_client,隨着設備文件的打開而產生,並隨着設備文件的關閉而撤銷。I2c-dev.c針對每個I2C適配器生成一個主設備號為89的設備文件,實現了i2c_driver的成員函數以及文件操作接口,所以i2c-dev.c的主題是”i2c_driver成員函數 字符設備驅動”。

 

3.2 i2c-dev.c源碼分析

初始化模塊

[cpp] view plaincopy
  1. static int __init i2c_dev_init(void)  
  2. {  
  3.          res= register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops);  
  4.    
  5.          i2c_dev_class= class_create(THIS_MODULE, "i2c-dev");  
  6.    
  7.          /*Keep track of adapters which will be added or removed later */  
  8.          res= bus_register_notifier(&i2c_bus_type, &i2cdev_notifier);  
  9.    
  10.          /*綁定已經存在的適配器 */  
  11.          i2c_for_each_dev(NULL,i2cdev_attach_adapter);  
  12. }  

I2c-dev初始化函數主要做了注冊名為”i2c”的字符設備文件和”i2c-dev”的類

 

i2cdev_read和i2cdev_write

I2c-dev.c中實現的i2cdev_read和i2cdev_write函數不具有太強的通用性,只適合下面這種單開始信號情況:

linuxok6410的I2C驅動分析 -开发者知识库,第2张

而不適合多開始信號的情況:

linuxok6410的I2C驅動分析 -开发者知识库,第3张

所以我們經常會使用i2cdev_ioctl函數的I2C_RDWR,在分析i2cdev_ioctl函數之前,我們需要了解一個結構體:

[cpp] view plaincopy
  1. /* This is the structure as used in theI2C_RDWR ioctl call */  
  2. struct i2c_rdwr_ioctl_data {  
  3.          structi2c_msg __user *msgs;         /* pointersto i2c_msgs */  
  4.          __u32nmsgs;                    /* number ofi2c_msgs */  
  5. };  

Msgs     表示單個開始信號傳遞的數據;

Nmsgs     表示有多少個msgs,比如上圖,單開始信號時,nmsgs等於1;多開始信號時,nmsgs等於2

 

[cpp] view plaincopy
  1. struct i2c_msg {  
  2.          __u16addr;     /* slave address                         */  
  3.          __u16flags;  /* 默認為寫入 */  
  4. #define I2C_M_TEN                  0x0010     /*this is a ten bit chip address */  
  5. #define I2C_M_RD           0x0001     /* read data,from slave to master */  
  6. #define I2C_M_NOSTART                  0x4000     /* if I2C_FUNC_PROTOCOL_MANGLING */  
  7. #define I2C_M_REV_DIR_ADDR     0x2000     /*if I2C_FUNC_PROTOCOL_MANGLING */  
  8. #define I2C_M_IGNORE_NAK          0x1000     /*if I2C_FUNC_PROTOCOL_MANGLING */  
  9. #define I2C_M_NO_RD_ACK           0x0800     /* if I2C_FUNC_PROTOCOL_MANGLING */  
  10. #define I2C_M_RECV_LEN               0x0400     /* length will be first received byte */  
  11.          __u16len;                  /* msg length                              */  
  12.          __u8*buf;                 /* pointer to msgdata                       */  
  13. };  

 

3.3 eeprom實例

預備知識

使用的ok6410開發板,eeprom的地址為0x50,實驗完成一個數據的讀寫,先看下讀寫時序

AT24C02任意地址字節寫的時序:

linuxok6410的I2C驅動分析 -开发者知识库,第4张

AT24C02任意地址字節寫的時序:

linuxok6410的I2C驅動分析 -开发者知识库,第5张

用戶態驅動:

 

#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <stdlib.h>
//#include <linux/types.h>

#define I2C_RDWR0x0707

struct i2c_msg {
unsigned short addr;/* slave address*/
unsigned short flags;
unsigned short len;/* msg length*/
unsigned char *buf;/* pointer to msg data*/
};

struct i2c_rdwr_ioctl_data {
struct i2c_msg *msgs;/* pointers to i2c_msgs */
unsigned int nmsgs;/* number of i2c_msgs */
};

int main()
{
int fd;
struct i2c_rdwr_ioctl_data e2prom_data;

//1. 打開通用設備文件
fd = open("/dev/i2c-0", O_RDWR);

//為i2c_rdwr_ioctl_data中的struct i2c_msg *分配空間
e2prom_data.msgs = (struct i2c_msg *)malloc(2*sizeof(struct i2c_msg));// 構造兩條消息

//2. 構造寫數據到eeprom
e2prom_data.nmsgs = 1; // 只有一條消息
(e2prom_data.msgs[0]).len = 2;//長度等於2,第一個字節代表的是i2c設備的內部地址,第二個字節代表的是寫入的數據
(e2prom_data.msgs[0]).addr = 0x50; // 從設備地址(e2prom的地址),注意這里是不帶方向的!
(e2prom_data.msgs[0]).flags = 0;// 方向由flag標志位來指明,0代表了寫,1代表了讀
(e2prom_data.msgs[0]).buf = (unsigned char*)malloc(2); // 這里只分配兩個字節(內部偏移地址一字節,數據1字節)
(e2prom_data.msgs[0]).buf[0] = 0x10;// 數據將寫入e2prom中的內部0x10地址中
(e2prom_data.msgs[0]).buf[1] = 0x60;// 寫入e2prom中內 部0x10地址中的數據位0x60

//3. 使用ioctl寫入數據
ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data, I2C_RDWR);// 這里面的命令參數對應的是驅動內部的ioctl中的case語句中的參數

//4. 構造從eeprom讀數據的消息
e2prom_data.nmsgs = 2; // 讀數據需要兩條消息
(e2prom_data.msgs[0]).len = 1;//長度為一個字節,代表的是i2c設備的內部地址
(e2prom_data.msgs[0]).addr = 0x50; // 從設備地址(e2prom的地址),注意這里是不帶方向的!
(e2prom_data.msgs[0]).flags = 0;// 方向由flag標志位來指明,0代表了寫,1代表了讀
(e2prom_data.msgs[0]).buf[0] = 0x10;// 數據將寫入e2prom中的內部0x10地址中
// 第二條消息(讀數據)
(e2prom_data.msgs[1]).len = 1;//長度為一個字節,代表的是i2c設備的內部地址
(e2prom_data.msgs[1]).addr = 0x50; // 從設備地址(e2prom的地址),注意這里是不帶方向的!
(e2prom_data.msgs[1]).flags = 1;// 方向由flag標志位來指明,0代表了寫,1代表了讀
(e2prom_data.msgs[1]).buf = (unsigned char*)malloc(2);
(e2prom_data.msgs[1]).buf[0] = 0;// 數據從e2prom中的內部0x10地址中讀出

//5. 使用ioctl讀出數據
ioctl(fd, I2C_RDWR, (unsigned long)&e2prom_data);
printf("buf[0] = %x\n", (e2prom_data.msgs[1]).buf[0]);

//6. 關閉設備
close(fd);
}

  

最佳答案:

DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
U19学习网站 » linuxok6410的I2C驅動分析 -开发者知识库