首先看一张代码层次图,有助于我们的理解

 

 

 

Exynos4412 IIC总线驱动开发(二)—— IIC 驱动开发_#include

 

 

 

       上面这些代码的展示是告诉我们 ​内核和芯片提供商为我们的的驱动程序提供了 i2c驱动的框架,以及框架底层与硬件相关的代码的实现。

 

   剩下的就是针对挂载在i2c两线上的i2c设备了device,而编写的即具体设备驱动了,这里的设备就是硬件接口外挂载的设备,而非硬件接口本身(soc硬件接口本身的驱动可以理解为总线驱动)

 

 

 

一、编写驱动需要完成的工作

 

       编写具体的I2C驱动时,工程师需要处理的主要工作如下:

 

1)、提供I2C适配器的硬件驱动,探测,初始化I2C适配器(如申请I2C的I/O地址和中断号),驱动CPU控制的I2C适配器从硬件上产生。

 

2)、提供I2C控制的algorithm, 用具体适配器的xxx_xfer()函数填充i2c_algorithm的master_xfer指针,并把i2c_algorithm指针赋给i2c_adapter的algo指针。

 

3)、实现I2C设备驱动中的i2c_driver接口,用具体yyy的yyy_probe(),yyy_remove(),yyy_suspend(),yyy_resume()函数指针和i2c_device_id设备ID表赋给i2c_driver的probe,remove,suspend,resume和id_table指针。

 

4)、实现I2C设备所对应类型的具体驱动,i2c_driver只是实现设备与总线的挂接。

 

  上面的工作中前两个属于I2C总线驱动,后面两个属于I2C设备驱动。

 

 

 

二、开发实例

 

-------------------------------------------------------------------

 

开发板:Exynos4412-fs4412

 

Linux 内核版本:Linux 3.14

 

IIC 从机对象:陀螺仪MPU6050

 

--------------------------------------------------------------------

 

1、查看原理图

 

Exynos4412 IIC总线驱动开发(二)—— IIC 驱动开发_设备驱动_02

 

对应核心板pin

 

Exynos4412 IIC总线驱动开发(二)—— IIC 驱动开发_陀螺仪_03

 

从机地址

 

Exynos4412 IIC总线驱动开发(二)—— IIC 驱动开发_linux_04

 

 

 

可以获取的信息:

 

1、MPU6050 对应 IIC 通道5;

 

2、对应中断 EINT27  父节点 GPX3  3

 

3、因为ad0接地,所以从设备地址0x68

 

      base address 0x138B0000

 

 

 

2、创建设备树节点

 

      通过上面获取的信息,可以写出

 

 ​

  1. i2c@138b0000 {  
  2.         #address-cells = <1>;  
  3.         #size-cells = <0>;  
  4.   
  5.         samsung,i2c-sda-delay = <100>;  
  6.         samsung,i2c-max-bus-freq = <20000>;  
  7.         pinctrl-0 = <&i2c5_bus>;  
  8.         pinctrl-names = "default";  
  9.         status = "okay";  
  10.      
  11.         pmu6050-3-asix@68 {  
  12.                compatible = "invense,mpu6050";  
  13.                reg = <0x68>;  
  14.               interrupt-parent = <&gpx3>;  
  15.               interrupts = <3 2>;  
  16.         };  
  17. };  


 

 

 

3、MPU6050相应寄存器

 

 

 


  ​

  1. #define SMPLRT_DIV 0x19 //采样率分频,典型值: 0x07(125Hz) */  
  2. #define CONFIG 0x1A // 低通滤波频率,典型值: 0x06(5Hz) */  
  3. #define GYRO_CONFIG 0x1B // 陀螺仪自检及测量范围,典型值: 0x18(不自检,2000deg/s) */  
  4. #define ACCEL_CONFIG 0x1C // 加速计自检、测量范围及高通滤波频率,典型值: 0x01(不自检, 2G, 5Hz) */  
  5. #define ACCEL_XOUT_H 0x3B // 存储最近的 X 轴、 Y 轴、 Z 轴加速度感应器的测量值 */  
  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 // 存储最近的 X 轴、 Y 轴、 Z 轴陀螺仪感应器的测量值 */  
  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(正常启用) */  
  20. #define WHO_AM_I 0x75 //IIC 地址寄存器(默认数值 0x68,只读) */  


4、具体程序

 

 

mpu6050.h

 



 ​

  1. #ifndef MPU6050_HHHH  
  2. #define MPU6050_HHHH  
  3.   
  4. #define MPU6050_MAGIC 'K'  
  5.   
  6. union mpu6050_data  
  7. {  
  8.     struct {  
  9.         unsigned short x;  
  10.         unsigned short y;  
  11.         unsigned short z;  
  12.     }accel;  
  13.     struct {  
  14.         unsigned short x;  
  15.         unsigned short y;  
  16.         unsigned short z;  
  17.     }gyro;  
  18.     unsigned short temp;  
  19. };  
  20.   
  21. #define GET_ACCEL _IOR(MPU6050_MAGIC, 0, union mpu6050_data)  
  22. #define GET_GYRO  _IOR(MPU6050_MAGIC, 1, union mpu6050_data)   
  23. #define GET_TEMP  _IOR(MPU6050_MAGIC, 2, union mpu6050_data)  
  24.   
  25. #endif  


 

 

 

i2c_driver

 

 

 




 

  1. #include <linux/kernel.h>  
  2. #include <linux/module.h>  
  3. #include <linux/i2c.h>  
  4. #include <linux/cdev.h>  
  5. #include <linux/slab.h>  
  6. #include <linux/fs.h>  
  7. #include <linux/delay.h>  
  8.   
  9. #include <asm/uaccess.h>  
  10.   
  11. #include "mpu6050.h"  
  12.   
  13. MODULE_LICENSE("GPL");  
  14.   
  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.   
  35. #define MPU6050_MAJOR 500  
  36. #define MPU6050_MINOR 0  
  37.   
  38. struct mpu6050_device {  
  39.     struct cdev cdev;  
  40.     struct i2c_client *client;  
  41. };  
  42. struct mpu6050_device *mpu6050;   
  43.   
  44. static int mpu6050_read_byte(struct i2c_client *client, unsigned char reg)  
  45. {  
  46.     int ret;  
  47.   
  48.     char txbuf[1] = { reg };  
  49.     char rxbuf[1];  
  50.   
  51.     struct i2c_msg msg[2] = {  
  52.         {client->addr, 0, 1, txbuf},  
  53.         {client->addr, I2C_M_RD, 1, rxbuf}  
  54.     };  
  55.   
  56.     ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));  
  57.     if (ret < 0) {  
  58.         printk("ret = %d\n", ret);  
  59.         return ret;  
  60.     }  
  61.   
  62.     return rxbuf[0];  
  63. }  
  64.   
  65. static int mpu6050_write_byte(struct i2c_client *client, unsigned char reg, unsigned char val)  
  66. {  
  67.     char txbuf[2] = {reg, val};  
  68.   
  69.     struct i2c_msg msg[2] = {  
  70.         {client->addr, 0, 2, txbuf},  
  71.     };  
  72.   
  73.     i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));  
  74.   
  75.     return 0;  
  76. }  
  77.   
  78.   
  79. static int mpu6050_open(struct inode *inode, struct file *file)   
  80. {  
  81.     return 0;  
  82. }  
  83.   
  84. static int mpu6050_release(struct inode *inode, struct file *file)   
  85. {  
  86.     return 0;  
  87. }  
  88.   
  89. static long mpu6050_ioctl(struct file *file, unsigned int cmd, unsigned long arg)  
  90. {  
  91.     union mpu6050_data data;  
  92.     struct i2c_client *client = mpu6050->client;  
  93.   
  94.     switch(cmd) {  
  95.     case GET_ACCEL:  
  96.         data.accel.x = mpu6050_read_byte(client, ACCEL_XOUT_L);  
  97.         data.accel.x |= mpu6050_read_byte(client, ACCEL_XOUT_H) << 8;  
  98.   
  99.         data.accel.y = mpu6050_read_byte(client, ACCEL_YOUT_L);  
  100.         data.accel.y |= mpu6050_read_byte(client, ACCEL_YOUT_H) << 8;  
  101.   
  102.         data.accel.z = mpu6050_read_byte(client, ACCEL_ZOUT_L);  
  103.         data.accel.z |= mpu6050_read_byte(client, ACCEL_ZOUT_H) << 8;  
  104.         break;  
  105.   
  106.     case GET_GYRO:  
  107.   
  108.         data.gyro.x = mpu6050_read_byte(client, GYRO_XOUT_L);  
  109.         data.gyro.x |= mpu6050_read_byte(client, GYRO_XOUT_H) << 8;  
  110.   
  111.         data.gyro.y = mpu6050_read_byte(client, GYRO_YOUT_L);  
  112.         data.gyro.y |= mpu6050_read_byte(client, GYRO_YOUT_H) << 8;  
  113.   
  114.         data.gyro.z = mpu6050_read_byte(client, GYRO_ZOUT_L);  
  115.         data.gyro.z |= mpu6050_read_byte(client, GYRO_ZOUT_H) << 8;  
  116.         break;  
  117.   
  118.     case GET_TEMP:    
  119.         data.temp = mpu6050_read_byte(client, TEMP_OUT_L);  
  120.         data.temp |= mpu6050_read_byte(client, TEMP_OUT_H) << 8;  
  121.         break;  
  122.   
  123.     default:  
  124.         printk("invalid argument\n");  
  125.         return -EINVAL;  
  126.     }  
  127.   
  128.     if (copy_to_user((void *)arg, &data, sizeof(data)))  
  129.         return -EFAULT;  
  130.   
  131.     return sizeof(data);  
  132. }  
  133.   
  134. struct file_operations mpu6050_fops = {  
  135.     .owner      = THIS_MODULE,  
  136.     .open       = mpu6050_open,  
  137.     .release    = mpu6050_release,  
  138.     .unlocked_ioctl = mpu6050_ioctl,  
  139. };  
  140.   
  141. static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)  
  142. {  
  143.     int ret;  
  144.     dev_t devno = MKDEV(MPU6050_MAJOR, MPU6050_MINOR);  
  145.     printk("match OK!\n");  
  146.   
  147.     mpu6050 = kzalloc(sizeof(*mpu6050), GFP_KERNEL);  
  148.     if (mpu6050 == NULL) {  
  149.         return -ENOMEM;  
  150.     }  
  151.   
  152.     mpu6050->client = client;  
  153.   
  154.     ret = register_chrdev_region(devno, 1, "mpu6050");  
  155.     if (ret < 0) {  
  156.         printk("failed to register char device region!\n");  
  157.         goto err1;  
  158.     }  
  159.   
  160.     cdev_init(&mpu6050->cdev, &mpu6050_fops);  
  161.     mpu6050->cdev.owner = THIS_MODULE;  
  162.     ret = cdev_add(&mpu6050->cdev, devno, 1);  
  163.     if (ret < 0) {  
  164.         printk("failed to add device\n");  
  165.         goto err2;  
  166.     }  
  167.       
  168.     mpu6050_write_byte(client, PWR_MGMT_1, 0x00);  
  169.     mpu6050_write_byte(client, SMPLRT_DIV, 0x07);  
  170.     mpu6050_write_byte(client, CONFIG, 0x06);  
  171.     mpu6050_write_byte(client, GYRO_CONFIG, 0xF8);  
  172.     mpu6050_write_byte(client, ACCEL_CONFIG, 0x19);  
  173.   
  174.     return 0;  
  175. err2:  
  176.     unregister_chrdev_region(devno, 1);  
  177. err1:  
  178.     kfree(mpu6050);  
  179.     return ret;  
  180. }  
  181.   
  182. static int mpu6050_remove(struct i2c_client *client)  
  183. {  
  184.     dev_t devno = MKDEV(MPU6050_MAJOR, MPU6050_MINOR);  
  185.     cdev_del(&mpu6050->cdev);  
  186.     unregister_chrdev_region(devno, 1);  
  187.     kfree(mpu6050);  
  188.   
  189.     return 0;  
  190. }  
  191.   
  192. static const struct i2c_device_id mpu6050_id[] = {  
  193.     { "mpu6050", 0},  
  194.     {}  
  195. };   
  196.   
  197. static struct of_device_id mpu6050_dt_match[] = {  
  198.     {.compatible = "invense,mpu6050" },  
  199.     {/*northing to be done*/},  
  200. };  
  201.   
  202. struct i2c_driver mpu6050_driver = {  
  203.     .driver = {  
  204.         .name           = "mpu6050",  
  205.         .owner          = THIS_MODULE,  
  206.         .of_match_table = of_match_ptr(mpu6050_dt_match),  
  207.     },  
  208.     .probe      = mpu6050_probe,  
  209.     .remove     = mpu6050_remove,  
  210.     .id_table   = mpu6050_id,  
  211. };  
  212.   
  213. static init _init mpu6050_init(void)  
  214. {  
  215.     return i2c_add_driver(&mpu6050_driver);  
  216. }  
  217.   
  218. static void _exit mpu6050_exit(void)  
  219. {  
  220.     return i2c_del_driver(&mpu6050_driver);  
  221. }  
  222.   
  223. module_init(&mpu6050_init);  
  224. module_exit(&mpu6050_exit);  


 

 

 

 

test.c




 

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <unistd.h>  
  4. #include <fcntl.h>  
  5. #include <sys/ioctl.h>  
  6.   
  7. #include "mpu6050.h"  
  8.   
  9. int main(int argc, const char *argv[])  
  10. {  
  11.     int fd;  
  12.     union mpu6050_data data;   
  13.       
  14.     fd = open("/dev/mpu6050", O_RDWR);  
  15.     if (fd < 0) {  
  16.         perror("open");  
  17.         exit(1);  
  18.     }  
  19.   
  20.     while(1) {  
  21.         ioctl(fd, GET_ACCEL, &data);  
  22.         printf("acceleration data: x = %04x, y = %04x, z = %04x\n",   
  23.                 data.accel.x, data.accel.y, data.accel.z);  
  24.   
  25.         ioctl(fd, GET_GYRO, &data);  
  26.         printf("gyroscope data: x = %04x, y = %04x, z = %04x\n",   
  27.                 data.accel.x, data.accel.y, data.accel.z);  
  28.   
  29.         sleep(1);  
  30.     }  
  31.   
  32.     close(fd);  
  33.   
  34.     return 0;  
  35. }