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上有哪些 Devicei2cdetect -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从机地址
/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设备
- 添加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
- 删除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
版权声明:本文为博主原创文章,转载请附上博文链接!