i2c总线之通信原理及linux驱动中读写时序的实现

一、I2C简介
I2C使用两条线在主控制器和从机之间通信,SCL(串行时钟线)和SDA(串行数据线),这两条线需接5~10欧上拉电阻,总线空闲空闲时,SCL和SDA处于高电平,I2C总线标准模式速度可以达到100K/S,快速模式可以达到400K/S。
一个I2C总线可以挂多个I2C从设备,通过I2C器件地址去识别不同的设备,如图
在这里插入图片描述
二、I2C通信原理
1.起始位
在这里插入图片描述
2.停止位
在这里插入图片描述
3.数据传输:I2C在数据传输时要保证SCL高电平期间,SDA数据稳定,因此SDA上数据变化只能SCL低电平期间发生,如图
在这里插入图片描述
4.应答信号:当I2C主机发送完8位数据后,将SDA设置为输入状态,等待I2C从机应答,应答信号由从机发出,主机需要提供应答信号所需时钟,主机发送完8位数据后,紧跟一个时钟信号,就是给应答信号提供的,从机将SDA拉低来表示发出应答信号,表示通信成功,否则表示通信失败,I2C总线是单字节读写时序
5.I2C写时序
在这里插入图片描述
在这里插入图片描述

  • 开始信号
  • 发送 I2C 设备地址,每个 I2C 器件都有一个设备地址,通过发送具体的设备地址来决定访问哪个I2C 器件。这是一个8位的数据,其中高7位是设备地址,最后1位是读写位,为1的话表示这是一个读操作,为0的话表示这是一个写操作。
  • I2C 器件地址后面跟着一个读写位,为0表示写操作,为1表示读操作。
  • 从机发送的 ACK 应答信号。
  • 重新发送开始信号。
  • 发送要写写入数据的寄存器地址。
  • 从机发送的 ACK 应答信号。
  • 发送要写入寄存器的数据
  • 从机发送的 ACK 应答信号。
  • 停止信号。

6.I2C读时序:读时序分为4大步,第一步是发送设备地址,第二步是发送要读取的寄存器地址,第三步重新发送设备地址,最后一步就是I2C从器件输出要读取的寄存器值
在这里插入图片描述
在这里插入图片描述

  • 主机发送起始信号。
  • 主机发送要读取的I2C从设备地址。
  • 读写控制位,因为是向I2C从设备发送数据,因此是写信号0。
  • 从机发送的ACK应答信号。
  • 重新发送START信号。
  • 主机发送要读取的寄存器地址。
  • 从机发送的ACK应答信号。
  • 重新发送START信号。
  • 重新发送要读取的I2C从设备地址。
  • 读写控制位,这里是读信号1,表示接下来是从I2C从设备里面读取数据。
  • 从机发送的ACK应答信号。
  • 从 I2C 器件里面读取到的数据。
  • 主机发出NO ACK信号,表示读取完成,不需要从机再发送ACK信号了。
  • 主机发出STOP信号,停止I2C通信

三、代码实现I2C读写时序

#include <linux/i2c.h>
static int i2c_write(struct i2c_adapter *i2c_adap,unsigned int address,
			unsigned int len, unsigned char const *data)
{
	struct i2c_msg msgs[1];
	int ret;
	if(!data || !i2c_adap){
		printk("---error---\n");
	}
	msgs[0].addr=address;
	msgs[0].flags=0; //write flag
	msgs[0].buf=(unsigned char *)data;
	msgs[0].len=len;
	/*i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)
		num:消息数量,也就是 msgs 的数量
		返回值:负值,失败,其他非负值,发送的 msgs 数量*/
	ret=i2c_transfer(i2c_adap,msgs,1);
	if (ret == 1)
		return 0;
	else if(ret == 0)
		return -EBUSY;
	else
		return ret;
}

static int i2c_read(struct i2c_adapter *i2c_adap,
			unsigned int address, unsigned int reg,
			unsigned int len, unsigned char *data)
{
	struct i2c_msg msgs[2];
	int ret;
	if(!data || !i2c_adap){
		printk("---error---\n");
	}	
	//发送要读取的寄存器首地址
	msgs[0].addr=address;
	msgs[0].flags=0; //write flag
	msgs[0].buf=&reg; //读取的首地址
	msgs[0].len=1;
	
	//读取寄存器数据
	msgs[1].addr=address;
	msgs[1].flags=I2C_M_RD; //read flag
	msgs[1].buf=data;
	msgs[1].len=len; //读取的数据长度
	
	ret=i2c_transfer(i2c_adap,msgs,2); 
	if (ret == 2)
		return 0;
	else if(ret == 0)
		return -EBUSY;
	else
		return ret;
}

在学习中进步,如有错误,请多多批评指正

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

CodeAmmon

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值