从机,主机对从机发一个字节之后,主机要读取从机的响应信号(主机读SDA线)_linux i2c 驱动开发">
当前位置:   article > 正文

Linux驱动开发--IIC总线开发_linux i2c 驱动开发

linux i2c 驱动开发

一、I2C总线背景知识

SOC芯片平台的外设分为:

  1. 一级外设:外设控制器集成在SOC芯片内部
  2. 二级外设:外设控制器由另一块芯片负责,通过一些通讯总线与SOC芯片相连

 Inter-Integrated Circuit: 字面意思是用于“集成电路之间”的通信总线,简写:IIC(或者I2C)

 

 

i2c传输的要点就是: 传输一个字节 后面必然紧跟一个"响应"信号----应答信号.这个响应信号可能来自主机,或者是从机,具体是谁,就要看传输方向。 传输方向分两种情况(每种情况又有两种可能: A无应答和 B有应答):

1.主机->从机,主机对从机发一个字节之后,主机要读取从机的响应信号(主机读SDA线)

  1. A) 主机读SDA为高电平,说明从机无应答(意味着从机接收完毕,主机发送停止信号)
  2. B) 主机读SDA为低电平,说明从机有应答。(可继续发送下一个字节)

2.从机->主机, 主机读取从机一个字节之后,主机要向从机发送一个响应信号(主机写SDA线)

​ A) 主机写SDA为高电平,从机收到主机的无应答信号之后,从机停止传输,等待主机的停止信号。​ B) 主机写SDA为低电平,从机收到主机的应答信号之后,从机继续输出下一字节

二、Exynos4412 I2C收发实现之裸机版

I2CCON寄存器:控制寄存器

第7位:决定是否允许产生应答信号,无论发送还是接收前,需置1

第6位:传输时时钟线分频,一般选置1

第5位:决定是否开启发送或接收结束时发通知,无论发送还是接收前,需置1

第4位:接收或发送是否完毕可以通过检查此位是否为1,接收或发送完毕后需置0

I2CSTAT寄存器:状态寄存器

第6、7位:每次传输前需选择传输模式

第5位:置0产生将产生终止信号,传输前置1产生起始信号

第4位:使能数据输出,传输前需置1

I2CDS寄存器:数据寄存器,发送前被发送的数据存放处,接收后结果也从此处读取

2.1 发送

 

 

  1. void iic_write (unsigned char slave_addr, unsigned char addr, unsigned char data)
  2. {
  3. //从设备寻址
  4. I2C5.I2CDS = slave_addr;
  5. I2C5.I2CCON = 1<<7 | 1<<6 | 1<<5;/*ENABLE ACK BIT, PRESCALER:512, ,ENABLE RX/TX */
  6. I2C5.I2CSTAT = 0x3 << 6 | 1<<5 | 1<<4;/*Master Trans mode ,START ,ENABLE RX/TX ,*/
  7. while(!(I2C5.I2CCON & (1<<4)));
  8. I2C5.I2CDS = addr;
  9. I2C5.I2CCON &= ~(1<<4); //Clear pending bit to resume.
  10. while(!(I2C5.I2CCON & (1<<4)));
  11. //发送数据
  12. I2C5.I2CDS = data; // Data
  13. I2C5.I2CCON &= ~(1<<4); //Clear pending bit to resume.
  14. while(!(I2C5.I2CCON & (1<<4)));
  15. I2C5.I2CSTAT = 0xD0; //stop
  16. I2C5.I2CCON &= ~(1<<4);//Clear pending bit to resume.
  17. mydelay_ms(10);
  18. }

2.2 接收

 

 

  1. void iic_read(unsigned char slave_addr, unsigned char addr, unsigned char *data)
  2. {
  3. //从设备寻址
  4. I2C5.I2CDS = slave_addr;
  5. I2C5.I2CCON = 1<<7 | 1<<6 | 1<<5;/*ENABLE ACK BIT, PRESCALER:512, ENABLE RX/TX Interrupt-enable */
  6. I2C5.I2CSTAT = 0x3 << 6 | 1<<5 | 1<<4;/*Master Trans mode ,START ,ENABLE RX/TX ,*/
  7. while(!(I2C5.I2CCON & (1<<4))); /*对应位为1表示slave_addr传输完成,线路处于挂起状态*/
  8. I2C5.I2CDS = addr;
  9. I2C5.I2CCON &= ~(1<<4); //Clear pending bit to resume. 继续传输
  10. while(!(I2C5.I2CCON & (1<<4)));
  11. I2C5.I2CSTAT = 0xD0; //stop 第5位写0,表示要求产生stop信号
  12. //接收数据
  13. I2C5.I2CDS = slave_addr | 0x01; // Read
  14. I2C5.I2CCON = 1<<7 | 1<<6 | 1<<5;/*ENABLE ACK BIT, PRESCALER:512, ENABLE RX/TX Interrupt-enable */
  15. I2C5.I2CSTAT = 2<<6 | 1<<5 | 1<<4;/*Master receive mode ,START ,ENABLE RX/TX , 0xB0*/
  16. while(!(I2C5.I2CCON & (1<<4)));
  17. I2C5.I2CCON &= ~((1<<7) | (1<<4));/* Resume the operation & no ack*/
  18. while(!(I2C5.I2CCON & (1<<4)));
  19. I2C5.I2CSTAT = 0x90; //stop 第5位写0,表示要求产生stop信号
  20. I2C5.I2CCON &= ~(1<<4); /*clean interrupt pending bit */
  21. *data = I2C5.I2CDS;
  22. mydelay_ms(10);
  23. }

 三、Linux内核对I2C总线的支持

**I2C设备驱动:**即挂接在I2C总线上的二级外设的驱动,也称客户(client)驱动,实现对二级外设的各种操作,二级外设的几乎所有操作全部依赖于对其自身内部寄存器的读写,对这些二级外设寄存器的读写又依赖于I2C总线的发送和接收

**I2C总线驱动:**即对I2C总线自身控制器的驱动,一般SOC芯片都会提供多个I2C总线控制器,每个I2C总线控制器提供一组I2C总线(SDA一根+SCL一根),每一组被称为一个I2C通道,Linux内核里将I2C总线控制器叫做适配器(adapter),适配器驱动主要工作就是提供通过本组I2C总线与二级外设进行数据传输的接口,每个二级外设驱动里必须能够获得其对应的adapter对象才能实现数据传输

**I2C核心:**承上启下,为I2C设备驱动和I2C总线驱动开发提供接口,为I2C设备驱动层提供管理多个i2c_driver、i2c_client对象的数据结构,为I2C总线驱动层提供多个i2c_algorithm、i2c_adapter对象的数据结构

四大核心对象之间的关系图

 

 

i2c二级外设驱动开发涉及到核心结构体及其相关接口函数:

  1. struct i2c_board_info {
  2. char type[I2C_NAME_SIZE];
  3. unsigned short flags;
  4. unsigned short addr;
  5. void *platform_data;
  6. struct dev_archdata *archdata;
  7. struct device_node *of_node;
  8. int irq;
  9. };
  10. /*用来协助创建i2c_client对象
  11. 重要成员
  12. type:用来初始化i2c_client结构中的name成员
  13. flags:用来初始化i2c_client结构中的flags成员
  14. addr:用来初始化i2c_client结构中的addr成员
  15. platform_data:用来初始化i2c_client结构中的.dev.platform_data成员
  16. archdata:用来初始化i2c_client结构中的.dev.archdata成员
  17. irq:用来初始化i2c_client结构中的irq成员
  18. 关键就是记住该结构和i2c_client结构成员的对应关系。在i2c子系统不直接创建i2c_client结构,只是提供struct i2c_board_info结构信息,让子系统动态创建,并且注册。
  19. */
  1. struct i2c_client {
  2. unsigned short flags;
  3. unsigned short addr;
  4. char name[I2C_NAME_SIZE];
  5. struct i2c_adapter *adapter;
  6. struct i2c_driver *driver;
  7. struct device dev;
  8. int irq;
  9. struct list_head detected;
  10. };
  11. /*重要成员:
  12. flags:地址长度,如是10位还是7位地址,默认是7位地址。如果是10位地址器件,则设置为I2C_CLIENT_TEN
  13. addr:具体I2C器件如(at24c02),设备地址,低7位
  14. name:设备名,用于和i2c_driver层匹配使用的,可以和平台模型中的平台设备层platform_driver中的name作用是一样的。
  15. adapter:本设备所绑定的适配器结构(CPU有很多I2C适配器,类似单片机有串口1、串口2等等,在linux中每个适配器都用一个结构描述)
  16. driver:指向匹配的i2c_driver结构,不需要自己填充,匹配上后内核会完成这个赋值操作
  17. dev:内嵌的设备模型,可以使用其中的platform_data成员传递给任何数据给i2c_driver使用。
  18. irq:设备需要使用到中断时,把中断编号传递给i2c_driver进行注册中断,如果没有就不需要填充。(有的I2C器件有中断引脚编号,与CPU相连)
  19. */
  20. /* 获得/释放 i2c_adapter 路径:i2c-core.c linux-3.5\drivers\i2c */
  21. /*功能:通过i2c总线编号获得内核中的i2c_adapter结构地址,然后用户可以使用这个结构地址就可以给i2c_client结构使用,从而实现i2c_client进行总线绑定,从而增加适配器引用计数。
  22. 返回值:
  23. NULL:没有找到指定总线编号适配器结构
  24. 非NULL:指定nr的适配器结构内存地址*/
  25. struct i2c_adapter *i2c_get_adapter(int nr);
  26. /*减少引用计数:当使用·i2c_get_adapter·后,需要使用该函数减少引用计数。(如果你的适配器驱动不需要卸载,可以不使用)*/
  27. void i2c_put_adapter(struct i2c_adapter *adap);
  28. /*
  29. 功能:根据参数adap,info,addr,addr_list动态创建i2c_client并且进行注册
  30. 参数:
  31. adap:i2c_client所依附的适配器结构地址
  32. info:i2c_client基本信息
  33. addt_list: i2c_client的地址(地址定义形式是固定的,一般是定义一个数组,数组必须以I2C_CLIENT_END结束,示例:unsigned short ft5x0x_i2c[]={0x38,I2C_CLIENT_END};
  34. probe:回调函数指针,当创建好i2c_client后,会调用该函数,一般没有什么特殊需求传递NULL。
  35. 返回值:
  36. 非NULL:创建成功,返回创建好的i2c_client结构地址
  37. NULL:创建失败
  38. */
  39. struct i2c_client * i2c_new_probed_device
  40. (
  41. struct i2c_adapter *adap,
  42. struct i2c_board_info *info,
  43. unsigned short const *addr_list,
  44. int (*probe)(struct i2c_adapter *, unsigned short addr)
  45. );
  46. /*示例:
  47. struct i2c_adapter *ad;
  48. struct i2c_board_info info={""};
  49. unsigned short addr_list[]={0x38,0x39,I2C_CLIENT_END};
  50. //假设设备挂在i2c-2总线上
  51. ad=i2c_get_adapter(2);
  52. //自己填充board_info
  53. strcpy(inf.type,"xxxxx");
  54. info.flags=0;
  55. //动态创建i2c_client并且注册
  56. i2c_new_probed_device(ad,&info,addr_list,NULL);
  57. i2c_put_adapter(ad);
  58. */
  59. /*注销*/
  60. void i2c_unregister_device(struct i2c_client *pclt)
  61. struct i2c_client * i2c_new_device
  62. (
  63. struct i2c_adapter *padap,
  64. struct i2c_board_info const *pinfo
  65. );
  66. /*示例:
  67. struct i2c_adapter *ad;
  68. struct i2c_board_info info={
  69. I2C_BOARD_INFO(name,二级外设地址)
  70. };
  71. //假设设备挂在i2c-2总线上
  72. ad=i2c_get_adapter(2);
  73. //动态创建i2c_client并且注册
  74. i2c_new_device(ad,&info);
  75. i2c_put_adapter(ad);
  76. */
  1. struct i2c_driver {
  2. unsigned int class;
  3. /* Standard driver model interfaces */
  4. int (*probe)(struct i2c_client *, const struct i2c_device_id *);
  5. int (*remove)(struct i2c_client *);
  6. /* driver model interfaces that don't relate to enumeration */
  7. void (*shutdown)(struct i2c_client *);
  8. int (*suspend)(struct i2c_client *, pm_message_t mesg);
  9. int (*resume)(struct i2c_client *);
  10. void (*alert)(struct i2c_client *, unsigned int data);
  11. /* a ioctl like command that can be used to perform specific functions
  12. * with the device.
  13. */
  14. int (*command)(struct i2c_client *client, unsigned int cmd, void *arg);
  15. struct device_driver driver;
  16. const struct i2c_device_id *id_table;
  17. /* Device detection callback for automatic device creation */
  18. int (*detect)(struct i2c_client *, struct i2c_board_info *);
  19. const unsigned short *address_list;
  20. struct list_head clients;
  21. };
  22. /*重要成员:
  23. probe:在i2c_client与i2c_driver匹配后执行该函数
  24. remove:在取消i2c_client与i2c_driver匹配绑定后后执行该函数
  25. driver:这个成员类型在平台设备驱动层中也有,而且使用其中的name成员来实现平台设备匹配,但是i2c子系统中不使用其中的name进行匹配,这也是i2c设备驱动模型和平台设备模型匹配方法的一点区别
  26. id_table:用来实现i2c_client与i2c_driver匹配绑定,当i2c_client中的name成员和i2c_driver中id_table中name成员相同的时候,就匹配上了。
  27. 补充:i2c_client与i2c_driver匹配问题
  28. - i2c_client中的name成员和i2c_driver中id_table中name成员相同的时候
  29. - i2c_client指定的信息在物理上真实存放对应的硬件,并且工作是正常的才会绑定上,并执行其中的probe接口函数这第二点要求和平台模型匹配有区别,平台模型不要求设备层指定信息在物理上真实存在就能匹配
  30. */
  31. /*功能:向内核注册一个i2c_driver对象
  32. 返回值:0成功,负数 失败*/
  33. #define i2c_add_driver(driver) i2c_register_driver(THIS_MODULE, driver)
  34. int i2c_register_driver(struct module *owner, struct i2c_driver *driver);
  35. /*功能:从内核注销一个i2c_driver对象
  36. 返回值:无 */
  37. void i2c_del_driver(struct i2c_driver *driver);
  1. struct i2c_msg {
  2. __u16 addr; /* slave address */
  3. __u16 flags;
  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. __u16 len; /* msg length */
  7. __u8 *buf; /* pointer to msg data */
  8. };
  9. /* 重要成员:
  10. addr:要读写的二级外设地址
  11. flags:表示地址的长度,读写功能。如果是10位地址必须设置I2C_M_TEN,如果是读操作必须设置有I2C_M_RD······,可以使用或运算合成。
  12. buf:要读写的数据指针。写操作:数据源 读操作:指定存放数据的缓存区
  13. len:读写数据的数据长度
  14. */
  15. /*i2c收发一体化函数,收还是发由参数msgs的成员flags决定*/
  16. int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
  17. /*
  18. 功能:根据msgs进行手法控制
  19. 参数:
  20. adap:使用哪一个适配器发送信息,一般是取i2c_client结构中的adapter指针作为参数
  21. msgs:具体发送消息指针,一般情况下是一个数组
  22. num:表示前一个参数msgs数组有多少个消息要发送的
  23. 返回值:
  24. 负数:失败
  25. > 0 表示成功发送i2c_msg数量
  26. */
  27. /*I2C读取数据函数*/
  28. int i2c_master_recv(const struct i2c_client *client, char *buf, int count)
  29. /*功能:实现标准的I2C读时序,数据可以是N个数据,这个函数调用时候默认已经包含发送从机地址+读方向这一环节了
  30. 参数:
  31. client:设备结构
  32. buf:读取数据存放缓冲区
  33. count:读取数据大小 不大于64k
  34. 返回值:
  35. 失败:负数
  36. 成功:成功读取的字节数
  37. */
  38. /*I2C发送数据函数*/
  39. int i2c_master_send(const struct i2c_client *client, const char *buf, int count)
  40. /*功能:实现标准的I2C写时序,数据可以是N个数据,这个函数调用时候默认已经包含发送从机地址+写方向这一环节了
  41. 参数:
  42. client:设备结构地址
  43. buf:发送数据存放缓冲区
  44. count:发送数据大小 不大于64k
  45. 返回值:
  46. 失败:负数
  47. 成功:成功发送的字节数
  48. */

四、MPU6050

三轴角速度+三轴加速度+温度传感器

  1. #define SMPLRT_DIV 0x19 //陀螺仪采样率,典型值:0x07(125Hz)
  2. #define CONFIG 0x1A //低通滤波频率,典型值:0x06(5Hz)
  3. #define GYRO_CONFIG 0x1B //陀螺仪自检及测量范围,典型值:0xF8(不自检,+/-2000deg/s)
  4. #define ACCEL_CONFIG 0x1C //加速计自检、测量范围,典型值:0x19(不自检,+/-G)
  5. #define ACCEL_XOUT_H 0x3B
  6. #define ACCEL_XOUT_L 0x3C
  7. #define ACCEL_YOUT_H 0x3D
  8. #define ACCEL_YOUT_L 0x3E
  9. #define ACCEL_ZOUT_H 0x3F
  10. #define ACCEL_ZOUT_L 0x40
  11. #define TEMP_OUT_H 0x41
  12. #define TEMP_OUT_L 0x42
  13. #define GYRO_XOUT_H 0x43
  14. #define GYRO_XOUT_L 0x44
  15. #define GYRO_YOUT_H 0x45
  16. #define GYRO_YOUT_L 0x46
  17. #define GYRO_ZOUT_H 0x47
  18. #define GYRO_ZOUT_L 0x48
  19. #define PWR_MGMT_1 0x6B //电源管理,典型值:0x00(正常启用)

五、应用层直接使用I2C通道

5.1 预备工作:

5.1.1 exynos4412平台每个i2c通道的信息是通过设备树提供的,因此需要首先在exynos4412-fs4412.dts中增加5通道的节点:

不要忘记:

  1. 回内核源码顶层目录执行:make dtbs
  2. 将新生成的dtb拷贝到/tftpboot

5.1.2 i2c总线驱动层提供了一个字符设备驱动,以便于应用层可以直接通过它去使用i2c总线通讯去操作二级外设,但需要

内核编译时添加此字符设备驱动代码(i2c-dev.c),因此需要修改make menuconfig的配置:

 

不要忘记:

  1. 回内核源码顶层目录执行:make uImage
  2. 将新生成的uImage拷贝到/tftpboot

5.2 应用层直接使用i2c总线的代码实现

5.2.1 调用read、write实现接收、发送

  1. #include "mpu6050.h"
  2. int main(int argc,char *argv[])
  3. {
  4. int fd = -1;
  5. if(argc < 2)
  6. {
  7. printf("Argument is too few\n");
  8. return 1;
  9. }
  10. /*open */
  11. fd = open(argv[1],O_RDWR);
  12. if(fd < 0)
  13. {
  14. printf("open %s failed\n",argv[1]);
  15. return 2;
  16. }
  17. /*init mpu6050*/
  18. init_mpu6050(fd);
  19. while(1)
  20. {
  21. sleep(2);
  22. /*read and print data from 6050*/
  23. printf("Accel-X:0x%x\n",read_accelx(fd));
  24. printf("Accel-Y:0x%x\n",read_accely(fd));
  25. printf("Accel-Z:0x%x\n",read_accelz(fd));
  26. printf("Temp:0x%x\n",read_temp(fd));
  27. printf("GYRO-X:0x%x\n",read_gyrox(fd));
  28. printf("GYRO-Y:0x%x\n",read_gyroy(fd));
  29. printf("GYRO-z:0x%x\n",read_gyroz(fd));
  30. printf("\n");
  31. }
  32. /*close*/
  33. close(fd);
  34. fd = -1;
  35. return 0;
  36. }
  1. #include "mpu6050.h"
  2. int main(int argc,char *argv[])
  3. {
  4. int fd = -1;
  5. if(argc < 2)
  6. {
  7. printf("Argument is too few\n");
  8. return 1;
  9. }
  10. /*open */
  11. fd = open(argv[1],O_RDWR);
  12. if(fd < 0)
  13. {
  14. printf("open %s failed\n",argv[1]);
  15. return 2;
  16. }
  17. /*init mpu6050*/
  18. init_mpu6050(fd);
  19. while(1)
  20. {
  21. sleep(2);
  22. /*read and print data from 6050*/
  23. printf("Accel-X:0x%x\n",read_accelx(fd));
  24. printf("Accel-Y:0x%x\n",read_accely(fd));
  25. printf("Accel-Z:0x%x\n",read_accelz(fd));
  26. printf("Temp:0x%x\n",read_temp(fd));
  27. printf("GYRO-X:0x%x\n",read_gyrox(fd));
  28. printf("GYRO-Y:0x%x\n",read_gyroy(fd));
  29. printf("GYRO-z:0x%x\n",read_gyroz(fd));
  30. printf("\n");
  31. }
  32. /*close*/
  33. close(fd);
  34. fd = -1;
  35. return 0;
  36. }
  37. book@100ask:~/Linux_4412/mydrivercode/i2c/arr_rw$ cat mpu6050_op_rw.c
  38. #include "mpu6050.h"
  39. static int read_data_from_mpu6050(int fd,unsigned char reg, unsigned char *pdata)
  40. {
  41. int ret;
  42. unsigned char buf[1] = {reg};
  43. ret = write(fd,buf,1);
  44. if(ret != 1)
  45. {
  46. printf("write reg failed, in read_data_from_mpu6050\n");
  47. return -1;
  48. }
  49. buf[0] = 0;
  50. ret = read(fd, buf, 1);
  51. if(ret != 1)
  52. {
  53. printf("read data failed, in read_data_from_mpu6050\n");
  54. return -1;
  55. }
  56. *pdata = buf[0];
  57. return 0;
  58. }
  59. static int write_data_to_mpu6050(int fd,unsigned char reg, unsigned char data)
  60. {
  61. unsigned char buf[2] = {reg,data};
  62. int ret = 0;
  63. ret = write(fd, buf, 2);
  64. if(ret != 2)
  65. {
  66. printf("write data failed, in read_data_from_mpu6050\n");
  67. return -1;
  68. }
  69. return 0;
  70. }
  71. int init_mpu6050(int fd)
  72. {
  73. int ret = 0;
  74. ret = ioctl(fd,I2C_TENBIT,0);
  75. if(ret < 0)
  76. {
  77. printf("ioctl I2C_TENBIT failed, in init_mpu6050\n");
  78. return -1;
  79. }
  80. ret = ioctl(fd,I2C_SLAVE,0x68);
  81. if(ret < 0)
  82. {
  83. printf("ioctl I2C_TENBIT failed, in init_mpu6050\n");
  84. return -1;
  85. }
  86. ret = write_data_to_mpu6050(fd,PWR_MGMT_1,0x00);
  87. ret += write_data_to_mpu6050(fd,SMPLRT_DIV,0x07);
  88. ret += write_data_to_mpu6050(fd,ACCEL_CONFIG,0x19);
  89. ret += write_data_to_mpu6050(fd,GYRO_CONFIG,0xF8);
  90. if(ret < 0)
  91. {
  92. printf("write init data to mpu6050 failed,in init_mpu6050\n");
  93. return -1;
  94. }
  95. return 0;
  96. }
  97. int read_accelx(int fd)
  98. {
  99. unsigned short val = 0;
  100. unsigned char d = 0;
  101. int ret = 0;
  102. ret = read_data_from_mpu6050(fd,ACCEL_XOUT_L,&d);
  103. val = d;
  104. ret = read_data_from_mpu6050(fd,ACCEL_XOUT_H,&d);
  105. val |= d << 8;
  106. if(ret < 0)
  107. {
  108. printf("read accel x value failed,in read_accelx\n");
  109. return -1;
  110. }
  111. else
  112. {
  113. return val;
  114. }
  115. }
  116. int read_accely(int fd)
  117. {
  118. unsigned short val = 0;
  119. unsigned char d = 0;
  120. int ret = 0;
  121. ret = read_data_from_mpu6050(fd,ACCEL_YOUT_L,&d);
  122. val = d;
  123. ret = read_data_from_mpu6050(fd,ACCEL_YOUT_H,&d);
  124. val |= d << 8;
  125. if(ret < 0)
  126. {
  127. printf("read accel y value failed,in read_accely\n");
  128. return -1;
  129. }
  130. else
  131. {
  132. return val;
  133. }
  134. }
  135. int read_accelz(int fd)
  136. {
  137. unsigned short val = 0;
  138. unsigned char d = 0;
  139. int ret = 0;
  140. ret = read_data_from_mpu6050(fd,ACCEL_ZOUT_L,&d);
  141. val = d;
  142. ret = read_data_from_mpu6050(fd,ACCEL_ZOUT_H,&d);
  143. val |= d << 8;
  144. if(ret < 0)
  145. {
  146. printf("read accel z value failed,in read_accelz\n");
  147. return -1;
  148. }
  149. else
  150. {
  151. return val;
  152. }
  153. }
  154. int read_temp(int fd)
  155. {
  156. unsigned short val = 0;
  157. unsigned char d = 0;
  158. int ret = 0;
  159. ret = read_data_from_mpu6050(fd,TEMP_OUT_L,&d);
  160. val = d;
  161. ret = read_data_from_mpu6050(fd,TEMP_OUT_H,&d);
  162. val |= d << 8;
  163. if(ret < 0)
  164. {
  165. printf("read temp value failed,in read_temp\n");
  166. return -1;
  167. }
  168. else
  169. {
  170. return val;
  171. }
  172. }
  173. int read_gyrox(int fd)
  174. {
  175. unsigned short val = 0;
  176. unsigned char d = 0;
  177. int ret = 0;
  178. ret = read_data_from_mpu6050(fd,GYRO_XOUT_L,&d);
  179. val = d;
  180. ret = read_data_from_mpu6050(fd,GYRO_XOUT_H,&d);
  181. val |= d << 8;
  182. if(ret < 0)
  183. {
  184. printf("read gyro x value failed,in read_gyrox\n");
  185. return -1;
  186. }
  187. else
  188. {
  189. return val;
  190. }
  191. }
  192. int read_gyroy(int fd)
  193. {
  194. unsigned short val = 0;
  195. unsigned char d = 0;
  196. int ret = 0;
  197. ret = read_data_from_mpu6050(fd,GYRO_YOUT_L,&d);
  198. val = d;
  199. ret = read_data_from_mpu6050(fd,GYRO_YOUT_H,&d);
  200. val |= d << 8;
  201. if(ret < 0)
  202. {
  203. printf("read gyro y value failed,in read_gyroy\n");
  204. return -1;
  205. }
  206. else
  207. {
  208. return val;
  209. }
  210. }
  211. int read_gyroz(int fd)
  212. {
  213. unsigned short val = 0;
  214. unsigned char d = 0;
  215. int ret = 0;
  216. ret = read_data_from_mpu6050(fd,GYRO_ZOUT_L,&d);
  217. val = d;
  218. ret = read_data_from_mpu6050(fd,GYRO_ZOUT_H,&d);
  219. val |= d << 8;
  220. if(ret < 0)
  221. {
  222. printf("read gyro z value failed,in read_gyroz\n");
  223. return -1;
  224. }
  225. else
  226. {
  227. return val;
  228. }
  229. }
  1. #ifndef MPU_6050_H
  2. #define MPU_6050_H
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <fcntl.h>
  6. #include <unistd.h>
  7. #include <sys/ioctl.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. int init_mpu6050(int fd);
  12. int read_accelx(int fd);
  13. int read_accely(int fd);
  14. int read_accelz(int fd);
  15. int read_temp(int fd);
  16. int read_gyrox(int fd);
  17. int read_gyroy(int fd);
  18. int read_gyroz(int fd);
  19. #define SMPLRT_DIV 0x19
  20. #define CONFIG 0x1A
  21. #define GYRO_CONFIG 0x1B
  22. #define ACCEL_CONFIG 0x1C
  23. #define ACCEL_XOUT_H 0x3B
  24. #define ACCEL_XOUT_L 0x3C
  25. #define ACCEL_YOUT_H 0x3D
  26. #define ACCEL_YOUT_L 0x3E
  27. #define ACCEL_ZOUT_H 0x3F
  28. #define ACCEL_ZOUT_L 0x40
  29. #define TEMP_OUT_H 0x41
  30. #define TEMP_OUT_L 0x42
  31. #define GYRO_XOUT_H 0x43
  32. #define GYRO_XOUT_L 0x44
  33. #define GYRO_YOUT_H 0x45
  34. #define GYRO_YOUT_L 0x46
  35. #define GYRO_ZOUT_H 0x47
  36. #define GYRO_ZOUT_L 0x48
  37. #define PWR_MGMT_1 0x6B
  38. #define I2C_SLAVE 0x0703 /* Use this slave address */
  39. #define I2C_TENBIT 0x0704 /* 0 for 7 bit addrs, != 0 for 10 bit */
  40. #endif

5.2.2 调用ioctl实现接收、发送

  1. #ifndef MPU_6050_H
  2. #define MPU_6050_H
  3. #include <sys/types.h>
  4. #include <sys/stat.h>
  5. #include <fcntl.h>
  6. #include <unistd.h>
  7. #include <sys/ioctl.h>
  8. #include <stdio.h>
  9. #include <stdlib.h>
  10. #include <string.h>
  11. int init_mpu6050(int fd);
  12. int read_accelx(int fd);
  13. int read_accely(int fd);
  14. int read_accelz(int fd);
  15. int read_temp(int fd);
  16. int read_gyrox(int fd);
  17. int read_gyroy(int fd);
  18. int read_gyroz(int fd);
  19. #define SMPLRT_DIV 0x19
  20. #define CONFIG 0x1A
  21. #define GYRO_CONFIG 0x1B
  22. #define ACCEL_CONFIG 0x1C
  23. #define ACCEL_XOUT_H 0x3B
  24. #define ACCEL_XOUT_L 0x3C
  25. #define ACCEL_YOUT_H 0x3D
  26. #define ACCEL_YOUT_L 0x3E
  27. #define ACCEL_ZOUT_H 0x3F
  28. #define ACCEL_ZOUT_L 0x40
  29. #define TEMP_OUT_H 0x41
  30. #define TEMP_OUT_L 0x42
  31. #define GYRO_XOUT_H 0x43
  32. #define GYRO_XOUT_L 0x44
  33. #define GYRO_YOUT_H 0x45
  34. #define GYRO_YOUT_L 0x46
  35. #define GYRO_ZOUT_H 0x47
  36. #define GYRO_ZOUT_L 0x48
  37. #define PWR_MGMT_1 0x6B
  38. #define I2C_SLAVE 0x0703 /* Use this slave address */
  39. #define I2C_TENBIT 0x0704 /* 0 for 7 bit addrs, != 0 for 10 bit */
  40. #define I2C_RDWR 0x0707 /* Combined R/W transfer (one STOP only) */
  41. struct i2c_msg {
  42. unsigned short addr; /* slave address */
  43. unsigned short flags;
  44. #define I2C_M_TEN 0x0010 /* this is a ten bit chip address */
  45. #define I2C_M_RD 0x0001 /* read data, from slave to master */
  46. unsigned short len; /* msg length */
  47. unsigned char *buf; /* pointer to msg data */
  48. };
  49. /*This is the structure as used in the I2C_RDWR ioctl call*/
  50. struct i2c_rdwr_ioctl_data{
  51. struct i2c_msg *msgs; /* pointers to i2c msgs */
  52. unsigned int nmsgs; /* number of i2c_msgs */
  53. };
  54. #endif
  1. #include "mpu6050.h"
  2. static int read_data_from_mpu6050(int fd,unsigned char slave,unsigned char reg,unsigned char *pdata)
  3. {
  4. struct i2c_rdwr_ioctl_data work = {NULL};
  5. struct i2c_msg msgs[2] = {{0}};
  6. unsigned char buf1[1] = {reg};
  7. unsigned char buf2[1] = {0};
  8. int ret = 0;
  9. work.msgs = msgs;
  10. work.nmsgs = 2;
  11. msgs[0].addr = slave;
  12. msgs[0].flags = 0;
  13. msgs[0].buf = buf1;
  14. msgs[0].len = 1;
  15. msgs[1].addr = slave;
  16. msgs[1].flags = I2C_M_RD;
  17. msgs[1].buf = buf2;
  18. msgs[1].len = 1;
  19. ret = ioctl(fd,I2C_RDWR,&work);
  20. if(ret < 0)
  21. {
  22. printf("ioctl I2C_RDWR failed,in read_data_from_mpu6050\n");
  23. return -1;
  24. }
  25. else
  26. {
  27. *pdata = buf2[0];
  28. return 0;
  29. }
  30. }
  31. static int write_data_to_mpu6050(int fd,unsigned char slave,unsigned char reg,unsigned char data)
  32. {
  33. struct i2c_rdwr_ioctl_data work = {NULL};
  34. struct i2c_msg msg = {0};
  35. unsigned char buf[2] = {reg,data};
  36. int ret = 0;
  37. work.msgs = &msg;
  38. work.nmsgs = 1;
  39. msg.addr = slave;
  40. msg.flags = 0;
  41. msg.buf = buf;
  42. msg.len = 2;
  43. ret = ioctl(fd,I2C_RDWR,&work);
  44. if(ret < 0)
  45. {
  46. printf("ioctl I2C_RDWR failed,in write_data_to_mpu6050\n");
  47. return -1;
  48. }
  49. else
  50. {
  51. return 0;
  52. }
  53. }
  54. int init_mpu6050(int fd)
  55. {
  56. int ret = 0;
  57. ret = ioctl(fd,I2C_TENBIT,0);
  58. if(ret < 0)
  59. {
  60. printf("ioctl I2C_TENBIT failed,in init_mpu6050\n");
  61. return -1;
  62. }
  63. ret = ioctl(fd,I2C_SLAVE,0x68);
  64. if(ret < 0)
  65. {
  66. printf("ioctl I2C_TENBIT failed,in init_mpu6050\n");
  67. return -1;
  68. }
  69. ret = write_data_to_mpu6050(fd,0x68,PWR_MGMT_1,0x00);
  70. ret += write_data_to_mpu6050(fd,0x68,SMPLRT_DIV,0x07);
  71. ret += write_data_to_mpu6050(fd,0x68,ACCEL_CONFIG,0x19);
  72. ret += write_data_to_mpu6050(fd,0x68,GYRO_CONFIG,0xF8);
  73. if(ret < 0)
  74. {
  75. printf("write init data to mpu6050 failed,in init_mpu6050\n");
  76. return -1;
  77. }
  78. return 0;
  79. }
  80. int read_accelx(int fd)
  81. {
  82. unsigned short val = 0;
  83. unsigned char d = 0;
  84. int ret = 0;
  85. ret = read_data_from_mpu6050(fd,0x68,ACCEL_XOUT_L,&d);
  86. val = d;
  87. ret = read_data_from_mpu6050(fd,0x68,ACCEL_XOUT_H,&d);
  88. val |= d << 8;
  89. if(ret < 0)
  90. {
  91. printf("read accel x value failed,in read_accelx\n");
  92. return -1;
  93. }
  94. else
  95. {
  96. return val;
  97. }
  98. }
  99. int read_accely(int fd)
  100. {
  101. unsigned short val = 0;
  102. unsigned char d = 0;
  103. int ret = 0;
  104. ret = read_data_from_mpu6050(fd,0x68,ACCEL_YOUT_L,&d);
  105. val = d;
  106. ret = read_data_from_mpu6050(fd,0x68,ACCEL_YOUT_H,&d);
  107. val |= d << 8;
  108. if(ret < 0)
  109. {
  110. printf("read accel y value failed,in read_accely\n");
  111. return -1;
  112. }
  113. else
  114. {
  115. return val;
  116. }
  117. }
  118. int read_accelz(int fd)
  119. {
  120. unsigned short val = 0;
  121. unsigned char d = 0;
  122. int ret = 0;
  123. ret = read_data_from_mpu6050(fd,0x68,ACCEL_ZOUT_L,&d);
  124. val = d;
  125. ret = read_data_from_mpu6050(fd,0x68,ACCEL_ZOUT_H,&d);
  126. val |= d << 8;
  127. if(ret < 0)
  128. {
  129. printf("read accel z value failed,in read_accelz\n");
  130. return -1;
  131. }
  132. else
  133. {
  134. return val;
  135. }
  136. }
  137. int read_temp(int fd)
  138. {
  139. unsigned short val = 0;
  140. unsigned char d = 0;
  141. int ret = 0;
  142. ret = read_data_from_mpu6050(fd,0x68,TEMP_OUT_L,&d);
  143. val = d;
  144. ret = read_data_from_mpu6050(fd,0x68,TEMP_OUT_H,&d);
  145. val |= d << 8;
  146. if(ret < 0)
  147. {
  148. printf("read temp value failed,in read_temp\n");
  149. return -1;
  150. }
  151. else
  152. {
  153. return val;
  154. }
  155. }
  156. int read_gyrox(int fd)
  157. {
  158. unsigned short val = 0;
  159. unsigned char d = 0;
  160. int ret = 0;
  161. ret = read_data_from_mpu6050(fd,0x68,GYRO_XOUT_L,&d);
  162. val = d;
  163. ret = read_data_from_mpu6050(fd,0x68,GYRO_XOUT_H,&d);
  164. val |= d << 8;
  165. if(ret < 0)
  166. {
  167. printf("read gyro x value failed,in read_gyrox\n");
  168. return -1;
  169. }
  170. else
  171. {
  172. return val;
  173. }
  174. }
  175. int read_gyroy(int fd)
  176. {
  177. unsigned short val = 0;
  178. unsigned char d = 0;
  179. int ret = 0;
  180. ret = read_data_from_mpu6050(fd,0x68,GYRO_YOUT_L,&d);
  181. val = d;
  182. ret = read_data_from_mpu6050(fd,0x68,GYRO_YOUT_H,&d);
  183. val |= d << 8;
  184. if(ret < 0)
  185. {
  186. printf("read gyro y value failed,in read_gyroy\n");
  187. return -1;
  188. }
  189. else
  190. {
  191. return val;
  192. }
  193. }
  194. int read_gyroz(int fd)
  195. {
  196. unsigned short val = 0;
  197. unsigned char d = 0;
  198. int ret = 0;
  199. ret = read_data_from_mpu6050(fd,0x68,GYRO_ZOUT_L,&d);
  200. val = d;
  201. ret = read_data_from_mpu6050(fd,0x68,GYRO_ZOUT_H,&d);
  202. val |= d << 8;
  203. if(ret < 0)
  204. {
  205. printf("read gyro z value failed,in read_gyroz\n");
  206. return -1;
  207. }
  208. else
  209. {
  210. return val;
  211. }
  212. }

缺点:

  1. 需要应用程序开发人员查阅原理图和芯片手册,增加了他们的开发负担
  2. 开发出的应用程序缺乏可移植性

六、I2C总线二级外设驱动开发方法

  1. 查阅原理图以便得知二级外设挂在哪条I2C总线上、二级外设的身份标识(二级外设自身的地址)

  2. 参照platform样式搭建二级外设驱动框架

  3. 查询二级外设芯片手册以便得知驱动需要用到的寄存器地址

    注意:

    1. 此处寄存器是指二级外设内部的寄存器,每个寄存器在芯片手册里有个对应编号(也被称为地址),但不是内存地址,特别提醒此寄存器不是SOC芯片内部参与内存统一编址的寄存器,更不是ARM核-CPU的寄存器
    2. 通过调用i2c_tranfer函数完成与相应寄存器的数据交互
  4. 参照字符驱动完成其余代码编写

  5. 创建对应的i2c_client对象

    linux-3.14\Documentation\i2c\instantiating-devices

    匹配方式:

    1. 名称匹配

    2. 设备树匹配

    3. ACPI匹配

      Advanced Configuration and Power Management Interface 高级配置和电源管理接口

      PC机平台采用的一种硬件配置接口

i2c二级外设驱动框架:

  1. //其它struct file_operations函数实现原理同硬编驱动
  2. static int mpu6050_probe(struct i2c_client *pclt,const struct i2c_device_id *pid)
  3. {
  4. //做硬编驱动模块入口函数的活
  5. }
  6. static int mpu6050_remove(struct i2c_client *pclt)
  7. {
  8. //做硬编驱动模块出口函数的活
  9. }
  10. /*名称匹配时定义struct i2c_device_id数组*/
  11. static struct i2c_device_id mpu6050_ids =
  12. {
  13. {"mpu6050",0},
  14. //.....
  15. {}
  16. };
  17. /*设备树匹配时定义struct of_device_id数组*/
  18. static struct of_device_id mpu6050_dts =
  19. {
  20. {.compatible = "invensense,mpu6050"},
  21. //....
  22. {}
  23. };
  24. /*通过定义struct i2c_driver类型的全局变量来创建i2c_driver对象,同时对其主要成员进行初始化*/
  25. struct i2c_driver mpu6050_driver =
  26. {
  27. .driver = {
  28. .name = "mpu6050",
  29. .owner = THIS_MODULE,
  30. .of_match_table = mpu6050_dts,
  31. },
  32. .probe = mpu6050_probe,
  33. .remove = mpu6050_remove,
  34. .id_table = mpu6050_ids,
  35. };
  36. /*以下其实是个宏,展开后相当于实现了模块入口函数和模块出口函数*/
  37. module_i2c_driver(mpu6050_driver);
  38. MODULE_LICENSE("GPL");

七、I2C总线二级外设驱动开发之名称匹配

这种匹配方式需要自己创建i2c_client对象

创建i2c_client对象有三种方式:

  1. i2c_register_board_info

    1. 1.当开发板上电内核跑起来的时候,肯定是架构相关的程序首先运行,也就是mach-xxx.c
    2. 2. mach-xxx.c文件里首先会定义i2c_board_info的结构体数组,在mach-xxx.c的初始化函数里调用
    3. i2c_register_board_info函数把i2c_board_inifo链接进内核的i2c_board_list链表当中去
    4. 3.在驱动i2c目录下和开发板板对应的驱动文件i2c-xxx.c里,创建i2c_adapter对象
    5. 4.这种方式严重依赖平台,缺乏灵活性,基本会被遗弃
  2. i2c_new_device:明确二级外设地址的情况下可用

    i2c二级外设client框架:

    1. #include <linux/kernel.h>
    2. #include <linux/module.h>
    3. #include <linux/i2c.h>
    4. static struct i2c_board_info mpu6050_info =
    5. {
    6. I2C_BOARD_INFO("mpu6050",二级外设地址)
    7. };
    8. static struct i2c_client *mpu6050_client;
    9. static int __init mpu6050_dev_init(void)
    10. {
    11. struct i2c_adapter *padp = NULL;
    12. padp = i2c_get_adapter(i2c通道编号);
    13. mpu6050_client = i2c_new_device(padp,&mpu6050_info);
    14. i2c_put_adapter(padp);
    15. return 0;
    16. }
    17. module_init(mpu6050_dev_init);
    18. static void __exit mpu6050_dev_exit(void)
    19. {
    20. i2c_unregister_device(mpu6050_client);
    21. }
    22. module_exit(mpu6050_dev_exit);
    23. MODULE_LICENSE("GPL");
  3. i2c_new_probed_device

    i2c二级外设client框架:不明确二级外设地址,但是知道是可能几个值之一的情况下可用

    1. #include <linux/kernel.h>
    2. #include <linux/module.h>
    3. #include <linux/i2c.h>
    4. static const unsigned short addr_list[] =
    5. {
    6. 0x68,
    7. //.....
    8. I2C_CLIENT_END
    9. };
    10. static struct i2c_client *mpu6050_client;
    11. static int __init mpu6050_dev_init(void)
    12. {
    13. struct i2c_adapter *padp = NULL;
    14. struct i2c_board_info mpu6050_info = {""};
    15. strcpy(mpu6050_info.type,"mpu6050");
    16. padp = i2c_get_adapter(i2c通道编号);
    17. mpu6050_client = i2c_new_probed_device(padp,&mpu6050_info,addr_list,NULL);
    18. i2c_put_adapter(padp);
    19. if(mpu6050_client != NULL)
    20. {
    21. return 0;
    22. }
    23. else
    24. {
    25. return -ENODEV;
    26. }
    27. }
    28. module_init(mpu6050_dev_init);
    29. static void __exit mpu6050_dev_exit(void)
    30. {
    31. i2c_unregister_device(mpu6050_client);
    32. }
    33. module_exit(mpu6050_dev_exit);
    34. MODULE_LICENSE("GPL");

 代码示例:

mpu6050_client.c

  1. #include <linux/kernel.h>
  2. #include <linux/module.h>
  3. #include <linux/i2c.h>
  4. static struct i2c_board_info mpu6050_info =
  5. {
  6. I2C_BOARD_INFO("mpu6050",0x68)
  7. };
  8. static struct i2c_client *gpmpu6050_client = NULL;
  9. static int __init mpu6050_client_init(void)
  10. {
  11. struct i2c_adapter *padp = NULL;
  12. padp = i2c_get_adapter(5);
  13. gpmpu6050_client = i2c_new_device(padp,&mpu6050_info);
  14. i2c_put_adapter(padp);
  15. return 0;
  16. }
  17. static void mpu6050_client_exit(void)
  18. {
  19. i2c_unregister_device(gpmpu6050_client);
  20. }
  21. module_init(mpu6050_client_init);
  22. module_exit(mpu6050_client_exit);
  23. MODULE_LICENSE("GPL");

 mpu6050_drv.c

  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/fs.h>
  4. #include <linux/i2c.h>
  5. #include <linux/cdev.h>
  6. #include <linux/wait.h>
  7. #include <linux/sched.h>
  8. #include <linux/poll.h>
  9. #include <linux/slab.h>
  10. #include <linux/mm.h>
  11. #include <linux/io.h>
  12. #include <asm/uaccess.h>
  13. #include <asm/atomic.h>
  14. #include "mpu6050.h"
  15. #define SMPLRT_DIV 0x19
  16. #define CONFIG 0x1A
  17. #define GYRO_CONFIG 0x1B
  18. #define ACCEL_CONFIG 0x1C
  19. #define ACCEL_XOUT_H 0x3B
  20. #define ACCEL_XOUT_L 0x3C
  21. #define ACCEL_YOUT_H 0x3D
  22. #define ACCEL_YOUT_L 0x3E
  23. #define ACCEL_ZOUT_H 0x3F
  24. #define ACCEL_ZOUT_L 0x40
  25. #define TEMP_OUT_H 0x41
  26. #define TEMP_OUT_L 0x42
  27. #define GYRO_XOUT_H 0x43
  28. #define GYRO_XOUT_L 0x44
  29. #define GYRO_YOUT_H 0x45
  30. #define GYRO_YOUT_L 0x46
  31. #define GYRO_ZOUT_H 0x47
  32. #define GYRO_ZOUT_L 0x48
  33. #define PWR_MGMT_1 0x6B
  34. int major = 11;
  35. int minor = 0;
  36. int mpu6050_num = 1;
  37. struct mpu6050_dev
  38. {
  39. struct cdev mydev;
  40. struct i2c_client *pclt;
  41. };
  42. struct mpu6050_dev *pgmydev = NULL;
  43. int mpu6050_read_byte(struct i2c_client *pclt,unsigned char reg)
  44. {
  45. int ret = 0;
  46. char txbuf[1] = {reg};
  47. char rxbuf[1] = {0};
  48. struct i2c_msg msg[2] =
  49. {
  50. {pclt->addr,0,1,txbuf},
  51. {pclt->addr,I2C_M_RD,1,rxbuf}
  52. };
  53. ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));
  54. if(ret < 0)
  55. {
  56. printk("ret = %d,in mpu6050_read_byte\n",ret);
  57. return ret;
  58. }
  59. return rxbuf[0];
  60. }
  61. int mpu6050_write_byte(struct i2c_client *pclt,unsigned char reg,unsigned char val)
  62. {
  63. int ret = 0;
  64. char txbuf[2] = {reg,val};
  65. struct i2c_msg msg[1] =
  66. {
  67. {pclt->addr,0,2,txbuf},
  68. };
  69. ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));
  70. if(ret < 0)
  71. {
  72. printk("ret = %d,in mpu6050_write_byte\n",ret);
  73. return ret;
  74. }
  75. return 0;
  76. }
  77. int mpu6050_open(struct inode *pnode,struct file *pfile)
  78. {
  79. pfile->private_data =(void *) (container_of(pnode->i_cdev,struct mpu6050_dev,mydev));
  80. return 0;
  81. }
  82. int mpu6050_close(struct inode *pnode,struct file *pfile)
  83. {
  84. return 0;
  85. }
  86. long mpu6050_ioctl(struct file *pfile,unsigned int cmd,unsigned long arg)
  87. {
  88. struct mpu6050_dev *pmydev = (struct mpu6050_dev *)pfile->private_data;
  89. union mpu6050_data data;
  90. switch(cmd)
  91. {
  92. case GET_ACCEL:
  93. data.accel.x = mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_L);
  94. data.accel.x |= mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_H) << 8;
  95. data.accel.y = mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_L);
  96. data.accel.y |= mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_H) << 8;
  97. data.accel.z = mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_L);
  98. data.accel.z |= mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_H) << 8;
  99. break;
  100. case GET_GYRO:
  101. data.gyro.x = mpu6050_read_byte(pmydev->pclt,GYRO_XOUT_L);
  102. data.gyro.x |= mpu6050_read_byte(pmydev->pclt,GYRO_XOUT_H) << 8;
  103. data.gyro.y = mpu6050_read_byte(pmydev->pclt,GYRO_YOUT_L);
  104. data.gyro.y |= mpu6050_read_byte(pmydev->pclt,GYRO_YOUT_H) << 8;
  105. data.gyro.z = mpu6050_read_byte(pmydev->pclt,GYRO_ZOUT_L);
  106. data.gyro.z |= mpu6050_read_byte(pmydev->pclt,GYRO_ZOUT_H) << 8;
  107. break;
  108. case GET_TEMP:
  109. data.temp = mpu6050_read_byte(pmydev->pclt,TEMP_OUT_L);
  110. data.temp |= mpu6050_read_byte(pmydev->pclt,TEMP_OUT_H) << 8;
  111. break;
  112. default:
  113. return -EINVAL;
  114. }
  115. if(copy_to_user((void *)arg,&data,sizeof(data)))
  116. {
  117. return -EFAULT;
  118. }
  119. return sizeof(data);
  120. }
  121. void init_mpu6050(struct i2c_client *pclt)
  122. {
  123. mpu6050_write_byte(pclt,PWR_MGMT_1,0x00);
  124. mpu6050_write_byte(pclt,SMPLRT_DIV,0x07);
  125. mpu6050_write_byte(pclt,CONFIG,0x06);
  126. mpu6050_write_byte(pclt,GYRO_CONFIG,0xF8);
  127. mpu6050_write_byte(pclt,ACCEL_CONFIG,0x19);
  128. }
  129. struct file_operations myops = {
  130. .owner = THIS_MODULE,
  131. .open = mpu6050_open,
  132. .release = mpu6050_close,
  133. .unlocked_ioctl = mpu6050_ioctl,
  134. };
  135. static int mpu6050_probe(struct i2c_client *pclt,const struct i2c_device_id *pid)
  136. {
  137. int ret = 0;
  138. dev_t devno = MKDEV(major,minor);
  139. /*申请设备号*/
  140. ret = register_chrdev_region(devno,mpu6050_num,"mpu6050");
  141. if(ret)
  142. {
  143. ret = alloc_chrdev_region(&devno,minor,mpu6050_num,"mpu6050");
  144. if(ret)
  145. {
  146. printk("get devno failed\n");
  147. return -1;
  148. }
  149. major = MAJOR(devno);//容易遗漏,注意
  150. }
  151. pgmydev = (struct mpu6050_dev *)kmalloc(sizeof(struct mpu6050_dev),GFP_KERNEL);
  152. if(NULL == pgmydev)
  153. {
  154. unregister_chrdev_region(devno,mpu6050_num);
  155. printk("kmalloc failed\n");
  156. return -1;
  157. }
  158. memset(pgmydev,0,sizeof(struct mpu6050_dev));
  159. pgmydev->pclt = pclt;
  160. /*给struct cdev对象指定操作函数集*/
  161. cdev_init(&pgmydev->mydev,&myops);
  162. /*将struct cdev对象添加到内核对应的数据结构里*/
  163. pgmydev->mydev.owner = THIS_MODULE;
  164. cdev_add(&pgmydev->mydev,devno,mpu6050_num);
  165. init_mpu6050(pgmydev->pclt);
  166. return 0;
  167. }
  168. static int mpu6050_remove(struct i2c_client *pclt)
  169. {
  170. dev_t devno = MKDEV(major,minor);
  171. cdev_del(&pgmydev->mydev);
  172. unregister_chrdev_region(devno,mpu6050_num);
  173. kfree(pgmydev);
  174. pgmydev = NULL;
  175. return 0;
  176. }
  177. struct i2c_device_id mpu6050_ids[] =
  178. {
  179. {"mpu6050",0},
  180. {}
  181. };
  182. struct i2c_driver mpu6050_driver =
  183. {
  184. .driver = {
  185. .name = "mpu6050",
  186. .owner = THIS_MODULE,
  187. },
  188. .probe = mpu6050_probe,
  189. .remove = mpu6050_remove,
  190. .id_table = mpu6050_ids,
  191. };
  192. #if 0
  193. int __init mpu6050_driver_init(void)
  194. {
  195. i2c_add_driver(&mpu6050_driver);
  196. }
  197. void __exit mpu6050_driver_exit(void)
  198. {
  199. i2c_del_driver(&mpu6050_driver);
  200. }
  201. module_init(mpu6050_driver_init);
  202. module_exit(mpu6050_driver_exit);
  203. #else
  204. module_i2c_driver(mpu6050_driver);
  205. #endif
  206. MODULE_LICENSE("GPL");
  1. #ifndef MPU_6050_H
  2. #define MPU_6050_H
  3. struct accel_data
  4. {
  5. unsigned short x;
  6. unsigned short y;
  7. unsigned short z;
  8. };
  9. struct gyro_data
  10. {
  11. unsigned short x;
  12. unsigned short y;
  13. unsigned short z;
  14. };
  15. union mpu6050_data
  16. {
  17. struct accel_data accel;
  18. struct gyro_data gyro;
  19. unsigned short temp;
  20. };
  21. #define MPU6050_MAGIC 'K'
  22. #define GET_ACCEL _IOR(MPU6050_MAGIC,0,union mpu6050_data)
  23. #define GET_GYRO _IOR(MPU6050_MAGIC,1,union mpu6050_data)
  24. #define GET_TEMP _IOR(MPU6050_MAGIC,2,union mpu6050_data)
  25. #endif

应用层:

  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <sys/ioctl.h>
  4. #include <fcntl.h>
  5. #include <unistd.h>
  6. #include <stdio.h>
  7. #include "mpu6050.h"
  8. int main(int argc,char *argv[])
  9. {
  10. int fd = -1;
  11. union mpu6050_data data;
  12. if(argc < 2)
  13. {
  14. printf("The argument is too few\n");
  15. return 1;
  16. }
  17. fd = open(argv[1],O_RDONLY);
  18. if(fd < 0)
  19. {
  20. printf("open %s failed \n",argv[1]);
  21. return 2;
  22. }
  23. while(1)
  24. {
  25. sleep(2);
  26. ioctl(fd,GET_ACCEL,&data);
  27. printf("Accel-x=0x%x\n",data.accel.x);
  28. printf("Accel-y=0x%x\n",data.accel.y);
  29. printf("Accel-z=0x%x\n",data.accel.z);
  30. ioctl(fd,GET_GYRO,&data);
  31. printf("Gyro-x=0x%x\n",data.gyro.x);
  32. printf("Gyro-y=0x%x\n",data.gyro.y);
  33. printf("Gyro-z=0x%x\n",data.gyro.z);
  34. ioctl(fd,GET_TEMP,&data);
  35. printf("Temp=0x%x\n",data.temp);
  36. printf("\n");
  37. }
  38. close(fd);
  39. fd = -1;
  40. return 0;
  41. }

八、I2C总线二级外设驱动开发之设备树匹配

  1. #include <linux/module.h>
  2. #include <linux/kernel.h>
  3. #include <linux/fs.h>
  4. #include <linux/i2c.h>
  5. #include <linux/cdev.h>
  6. #include <linux/wait.h>
  7. #include <linux/sched.h>
  8. #include <linux/poll.h>
  9. #include <linux/slab.h>
  10. #include <linux/mm.h>
  11. #include <linux/io.h>
  12. #include <asm/uaccess.h>
  13. #include <asm/atomic.h>
  14. #include "mpu6050.h"
  15. #define SMPLRT_DIV 0x19
  16. #define CONFIG 0x1A
  17. #define GYRO_CONFIG 0x1B
  18. #define ACCEL_CONFIG 0x1C
  19. #define ACCEL_XOUT_H 0x3B
  20. #define ACCEL_XOUT_L 0x3C
  21. #define ACCEL_YOUT_H 0x3D
  22. #define ACCEL_YOUT_L 0x3E
  23. #define ACCEL_ZOUT_H 0x3F
  24. #define ACCEL_ZOUT_L 0x40
  25. #define TEMP_OUT_H 0x41
  26. #define TEMP_OUT_L 0x42
  27. #define GYRO_XOUT_H 0x43
  28. #define GYRO_XOUT_L 0x44
  29. #define GYRO_YOUT_H 0x45
  30. #define GYRO_YOUT_L 0x46
  31. #define GYRO_ZOUT_H 0x47
  32. #define GYRO_ZOUT_L 0x48
  33. #define PWR_MGMT_1 0x6B
  34. int major = 11;
  35. int minor = 0;
  36. int mpu6050_num = 1;
  37. struct mpu6050_dev
  38. {
  39. struct cdev mydev;
  40. struct i2c_client *pclt;
  41. };
  42. struct mpu6050_dev *pgmydev = NULL;
  43. int mpu6050_read_byte(struct i2c_client *pclt,unsigned char reg)
  44. {
  45. int ret = 0;
  46. char txbuf[1] = {reg};
  47. char rxbuf[1] = {0};
  48. struct i2c_msg msg[2] =
  49. {
  50. {pclt->addr,0,1,txbuf},
  51. {pclt->addr,I2C_M_RD,1,rxbuf}
  52. };
  53. ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));
  54. if(ret < 0)
  55. {
  56. printk("ret = %d,in mpu6050_read_byte\n",ret);
  57. return ret;
  58. }
  59. return rxbuf[0];
  60. }
  61. int mpu6050_write_byte(struct i2c_client *pclt,unsigned char reg,unsigned char val)
  62. {
  63. int ret = 0;
  64. char txbuf[2] = {reg,val};
  65. struct i2c_msg msg[1] =
  66. {
  67. {pclt->addr,0,2,txbuf},
  68. };
  69. ret = i2c_transfer(pclt->adapter,msg,ARRAY_SIZE(msg));
  70. if(ret < 0)
  71. {
  72. printk("ret = %d,in mpu6050_write_byte\n",ret);
  73. return ret;
  74. }
  75. return 0;
  76. }
  77. int mpu6050_open(struct inode *pnode,struct file *pfile)
  78. {
  79. pfile->private_data =(void *) (container_of(pnode->i_cdev,struct mpu6050_dev,mydev));
  80. return 0;
  81. }
  82. int mpu6050_close(struct inode *pnode,struct file *pfile)
  83. {
  84. return 0;
  85. }
  86. long mpu6050_ioctl(struct file *pfile,unsigned int cmd,unsigned long arg)
  87. {
  88. struct mpu6050_dev *pmydev = (struct mpu6050_dev *)pfile->private_data;
  89. union mpu6050_data data;
  90. switch(cmd)
  91. {
  92. case GET_ACCEL:
  93. data.accel.x = mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_L);
  94. data.accel.x |= mpu6050_read_byte(pmydev->pclt,ACCEL_XOUT_H) << 8;
  95. data.accel.y = mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_L);
  96. data.accel.y |= mpu6050_read_byte(pmydev->pclt,ACCEL_YOUT_H) << 8;
  97. data.accel.z = mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_L);
  98. data.accel.z |= mpu6050_read_byte(pmydev->pclt,ACCEL_ZOUT_H) << 8;
  99. break;
  100. case GET_GYRO:
  101. data.gyro.x = mpu6050_read_byte(pmydev->pclt,GYRO_XOUT_L);
  102. data.gyro.x |= mpu6050_read_byte(pmydev->pclt,GYRO_XOUT_H) << 8;
  103. data.gyro.y = mpu6050_read_byte(pmydev->pclt,GYRO_YOUT_L);
  104. data.gyro.y |= mpu6050_read_byte(pmydev->pclt,GYRO_YOUT_H) << 8;
  105. data.gyro.z = mpu6050_read_byte(pmydev->pclt,GYRO_ZOUT_L);
  106. data.gyro.z |= mpu6050_read_byte(pmydev->pclt,GYRO_ZOUT_H) << 8;
  107. break;
  108. case GET_TEMP:
  109. data.temp = mpu6050_read_byte(pmydev->pclt,TEMP_OUT_L);
  110. data.temp |= mpu6050_read_byte(pmydev->pclt,TEMP_OUT_H) << 8;
  111. break;
  112. default:
  113. return -EINVAL;
  114. }
  115. if(copy_to_user((void *)arg,&data,sizeof(data)))
  116. {
  117. return -EFAULT;
  118. }
  119. return sizeof(data);
  120. }
  121. void init_mpu6050(struct i2c_client *pclt)
  122. {
  123. mpu6050_write_byte(pclt,PWR_MGMT_1,0x00);
  124. mpu6050_write_byte(pclt,SMPLRT_DIV,0x07);
  125. mpu6050_write_byte(pclt,CONFIG,0x06);
  126. mpu6050_write_byte(pclt,GYRO_CONFIG,0xF8);
  127. mpu6050_write_byte(pclt,ACCEL_CONFIG,0x19);
  128. }
  129. struct file_operations myops = {
  130. .owner = THIS_MODULE,
  131. .open = mpu6050_open,
  132. .release = mpu6050_close,
  133. .unlocked_ioctl = mpu6050_ioctl,
  134. };
  135. static int mpu6050_probe(struct i2c_client *pclt,const struct i2c_device_id *pid)
  136. {
  137. int ret = 0;
  138. dev_t devno = MKDEV(major,minor);
  139. /*申请设备号*/
  140. ret = register_chrdev_region(devno,mpu6050_num,"mpu6050");
  141. if(ret)
  142. {
  143. ret = alloc_chrdev_region(&devno,minor,mpu6050_num,"mpu6050");
  144. if(ret)
  145. {
  146. printk("get devno failed\n");
  147. return -1;
  148. }
  149. major = MAJOR(devno);//容易遗漏,注意
  150. }
  151. pgmydev = (struct mpu6050_dev *)kmalloc(sizeof(struct mpu6050_dev),GFP_KERNEL);
  152. if(NULL == pgmydev)
  153. {
  154. unregister_chrdev_region(devno,mpu6050_num);
  155. printk("kmalloc failed\n");
  156. return -1;
  157. }
  158. memset(pgmydev,0,sizeof(struct mpu6050_dev));
  159. pgmydev->pclt = pclt;
  160. /*给struct cdev对象指定操作函数集*/
  161. cdev_init(&pgmydev->mydev,&myops);
  162. /*将struct cdev对象添加到内核对应的数据结构里*/
  163. pgmydev->mydev.owner = THIS_MODULE;
  164. cdev_add(&pgmydev->mydev,devno,mpu6050_num);
  165. init_mpu6050(pgmydev->pclt);
  166. return 0;
  167. }
  168. static int mpu6050_remove(struct i2c_client *pclt)
  169. {
  170. dev_t devno = MKDEV(major,minor);
  171. cdev_del(&pgmydev->mydev);
  172. unregister_chrdev_region(devno,mpu6050_num);
  173. kfree(pgmydev);
  174. pgmydev = NULL;
  175. return 0;
  176. }
  177. struct of_device_id mpu6050_dt[] =
  178. {
  179. {.compatible = "invensense,mpu6050"},
  180. {}
  181. };
  182. struct i2c_device_id mpu6050_ids[] =
  183. {
  184. {"mpu6050",0},
  185. {}
  186. };
  187. struct i2c_driver mpu6050_driver =
  188. {
  189. .driver = {
  190. .name = "mpu6050",
  191. .owner = THIS_MODULE,
  192. .of_match_table = mpu6050_dt,
  193. },
  194. .probe = mpu6050_probe,
  195. .remove = mpu6050_remove,
  196. .id_table = mpu6050_ids,
  197. };
  198. #if 0
  199. int __init mpu6050_driver_init(void)
  200. {
  201. i2c_add_driver(&mpu6050_driver);
  202. }
  203. void __exit mpu6050_driver_exit(void)
  204. {
  205. i2c_del_driver(&mpu6050_driver);
  206. }
  207. module_init(mpu6050_driver_init);
  208. module_exit(mpu6050_driver_exit);
  209. #else
  210. module_i2c_driver(mpu6050_driver);
  211. #endif
  212. MODULE_LICENSE("GPL");

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小蓝xlanll/article/detail/205848
推荐阅读
相关标签
  

闽ICP备14008679号