i2c子系统-----mpu6050

1.

  I2C驱动开发:

        I2C从设备驱动开发

        硬件芯片:从设备

        cmos camera

        ts :电容触摸屏

        gsenor: 重力传感器

        eeprom: 存储设备

        HDMI:高清输出接口

2. 

 i2c子系统软件框架

        应用

------------------------------------------

i2c driver:从设备驱动层

         需要和应用层进行交互

         封包数据,不知道数据是如何写入到硬件

------------------------------------------

i2c 核心层:维护i2c 总线,包括i2c driver, i2c client链表

        drivers/i2c/i2c-core.c

---------------------------------------------------

i2c adapter层:i2c控制层,初始化i2c控制器

        完成将数据写入或读取-从设备硬件

        不知道数据具体是什么,但是知道如何操作从设备

drivers/i2c/busses/i2c-s3c2410.c

================================================================

确保i2c core和i2c adatper层必须编译进内核

3.

   I2c子系统中涉及到的设备树文件:

 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信息*/
                        compatible = "invensense,mpu6050";
                        reg = <0x68>;
				};
        };

4.

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)

5.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(正常启用)

6. ioctrl

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:传给驱动数据类型

7.

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

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 = &reg;


    //然后在读寄存器的值
    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");



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;
}
~                                                

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值