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、创建设备树节点

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


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

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. #define MPU6050_MAGIC 'K'
  4. union mpu6050_data
  5. {
  6. struct {
  7. unsigned short x;
  8. unsigned short y;
  9. unsigned short z;
  10. }accel;
  11. struct {
  12. unsigned short x;
  13. unsigned short y;
  14. unsigned short z;
  15. }gyro;
  16. unsigned short temp;
  17. };
  18. #define GET_ACCEL _IOR(MPU6050_MAGIC, 0, union mpu6050_data)
  19. #define GET_GYRO _IOR(MPU6050_MAGIC, 1, union mpu6050_data)
  20. #define GET_TEMP _IOR(MPU6050_MAGIC, 2, union mpu6050_data)
  21. #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. #include <asm/uaccess.h>
  9. #include "mpu6050.h"
  10. MODULE_LICENSE( "GPL");
  11. #define SMPLRT_DIV 0x19
  12. #define CONFIG 0x1A
  13. #define GYRO_CONFIG 0x1B
  14. #define ACCEL_CONFIG 0x1C
  15. #define ACCEL_XOUT_H 0x3B
  16. #define ACCEL_XOUT_L 0x3C
  17. #define ACCEL_YOUT_H 0x3D
  18. #define ACCEL_YOUT_L 0x3E
  19. #define ACCEL_ZOUT_H 0x3F
  20. #define ACCEL_ZOUT_L 0x40
  21. #define TEMP_OUT_H 0x41
  22. #define TEMP_OUT_L 0x42
  23. #define GYRO_XOUT_H 0x43
  24. #define GYRO_XOUT_L 0x44
  25. #define GYRO_YOUT_H 0x45
  26. #define GYRO_YOUT_L 0x46
  27. #define GYRO_ZOUT_H 0x47
  28. #define GYRO_ZOUT_L 0x48
  29. #define PWR_MGMT_1 0x6B
  30. #define MPU6050_MAJOR 500
  31. #define MPU6050_MINOR 0
  32. struct mpu6050_device {
  33. struct cdev cdev;
  34. struct i2c_client *client;
  35. };
  36. struct mpu6050_device *mpu6050;
  37. static int mpu6050_read_byte(struct i2c_client *client, unsigned char reg)
  38. {
  39. int ret;
  40. char txbuf[ 1] = { reg };
  41. char rxbuf[ 1];
  42. struct i2c_msg msg[2] = {
  43. {client->addr, 0, 1, txbuf},
  44. {client->addr, I2C_M_RD, 1, rxbuf}
  45. };
  46. ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
  47. if (ret < 0) {
  48. printk( "ret = %d\n", ret);
  49. return ret;
  50. }
  51. return rxbuf[ 0];
  52. }
  53. static int mpu6050_write_byte(struct i2c_client *client, unsigned char reg, unsigned char val)
  54. {
  55. char txbuf[ 2] = {reg, val};
  56. struct i2c_msg msg[2] = {
  57. {client->addr, 0, 2, txbuf},
  58. };
  59. i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
  60. return 0;
  61. }
  62. static int mpu6050_open(struct inode *inode, struct file *file)
  63. {
  64. return 0;
  65. }
  66. static int mpu6050_release(struct inode *inode, struct file *file)
  67. {
  68. return 0;
  69. }
  70. static long mpu6050_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  71. {
  72. union mpu6050_data data;
  73. struct i2c_client *client = mpu6050->client;
  74. switch(cmd) {
  75. case GET_ACCEL:
  76. data.accel.x = mpu6050_read_byte(client, ACCEL_XOUT_L);
  77. data.accel.x |= mpu6050_read_byte(client, ACCEL_XOUT_H) << 8;
  78. data.accel.y = mpu6050_read_byte(client, ACCEL_YOUT_L);
  79. data.accel.y |= mpu6050_read_byte(client, ACCEL_YOUT_H) << 8;
  80. data.accel.z = mpu6050_read_byte(client, ACCEL_ZOUT_L);
  81. data.accel.z |= mpu6050_read_byte(client, ACCEL_ZOUT_H) << 8;
  82. break;
  83. case GET_GYRO:
  84. data.gyro.x = mpu6050_read_byte(client, GYRO_XOUT_L);
  85. data.gyro.x |= mpu6050_read_byte(client, GYRO_XOUT_H) << 8;
  86. data.gyro.y = mpu6050_read_byte(client, GYRO_YOUT_L);
  87. data.gyro.y |= mpu6050_read_byte(client, GYRO_YOUT_H) << 8;
  88. data.gyro.z = mpu6050_read_byte(client, GYRO_ZOUT_L);
  89. data.gyro.z |= mpu6050_read_byte(client, GYRO_ZOUT_H) << 8;
  90. break;
  91. case GET_TEMP:
  92. data.temp = mpu6050_read_byte(client, TEMP_OUT_L);
  93. data.temp |= mpu6050_read_byte(client, TEMP_OUT_H) << 8;
  94. break;
  95. default:
  96. printk( "invalid argument\n");
  97. return -EINVAL;
  98. }
  99. if (copy_to_user(( void *)arg, &data, sizeof(data)))
  100. return -EFAULT;
  101. return sizeof(data);
  102. }
  103. struct file_operations mpu6050_fops = {
  104. .owner = THIS_MODULE,
  105. .open = mpu6050_open,
  106. .release = mpu6050_release,
  107. .unlocked_ioctl = mpu6050_ioctl,
  108. };
  109. static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
  110. {
  111. int ret;
  112. dev_t devno = MKDEV(MPU6050_MAJOR, MPU6050_MINOR);
  113. printk( "match OK!\n");
  114. mpu6050 = kzalloc( sizeof(*mpu6050), GFP_KERNEL);
  115. if (mpu6050 == NULL) {
  116. return -ENOMEM;
  117. }
  118. mpu6050->client = client;
  119. ret = register_chrdev_region(devno, 1, "mpu6050");
  120. if (ret < 0) {
  121. printk( "failed to register char device region!\n");
  122. goto err1;
  123. }
  124. cdev_init(&mpu6050->cdev, &mpu6050_fops);
  125. mpu6050->cdev.owner = THIS_MODULE;
  126. ret = cdev_add(&mpu6050->cdev, devno, 1);
  127. if (ret < 0) {
  128. printk( "failed to add device\n");
  129. goto err2;
  130. }
  131. mpu6050_write_byte(client, PWR_MGMT_1, 0x00);
  132. mpu6050_write_byte(client, SMPLRT_DIV, 0x07);
  133. mpu6050_write_byte(client, CONFIG, 0x06);
  134. mpu6050_write_byte(client, GYRO_CONFIG, 0xF8);
  135. mpu6050_write_byte(client, ACCEL_CONFIG, 0x19);
  136. return 0;
  137. err2:
  138. unregister_chrdev_region(devno, 1);
  139. err1:
  140. kfree(mpu6050);
  141. return ret;
  142. }
  143. static int mpu6050_remove(struct i2c_client *client)
  144. {
  145. dev_t devno = MKDEV(MPU6050_MAJOR, MPU6050_MINOR);
  146. cdev_del(&mpu6050->cdev);
  147. unregister_chrdev_region(devno, 1);
  148. kfree(mpu6050);
  149. return 0;
  150. }
  151. static const struct i2c_device_id mpu6050_id[] = {
  152. { "mpu6050", 0},
  153. {}
  154. };
  155. static struct of_device_id mpu6050_dt_match[] = {
  156. {.compatible = "invense,mpu6050" },
  157. { /*northing to be done*/},
  158. };
  159. struct i2c_driver mpu6050_driver = {
  160. .driver = {
  161. .name = "mpu6050",
  162. .owner = THIS_MODULE,
  163. .of_match_table = of_match_ptr(mpu6050_dt_match),
  164. },
  165. .probe = mpu6050_probe,
  166. .remove = mpu6050_remove,
  167. .id_table = mpu6050_id,
  168. };
  169. static init _ init mpu6050_init(void)
  170. {
  171. return i2c_add_driver(&mpu6050_driver);
  172. }
  173. static void _ exit mpu6050_exit(void)
  174. {
  175. return i2c_del_driver(&mpu6050_driver);
  176. }
  177. module_init(&mpu6050_init);
  178. 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. #include "mpu6050.h"
  7. int main(int argc, const char *argv[])
  8. {
  9. int fd;
  10. union mpu6050_data data;
  11. fd = open( "/dev/mpu6050", O_RDWR);
  12. if (fd < 0) {
  13. perror( "open");
  14. exit( 1);
  15. }
  16. while( 1) {
  17. ioctl(fd, GET_ACCEL, &data);
  18. printf( "acceleration data: x = %04x, y = %04x, z = %04x\n",
  19. data.accel.x, data.accel.y, data.accel.z);
  20. ioctl(fd, GET_GYRO, &data);
  21. printf( "gyroscope data: x = %04x, y = %04x, z = %04x\n",
  22. data.accel.x, data.accel.y, data.accel.z);
  23. sleep( 1);
  24. }
  25. close(fd);
  26. return 0;
  27. }



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值