i2c


原文链接: i2c

(使用Beaglebone Black的I2C(一) - Techfantastic - CSDN博客
linux 3.4 hisi i2c用户空间驱动/dev/i2c-0-文峰聊书斋-ChinaUnix博客
树莓派 -- i2c学习 - feiwatson - 博客园
linux下I2C驱动架构全面分析【转】 - 爱做梦的90后--庄泽彬 - 博客园
用户空间使用i2c_dev - 木花猫 - 博客园
(i2c-dev - jason的笔记 - CSDN博客

读取mpu6050设备地址

使用i2cget指令尝试读取0x68这个I2C设备的寄存器0x75的值:

sudo i2cget -y 4 0x68 0x75

正常来说,MPU-6050的寄存器0x75的值会是0x68,输出应该会像这样:

0x68

唤醒mpu6050

i2cset -y4 0x68 0x6b 0x00

使用 i2c-tools

sudo apt-get install i2c-tools python-smbus

i2cdetect – 用來列舉I2C bus和上面所有的裝置
i2cdump – 顯示裝置上所有register的值
i2cget – 讀取裝置上某個register的值
i2cset – 寫入裝置上某個register

i2cdetect -l
i2cdetect -y 1 ==> 列出I2C-1 上的设备
-- : 没有设备
UU : 正被使用
0x1d : 可使用
另外我們也可以使用 i2cdetect 來確認 Bus上有哪些 Device
i2cdetect -r -y 4 ===> 列出 Bus 4上的所有Device

2.I2C总线扫描

通过i2cdetect -l指令可以查看树莓派上的I2C总线,从返回的结果来看树莓派含有两个I2C总线,通过阅读相关的资料,树莓派1代使用I2C0,而树莓派2代使用I2C1。

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
pi@raspberrypi:~$ i2cdetect -l
i2c-0 i2c bcm2708_i2c.0 I2C adapter
i2c-1 i2c bcm2708_i2c.1 I2C adapter

3.I2C设备查询

若总线上挂载I2C从设备,可通过i2cdetect扫描某个I2C总线上的所有设备。可通过控制台输入i2cdetect -y 1,结果如下所示。

[cpp] view plain copy 在CODE上查看代码片 派生到我的代码片
pi@raspberrypi:~$ i2cdetect -y 1

 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f  

00: -- -- -- -- -- -- -- -- -- -- -- -- --
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
20: 20 -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
50: 50 51 -- -- -- -- -- -- -- -- -- -- -- -- -- --
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
70: -- -- -- -- -- -- -- --

说明1:-y为一个可选参数,如果有-y参数的存在则会有一个用户交互过程,意思是希望用户停止使用该I2C总线。如果写入该参数,则没有这个交互过程,一般该参数在脚本中使用。
说明2:此处I2C总线共挂载两个设备——PCF8574和AT24C04,从机地址0x20为PCF8574,从机地址0x50和0x51为AT24C04,请注意AT24C04具备两个I2C地址,相关内容请产看参考资料6.4。

4.寄存器内容导出

通过i2cdump指令可导出I2C设备中的所有寄存器内容,例如输入i2cdump -y 1 0x51,可获得以下内容: 

[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
pi@raspberrypi:~$ i2cdump -y 1 0x51
No size specified (using byte-data access)

 0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f    0123456789abcdef  

00: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
10: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
20: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
30: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
40: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
50: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
60: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
70: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
80: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
90: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
a0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
f0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................

i2cdump -y 1 0x51指令中,
-y        代表取消用户交互过程,直接执行指令;
1         代表I2C总线编号;
0x51    代表I2C设备从机地址,此处选择AT24C04的高256字节内容。
在该AT24C04的高256字节尚处于出厂默认状态,所有的存储地址中的内容均为0XFF。

5.寄存器内容写入

如果向I2C设备中写入某字节,可输入指令`i2cset -y 1 0x50 0x00 0x13`
-y        代表曲线用户交互过程,直接执行指令
1         代表I2C总线编号
0x50    代表I2C设备地址,此处选择AT24C04的低256字节内容
0x00    代表存储器地址
0x13    代表存储器地址中的具体内容 

6.寄存器内容读出
[plain] view plain copy 在CODE上查看代码片 派生到我的代码片
pi@raspberrypi:~$ i2cget -y 1 0x50 0x00
0x13

如果从I2C从设备中读出某字节,可输入执行i2cget -y 1 0x50 0x00,可得到以下反馈结果

-y        代表曲线用户交互过程,直接执行指令
1         代表I2C总线编号
0x50    代表I2C设备地址,此处选择AT24C04的低256字节内容
0x00    代表存储器地址

7.总结

i2ctools是一个简单好用的工具,该工具使得I2C设备的调试更加方便。 

8.参考资料
【1】i2ctools官方参考资料
【2】为什么AT24C04具备两个I2C从机地址

例子: http://wenku.baidu.com/link?url=-C55QRSgVpT1lOBmP2PqYBbjGL-0fR48uu3BARn-BJ6IWcw_D5iLXLm5nYDCeNAWOkhKK6XbAV4Ga5bhDh4KDlWn6QGzLf0WQgIoi3Rqc2K

/i2c-tool -w 0 0x48 4 0x02 0xA0 0x40 0x03
SLAVE ADDRESS: 0x48
NUMBER OF TBYTESO WRITE: 4
MEMORY ALLOCATED AT ADDRESS: 0x12008

/dev/i2c-0 OPENDED!
Failed writing to the I2C-bus: Connection timed out

用户空间使用i2c_dev

对于注册的i2c适配器,用户空间可以使用它们。上面的驱动对每个适配器生成一个主设备号为89的设备节点,实现了文件操作接口,用户空间可以通过i2c设备节点访问i2c适配器。适配器的编号从0开始,和适配器的设备节点的次设备号相同。i2c适配器的设备节点是/dev/i2c-x,其中x是数字,代表适配器的编号。由于适配器编号是动态分配的(和注册次序有关),所以想了解哪一个适配器对应什么编号,可以查看/sys/class/i2c-dev/目录下的文件内容。

对于注册的i2c适配器,用户空间也可以使用它们。在Linux内核代码文件/include/linux/i2c-dev.c中针对每个适配器生成一个主设备号为89的设备节点,实现了文件操作接口,用户空间可以通过i2c设备节点访问i2c适配器。适配器的编号从0开始,和适配器的设备节点的次设备号相同。

i2c适配器的设备节点是/dev/i2c-x,其中x是数字,代表适配器的编号。
由于适配器编号是动态分配的(和注册次序有关),所以想了解哪一个适配器对应什么编号,可以查看/sys/class/i2c-dev/目录下的文件内容。
然后就可以打开设备节点了。但是打开哪一个呢?因为适配器的编号并不固定。为此我们在中端中运行以下命令:

[root@zlg /]# cat /sys/class/i2c-dev/i2c-0/name
PNX4008-I2C0

[root@zlg /]# cat /sys/class/i2c-dev/i2c-1/name
PNX4008-I2C1

[root@zlg /]# cat /sys/class/i2c-dev/i2c-2/name
USB-I2C
如果我们想打开第二个适配器,刚好它的编号是1,对应的设备节点是/dev/i2c-1

查看I2C总线: ls -l /sys/bus/i2c
查看I2C设备: ls -l /sys/bus/i2c/devices/

/sys/devices/

用户空间创建i2c设备

  1. 添加rtc设备 $ echo ds3231 0x68 | sudo tee /sys/class/i2c-adapter/i2c-1/new_device
    ds3231 0x68 在目录下多了一个1-0068

pi@raspberrypi:ls /sys/class/i2c-adapter/i2c-1
1-0068 device name of_node subsystem
delete_device i2c-dev new_device power uevent

pi@raspberrypi: $ cd /sys/class/i2c-adapter/i2c-1/1-0068/
pi@raspberrypi:$ ls /sys/class/i2c-adapter/i2c-1/1-0068
driver hwmon modalias name power rtc subsystem uevent

pi@raspberrypi:/sys/class/i2c-adapter/i2c-1/1-0068 $ cat name
ds3231
查看1-0068

cd /sys/class/i2c-adapter/i2c-1/1-0068/rtc/rtc0/
ls /sys/class/i2c-adapter/i2c-1/1-0068/rtc/rtc0
date device max_user_freq power subsystem uevent
dev hctosys name since_epoch time

cat /sys/class/i2c-adapter/i2c-1/1-0068/rtc/rtc0/name
rtc-ds1307 1-0068
pi@raspberrypi:/sys/class/i2c-adapter/i2c-1/1-0068/rtc/rtc0 $
cat /dev/rtc0

pi@raspberrypi:/dev $ ls -la |grep rtc
lrwxrwxrwx 1 root root 4 Jul 14 13:43 rtc -> rtc0
crw------- 1 root root 253, 0 Jul 14 13:43 rtc0

  1. 删除rtc设备 $ echo ds3231 0x68 | sudo tee /sys/class/i2c-adapter/i2c-1/delete_device

测试

pi@raspberrypi:~ $ sudo hwclock
2000-01-01 00:07:24.525370+0000
pi@raspberrypi:~ $ sudo hwclock --debug
hwclock from util-linux 2.29.2
Using the /dev interface to the clock.
Assuming hardware clock is kept in UTC time.
Waiting for clock tick...
/dev/rtc does not have interrupt functions. Waiting in loop for time from /dev/rtc to change
...got clock tick
Time read from Hardware Clock: 2000/01/01 00:07:40
Hw clock time : 2000/01/01 00:07:40 = 946685260 seconds since 1969
Time since last adjustment is 946685260 seconds
Calculated Hardware Clock drift is 0.000000 seconds
2000-01-01 00:07:39.551217+0000

本小节介绍两个在 linux 应用层访问 eeprom 的方法,并给出示例代码方便大家理解。第一个方法是通过 sysfs 文件系统对 eeprom 进行访问,第二个方法是通过 eeprom 的设备文件进行访问。这两个方法分别对应了 i2c 设备驱动的两个不同的实现,在后面的小结会详细的分析。

1. 通过 sysfs 文件系统访问 I2C 设备

eeprom 的设备驱动在 / sys/bus/i2c/devices/0-0050 /目录下把 eeprom 设备映射为一个二进制节点,文件名为 eeprom。对这个 eeprom 文件的读写就是对 eeprom 进行读写。

我们可以先用 cat 命令来看下 eeprom 的内容。

[root@FORLINX210]# cat eeprom                                                                      
�����������X�����������������������������������������������

发现里面都是乱码,然后用 echo 命令把字符串 “test” 输入给 eeprom 文件,然后再 cat 出来。

[root@FORLINX210]# echo "test" > eeprom  
[root@FORLINX210]# cat eeprom                                                 
test                                                                            
�����������X�����������������������������������������������

就会发现字符串 test 已经存在 eeprom 里面了,我们知道 sysfs 文件系统断电后就没了,也无法对数据进行保存,为了验证确实把 “test” 字符串存储在了 eeprom,可以把系统断电重启,然后 cat eeprom,会发现 test 还是存在的,证明确实对 eeprom 进行了写入操作。

当然,因为 eeprom 已经映射为一个文件了,我们还可以通过文件 I/O 写应用程序对其进行简单的访问测试。比如以下程序对特定地址(0x40)写入特定数据(Hi,this is an eepromtest!),然后再把写入的数据在此地址上读出来。

#include<stdio.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string.h>

int main(void){
    int fd, size, len, i;
    char buf[50]= {0};
    char *bufw="Hi,this is an eepromtest!";//要写入的数据

    len=strlen(bufw);//数据长度
    fd= open("/sys/bus/i2c/devices/0-0050/eeprom",O_RDWR);//打开文件
    if(fd< 0)
    {
        printf("####i2c test device open failed####/n");
        return(-1);
    }
    //写操作
    lseek(fd,0x40,SEEK_SET); //定位地址,地址是0x40
    if((size=write(fd,bufw, len))<0)//写入数据
    {
        printf("write error\n");
        return 1;
    }
    printf("writeok\n");
    //读操作
    lseek(fd,0x40, SEEK_SET);//准备读,首先定位地址,因为前面写入的时候更新了当前文件偏移量,所以这边需要重新定位到0x40.
    if((size=read(fd,buf,len))<0)//读数据
    {
        printf("readerror\n");
        return 1;
    }
    printf("readok\n");
    for(i=0; i< len; i++)
        printf("buff[%d]=%x\n",i, buf[i]);//打印数据
    close(fd);
    
    return 0;
}

2. 通过 devfs 访问 I2C 设备

linux 的 i2c 驱动会针对每个 i2c 适配器在 / dev / 目录下生成一个主设备号为 89 的设备文件,简单的来说,对于本例的 eeprom 驱动,/dev/i2c/0 就是它的设备文件,因此接下来的 eeprom 的访问就变为了对此设备文件的访问。

我们需要用到两个结构体 i2c_msg 和 i2c_rdwr_ioctl_data。

struct i2c_msg { //i2c消息结构体,每个i2c消息对应一个结构体
     __u16 addr; /* 从设备地址,此处就是eeprom地址,即0x50 */
     __u16 flags;    /* 一些标志,比如i2c读等*/
     __u16 len;      /* i2c消息的长度 */
     __u8 *buf;      /* 指向i2c消息中的数据 */
 };

 struct i2c_rdwr_ioctl_data {
     struct i2c_msg __user *msgs;    /* 指向一个i2c消息 */
     __u32 nmsgs;            /* i2c消息的数量 */
 };

对一个 eeprom 上的特定地址(0x10)写入特定数据(0x58)并在从此地址读出写入数据的示例程序如下所示。

#include <stdio.h>
#include <linux/types.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <errno.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>

int main()
{
    int fd,ret;
    struct i2c_rdwr_ioctl_data e2prom_data;
    fd=open("/dev/i2c/0",O_RDWR);//打开eeprom设备文件结点
    if(fd<0)
    {
        perror("open error");
    }

    e2prom_data.nmsgs=2; 
    e2prom_data.msgs=(struct i2c_msg*)malloc(e2prom_data.nmsgs*sizeof(struct i2c_msg));//分配空间
    if(!e2prom_data.msgs)
    {
        perror("malloc error");
        exit(1);
    }
    ioctl(fd,I2C_TIMEOUT,1);/*超时时间*/
    ioctl(fd,I2C_RETRIES,2);/*重复次数*/
    
    /*写eeprom*/
    e2prom_data.nmsgs=1;//由前面eeprom读写分析可知,写eeprom需要一条消息
    (e2prom_data.msgs[0]).len=2; //此消息的长度为2个字节,第一个字节是要写入数据的地址,第二个字节是要写入的数据
    (e2prom_data.msgs[0]).addr=0x50;//e2prom 设备地址
    (e2prom_data.msgs[0]).flags=0; //写
    (e2prom_data.msgs[0]).buf=(unsigned char*)malloc(2);
    (e2prom_data.msgs[0]).buf[0]=0x10;// e2prom 写入目标的地址
    (e2prom_data.msgs[0]).buf[1]=0x58;//写入的数据
    ret=ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);//通过ioctl进行实际写入操作,后面会详细分析
    if(ret<0)
    {
        perror("ioctl error1");
    }
    sleep(1);
    
    /*读eeprom*/
    e2prom_data.nmsgs=2;//读eeprom需要两条消息
    (e2prom_data.msgs[0]).len=1; //第一条消息实际是写eeprom,需要告诉eeprom需要读数据的地址,因此长度为1个字节
    (e2prom_data.msgs[0]).addr=0x50; // e2prom 设备地址
    (e2prom_data.msgs[0]).flags=0;//先是写
    (e2prom_data.msgs[0]).buf[0]=0x10;//e2prom上需要读的数据的地址
    (e2prom_data.msgs[1]).len=1;//第二条消息才是读eeprom,
    (e2prom_data.msgs[1]).addr=0x50;// e2prom 设备地址 
    (e2prom_data.msgs[1]).flags=I2C_M_RD;//然后是读
    (e2prom_data.msgs[1]).buf=(unsigned char*)malloc(1);//存放返回值的地址。
    (e2prom_data.msgs[1]).buf[0]=0;//初始化读缓冲,读到的数据放到此缓冲区
    ret=ioctl(fd,I2C_RDWR,(unsigned long)&e2prom_data);//通过ioctl进行实际的读操作
    if(ret<0)
    {
        perror("ioctl error2");
    }
    
    printf("buff[0]=%x\n",(e2prom_data.msgs[1]).buf[0]);
    /***打印读出的值,没错的话,就应该是前面写的0x58了***/
    close(fd);
    
    return 0;
}

3. 总结

本小节介绍了两种在 linux 应用层访问 eeprom 的方法,并且给出了示例程序,通过 sysfs 文件系统访问 eeprom 操作简单,无需了解 eeprom 的硬件特性以及访问时序,而通过 devfs 访问 eeprom 的方法则需要了解 eeprom 的读写时序。

后面分析后会发现,第一种通过 sysfs 文件系统的二进制结点访问 eeprom 的方法是由 eeprom 的设备驱动实现的,是一种专有的方法;而第二种通过 devfs 访问 eeprom 的方法是 linux i2c 提供的一种通用的方法,访问设备的能力有限。

###
1设备的4种构建方法
1.1定义一个i2c_board_info,里面有名字,地址
然后i2c_register_board_info(busnum,……)(把他们放入链表)
list_add_tail(&devinfo->list, &__i2c_board_list);
链表何时使用
i2c_register_adapter->i2c_scan_static_board_info(struct i2c_adapter *adapter)>i2c_new_device

使用限制:必须i2c_register_adapter之前i2c_register_board_info
所以不适合我们动态加载insmod
1.2直接i2c_new_device,i2c_new_probe_device
1.2.1 i2c_new_device :认为设备肯定存在
1.2.2 i2c_new_probed_device :对于“已经识别出来的设备”probed__device”,才会(new)
probe(adap, addr_list[i] //确定设备是否真的存在
info->addr = addr_list[i];
return i2c_new_device(adap, info);
1.3从用户空间创建设备
创建设备
echo at24c02 0x50 > /sys/class/i2c-adapter/i2c-7/new_device
删除设备
echo 0x50 > /sys/class/i2c-adapter/i2c-7/new_device
导致i2c_unregister_device
1.4前面的3种方法都要确定适配器(I2C总线、I2C控制器)
如果事先不知道这个I2C设备在哪个适配器,怎么办?去class表示的所有的适配器上查找,有一些I2C设备的地址是一样的,怎么继续分配它是哪一款?用detect函数确定

static struct i2c_driver at24cxx_driver = {

.class=I2C_CLASS_HWMON//哪一类设备器查找支持的设备
.driver = {
    .name   = "100ask",
},
.probe      = at24cxx_probe,
.remove     = __devexit_p(at24cxx_remove),
.id_table   = at24cxx_id_table,
.detect=at24cxx_detect,//用这个函数来检测能否找到设备
.address_list=addr_list,//这些设备的地址

};
去“class表示的这一类“i2c适配器,用“detect函数“来确定能否找到"address_list"里的设备
如果能找到就调用好i2c_new_device来注册i2c_client,这会和i2c_driver的id_table比较,如果匹配,调用probe
首先在入口函数中调用.i2c_add_driver
a.i2c_add_driver

    i2c_register_driver.
        at24cxx_driver放入i2c_bus_type的drv链表,
并且从dev链表里取出能匹配的i2c_client并调用probe
    driver_register
如果dev链表没有,此时则调用i2c_for_each_dev(driver,__process_new_driver);   对每一个适配器进行循环

b.对于每一个是适配器,调用__process_new_driver
对于每一个适配器,调用它的函数确定address_list里的设备是否存在
如果存在,在调用detect进一步确定、设置,然后i2c_new_device

i2c_for_each_dev(driver,__process_new_driver);  
    __process_new_driver
        i2c_do_add_adapter
        i2c_detect(adap,driver);
            for(i=0;address_list[i]!=I2C_CLIENT_END;i+=1)
            err=i2c_detect_address(temp_client,driver);
        //判断
        if(!i2c_default_probe(adapter,addr))
        return 0;
    memset(&info,0,sizeof(struct i2c_board_info));
    info.addr=addr;
    //设置info的结构体
    err=driver->detect(temp_client,&info);

2.实例驱动程序编写

/*
I2C设备:mpu6050
内核版本:3.0.15
开发板:itop4412
*/

// mpu6050.h

#ifndef __MPU6050_H_
#define __MPU6050_H_
#define MPU6050_MAGIC 'K'
union mpu6050_data{
    struct{
        unsigned short x;
        unsigned short y;
        unsigned short z;
    }accel;
    struct{
        unsigned short x;
        unsigned short y;
        unsigned short z;
    }gyro;
    unsigned short temp;
};
#define GET_ACCEL _IOR(MPU6050_MAGIC,0,union mpu6050_data)
#define GET_GYRO _IOR(MPU6050_MAGIC,1,union mpu6050_data)
#define GET_TEMP _IOR(MPU6050_MAGIC,2,union mpu6050_data)
#endif

// MPU6050.c

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/err.h>
#include "mpu6050.h"
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/sched.h>
#include <asm/uaccess.h>
#include <linux/delay.h>

#define SMPLRT_DIV      0x19  
#define CONFIG          0x1A  
#define GYRO_CONFIG     0x1B  
#define ACCEL_CONFIG    0x1C  
#define ACCEL_XOUT_H    0x3B  
#define ACCEL_XOUT_L    0x3C  
#define ACCEL_YOUT_H    0x3D  
#define ACCEL_YOUT_L    0x3E  
#define ACCEL_ZOUT_H    0x3F  
#define ACCEL_ZOUT_L    0x40  
#define TEMP_OUT_H      0x41  
#define TEMP_OUT_L      0x42  
#define GYRO_XOUT_H     0x43  
#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  
#define PWR_MGMT_1      0x6B  

static int major;
struct cdev cdev;
static struct class *class;
static const unsigned short addr_list[] = { 0x68,I2C_CLIENT_END};
static struct i2c_client *mpu6050_client;
static struct i2c_client *my_client;
static int mpu6050_write_byte(struct i2c_client *client,unsigned char reg,unsigned char val)
{

    if(!i2c_smbus_write_byte_data(client,reg,val))
            return 2;
        else
            return -EIO;

    return 0;
}
static int mpu6050_read_byte(struct i2c_client *client,unsigned char reg)
{
     unsigned char data;
     data=i2c_smbus_read_byte_data(client,reg);
     return data;
}
static int mpu6050_open(struct inode * inode, struct file * file)
{
    return 0;
}
static int mpu6050_release(struct inode *inode, struct file *file)
{
    return 0;
}

static int mpu6050_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
    union mpu6050_data data;
    struct i2c_client *client = my_client;
    switch(cmd){
    case GET_ACCEL:
    {
        data.accel.x=mpu6050_read_byte(client,ACCEL_XOUT_L);
        data.accel.x|=mpu6050_read_byte(client,ACCEL_XOUT_H)<<8;

        data.accel.y=mpu6050_read_byte(client,ACCEL_YOUT_L);
        data.accel.y|=mpu6050_read_byte(client,ACCEL_YOUT_H)<<8;


        data.accel.z=mpu6050_read_byte(client,ACCEL_ZOUT_L);
        data.accel.z|=mpu6050_read_byte(client,ACCEL_ZOUT_H)<<8;
        printk(KERN_EMERG"GET_ACCEL X = %04x\n",data.accel.x);
        printk(KERN_EMERG"GET_ACCEL y = %04x\n",data.accel.y);
        printk(KERN_EMERG"GET_ACCEL z = %04x\n",data.accel.z);
        printk("GET_ACCEL finished\n");
        break;
    }
    case GET_GYRO:
    {
        data.gyro.x=mpu6050_read_byte(client,GYRO_XOUT_L);
        data.gyro.x|=mpu6050_read_byte(client,GYRO_XOUT_H)<<8;

        data.gyro.y=mpu6050_read_byte(client,GYRO_YOUT_L);
        data.gyro.y|=mpu6050_read_byte(client,GYRO_YOUT_H)<<8;

        data.gyro.z=mpu6050_read_byte(client,GYRO_ZOUT_L);
        data.gyro.z|=mpu6050_read_byte(client,GYRO_ZOUT_H)<<8;
        printk("GET_GYRO finished\n");
        break;
    }
    case GET_TEMP:
    {
        data.temp=mpu6050_read_byte(client,TEMP_OUT_L);
        data.temp|=mpu6050_read_byte(client,TEMP_OUT_H)<<8;
        break;
    }
    default:
    {
        printk("magic error\n");
        return -EINVAL;
    }   
}
    if(copy_to_user((void*)arg,&data,sizeof(data)))
        return -EINVAL;
    return sizeof(data);

}

struct file_operations mpu6050_ops={
    .owner = THIS_MODULE,
    .open  = mpu6050_open,
    .release = mpu6050_release,
    .unlocked_ioctl = mpu6050_ioctl,
};
static int __devinit mpu6050_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
    printk("match\n");
    my_client=kzalloc(sizeof(struct i2c_client),GFP_KERNEL);
    if(my_client == NULL) 
     {  
        return -ENOMEM;  
    }  
    my_client=client;
    major = register_chrdev(0, "mpu6050", &mpu6050_ops);
    class = class_create(THIS_MODULE, "mpu6050");
    device_create(class, NULL, MKDEV(major, 0), NULL, "mpu6050");
    printk("create successful\n");
    /*mpu6050的初始化*/
    mpu6050_write_byte(my_client,PWR_MGMT_1,0x00);//解除睡眠
    mpu6050_write_byte(my_client,SMPLRT_DIV,0x07);//采样频率分频器
    mpu6050_write_byte(my_client,CONFIG,0x06);//配置EXT_SYNC_SET和FSYNC,初始化温度
    mpu6050_write_byte(my_client,GYRO_CONFIG,0xF8);//选通陀螺仪的三轴及量程
    mpu6050_write_byte(my_client,ACCEL_CONFIG,0x19);//执行加速度自检和量程
    printk("init finished\n");
    return 0;
}
static int __devexit mpu6050_remove(struct i2c_client *client)
{
    device_destroy(class,MKDEV(major, 0));
    class_destroy(class);
    unregister_chrdev(major, "mpu6050");
    return 0;
}

static const struct i2c_device_id mpu6050_id_table[] = {
    { "mpu6050", 0 },
    { }
};

static struct i2c_driver mpu6050_driver = {
    .driver = {
        .name   = "100ask",
    },
    .probe      = mpu6050_probe,
    .remove     = __devexit_p(mpu6050_remove),
    .id_table   = mpu6050_id_table,

};

static int mpu6050_init(void)
{
    i2c_add_driver(&mpu6050_driver);
 struct i2c_adapter *i2c_adap;
  struct i2c_board_info mpu6050_info;
  memset(&mpu6050_info,0,sizeof(struct i2c_board_info));
  strlcpy(mpu6050_info.type,"mpu6050",I2C_NAME_SIZE);
  i2c_adap=i2c_get_adapter(7);
  mpu6050_client=i2c_new_probed_device(i2c_adap,&mpu6050_info,addr_list,NULL);
  i2c_put_adapter(i2c_adap);
  if(mpu6050_client)
    return 0;
  else
    return -ENODEV;
   return 0;
}
static void mpu6050_exit(void)
{
      i2c_del_driver(&mpu6050_driver);//必须先注销驱动
      i2c_unregister_device(mpu6050_client);

}
module_init(mpu6050_init);
module_exit(mpu6050_exit);
MODULE_LICENSE("GPL");

测试程序:

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/ioctl.h>
#include"mpu6050.h"
int main(int argc,char** argv)
{
    int fd;
    union mpu6050_data data;

    fd=open("/dev/mpu6050",O_RDWR);
    if(fd<0)
    {
        perror("open");
        exit(1);
    }
    while(1)
    {
        ioctl(fd,GET_ACCEL,&data);
        printf("acceleration data: x =  %04x,y = %04x,z = %04x\n",data.accel.x,data.accel.y,data.accel.z);

        ioctl(fd,GET_GYRO,&data);
        printf("gyroscope data : x = %04x,y = %04x,z = %04x\n",data.gyro.x,data.gyro.y,data.gyro.z);
        sleep(1);
    }
    close(fd);
    return 0;

}

测试程序编译
arm-none-Linux-gnueabi-gcc mpu6050_app.c -o mpu6050_app -static
下载到开发板中
insmod mpu6050.ko
./mpu6050_app
acceleration data: x = 0183,y = 001a,z = f814

gyroscope data : x = 07ea,y = 0932,z = 0c0e

作者:蜗蜗前行
来源:CSDN
原文:https://blog.csdn.net/cmh477660693/article/details/54981130
版权声明:本文为博主原创文章,转载请附上博文链接!

`