文章目录
-
- linux I2C子系统框架
- 在设备树中添加从设备信息,mpu5060
- I2C driver 程序的编写
- mpu6050 I2C程序具体实现
linux I2C子系统框架
在之前的一篇文章中记录了 I2C协议相关内容,这里记录一下I2C在linux系统上I2C子系统框架,经过总结,I2C子系统框架可以总结为如下一张图
五层(其实是三层,不算应用层和硬件层)
- 应用层
-
i2c driver层:从设备驱动层
需要和应用层交互
封包数据,不知道数据是如何写入到硬件
-
i2c 核心层:drivers/i2c/i2c-core.c
维护ic总线,包括i2c driver,i2c client链表
-
i2c adapter层:drivers/i2c/busses/i2c-s3c2410.c i2c 控制层,初始化 i2c 控制器
完成将数据写入或读取 ---------从设备硬件
不知道数据具体是什么,但是知道如何操作从设备
-
硬件层
确保 i2c core 和 i2c adapter 侧面和必须编译进内核:
在设备树中添加从设备信息,mpu5060
-
I2C子系统中涉及到的设备树文件
i2c 控制器地址:
0x1386_0000,
0x1387_0000,
0x1388_0000,
0x1389_0000,
0x138A_0000,
0x138B_0000, ------MPU5060
0x138C_0000,
0x138D_0000,
0x138E_0000,
其中MPU5060:从设备地址是 0x68
soc GPB_3 ---I2C_SCL5
GPB_3 --- I2C_SCL5
GPX_3 --- GYRO_INT
-
模板:
控制器中对应的设备树:arch/arm/boot/dts/exynos4.dtsi
- 新增加 i2c 从设备,/arch/arm/boot/dts/exynos4412-fs4412.dts 增加 i2c 控制和它包含的从设备
i2c@138B0000 {
#address-cells = <1>'
#size-cells = <0>;
samsung,i2c-sda-delay = <100>;
samsung,i2c-max-bus = <20000>;
pinctrl-0 = <&i2c5_bus>;
pinctrl-names = "default";
status = "okay";
mpu6050@68 {
compatible = "invensense,mpu6050";
reg = <0x68>;
};
};
保存之后 make dtbs
I2C driver 程序的编写
- I2C driver 程序的编写
- 添加 i2c client 的信息,必须在控制器对应节点中
-
直接编写 i2c driver
(1)构建 i2c driver,并注册到 i2c 总线
(2)实现 probe方法:
a.申请设备号
b.创建设备文件
c.通过 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_if *of_match_table;
const struct i2c_device_id *id_table; //用于作比对,非设备树情况 与i2c_client
};
//注册和注销i2c_driver
//注册
int i2c_add_driver( struct i2c_driver *driver);
//删除
void i2c_del_driver(struct i2c_driver *);
struct i2c_client //描述一个从设备的信息,不需要在代码中创建,因为是由i2c_adapter构建
{
unsigned short flags; /* div., see below */
unsigned short addr; //从设备地址,来自于设备树中的 <reg>
char name[I2C_NAME_SIZE]; //用于和i2c_driver进行匹配,来自于设备树中的 compatible
struct i2c_adapter *adapter; //指向当前从设备存在的i2c_adapter
struct device dev; //继承了父类
int irq;
struct list_head detected;
};
//创建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; //编号,类似i2c_5
};
//注册和注销 i2c_adapter
int i2c_add_adapter(struct i2c_adapter *);
void i2c_del_adapter(struct i2c_adapter *);
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);
//以上两个函数都调用了:i2c_transfer
int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num);
mpu6050 I2C程序具体实现
- 头文件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,mpu6050_data)
#define IOC_GET_GYRO _IOR('M',0x35,mpu6050_data)
#define IOC_GET_TEMP _IOR('M',0x36,mpu6050_data)
#endif
- mpu6050 (陀螺仪)驱动程序设计
#include <linux/init.h>
#include <linux/module.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_gpio.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "mpu6050.h"
#define SMPLRT_DIV 0x19 //采样频率寄存器 - 25 典型值:0x07(125HZ)
#define CONFIG 0x1A //配置寄存器 -26 典型值:0x06(5HZ)
#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
#define ACCEL_YOUT_L 0x3E
#define ACCEL_ZOUT_H 0x3F
#define ACCEL_ZOUT_L 0x40
#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
#define PWR_MGMT_1 0x6B //电源管理 典型值:0x00(正常启用)
//设计一个全局的设备对象
struct mpu_senor
{
int dev_major;
struct device *dev;
struct class *cls;
struct i2c_client *client; //记录probe中的client
};
struct mpu_senor *mpu_dev;
//mpu6050 i2c写数据
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.len = count;
msg.buf = buf;
ret = i2c_transfer(adapter,&msg,1);
return ret == 1 ? count : ret;
}
//mpu6050 i2c读数据
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 |= I2C_M_RD;
msg.len = count;
msg.buf = buf;
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("i2c_transfer read error\n");
return ret;
}
return rxbuf[0];
}
int mpu6050_drv_open(struct inode *inode,struct file *filp)
{
return 0;
}
int mpu6050_drv_release(struct inode *inode,struct file *filp )
{
return 0;
}
/*应用程序:ioctl给驱动发送不同的指令
ioctl(fd,cmd,args)
如何定义命令:
1,直接定义一个数字
#define IOC_GET_ACCEL 0x9999
2,通过系统的接口
_IO(x,y)
_IOR(x,y,z)
_IOW(x,y,z)
参数1:表示magic,字符
参数2:区分不同命令,整数
参数3:传给驱动数据类型
*/
long mpu6050_drv_ioctl(struct file *filp,unsigned int cmd,unsigned long args)
{
union mpu6050_data data;
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;
}
//将所有数据交给用户
if(copy_to_user((void __user *)args,&data,sizeof(data))>0)
{
return -EFAULT;
}
return 0;
}
const struct file_operations mpu6050_fops = {
.open = mpu6050_drv_open,
.release = mpu6050_drv_release,
.unlocked_ioctl = mpu6050_drv_ioctl,
};
int mpu6050_drv_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
/*实现 probe方法:
a.申请设备号
b.创建设备文件
c.通过 i2c 的接口去初始化 i2c 从设备
*/
//分配空间
mpu_dev = kzalloc(sizeof(struct mpu_senor),GFP_KERNEL);
//记录下client
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");
//初始化各种寄存器
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);
//i2c_master_send(client, const char * buf, int count);
}
int mpu6050_drv_remove(struct i2c_client *client)
{
//释放各种资源
class_destroy(mpu_dev->cls);
unregister_chrdev(mpu_dev->dev_major,"mpu_drv");
device_destroy(mpu_dev->cls,MKDEV(mpu_dev->dev_major,0));
kfree(mpu_dev);
}
const struct of_device_id of_mpu6050_id[] = {
{
.compatible = "invensense,mpu6050",
},
{/*nothing to be done*/}
};
const struct i2c_device_id mpu_id_table[] = {
{"mpu6050_drv",0x1111},
{/*nothing to be done*/}
};
struct i2c_driver mpu6050_drv = {
.probe = mpu6050_drv_probe,
.remove = mpu6050_drv_remove,
.driver = {
.name = "mpu6050_drv"; //随便写, /sys/bus/i2c/driver/
.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");
- mpu6050 (陀螺仪)应用程序设计
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <linux/input.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "mpu6050.h"
int main(int argc,char *argv[])
{
union mpu6050_data data;
int fd;
fd = open("/dev/mpu_sensor",O_RDWR);
if(fd < 0)
{
perror("open");
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);
sleep(1);
}
close(fd);
return 0;
}