驱动———IIC子系统(二)

总结一下编程步骤。
我们只需要编写drv的代码即调用层的代码。
再调用层做的事情就是,把drv注册到iIC bus里面,然后实现按照iic的总线通信协议,进行数据交互。
具体步骤。

1)编写设备树文件,根据芯片手册,找到我们的硬件连接在第几组iic总线,以及地址,中断等信息。
2)实例化i2c_driver结构体并把它注册到drv总线里面去。
注册成功后,匹配完成就会调用i2c_driver结构体的probe方法
我们需要在probe方法中:
3)申请设备号,以及实现file_operation
4)创建设备节点
这样我们一个设备就创建好了。

然后我们还需要通过i2c的接口去初始化i2c从设备
数据的发送的接收依赖于iic_transfor这个接口。按照ii2总线的通信协议步骤去实现数据的发送和接收。

iic _transform的原理就是——在probe方法中拿到iic_client,然后通过iic_client找到iic_adepter,再把自己里面存储数据的iic_msg的信息结构体,给到iic_adepter。这样iic_adepter就可以根据提供的信息,利用自己结构体里面的一个算法alogitim以及控制iic通信的master_xop这个函数,来实现通信。

5)所以我们还要实现iic_msg,并把iic_msg给到iic _transform

6)把初始化的值给到寄存器。

7)读取寄存器的值。
如果利用read的文件接口我们就不能指定读什么了,只能写死的数据。比如我用个一个FIFO来接受数据,那数据的类型,只能是我怎么写的它就怎么读,但是,然后我是mpu6050我只想读加速度,或者都想都,这就不能同时实现
所以我们引入了 ioctl 这个文件操作。和read类似,但是可以根据设定的值来确定想读的数据。

自此我们要编写的驱动模块完成
———————————————————————代码—————————————————————————

#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>



/************************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,可以配置自检和满量程范围
							//典型值:x180(不自检,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(正常启用)

/************************************************************************/


//数据共用体
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', 0x01,union mpu6050_data)     //系统接口定义命令1
#define IOC_GET_GYRO  _IOR('M', 0x02,union mpu6050_data)		//参数1 魔幻数     	参数二 命令值 参数三数据类型
#define IOC_GET_TEMP  _IOR('M', 0x03,union mpu6050_data)




//设备信息结构体
struct mpu_sensor {
	unsigned int major;
	struct class *cls;
	struct device *dev;
	struct i2c_client *client;

};

struct mpu_sensor * mpu_dev;





//我们自己写一个和i2c_master_send一样的函数
int mpu_write_bytes (const struct i2c_client * client, const char * buf, int count){


//	printk("---------%s---------\n",__FUNCTION__);

	int ret;
//i2c_adapter * adap
	struct i2c_adapter *adapter = client->adapter;    //client是由adapter创建,它里面也有一个adapter指针

//	struct i2c_msg * msgs
	struct i2c_msg  msg;

	msg.addr = client->addr;
	msg.flags = 0;     //写标志为 1为写      	0 为读 
	msg.len = count;		//数据大小
	msg.buf = buf;

//这个函数就是要实现这个接口去发送数据。
  ret = i2c_transfer(adapter, &msg, 1);    //发送一个数据包

 //返回值:如果失败返回负数,如果成功返回发送的数据个数
	
	return ret ==1?count:ret; 
}

int mpu_read_bytes (const struct i2c_client * client, const 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;	   //读写标志为 1为写 0 为读 
	msg.len = count;		//数据大小
	msg.buf = buf;

	ret = i2c_transfer(adapter, &msg, 1);	 //发送一个数据包

	return ret ==1?count:ret;


}

//读取某个寄存器的值,时序是先把把地址写进行,然后在读
int mpu_read_reg_bytes (const struct i2c_client * client, char reg){

	int ret;
	struct i2c_adapter *adapter = client->adapter;
	struct i2c_msg msg[2];

	char rbuf[1];

	msg[0].addr = client->addr;
	msg[0].flags = 0;
	msg[0].len = 1;
	msg[0].buf = &reg;

	msg[1].addr = client->addr;
	msg[1].flags = I2C_M_RD;
	msg[1].len = 1;
	msg[1].buf = rbuf;    //把寄存器里面的值存储到rxbuf里面
	
	ret = i2c_transfer(adapter, msg,  2);
	if(ret < 0)
	{
		printk("i2c_transfer read error\n");
		return ret;
	}


	//返回的是读到的值
	return rbuf[0];




}






int mpu_fops_open (struct inode *inode, struct file *filp){

	return 0;
}
int mpu_fops_release (struct inode *inode, struct file *filp){

	return 0;
}
long  mpu_fops_ioctl (struct file *filp, unsigned int cmd, unsigned long args){
		
	union mpu6050_data data;

	switch(cmd){
		case IOC_GET_ACCEL:
					data.accel.x = mpu_read_reg_bytes(mpu_dev->client,ACCEL_XOUT_L);
					data.accel.x |= mpu_read_reg_bytes(mpu_dev->client,ACCEL_XOUT_H) << 8;
		
					data.accel.y = mpu_read_reg_bytes(mpu_dev->client,ACCEL_YOUT_L);
					data.accel.y |= mpu_read_reg_bytes(mpu_dev->client,ACCEL_YOUT_H) << 8;

					data.accel.z = mpu_read_reg_bytes(mpu_dev->client,ACCEL_ZOUT_L);
					data.accel.z |= mpu_read_reg_bytes(mpu_dev->client,ACCEL_ZOUT_H) << 8;
					break;
		case IOC_GET_GYRO:
					data.gyro.x = mpu_read_reg_bytes(mpu_dev->client,GYRO_XOUT_L);
					data.gyro.x |= mpu_read_reg_bytes(mpu_dev->client,GYRO_XOUT_H) << 8;

					data.gyro.y = mpu_read_reg_bytes(mpu_dev->client,GYRO_YOUT_L);
					data.gyro.y |= mpu_read_reg_bytes(mpu_dev->client,GYRO_YOUT_H) << 8;

					data.gyro.z = mpu_read_reg_bytes(mpu_dev->client,GYRO_ZOUT_L);
					data.gyro.z |= mpu_read_reg_bytes(mpu_dev->client,GYRO_ZOUT_H) << 8;
					break;
		case IOC_GET_TEMP:
					data.temp = mpu_read_reg_bytes(mpu_dev->client,TEMP_OUT_L);
					data.temp |= mpu_read_reg_bytes(mpu_dev->client,TEMP_OUT_H) << 8;
					break;
		default:
			printk("invalid cmd\n");
			return -EFAULT;

		}

	if(copy_to_user((void __user * )args, &data, sizeof(data)) > 0)
	return -EFAULT;

	return 0;


}


const struct file_operations mpu_fops = {
	.open = mpu_fops_open,
	.release = mpu_fops_release,
	.unlocked_ioctl =  mpu_fops_ioctl,

};



int mpu_drv_probe(struct i2c_client *client , const struct i2c_device_id *id){

//	printk("---------%s---------\n",__FUNCTION__);
	/*匹配完成了就会调用这个proble方法。
			2,实现probe:
			|
			申请设备号,实现fops
			创建设备文件
			通过i2c的接口去初始化i2c从设备   */

	//记录,和drv匹配的client 由系统传参传过来
	
	
	//1.申请设备号,实现fops  我们需要一个设备信息结构体
	mpu_dev = kzalloc(sizeof(struct mpu_sensor), GFP_KERNEL);
	if(mpu_dev == NULL){
		printk("kzalloc error\n");
		return -ENOMEM;
		}
	mpu_dev->major = register_chrdev(0, "mpu_drv", &mpu_fops);      //proc/device
	if(mpu_dev->major < 0){
		printk("register_chrdev\n");
		goto err0;
		}
	//记录,和drv匹配的client 由系统传参传过来
	mpu_dev->client = client;
	//创建设备文件
	mpu_dev->cls = class_create(THIS_MODULE, "mpu_cls");
	
//	printk("----class_create---\n");
	
	if(mpu_dev->cls == NULL){
	printk("class_create\n");
	goto err1;
		}
	mpu_dev->dev = device_create(mpu_dev->cls,NULL,MKDEV(mpu_dev->major,0),NULL, "MPU6050");
	if(mpu_dev->dev == NULL){
		printk("device_creat\n");
		goto err2;
		}
//	printk("----device_create---\n");

	//初始化通过i2c的接口去初始化i2c从设备。根据iic的通信协议去写
	//我们的目的是读三轴角速度和加速度的值,我们要先写mpu6050的一些设定标准的寄存器的值

	//i2c_master_send(const struct i2c_client * client, const char * buf, int count) 
	//这个是系统自带的接口,把buf送到client里面
/*	char buf[4][1]= {{PWR_MGMT_1,0x00},{SMPLRT_DIV ,0x07 },{CONFIG,0x06},
						{GYRO_CONFIG,0x180},{ACCEL_CONFIG,0x01}};

	mpu_write_bytes (mpu_dev->client, buf[0], 2);
	mpu_write_bytes (mpu_dev->client, buf[1], 2);
	mpu_write_bytes (mpu_dev->client, buf[2], 2);
	mpu_write_bytes (mpu_dev->client, buf[3], 2);
	mpu_write_bytes (mpu_dev->client, buf[4], 2);

	*/
	
	char buf1[2] = {PWR_MGMT_1, 0x0};
	mpu_write_bytes(mpu_dev->client, buf1, 2);

	char buf2[2] = {SMPLRT_DIV, 0x07};
	mpu_write_bytes(mpu_dev->client, buf2, 2);

	char buf3[2] = {CONFIG, 0x06};
	mpu_write_bytes(mpu_dev->client, buf3, 2);

	char buf4[2] ={GYRO_CONFIG, 0x18};
	mpu_write_bytes(mpu_dev->client, buf4, 2);

	char buf5[2] = {ACCEL_CONFIG, 0x01};
	mpu_write_bytes(mpu_dev->client, buf5, 2);			

	return 0;
	

err2:
	class_destroy(mpu_dev->cls);
err1:

	unregister_chrdev(mpu_dev->major, "mpu_6050");
err0:
	kfree(mpu_dev);
	return -EFAULT;

}
int mpu_drv_remove(struct i2c_client *client){

	device_destroy(mpu_dev->cls, MKDEV(mpu_dev->major, 0));
	unregister_chrdev(mpu_dev->major, "mpu_6050");
	class_destroy(mpu_dev->cls);

	kfree(mpu_dev);
	
}

const struct of_device_id	of_mpu6050_id[] ={
	
	//信息要和client里面的信息一样,即和设备树的信息一样
	{.compatible = "invensense,mpu6050",},
	{/*northing to be done*/},
};

const struct i2c_device_id mpu_id_table[] ={


	{"mpu6050_drv", 0x1111},
	{/*northing to be done*/},

};

struct i2c_driver mpu6050_drv = {
	.probe = mpu_drv_probe,
	.remove = mpu_drv_remove,  
	.driver = {
		.name = "mpu6050_drv",     //这个自定义 ,会在/sys/bus/i2c/driver显示
		.of_match_table = of_match_ptr(of_mpu6050_id),   //用于与client里面的信息比对进行匹配  											
		},
	.id_table = mpu_id_table, //非设备树环境下用这个匹配,但是我这里用了设备树,所以不填也可以
};



static int __init mpu6050_drv_init(void){
	printk("---------%s---------\n",__FUNCTION__);
	//把设备直接注册到i2c bus中去,然后就会调用probe方法
	return i2c_add_driver(&mpu6050_drv);       //sys/bus/i2c/drives
	
}

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");

————————————————————应用程序读取数据——————————————————————

#include <stdio.h>
#include <string.h>

#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.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(0);
		}

	while(1){
	ioctl(fd, IOC_GET_ACCEL,&data);
	printf("a: x=%2d,y=%2d,z=%2d\n",data.accel.x,data.accel.y,data.accel.z);
	ioctl(fd, IOC_GET_GYRO,&data);
	printf("w: x=%2d,y=%2d,z=%2d\n",data.gyro.x,data.gyro.y,data.gyro.y);
	ioctl(fd, IOC_GET_TEMP,&data);
	printf("T=%2d F\n",data.temp);

	sleep(1);

		}

	close(fd);
	


}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值