文章目录
- 引言
- 一、I2C 总线驱动框架
- 二、I2C驱动框图(重点)
- 三、I2C 子系统软件框架
- 3.1 I2C子系统的4个关键结构体
- 3.2 I2C总线与平台总线的结合
- 3.3 在设备树信息添加i2c从设备
- 3.4 新增加i2c从设备
- 四、i2c driver驱动的编写
- 4.1 陀螺仪和加速度工作原理
- 4.2 mpu6050的寄存器信息和设置
- 4.3 ioctl: 给驱动发送不同指令
- 4.4 代码实现
- 4.4.1 mpu6050_drv.c
- 4.4.2 mpu6050.h
- 4.4.3 mpu6050_test.c
- 4.4.4 Makefile
- 参考资料
整理是为了对知识进行复习回顾,融会贯通。本文来源于华清嵌入式知识课堂,如有侵权请联系本人删除。
个人觉得IIC子系统这部分,讲的很透彻。
引言
嵌入式项目中,外设经常通过IIC总线进行设备连接。基于硬件的IIC基本通信原理学习地址。比如,gsenor: 重力传感器,eeprom: 存储等设备,但是在linux中,基于平台总线抽象出了IIC总线模型,逻辑实现上变的复杂,但是在使用上就变的简单了,我们需要理解linux中的IIC总线框架。
一、I2C 总线驱动框架
I2C驱动有4个重要的东西,I2C总线、I2C驱动、I2C设备、I2C设备器
I2C总线:维护着两个链表(I2C驱动、I2C设备),管理I2C设备和I2C驱动的匹配和删除等
I2C驱动:对应的就是I2C设备的驱动程序
I2C设备:是具体硬件设备的一个抽象
I2C设配器:用于I2C驱动和I2C设备间的通用,是SOC上I2C控制器的一个抽象
i2c总线维护着两个链表,一个驱动链表,一个设备链表,每当注册进一个驱动(或设备),就会将其添加到总线上相应的链表上,然后遍历总线的设备(或驱动)链表的所有设备(或驱动),通过总线的匹配函数判断是够匹配,如果匹配,则调用驱动的probe函数,然后我们就可以在probe函数注册字符设备,创建设备节点,实现fops集等等
在I2C-core.c这个函数中,掌握下面的几个关键函数就可以了。
1. //增加/删除i2c_adapter
int i2c_add_adapter(struct i2c_adapter *adapter)
int i2c_del_adapter(struct i2c_adapter *adap)
2. //增加/删除i2c_driver
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
void i2c_del_driver(struct i2c_driver *driver)
3. //i2c_client依附/脱离
int i2c_attach_client(struct i2c_client *client)
int i2c_detach_client(struct i2c_client *client)
4. //I2C传输,发送和接收
int i2c_master_send(struct i2c_client *client,const char *buf ,int count)
int i2c_master_recv(struct i2c_client *client, char *buf ,int count)
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
I2c_transfer这个函数实现了core与adapter的联系。
二、I2C驱动框图(重点)
三、I2C 子系统软件框架
应用
------------------------------------------
i2c driver:从设备驱动层
需要和应用层进行交互
封包数据,不知道数据是如何写入到硬件
------------------------------------------
i2c 核心层:维护i2c 总线,包括i2c driver, i2c client链表
drivers/i2c/i2c-core.c
---------------------------------------------------
i2c adapter层:i2c控制层,初始化i2c控制器
完成将数据写入或读取-从设备硬件
不知道数据具体是什么,但是知道如何操作从设备
drivers/i2c/busses/i2c-s3c2410.c
3.1 I2C子系统的4个关键结构体
(1)struct i2c_adapter
描述主机所对应的iic控制器,每个控制器对应一个i2c_adapter结构体(主机驱动)
(2)struct i2c_algorithm
描述主机IIC跟从机iic设备通信的算法,算法由iic控制器执行,i2c_algorithm包含在i2c_adapter结构体中
(3)struct i2c_client
其实就是i2c_device,跟i2c_driver配对,是iic从机的设备信息
(4)struct i2c_driver
iic的设备驱动(从机设备)
在内核编译时,确保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控制器。
3.2 I2C总线与平台总线的结合
两者各司其职,平台总线用于平台升级,由s3c2410-i2c.c就可知, I2C client 和 平台设备pdev都在设备树中描述。
3.3 在设备树信息添加i2c从设备
i2c信息在设备树的哪个位置
对应在文件目录下有:/sys/bus/platform/devices
可以看到在内核中默认就有i2c0,那13860000是什么意思,到数据手册看一下。
可知,实际上Exynos4412支持9组i2c总线,即有SCL0, SDA0 、SCL1,SDA1… SCL8,SDA8九组i2c总线。
下面以MPU6050为例看一下,i2c从设备与SOC的硬件相关信息
MPU6050对应第6组i2c总线,i2c的控制器基地址为0x138B_0000
MPU6050: 从设备地址是0x68
SOC MPU6050
GPB_3 — I2C_SCL5
GPB_2 — 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 // 模板
i2c_0: i2c@13860000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "samsung,s3c2440-i2c";
reg = <0x13860000 0x100>;
interrupts = <0 58 0>;
clocks = <&clock 317>;
clock-names = "i2c";
pinctrl-names = "default";
pinctrl-0 = <&i2c0_bus>;
status = "disabled"; //状态位disable,
}; //在内核中不会自动执行adapter控制器的代码
i2c_5: i2c@138B0000 {
#address-cells = <1>;
#size-cells = <0>;
compatible = "samsung,s3c2440-i2c";
reg = <0x138B0000 0x100>;
interrupts = <0 63 0>;
clocks = <&clock 322>;
clock-names = "i2c";
status = "disabled";
}
回到exynos4412-fs4412.dts
描述从设备信息的设备树的模板 :exynos4412-fs4412.dts
i2c@13860000 {
#address-cells = <1>;
#size-cells = <0>;
samsung,i2c-sda-delay = <100>;
samsung,i2c-max-bus-freq = <20000>;
pinctrl-0 = <&i2c0_bus>;
pinctrl-names = "default";
status = "okay"; //状态okay
//子节点:从设备信息--电源管理芯片
s5m8767_pmic@66 {
compatible = "samsung,s5m8767-pmic";
reg = <0x66>;
//对应/sys/bus/i2c/devices/i2c_0 -->0_0066
}
}
在内核目录/sys/bus/i2c/devices/i2c_0目录下
3.4 新增加i2c从设备
在 arch/arm/boot/dts/exynos4412-fs4412.dts 增加i2c5控制器和它包含了从设备
- 确定i2c从设备的从设备号
看到MPU6050数据手册
mpu6050的AD0接地,低电平,故7位的从设备地址为b 110 1000 --> 0x68
- 确定slave名字 – 厂商+型号 – invensense ,mpu6050
i2c@138B0000 {/*i2c adapter5信息*/
#address-cells = <1>;
#size-cells = <0>;
samsung,i2c-sda-delay = <100>;
samsung,i2c-max-bus-freq = <20000>;
pinctrl-0 = <&i2c5_bus>;
pinctrl-names = "default";
status = "okay";
mpu6050@68 { /*i2c client信息 查数据手册得从设备号为68*/
compatible = "invensense,mpu6050";
reg = <0x68>;
};
};
保存后,make dtbs ,更新设备树文件。
打开内核目录下平台设备的设备文件
进入adapter 138b0000.i2c
再查看i2c总线下是否有从设备
设置好设备树中的硬件信息,就可以开始写驱动了。
四、i2c driver驱动的编写
a. 添加i2c client的信息,必须包含在控制器对应的节点中
b. 直接编写i2c driver
1. 构建i2c driver,并注册到i2c总线
2. 实现probe:
|
申请设备号,实现fops
创建设备文件
通过i2c的接口去初始化i2c从设备
几个常用的对象:
struct i2c_driver {//表示是一个从设备的驱动对象
int (*probe)(struct i2c_client *, const struct i2c_device_id *);
int (*remove)(struct i2c_client *);
struct device_driver driver; //继承了父类
|
const struct of_device_id *of_match_table;
const struct i2c_device_id *id_table;//用于做比对,非设备树的情况
}
注册和注销
int i2c_add_driver( struct i2c_driver *driver);
void i2c_del_driver(struct i2c_driver *driver);
struct i2c_client {//描述一个从设备的信息,不需要在代码中创建,因为是由i2c adapter帮我们创建
unsigned short addr; //从设备地址,来自于设备树中<reg>
char name[I2C_NAME_SIZE]; //用于和i2c driver进行匹配,来自于设备树中compatible
struct i2c_adapter *adapter;//指向当前从设备所存在的i2c adapter
struct device dev; // 继承了父类
};
创建i2c client的函数
struct i2c_client *i2c_new_device(struct i2c_adapter *adap, struct
i2c_board_info const *info)
struct i2c_adapter {//描述一个i2c控制器,也不是我们要构建,原厂的代码会帮我们构建
const struct i2c_algorithm *algo; //算法
|
int (*master_xfer)(struct i2c_adapter *adap, struct i2c_msg *msgs,int num);
struct device dev; //继承了父类,也会被加入到i2c bus
int nr; //编号
}
注册和注销:
int i2c_add_adapter(struct i2c_adapter * adapter);
void i2c_del_adapter(struct i2c_adapter * adap);
struct i2c_msg {//描述一个从设备要发送的数据的数据包
__u16 addr; //从设备地址,发送给那个从设备
__u16 flags; //读1还是写0
__u16 len; //发送数据的长度
__u8 *buf; //指向数据的指针
};
//写从设备
int i2c_master_send(const struct i2c_client * client,const char * buf,int count)
//读从设备
int i2c_master_recv(const struct i2c_client * client,char * buf,int count)
以上两个函数都调用了:
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
4.1 陀螺仪和加速度工作原理
陀螺仪的作用:
原理:
小时候玩过陀螺,如果给它一定的旋转速度,陀螺会竖立旋转起来而不会倒,
主要因为高速旋转有抗拒方向改变的趋向
陀螺仪就是内部的转子高速旋转,形成一个固定的初始化的参考平面,
这样就可以通过测量初始的参考平面偏差计算出物体的旋转情况
陀螺仪的强项在于测量设备自身的旋转运动
陀螺仪的产生:
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,计步器功能
4.2 mpu6050的寄存器信息和设置
寄存器:
#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(正常启用)
4.3 ioctl: 给驱动发送不同指令
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:传给驱动数据类型
4.4 代码实现
4.4.1 mpu6050_drv.c
#include <linux/init.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/i2c.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include "mpu6050.h"
#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(正常启用)
//设计一个全局的设备对象
struct mpu_sensor{
int dev_major;
struct device *dev;
struct class *cls;
struct i2c_client *client; //记录probe中的client
};
struct mpu_sensor *mpu_dev;
int mpu6050_write_bytes(struct i2c_client * client,char * buf,int count)
{
int ret;
struct i2c_adapter *adapter = client->adapter;
struct i2c_msg msg;
msg.addr = client->addr;
msg.flags = 0;
msg.buf = buf;
msg.len = count;
ret = i2c_transfer(adapter,&msg,1);
return ret == 1?count:ret;
}
int mpu6050_read_bytes(struct i2c_client * client,char * buf,int count)
{
int ret;
struct i2c_adapter *adapter = client->adapter;
struct i2c_msg msg;
msg.addr = client->addr;
msg.flags = 1;
msg.buf = buf;
msg.len = count;
ret = i2c_transfer(adapter,&msg,1);
return ret == 1?count:ret;
}
//读取某个特定寄存器的地址,然后返回值
int mpu6050_read_reg_byte(struct i2c_client * client,char reg)
{
// 先写寄存器的地址
int ret;
struct i2c_adapter *adapter= client->adapter;
struct i2c_msg msg[2];
char rxbuf[1];
msg[0].addr = client->addr;
msg[0].flags = 0;
msg[0].len = 1;
msg[0].buf = ®
//然后在读寄存器的值
msg[1].addr = client->addr;
msg[1].flags = 1;
msg[1].len = 1;
msg[1].buf = rxbuf;
ret = i2c_transfer(adapter,msg,2);
if(ret < 0)
{
printk("%s error \n",__FUNCTION__);
return ret;
}
return rxbuf[0];
}
int mpu6050_dv_open (struct inode *inode, struct file *filp)
{
return 0;
}
int mpu6050_drv_close (struct inode *inode, struct file *filp)
{
return 0;
}
long mpu6050_drv_ioctrl (struct file *filp, unsigned int cmd, unsigned long args)
{
union mpu6050_data data;
int ret;
switch(cmd)
//读数据
{
case IOC_GET_ACCEL:
data.accel.x = mpu6050_read_reg_byte(mpu_dev->client,ACCEL_XOUT_L);
data.accel.x |= mpu6050_read_reg_byte(mpu_dev->client,ACCEL_XOUT_H)<<8;
data.accel.y = mpu6050_read_reg_byte(mpu_dev->client,ACCEL_YOUT_L);
data.accel.y |= mpu6050_read_reg_byte(mpu_dev->client,ACCEL_YOUT_H)<<8;
data.accel.z = mpu6050_read_reg_byte(mpu_dev->client,ACCEL_ZOUT_L);
data.accel.z |= mpu6050_read_reg_byte(mpu_dev->client,ACCEL_ZOUT_H)<<8;
break;
case IOC_GET_GYRO:
data.gyro.x = mpu6050_read_reg_byte(mpu_dev->client,GYRO_XOUT_L);
data.gyro.x |= mpu6050_read_reg_byte(mpu_dev->client,GYRO_XOUT_H)<<8;
data.gyro.y= mpu6050_read_reg_byte(mpu_dev->client,GYRO_YOUT_L);
data.gyro.y|= mpu6050_read_reg_byte(mpu_dev->client,GYRO_YOUT_H)<<8;
data.gyro.z= mpu6050_read_reg_byte(mpu_dev->client,GYRO_ZOUT_L);
data.gyro.z|= mpu6050_read_reg_byte(mpu_dev->client,GYRO_ZOUT_H)<<8;
break;
case IOC_GET_TEMP:
data.temp = mpu6050_read_reg_byte(mpu_dev->client,TEMP_OUT_L);
data.temp |= mpu6050_read_reg_byte(mpu_dev->client,TEMP_OUT_H)<<8;
break;
default:
printk("invalid cmd\n");
return -EINVAL;
}
ret = copy_to_user((void __user *)args,&data,sizeof(data));
if(ret > 0)
{
printk ("%s error \n",__FUNCTION__);
return -1;
}
return 0;
}
const struct file_operations mpu6050_fops = {
.open = mpu6050_dv_open,
.release = mpu6050_drv_close,
.unlocked_ioctl = mpu6050_drv_ioctrl,
};
int mpu6050_drv_prob(struct i2c_client *client, const struct i2c_device_id *id)
{
printk("-----%s----\n", __FUNCTION__);
/*
申请设备号,实现fileoperation
创建设备文件
通过i2c接口去初始化i2c丛设备
*/
mpu_dev = kzalloc(sizeof(struct mpu_sensor),GFP_KERNEL);
mpu_dev->client = client;
mpu_dev->dev_major = register_chrdev(0,"mpu_drv",&mpu6050_fops);
mpu_dev ->cls = class_create(THIS_MODULE,"mpu_cls");
mpu_dev ->dev = device_create(mpu_dev ->cls,NULL,
MKDEV(mpu_dev->dev_major,0),NULL,"mpu_sensor");
//初始化i2c从设备
char buf1[2] = {PWR_MGMT_1, 0x0};
mpu6050_write_bytes(mpu_dev->client, buf1, 2);
char buf2[2] = {SMPLRT_DIV, 0x07};
mpu6050_write_bytes(mpu_dev->client, buf2, 2);
char buf3[2] = {CONFIG, 0x06};
mpu6050_write_bytes(mpu_dev->client, buf3, 2);
char buf4[2] ={GYRO_CONFIG, 0x18};
mpu6050_write_bytes(mpu_dev->client, buf4, 2);
char buf5[2] = {ACCEL_CONFIG, 0x01};
mpu6050_write_bytes(mpu_dev->client, buf5, 2);
return 0;
}
int mpu6050_drv_remove(struct i2c_client *client)
{
device_destroy(mpu_dev ->cls,mpu_dev ->dev);
class_destroy(mpu_dev ->cls);
unregister_chrdev(mpu_dev->dev_major,"mpu_drv");
kfree(mpu_dev);
}
const struct of_device_id of_mpu6050_id[] = {
{.compatible = "invensense,mpu6050"},
{},
};
const struct i2c_device_id mpu_id_table[] = {
{"mpu60xx_drv",0x1234567},
{},
};
struct i2c_driver mpu6050_drv = {
.probe = mpu6050_drv_prob,
.remove = mpu6050_drv_remove,
.driver = {
.name = "lin_mpu6050_drv",//随便写,/sys/bus/i2c/driver/lin_mpu6050_drv
.of_match_table = of_match_ptr(of_mpu6050_id),
},
.id_table = mpu_id_table,//可以不写,为了非设备树匹配
};
static int __init mpu6050_drv_init(void)
{
//1.构建i2c driver,并注册到i2c总线
return i2c_add_driver(&mpu6050_drv);
}
static void __exit mpu6050_drv_exit(void)
{
i2c_del_driver(&mpu6050_drv);
}
module_init(mpu6050_drv_init);
module_exit(mpu6050_drv_exit);
MODULE_LICENSE("GPL");
4.4.2 mpu6050.h
#ifndef __MPU6050_H__
#define __MPU6050_H__
union mpu6050_data{
struct {
short x;
short y;
short z;
}accel;
struct {
short x;
short y;
short z;
}gyro;
short temp;
};
#define IOC_GET_ACCEL _IOR('M',0x34,union mpu6050_data)
#define IOC_GET_GYRO _IOR('M',0x35,union mpu6050_data)
#define IOC_GET_TEMP _IOR('M',0x36,union mpu6050_data)
#endif
4.4.3 mpu6050_test.c
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "mpu6050.h"
int main(int argc, const char *argv[])
{
int fd;
union mpu6050_data data;
fd = open ("/dev/mpu_sensor",O_RDWR);
if(fd < 0)
{
perror("open\n");
exit(1);
}
while(1)
{
ioctl(fd,IOC_GET_ACCEL, &data);
printf("accel data : x = %d , y = %d , z = %d \n",data.accel.x,data.accel.y,data.accel.z);
ioctl(fd,IOC_GET_GYRO, &data);
printf("gyro data : x = %d , y = %d , z = %d \n",data.gyro.x,data.gyro.y,data.gyro.z);
ioctl(fd,IOC_GET_TEMP,&data);
printf("temp data = %d \n",data.temp);
printf("********************************\n");
sleep(1);
}
return 0;
}
~
4.4.4 Makefile
CROSS_COMPILE=arm-none-linux-gnueabi-
CC=$(CROSS_COMPILE)gcc
KERNEL_DIR=/home/linux/fs4412/kernel/linux-3.14
CUR_DIR=$(shell pwd)
ROOTFS_DIR=/home/linux/fs4412/share/rootfs
MODULE_NAME=mpu6050_drv
APP_NAME=mpu6050_test
ifeq ($(KERNELRELEASE),)
all:
make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
$(CC) $(APP_NAME).c -o $(APP_NAME)
clean:
make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
install:
cp *.ko $(APP_NAME) $(ROOTFS_DIR)/drv_module
else
obj-m += $(MODULE_NAME).o
endif
参考资料
IIC总线驱动框架