I2C

I2C时序

在这里插入图片描述

  • I2C是一种接口,硬件连接是通过SDA和SCL两条线,传输的时候遵从I2C协议。

  • 上图中:master主设备,由soc担当。SCL提供时钟,SDA提供数据。

  • 驱动是站在master角度读写设备的。所有的I2C从设备都在一条线上,每个从设备都有自己的从设备地址。当主设备需要访问从设备的时候,发送从设备地址。

  • 在scl为高电平时,SDA由高电平向低电平跳变,表示开始发送数据。发送8个bit,七个bit数据,最后一位为读写位。

  • 第九个周期的时候,SDA拉高,从设备回应ACK,把电平拉低表示回应。每次读写完成之后多有一个Ack表示是否读写成功。

  • 在scl为高电平时,SDA由低电平向高电平跳变,表示结束发送数据。

  • 设备接口,要遵循协议。读写是1/0表示。每个周期传递一个数据。

  • 在scl为高电平的时候sda数据才有效,

I2C驱动框架

在这里插入图片描述
图中I2C client(其实就是device)不是自己创建的,I2C driver才是自己创建的
i2c子系统软件框架

应用
------------------------------------------
i2c driver:从设备驱动层
	 	需要和应用层进行交互
	 	封包数据,不知道数据是如何写入到硬件
------------------------------------------
i2c 核心层:维护i2c 总线,包括i2c driver, i2c client链表
drivers/i2c/i2c-core.c
---------------------------------------------------
i2c adapter层:i2c控制层,初始化i2c控制器 (有个算法maste-xfer())
			完成将数据写入或读取-从设备硬件
			不知道数据具体是什么,但是知道如何操作从设备
drivers/i2c/busses/i2c-s3c2410.c

我们只需要编写I2C_driver和I2C_client
确保i2c core和i2c adatper层必须编译进内核:需要对内核进行配置

make menuconfig
	Device Drivers  --->
		-*- I2C support  ---> //编译i2c-core.c
			I2C Hardware Bus support  --->
				<*> S3C2410 I2C Driver // i2c-s3c2410.c


/sys/bus/i2c/
/sys/bus/i2c/devices/i2c-0 
			[root@farsight i2c-0]# cat name
					s3c2410-i2c
  • inux I2C从设备的设备树信息的添加

SOC中不一定有一组I2C接口,4412中有9组,通过查电路图,可以看到MPU6050接到第五组上边,有第0组开始。
i2c控制器地址
0x1386_0000,
0x1387_0000,
0x1388_0000,
0x1389_0000,
0x138A_0000,
0x138B_0000, ------ MPU6050
0x138C_0000,
0x138D_0000,
0x138E_0000

MPU6050: 从设备地

址是0x68
soc GPB_2— I2C_SCL5
GPB_3— I2C_SDA5
GPX3_3— GYRO_INT //确定设备什么时候动了。

在内核中默认就有了i2c0--13860000.i2c


模板:
	控制器对应的设备树: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";
    };

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


    };

描述从设备信息的设备树的模板
	arch/arm/boot/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";

            s5m8767_pmic@66 {
                    compatible = "samsung,s5m8767-pmic";
                    reg = <0x66>;
			}
				
	}

新增加i2c从设备,arch/arm/boot/dts/exynos4412-fs4412.dts增加i2c5控制和它包含的从设备设备
    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>;
			};
    };

	保存后make dtbs

	cp -raf arch/arm/boot/dts/exynos4412-fs4412.dtb /tftpboot/

I2Cdriver驱动的编写:
a, 添加i2c client的信息,必须包含在控制器对应的节点中
b,直接编写i2c driver
1,构建i2c driver,并注册到i2c总线
2,实现probe:
|
申请设备号,实现fops
创建设备文件
通过i2c的接口去初始化i2c从设备

几个常用的对象:
1.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;//of开头的,一般都表示设备树中的。
	const struct i2c_device_id *id_table;//用于做比对,非设备树的情况
}
注册和注销
	int i2c_add_driver( struct i2c_driver *driver);
	void i2c_del_driver(struct i2c_driver *);

2.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)

3.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);

4.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)
  • mpu6050的I2Cdriver驱动框架编写
#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.len = count;
	msg.buf = buf;
	
	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 = 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 = &reg;

		msg[1].addr = client->addr;
		msg[1].flags = I2C_M_RD;
		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_close(struct inode *inode, struct file *filp)
{
	return 0;
}

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_close,
	.unlocked_ioctl = mpu6050_drv_ioctl,

};

int mpu6050_drv_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
	printk("-----%s----\n", __FUNCTION__);
	/*
		申请设备号,实现fops
			创建设备文件
			通过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");
	
	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 mpu5060_drv_remove(struct i2c_client *client)
{
	printk("-----%s----\n", __FUNCTION__);
	device_destroy(mpu_dev->cls, MKDEV(mpu_dev->dev_major, 0));
	class_destroy(mpu_dev->cls);
	unregister_chrdev(mpu_dev->dev_major, "mpu_drv");
	kfree(mpu_dev);
	return 0;
}

const struct of_device_id  of_mpu6050_id[] = {
		{
			.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 = mpu6050_drv_probe,
	.remove = mpu5060_drv_remove,
	.driver = {
		.name = "mpu6050_drv",//随便写,/sys/bus/i2c/driver/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");
  • 0
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值