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

前面在Exynos4412 IIC总线驱动开发(一)—— IIC 基础概念及驱动架构分析 中学习了IIC驱动的架构,下面进入我们的驱动开发过程

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




       上面这些代码的展示是告诉我们:linux内核和芯片提供商为我们的的驱动程序提供了 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、查看原理图

对应核心板pin


从机地址



可以获取的信息:

1、MPU6050 对应 IIC 通道5;

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

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

      base address 0x138B0000


2、创建设备树节点

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

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  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相应寄存器

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  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

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  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

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  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
[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  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. }  

前面在Exynos4412 IIC总线驱动开发(一)—— IIC 基础概念及驱动架构分析 中学习了IIC驱动的架构,下面进入我们的驱动开发过程

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




       上面这些代码的展示是告诉我们:linux内核和芯片提供商为我们的的驱动程序提供了 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、查看原理图

对应核心板pin


从机地址



可以获取的信息:

1、MPU6050 对应 IIC 通道5;

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

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

      base address 0x138B0000


2、创建设备树节点

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

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  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相应寄存器

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  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

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  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

[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  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
[cpp] view plaincopy
在CODE上查看代码片派生到我的代码片
  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. }  

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/402240.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Exynos4412 IIC总线驱动开发(一)—— IIC 基础概念及驱动架构分析

关于Exynos4412 IIC 裸机开发请看 &#xff1a;Exynos4412 裸机开发 —— IIC总线 &#xff0c;下面回顾下 IIC 基础概念 一、IIC 基础概念 IIC(Inter&#xff0d;Integrated Circuit)总线是一种由PHILIPS公司开发的两线式串行总线&#xff0c;用于连接微控制器及其外围设备。I…

Python 爬虫进阶一之爬虫框架概述

综述 爬虫入门之后&#xff0c;我们有两条路可以走。 一个是继续深入学习&#xff0c;以及关于设计模式的一些知识&#xff0c;强化 Python 相关知识&#xff0c;自己动手造轮子&#xff0c;继续为自己的爬虫增加分布式&#xff0c;多线程等功能扩展。另一条路便是学习一些优秀…

1039. 到底买不买(20)

1039. 到底买不买&#xff08;20&#xff09; 小红想买些珠子做一串自己喜欢的珠串。卖珠子的摊主有很多串五颜六色的珠串&#xff0c;但是不肯把任何一串拆散了卖。于是小红要你帮忙判断一下&#xff0c;某串珠子里是否包含了全部自己想要的珠子&#xff1f;如果是&#xff0c…

Exynos4412 ADC 设备驱动开发

具体ADC硬件知识及裸机驱动请看&#xff1a; Exynos4412裸机开发 —— A/D转换器 1、原理图如下&#xff1a; 2、相关寄存器信息 ADC_BASE 0x126C0000ADCCON 0x0000 1<<0 | 1<<14 | 0X1<<16 | 0XFF<<6ADCDLY 0x000…

Python 爬虫进阶二之 PySpider 框架安装配置

PySpider官方文档 项目地址 官方文档 安装 phantomjs PhantomJS 是一个基于 WebKit 的服务器端 JavaScript API。它全面支持 web 而不需浏览器支持&#xff0c;其快速、原生支持各种 Web 标准&#xff1a;DOM 处理、CSS 选择器、JSON、Canvas 和 SVG。 PhantomJS 可以用于页…

Exynos4412 中断驱动开发(三)—— 设备树中中断节点的创建

提到中断就必须了解到GIC&#xff0c;下面先了解一下GIC 一、GIC概念 GIC&#xff08;Generic Interrupt Controller&#xff09;是ARM公司提供的一个通用的中断控制器。GIC通过AMBA&#xff08;Advanced Microcontroller Bus Architecture&#xff09;这样的片上总线连接到一个…

Exynos4412 中断驱动开发(二)—— 中断处理流程分析

前面已经学习了中断的注册过程&#xff0c;下面由一张流程图来看一下当中断发生时的处理流程&#xff1a; 中断发生之后处理流程 a -- 具体的CPU architecture相关模块进行现场保护&#xff0c;然后调用machine driver执行对应的中断处理handler; b -- machine driver对应中断处…

Exynos4412 中断驱动开发(一)—— 中断基础及中断的注册过程

一、中断基础概念 所谓中断&#xff0c;指CPU在执行程序的过程中&#xff0c;出现了某些突发事件即待处理&#xff0c;CPU必须暂停当前的程序。转去处理突发事件&#xff0c;处理完毕后CPU又返回原程序被中断的位置并继续执行。 1、中断分类 a -- 内部中断和外部中断 根据中断的…

rubymine 调试 redmine

1、安装debase和ruby-debug-ide包。&#xff08;注意版本&#xff0c;rubymine 8.0.2下&#xff0c;bitnami下的版本需安装debase -v 0.2.1版本&#xff0c;网上有文章说用debase -v 0.2.2beta6。容易在rubymine启动调试时出现找不到ruby-debug-ide等错误提示&#xff09; 启动…

Linux 设备驱动中的 I/O模型(二)—— 异步通知和异步I/O

阻塞和非阻塞访问、poll() 函数提供了较多地解决设备访问的机制&#xff0c;但是如果有了异步通知整套机制就更加完善了。 异步通知的意思是&#xff1a;一旦设备就绪&#xff0c;则主动通知应用程序&#xff0c;这样应用程序根本就不需要查询设备状态&#xff0c;这一点非常类…

判断链表是否有环

链表有环的情况一般是链表的尾指向前面的节点而不是null&#xff0c;如head->node1->node2->node3->node4->tail->node2&#xff0c;该链表存在环。判断环是否存在可以借助两个指针&#xff0c;一个指针每次迭代只移动一步&#xff0c;第二个指针每次迭代移动…

Linux 设备驱动中的 I/O模型(一)—— 阻塞和非阻塞I/O

在前面学习网络编程时&#xff0c;曾经学过I/O模型 Linux 系统应用编程——网络编程&#xff08;I/O模型&#xff09;&#xff0c;下面学习一下I/O模型在设备驱动中的应用。 回顾一下在Unix/Linux下共有五种I/O模型&#xff0c;分别是&#xff1a; a -- 阻塞I/O b -- 非阻塞I/O…

Python 爬虫进阶六之多进程的用法

python 中的多线程其实并不是真正的多线程&#xff0c;并不能做到充分利用多核 CPU 资源。 如果想要充分利用&#xff0c;在 python 中大部分情况需要使用多进程&#xff0c;那么这个包就叫做 multiprocessing。 借助它&#xff0c;可以轻松完成从单进程到并发执行的转换。mult…

Linux 设备驱动的并发控制

Linux 设备驱动中必须要解决的一个问题是多个进程对共享的资源的并发访问&#xff0c;并发的访问会导致竞态&#xff0c;即使是经验丰富的驱动工程师也常常设计出包含并发问题bug 的驱动程序。 一、基础概念 1、Linux 并发相关基础概念 a -- 并发&#xff08;concurrency&#…

第三章:多坐标系

第一节&#xff1a;为什么要有多坐标系 当我们使用一个坐标系来描绘整个场景的时候&#xff0c;场景中的任意点都可以用该坐标系描述&#xff0c;此时如果有一只羊一遍摇动着耳朵&#xff0c;一边走&#xff0c;这个时候如果进行坐标的转换会发现异常的麻烦&#xff0c;此时如果…

Linux 设备驱动开发 —— 设备树在platform设备驱动中的使用

关与设备树的概念&#xff0c;我们在Exynos4412 内核移植&#xff08;六&#xff09;—— 设备树解析 里面已经学习过&#xff0c;下面看一下设备树在设备驱动开发中起到的作用 Device Tree是一种描述硬件的数据结构&#xff0c;设备树源(Device Tree Source)文件&#xff08;以…

Android 网络通信框架Volley简介(Google IO 2013)

1. 什么是Volley 在这之前&#xff0c;我们在程序中需要和网络通信的时候&#xff0c;大体使用的东西莫过于AsyncTaskLoader&#xff0c;HttpURLConnection&#xff0c;AsyncTask&#xff0c;HTTPClient&#xff08;Apache&#xff09;等&#xff0c;今年的Google I/O 2013上&…

Linux 设备驱动开发 —— platform设备驱动应用实例解析

前面我们已经学习了platform设备的理论知识Linux 设备驱动开发 —— platform 设备驱动 &#xff0c;下面将通过一个实例来深入我们的学习。 一、platform 驱动的工作过程 platform模型驱动编程&#xff0c;需要实现platform_device(设备)与platform_driver&#xff08;驱动&am…

Python爬虫入门四urllib库的高级用法

1.设置headers 有些网站不会同意程序直接用上面的方式进行访问&#xff0c;如果识别有问题&#xff0c;那么站点根本不会响应&#xff0c;所以为了完全模拟浏览器的工作&#xff0c;我们需要设置一些 Headers 的属性。 首先&#xff0c;打开我们的浏览器&#xff0c;调试浏览器…

进程上下文、中断上下文及原子上下文

谈论进程上下文 、中断上下文 、 原子上下文之前&#xff0c;有必要讨论下两个概念&#xff1a; a -- 上下文 上下文是从英文context翻译过来&#xff0c;指的是一种环境。相对于进程而言&#xff0c;就是进程执行时的环境&#xff1b; 具体来说就是各个变量和数据&#xff0c;…