zoukankan      html  css  js  c++  java
  • Linux下I2C总线驱动框架

    I2C总线驱动相关知识点,一位博主在博客里写的很好,这里就直接贴上链接:

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

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

     Exynos4412 IIC 总线驱动开发相关问题总结

    1、i2c子系统软件框架

        应用
        ------------------------------------------
        i2c driver:从设备驱动层
             需要和应用层进行交互
             封包数据,不知道数据是如何写入到硬件
    
        ------------------------------------------
        i2c 核心层:维护i2c 总线,包括i2c driver, i2c client链表
        drivers/i2c/i2c-core.c
        ---------------------------------------------------
        i2c adapter层:i2c控制层,初始化i2c控制器
                完成将数据写入或读取-从设备硬件
                不知道数据具体是什么,但是知道如何操作从设备
        drivers/i2c/busses/i2c-s3c2410.c

    在内核编译时,确保i2c core和i2c adatper层必须编译进内核:

        make menuconfig
            Device Drivers  --->
                -*- I2C support  ---> //编译i2c-core.c
                    I2C Hardware Bus support  --->
                        <*> S3C2410 I2C Driver // i2c-s3c2410.c
        

    编译后如何确定是否有I2C总线以及i2c adapter控制器:

    在如下/sys/bus/i2c目录下查看

     i2c-0 即 i2c控制器

    2、I2C总线与平台总线的结合

    两者各司其职,平台总线用于平台升级,由s3c2410-i2c.c就可知

     I2C client 和 平台设备pdev都在设备树中描述。

    3、在设备树信息添加i2c从设备

    i2c信息在设备树的哪个位置?

     对应在文件目录下有:/sys/bus/platform/devices

     可以看到在内核中默认就有i2c0,那13860000是什么意思,到数据手册看一下

     可知,实际上Exynos4412支持9组i2c总线,即有SCL0, SDA0 、SCL1,SDA1... SCL8,SDA8九组i2c总线。

    下面以MPU6050为例看一下,i2c从设备与SOC的硬件相关信息

     Coreboard

    MPU6050对应第6组i2c总线,i2c的控制器基地址为0x138B_0000

    MPU6050: 从设备地址是0x68
     SOC             MPU6050

    GPB_3   ---   I2C_SCL5
    GPB_3   ---   I2C_SDA5
    GPX3_3 ---   GYRO_INT

    由于内核中没有i2c5,所以需要人工添加进去

    exynos4412-fs4412.dts设备树文件中只包含了一部分i2c信息,在文件开头,dts包含了exynos4412.dtsi文件

    逐个打开文件搜索i2c,查看文件包含信息

    exynos4412-fs4412.dts -->exynos4412.dtsi-->exynos4x12.dtsi-->exynos4,dtsi

    最终在exynos4.dtsi下找到大量i2c信息,其中i2c_0, i2c_5信息如下;

    模板:

    描述控制器对应的设备树:arch/arm/boot/dts/exynos4.dtsi

     1           i2c_0: i2c@13860000 {
     2                 #address-cells = <1>;
     3                 #size-cells = <0>;
     4                 compatible = "samsung,s3c2440-i2c";
     5                 reg = <0x13860000 0x100>;
     6                 interrupts = <0 58 0>;
     7                 clocks = <&clock 317>;
     8                 clock-names = "i2c";
     9                 pinctrl-names = "default";
    10                 pinctrl-0 = <&i2c0_bus>;
    11                 status = "disabled";  //状态位disable,
    12         };   //在内核中不会自动执行adapter控制器的代码
    13 
    14           i2c_5: i2c@138B0000 {
    15                 #address-cells = <1>;
    16                 #size-cells = <0>;
    17                 compatible = "samsung,s3c2440-i2c";
    18                 reg = <0x138B0000 0x100>;
    19                 interrupts = <0 63 0>;
    20                 clocks = <&clock 322>;
    21                 clock-names = "i2c";
    22                 status = "disabled";
    23         }
     status = "disabled";  //状态位disable //在内核中不会自动执行adapter控制器的代码

    回到exynos4412-fs4412.dts

    描述从设备信息的设备树的模板 :exynos4412-fs4412.dts

     1 i2c@13860000 {
     2         #address-cells = <1>;
     3         #size-cells = <0>;
     4         samsung,i2c-sda-delay = <100>;
     5         samsung,i2c-max-bus-freq = <20000>;
     6         pinctrl-0 = <&i2c0_bus>;
     7         pinctrl-names = "default";
     8         status = "okay";  //状态okay
     9         
    10         //子节点:从设备信息--电源管理芯片
    11         s5m8767_pmic@66 {
    12                 compatible = "samsung,s5m8767-pmic";
    13                 reg = <0x66>;  
    14                //对应/sys/bus/i2c/devices/i2c_0  -->0_0066
    15         }
    16             
    17 }            

    在内核目录/sys/bus/i2c/devices/i2c_0目录下

    新增加i2c从设备

    在  arch/arm/boot/dts/exynos4412-fs4412.dts  增加i2c5控制器和它包含了从设备

    1)确定i2c从设备的从设备号

    看到MPU6050数据手册

    mpu6050的AD0接地,低电平,故7位的从设备地址为b 110 1000 --> 0x68

     2)确定slave名字 -- 厂商+型号 -- invensense ,mpu6050

     1 i2c@138B0000 {/*i2c adapter5信息*/
     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 
    10         mpu6050@68 { /*i2c client信息 查数据手册得从设备号为68*/
    11                 compatible = "invensense,mpu6050";
    12                 reg = <0x68>;
    13         };
    14 };

    保存后,make dtbs ,更新设备树文件

    打开内核目录下平台设备的设备文件

     进入adapter  138b0000.i2c

    再查看i2c总线下是否有从设备

    设置好设备树中的硬件信息,就可以开始写驱动了

    4、i2c driver驱动的编写
      a, 添加i2c client的信息,必须包含在控制器对应的节点中
      b,直接编写i2c driver
        1,构建i2c driver,并注册到i2c总线
        2,实现probe:
          |
          申请设备号,实现fops
          创建设备文件
          通过i2c的接口去初始化i2c从设备

    几个常用的对象:

     1     struct i2c_driver {//表示是一个从设备的驱动对象
     2         int (*probe)(struct i2c_client *, const struct i2c_device_id *);
     3         int (*remove)(struct i2c_client *);
     4         struct device_driver driver; //继承了父类
     5                     |
     6                     const struct of_device_id    *of_match_table;
     7         const struct i2c_device_id *id_table;//用于做比对,非设备树的情况
     8     }
     9     注册和注销
    10         int i2c_add_driver( struct i2c_driver *driver);
    11         void i2c_del_driver(struct i2c_driver *);
    12 
    13 
    14     struct i2c_client {//描述一个从设备的信息,不需要在代码中创建,因为是由i2c adapter帮我们创建
    15         unsigned short addr;        //从设备地址,来自于设备树中<reg>
    16         char name[I2C_NAME_SIZE]; //用于和i2c driver进行匹配,来自于设备树中compatible
    17         struct i2c_adapter *adapter;//指向当前从设备所存在的i2c adapter
    18         struct device dev;        // 继承了父类
    19     };
    20     创建i2c client的函数
    21     struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct i2c_board_info const *info)
    22 
    23 
    24 
    25     struct i2c_adapter {//描述一个i2c控制器,也不是我们要构建,原厂的代码会帮我们构建
    26         const struct i2c_algorithm *algo; //算法
    27                     |
    28                     int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);
    29         
    30         struct device dev; //继承了父类,也会被加入到i2c bus
    31         int nr; //编号
    32 
    33     }
    34     注册和注销:
    35     int i2c_add_adapter(struct i2c_adapter * adapter);
    36     void i2c_del_adapter(struct i2c_adapter * adap);
    37 
    38 
    39     struct i2c_msg {//描述一个从设备要发送的数据的数据包
    40         __u16 addr;     //从设备地址,发送给那个从设备
    41         __u16 flags; //读1还是写0
    42         __u16 len;        //发送数据的长度
    43         __u8 *buf;        //指向数据的指针
    44     };
    45     //写从设备
    46     int i2c_master_send(const struct i2c_client * client,const char * buf,int count)
    47     //读从设备
    48     int i2c_master_recv(const struct i2c_client * client,char * buf,int count)
    49     以上两个函数都调用了:
    50     int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)

    5,陀螺仪和加速度工作原理,它们的应用

    陀螺仪的作用:
        原理:
            小时候玩过陀螺,如果给它一定的旋转速度,陀螺会竖立旋转起来而不会倒,
            主要因为高速旋转有抗拒方向改变的趋向
            
            陀螺仪就是内部的转子高速旋转,形成一个固定的初始化的参考平面,
            这样就可以通过测量初始的参考平面偏差计算出物体的旋转情况
            陀螺仪的强项在于测量设备自身的旋转运动
    
        陀螺仪的产生:
            1850年法国的物理学家福柯(J.Foucault)为了研究地球自转,首先发现高速转动中的转子(rotor),
            由于惯性作用它的旋转轴永远指向一固定方向,他用希腊字gyro(旋转)和skopein(看)
            两字合为gyro scopei一字来命名这种仪表
    
        陀螺仪的基本部件:
            (1) 陀螺转子,转子装在一支架内
            (2)内、外环,它是使陀螺自转轴获得所需角转动自由度的结构
                内环可环绕平面两轴作自由运动
                在内环架外加上一外环架,可以环绕平面做三轴作自由运动
            (3) 附件(是指力矩马达、信号传感器等)。
    
        陀螺仪的数据获取:        
            XYZ分别代表设备围绕XYZ三个轴旋转的角速度,陀螺仪可以捕捉很微小的运动轨迹变化,
            因此可以做高分辨率和快速反应的旋转检测,但不能测量当前的运行方向
    
        应用:
            1,陀螺仪用于飞行体运动的自动控制系统中,作为水平、垂直、俯仰、航向和角速度传感器
            2,手机上的摄像头配合使用,比如防抖
            3,各类手机游戏的传感器,包括一些第一视角类射击游戏,陀螺仪完整监测游戏者手的位移
               手机中的陀螺仪最早被iphone4应用,所以被大家所熟知
            4,导航,手机配合GPS,导航能力已经可以达到专用的gps导航仪
    
    重力加速度:
        原理:
            重力施加在物体上,使它产生一个加速度,重力大小和此物体的质量成正比
            物体在不同的运行中,会产生不同的重力,从而可以测量出物体的运动情况
    
        重力加速度的数据获取:
            加速度测量传感器有x、y、z三轴,注意在手机上屏幕的坐标,以左上角作为原点的,而且Y向下。
            注意区分这两个不同的坐标系。
            加速传感器的单位是加速度m/s2。如果手机平放好,x,y在位置为0,
            而z轴方向加速度=当前z方向加速度-g。由于g(重力加速度)垂直向下,
            则g=-9.81m/s2,即z轴 a=0-(-9.81)=9.81m/s2
        应用:
            1,图像自动翻转
            2,游戏控制
            3,计步器功能

    6,寄存器:

    #define SMPLRT_DIV        0x19 //采样频率寄存器-25 典型值:0x07(125Hz)
                                //寄存器集合里的数据根据采样频率更新
    #define CONFIG            0x1A    //配置寄存器-26-典型值:0x06(5Hz)
                                    //DLPF is disabled(DLPF_CFG=0 or 7)
    #define GYRO_CONFIG        0x1B//陀螺仪配置-27,可以配置自检和满量程范围
                                //典型值:0x18(不自检,2000deg/s)
    #define ACCEL_CONFIG        0x1C    //加速度配置-28 可以配置自检和满量程范围及高通滤波频率
                                    //典型值:0x01(不自检,2G,5Hz)
    #define ACCEL_XOUT_H    0x3B //59-65,加速度计测量值 XOUT_H
    #define ACCEL_XOUT_L    0x3C  // XOUT_L
    #define ACCEL_YOUT_H    0x3D  //YOUT_H
    #define ACCEL_YOUT_L    0x3E  //YOUT_L
    #define ACCEL_ZOUT_H    0x3F  //ZOUT_H
    #define ACCEL_ZOUT_L    0x40 //ZOUT_L---64
    #define TEMP_OUT_H        0x41 //温度测量值--65
    #define TEMP_OUT_L        0x42
    #define GYRO_XOUT_H        0x43 //陀螺仪值--67,采样频率(由寄存器 25 定义)写入到这些寄存器
    #define GYRO_XOUT_L        0x44
    #define GYRO_YOUT_H        0x45
    #define GYRO_YOUT_L        0x46
    #define GYRO_ZOUT_H        0x47
    #define GYRO_ZOUT_L        0x48 //陀螺仪值--72
    #define PWR_MGMT_1        0x6B //电源管理 典型值:0x00(正常启用)

    7、ioctl: 给驱动发送不同指令

         应用程序:
            ioctl(fd, cmd, args);
        ========================================
        驱动中:xxx_ioctl()
        {
                switch(cmd){          
                }
        }
    
        如何定义命令:
            1, 直接定义一个数字
                #define IOC_GET_ACCEL  0x9999
            2, 通过系统的接口
                _IO(x,y)
                _IOR(x,y,z)
                _IOW(x,y,z)
    
                参数1:表示magic,字符
                参数2:区分不同命令,整数 
                参数3:传给驱动数据类型

    8,mpu6050的数据

    陀螺仪可测范围为 欧拉角格式±250,±500,±1000,±2000°/秒(dps) ,加速度计可测范围为±2,±4,±8,±16g
    加速度读取的值为:
    AFS_SEL      Full scale rang                  LSB  Sensitivy
        0                +-2g -----------------------16384 LSB/g
        1                +-4g -----------------------8192 LSB/g
        2                +-8g -----------------------4096 LSB/g
        3                +-16g------------------------2048 LSB/g
    
    温度值:
            C = (TEMP_OUT Register Value )/340 + 36.53
        
    陀螺仪值:
        FS_SEL      Full scale rang                  LSB  Sensitivy
        0                +-250 度/s ------------------131 LSB 度/s
        1                +-500 度/s ------------------65.5 LSB 度/s
        2                +-1000 度/s -----------------32.8 LSB 度/s
        3                +-2000 度/s ------------------16.4 LSB 度/s

     测试代码:

      1 #include <linux/init.h>
      2 #include <linux/module.h>
      3 #include <linux/input.h>
      4 #include <linux/interrupt.h>
      5 #include <linux/slab.h>
      6 #include <linux/of.h>
      7 #include <linux/of_irq.h>
      8 #include <linux/of_gpio.h>
      9 #include <linux/i2c.h>
     10 
     11 #include <asm/io.h>
     12 #include <asm/uaccess.h>
     13 
     14 #include "mpu6050.h"
     15 
     16 
     17 #define SMPLRT_DIV        0x19 //采样频率寄存器-25 典型值:0x07(125Hz)
     18                                     //寄存器集合里的数据根据采样频率更新
     19 #define CONFIG            0x1A    //配置寄存器-26-典型值:0x06(5Hz)
     20                                         //DLPF is disabled(DLPF_CFG=0 or 7)
     21 #define GYRO_CONFIG        0x1B//陀螺仪配置-27,可以配置自检和满量程范围
     22                                     //典型值:0x18(不自检,2000deg/s)
     23 #define ACCEL_CONFIG        0x1C    //加速度配置-28 可以配置自检和满量程范围及高通滤波频率
     24                                         //典型值:0x01(不自检,2G,5Hz)
     25 #define ACCEL_XOUT_H    0x3B //59-65,加速度计测量值 XOUT_H
     26 #define ACCEL_XOUT_L    0x3C  // XOUT_L
     27 #define ACCEL_YOUT_H    0x3D  //YOUT_H
     28 #define ACCEL_YOUT_L    0x3E  //YOUT_L
     29 #define ACCEL_ZOUT_H    0x3F  //ZOUT_H
     30 #define ACCEL_ZOUT_L    0x40 //ZOUT_L---64
     31 #define TEMP_OUT_H        0x41 //温度测量值--65
     32 #define TEMP_OUT_L        0x42
     33 #define GYRO_XOUT_H        0x43 //陀螺仪值--67,采样频率(由寄存器 25 定义)写入到这些寄存器
     34 #define GYRO_XOUT_L        0x44
     35 #define GYRO_YOUT_H        0x45
     36 #define GYRO_YOUT_L        0x46
     37 #define GYRO_ZOUT_H        0x47
     38 #define GYRO_ZOUT_L        0x48 //陀螺仪值--72
     39 #define PWR_MGMT_1        0x6B //电源管理 典型值:0x00(正常启用)
     40 
     41 
     42 
     43 
     44 
     45 
     46 //设计一个全局的设备对象
     47 struct mpu_sensor{
     48     int dev_major;
     49     struct device *dev;
     50     struct class *cls;
     51     struct i2c_client *client;//记录probe中client
     52 };
     53 
     54 struct mpu_sensor *mpu_dev;
     55 
     56 
     57 int mpu6050_write_bytes(struct i2c_client *client, char *buf, int count)
     58 {
     59 
     60     int ret;
     61     struct i2c_adapter *adapter = client->adapter;
     62     struct i2c_msg msg;
     63 
     64     msg.addr = client->addr;
     65     msg.flags = 0;
     66     msg.len = count;
     67     msg.buf = buf;
     68     
     69 
     70     ret = i2c_transfer(adapter, &msg,  1);
     71 
     72     return ret==1?count:ret;
     73 
     74 }
     75 
     76 int mpu6050_read_bytes(struct i2c_client *client, char *buf, int count)
     77 {
     78     
     79         int ret;
     80         struct i2c_adapter *adapter = client->adapter;
     81         struct i2c_msg msg;
     82     
     83         msg.addr = client->addr;
     84         msg.flags = I2C_M_RD;
     85         msg.len = count;
     86         msg.buf = buf;
     87         
     88         ret = i2c_transfer(adapter, &msg,  1);
     89     
     90         return ret==1?count:ret;
     91 }
     92 
     93 //读取某个特定寄存器的地址,然后返回值
     94 int mpu6050_read_reg_byte(struct i2c_client *client, char reg)
     95 {
     96     // 先写寄存器的地址, 然后在读寄存器的值
     97 
     98         int ret;
     99         struct i2c_adapter *adapter = client->adapter;
    100         struct i2c_msg msg[2];
    101 
    102         char rxbuf[1];
    103     
    104         msg[0].addr = client->addr;
    105         msg[0].flags = 0;
    106         msg[0].len = 1;
    107         msg[0].buf = &reg;
    108 
    109         msg[1].addr = client->addr;
    110         msg[1].flags = I2C_M_RD;
    111         msg[1].len = 1;
    112         msg[1].buf = rxbuf;
    113         
    114         ret = i2c_transfer(adapter, msg,  2);
    115         if(ret < 0)
    116         {
    117             printk("i2c_transfer read error
    ");
    118             return ret;
    119         }
    120 
    121         return rxbuf[0];
    122 
    123 }
    124 
    125 int mpu6050_drv_open(struct inode *inode, struct file *filp)
    126 {
    127     return 0;
    128 }
    129 int mpu6050_drv_close(struct inode *inode, struct file *filp)
    130 {
    131     return 0;
    132 }
    133 
    134 long mpu6050_drv_ioctl (struct file *filp, unsigned int cmd, unsigned long args)
    135 {
    136     union mpu6050_data data;
    137 
    138 
    139     switch(cmd){
    140         case IOC_GET_ACCEL:
    141             //读数据
    142             data.accel.x = mpu6050_read_reg_byte(mpu_dev->client, ACCEL_XOUT_L);
    143             data.accel.x |= mpu6050_read_reg_byte(mpu_dev->client, ACCEL_XOUT_H) << 8;
    144 
    145             data.accel.y = mpu6050_read_reg_byte(mpu_dev->client, ACCEL_YOUT_L);
    146             data.accel.y |= mpu6050_read_reg_byte(mpu_dev->client, ACCEL_YOUT_H) << 8;
    147 
    148             data.accel.z = mpu6050_read_reg_byte(mpu_dev->client, ACCEL_ZOUT_L);
    149             data.accel.z |= mpu6050_read_reg_byte(mpu_dev->client, ACCEL_ZOUT_H) << 8;
    150             break;
    151         case IOC_GET_GYRO:
    152             data.gyro.x = mpu6050_read_reg_byte(mpu_dev->client, GYRO_XOUT_L);
    153             data.gyro.x |= mpu6050_read_reg_byte(mpu_dev->client, GYRO_XOUT_H) << 8;
    154 
    155 
    156             data.gyro.y = mpu6050_read_reg_byte(mpu_dev->client, GYRO_YOUT_L);
    157             data.gyro.y |= mpu6050_read_reg_byte(mpu_dev->client, GYRO_YOUT_H) << 8;
    158 
    159             data.gyro.z= mpu6050_read_reg_byte(mpu_dev->client, GYRO_ZOUT_L);
    160             data.gyro.z |= mpu6050_read_reg_byte(mpu_dev->client, GYRO_ZOUT_H) << 8;
    161             break;
    162         case IOC_GET_TEMP:
    163             data.temp = mpu6050_read_reg_byte(mpu_dev->client, TEMP_OUT_L);
    164             data.temp |= mpu6050_read_reg_byte(mpu_dev->client, TEMP_OUT_H) << 8;
    165             break;
    166         default:
    167             printk("invalid cmd
    ");
    168             return -EINVAL;
    169     }
    170 
    171     //将所有的数据交给用户
    172     if(copy_to_user((void __user * )args, &data, sizeof(data)) > 0)
    173         return -EFAULT;
    174 
    175 
    176     return 0;
    177 }
    178 
    179 const struct file_operations mpu6050_fops = {
    180     .open = mpu6050_drv_open,
    181     .release = mpu6050_drv_close,
    182     .unlocked_ioctl = mpu6050_drv_ioctl,
    183 
    184 };
    185 
    186 
    187 int mpu6050_drv_probe(struct i2c_client *client, const struct i2c_device_id *id)
    188 {
    189     printk("-----%s----
    ", __FUNCTION__);
    190 
    191     /*
    192         申请设备号,实现fops
    193             创建设备文件
    194             通过i2c的接口去初始化i2c从设备
    195     */
    196 
    197     mpu_dev = kzalloc(sizeof(struct mpu_sensor), GFP_KERNEL);
    198     
    199     mpu_dev->client = client;
    200 
    201     mpu_dev->dev_major = register_chrdev(0,"mpu_drv", &mpu6050_fops);
    202 
    203     mpu_dev->cls = class_create(THIS_MODULE, "mpu_cls");
    204 
    205     mpu_dev->dev = device_create(mpu_dev->cls, NULL, MKDEV(mpu_dev->dev_major, 0),
    206                 NULL, "mpu_sensor");
    207     
    208     char buf1[2] = {PWR_MGMT_1, 0x0};
    209     mpu6050_write_bytes(mpu_dev->client, buf1, 2);
    210 
    211     char buf2[2] = {SMPLRT_DIV, 0x07};
    212     mpu6050_write_bytes(mpu_dev->client, buf2, 2);
    213 
    214     char buf3[2] = {CONFIG, 0x06};
    215     mpu6050_write_bytes(mpu_dev->client, buf3, 2);
    216 
    217     char buf4[2] ={GYRO_CONFIG, 0x18};
    218     mpu6050_write_bytes(mpu_dev->client, buf4, 2);
    219 
    220     char buf5[2] = {ACCEL_CONFIG, 0x01};
    221     mpu6050_write_bytes(mpu_dev->client, buf5, 2);
    222     
    223     return 0;
    224 
    225 }
    226 
    227 
    228 int mpu5060_drv_remove(struct i2c_client *client)
    229 {
    230     printk("-----%s----
    ", __FUNCTION__);
    231     device_destroy(mpu_dev->cls, MKDEV(mpu_dev->dev_major, 0));
    232     class_destroy(mpu_dev->cls);
    233     unregister_chrdev(mpu_dev->dev_major, "mpu_drv");
    234     kfree(mpu_dev);
    235     return 0;
    236 }
    237 
    238 
    239 const struct of_device_id  of_mpu6050_id[] = {
    240         {
    241             .compatible = "invensense,mpu6050",
    242         },
    243         {/*northing to be done*/},
    244 
    245 };
    246 
    247 
    248 const struct i2c_device_id mpu_id_table[] = {
    249         {"mpu6050_drv", 0x1111},
    250         {/*northing to be done*/},
    251 };
    252     
    253 struct i2c_driver mpu6050_drv = {
    254     .probe = mpu6050_drv_probe,
    255     .remove = mpu5060_drv_remove,
    256     .driver = {
    257         .name = "mpu6050_drv",//随便写,/sys/bus/i2c/driver/mpu6050_drv
    258         .of_match_table = of_match_ptr(of_mpu6050_id),
    259     },
    260     
    261     .id_table = mpu_id_table,//非设备树情况下的匹配,在设备树的模式下不需要使用
    262 
    263 };
    264 
    265 
    266 static int __init mpu6050_drv_init(void)
    267 {
    268     // 1,构建i2c driver,并注册到i2c总线
    269     return i2c_add_driver(&mpu6050_drv);
    270 
    271 }
    272 
    273 static void __exit mpu6050_drv_exit(void)
    274 {
    275     i2c_del_driver(&mpu6050_drv);
    276 
    277 }
    278 
    279 module_init(mpu6050_drv_init);
    280 module_exit(mpu6050_drv_exit);
    281 MODULE_LICENSE("GPL");
    mpu6050_i2c_drv.c
     1 #include <stdio.h>
     2 #include <string.h>
     3 #include <stdlib.h>
     4 #include <sys/types.h>
     5 #include <sys/stat.h>
     6 #include <fcntl.h>
     7 #include <unistd.h>
     8 #include <sys/ioctl.h>
     9 
    10 
    11 #include "mpu6050.h"
    12 
    13 
    14 int main(int argc, char *argv[])
    15 {
    16     int fd;
    17     
    18     union mpu6050_data data;
    19     
    20     fd = open("/dev/mpu_sensor", O_RDWR);
    21     if(fd < 0)
    22     {
    23         perror("open");
    24         exit(1);
    25     }
    26 
    27     while(1)
    28     {
    29         ioctl(fd, IOC_GET_ACCEL, &data);
    30         printf("accel data :  x = %d, y=%d, z=%d
    ", data.accel.x, data.accel.y, data.accel.z);
    31 
    32     
    33         ioctl(fd, IOC_GET_GYRO, &data);
    34         printf("gyro data :  x = %d, y=%d, z=%d
    ", data.gyro.x, data.gyro.y, data.gyro.z);
    35 
    36         sleep(1);
    37 
    38     }
    39 
    40     close(fd);
    41     
    42 
    43     return 0;
    44 
    45 }
    mpu6050_test.c
     1 #ifndef __MPU6050_H__
     2 #define __MPU6050_H__
     3 
     4 union mpu6050_data{
     5     struct{    
     6         short x;
     7         short y;
     8         short z;
     9     }accel;
    10 
    11     struct{    
    12         short x;
    13         short y;
    14         short z;
    15     }gyro;
    16 
    17     short temp;
    18 };
    19 
    20 
    21 #define IOC_GET_ACCEL  _IOR('M', 0x34,union mpu6050_data)
    22 #define IOC_GET_GYRO  _IOR('M', 0x35,union mpu6050_data)
    23 #define IOC_GET_TEMP  _IOR('M', 0x36,union mpu6050_data)
    24 
    25 
    26 #endif
    mpu6050.h
     1 ROOTFS_DIR = /home/linux/source/rootfs
     2 
     3 MODULE_NAME = mpu6050_i2c_drv
     4 APP_NAME = mpu6050_test
     5 
     6 CROSS_COMPILE = /home/linux/toolchains/gcc-4.6.4/bin/arm-none-linux-gnueabi-
     7 CC = $(CROSS_COMPILE)gcc
     8 
     9 ifeq ($(KERNELRELEASE), )
    10 
    11 KERNEL_DIR = /home/linux/kernel/linux-3.14-fs4412
    12 
    13 CUR_DIR = $(shell pwd)
    14 
    15 all :
    16     make -C  $(KERNEL_DIR) M=$(CUR_DIR) modules
    17 
    18     $(CC) $(APP_NAME).c  -o $(APP_NAME)
    19 
    20 clean :
    21     make -C  $(KERNEL_DIR) M=$(CUR_DIR) clean
    22     rm -rf $(APP_NAME)    
    23 
    24 install:
    25     cp -raf *.ko $(APP_NAME)   $(ROOTFS_DIR)/drv_module
    26 
    27 
    28 else
    29 
    30 obj-m += $(MODULE_NAME).o
    31 
    32 
    33 endif
    Makefile

    测试·结构

  • 相关阅读:
    python把汉字转换成拼音实现程序
    C#第三方zip解压压缩工具,带事例源码
    实现在线压缩文件的实现程序代码
    如何处理JSON中的特殊字符
    《汉字简体、繁体相互转换》 查看源代码
    中文字符转拼音源码
    upper_bound()函数使用方法
    CodeFroces New Assignment 二分图匹配
    选拔赛 hash 字符串匹配 哈希算法(白书p374)
    lower_bound()函数使用
  • 原文地址:https://www.cnblogs.com/y4247464/p/12449345.html
Copyright © 2011-2022 走看看