文章来源:http://blog.csdn.net/zqixiao_09/article/details/50917655
在此驱动的基础修改了一点东西和自己的注释
.dts:
i2c@138b0000 {
#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";
pmu6050-3-asix@68 {
compatible = "invense,mpu6050";
reg = <0x68>;
interrupt-parent = <&gpx3>;
interrupts = <3 2>;
};
};
mpu6050.h:
<span style="font-size:18px;"><span style="font-size:18px;">
#ifndef MPU6050_HHHH
#define MPU6050_HHHH
#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;
}qyro;
unsigned short temp;
};
/*ioctl的一些参数定义*/
#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
</span></span>
i2c_driver.c:
<span style="font-size:18px;"><span style="font-size:18px;">
#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/i2c.h>
#include<linux/cdev.h>
#include<linux/slab.h>
#include<linux/fs.h>
#include<linux/delay.h>
#include<asm/uaccess.h>
#include"mpu6050.h"
#define SMPLRT_DIV 0x19 /*采样率分频,典型值:0x07(125Hz)*/
#define CONFIG 0x1A /*低通滤波频率,典型值:0x06(5Hz)*/
#define GYRO_CONFIG 0x1B /*陀螺仪自检及测量范围,典型值:0x18(不自检,2000deg/s)*/
#define ACCEL_CONFIG 0x1C // 加速计自检、测量范围及高通滤波频率,典型值: 0x01(不自检, 2G, 5Hz) */
#define ACCEL_XOUT_H 0x3B // 存储最近的 X 轴、 Y 轴、 Z 轴加速度感应器的测量值 */
#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 // 存储最近的 X 轴、 Y 轴、 Z 轴陀螺仪感应器的测量值 */
#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(正常启用) */
#define MPU6050_MAJOR 500
#define MPU6050_MINOR 0
struct mpu6050_device
{
struct cdev cdev;
struct i2c_client *client;
};
struct mpu6050_device *mpu6050;
/*从从机client读数据到适配器*/
static int mpu6050_read_byte(struct i2c_client *client,unsigned char reg)
{
int ret;
char txbuf[1]={reg};/*reg是个变量啦*/
char rxbuf[1];
/*用i2c_msg结构来接收读写的消息*/
struct i2c_msg msg[2]=
{
{client->addr,0,1,txbuf}, /*addr,flag,len,&data*/
{client->addr,I2C_M_RD,1,rxbuf}
};
/*将从机client读来的消息发给适配器adapter*/
ret=i2c_transfer(client->adapter,msg,ARRAY_SIZE(msg));
if(ret<0)
{
printk("ret=%d/n",ret);
return ret;
}
return rxbuf[0];
}
/*适配器将数据发给从机client*/
static int mpu6050_write_byte(struct i2c_client *client,unsigned char reg,unsigned char val)
{
char txbuf[2]={reg,val};/*reg和val都是char型变量*/
struct i2c_msg msg[2]=
{
{client->addr,0,2,txbuf},
};
i2c_transfer(client->adapter,msg,ARRAY_SIZE(msg));
return 0;
}
static int mpu6050_open(struct inode *inode,struct file *file)
{
return 0;
}
static int mpu_6050 release(struct inode *inode,struct file *file)
{
return 0;
}
static long mpu6050_ioctl(struct file *file,unsigned int cmd,unsigned long arg)
{
union mpu6050_data data;
struct i2c_client *client=mpu6050->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;
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;
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("invalid argument\n");/*无效的参数*/
return -EINVAL;
}
/*拷贝数据给用户程序*/
if(copy_to_user((void *)arg,&data,sizeof(data)))
return -EFAULT;
return sizeof(data);
}
struct file_operations mpu6050_fops=
{
.owner =THIS_MODULE,
.open =mpu6050_open,
.release=mpu6050_release,
.unlocked_ioctl=mpu6050_ioctl,
};
static int mpu6050_probe(struct i2c_client *client,const struct i2c_device_id *id)
{
int ret;
dev_t devno=MKDEV(MPU6050_MAJOR,MPU6050_MINOR);
printk("match ok\n");
/*申请一个mpu6050_device结构体指针并赋值*/
mpu6050=kzalloc(sizeof(*mpu6050),GFP_KERNEL);
if(mpu6050==NULL)
{
return -ENOMEM;
}
mpu6050->client=client;
ret=register_chrdev_region(devno,1,"mpu6050");/*通过register_chrdev_region来获取设备号*/
if(ret<0)
{
printk("fail to register char device region\n");
goto err1;
}
/*通过cdev来描述字符设备,主要包含dev_t和file_operations*/
cdev_init(&mpu6050->cdev,&mpu6050_fops);/*建立cdev与fops的连接*/
mpu6050->cdev.owner=THIS_MODULE;
ret=cdev_add(&mpu6050->cdev,devno,1);/*注册该驱动*/
if(ret<0)
{
printk("fail to add device\n");
goto err2;
}
mpu6050_write_byte(client,PWR_MGMT_1,0x00);
mpu6050_write_byte(client,SMPLRT_DIV,0x07);
mpu6050_write_byte(client,CONFIG,0x06);
mpu6050_write_byte(client,GYRO_CONFIG,0xF8);
mpu6050_write_byte(client,ACCEL_CONFIG,0x19);
return 0;
err2:
unregister_chrdev_region(devno,1);
err1:
kfree(mpu6050);
return ret;
}
static int mpu6050_remove(struct i2c_client *client)
{
dev_t devno=MKDEV(MPU6050_MAJOR,MPU6050_MINOR);
cdev_del(&mpu6050->cdev);/*注销驱动*/
unregister_chrdev_region(devno,1);/*释放设备号*/
kfree(mpu6050);/*释放mpu6050_device申请的空间*/
return 0;
}
/*要挂载到i2c_driver设备表中的设备ID项*/
static const struct i2c_device_id mpu6050_id[]=
{
{"mpu6050",0},/*设备名和设备的位置*/
{}
};
/*通过of_device_id和设备树节点中的.compatible进行匹配*/
static struct of_device_id mpu6050_dt_match[]=
{
{.compatible="invense,mpu6050"},
{/*northing to be done*/},
};
/*填充i2c_driver这个结构体*/
struct i2c_driver mpu6050_driver=
{
.driver= /*device_driver--设备驱动结构体*/
{
.name="mpu6050",
.owner=THIS_MODULE,
.of_match_table=of_match_ptr(mpu6050_dt_math),
},
.probe=mpu6050_probe,
.remove=mpu6050_remove,
.id_table=mpu6050_id, /*设备ID表*/
};
static int __init mpu6050_init(void)
{
return i2c_add_driver(&mpu6050_driver);/*将该驱动挂在到i2c总线上*/
}
static void __exit mpu6050_exit(void)
{
i2c_del_driver(&mpu6050_driver); /*从总线上卸载该驱动*/
}
module_init(&mpu6050_init);
module_exit(&mpu6050_exit);
MODULE_LICENSE("GPL");
MOUDLE_AUTHOR("ever");
</span></span>
test.c:
<span style="font-size:18px;"><span style="font-size:18px;">
#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跟struct有点像,不过每次只能有一个数据类型*/
union mpu6050_data data;
fd=open("/dev/mpu6050",O_RDWR);
if(fd<0)
{
printf("fail open\n");
return 0;
}
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.accel.x,data.accel.y,data.accel.z);
sleep(1);
}
close(fd);
return 0;
}
</span></span>
Makefile:
<span style="font-size:18px;"><span style="font-size:18px;">
ifneq ($(KERNELRELEASE),)
obj-m:=i2c_dirver.o
else
KDIR:=/lib/modules/$(shell uname -r)/build
PWD:=$(shell pwd)
default:
make -C $(KDIR) M=$(PWD) modules
gcc -Wall -o test test.c
clean:
make -C $(KDIR) M=$(PWD) clean
endif
</span></span>