IIC详细解答+ 面试 + 代码

IIC背景

I2C总线是由Philips(飞利浦)公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。
在这里插入图片描述
飞利浦早在1920年就进入了中国市场。1985年设立第一家合资企业。飞利浦已成为中国电子行业最大的投资合作伙伴之一,累计投资总额超过34亿美元,在中国建立了35家合资及独资企业,在全国设有60多个办事处,共有19,000多名员工。2002年飞利浦因在华营业额和出口创汇额在全国外商投资企业中双双排名第一位而获中国外商投资企业协会颁发的年度“双高企业特殊贡献奖”。2003年公司经营业绩持续增长,营业额达到75亿美元,在华国际采购额达到38.3亿美元。

提炼部分(面试)

  • 两根线SDA(串行数据线)- SCL(串行时钟线)都是双向I/O线,开漏输出,上拉电阻接电源VCC,总线空闲时.两根线都是高电平
  • 任何器件既可以作为主机也可以作为从机,但同一时刻只允许有一个主机
  • 三种类型信号, 它们分别是:开始信号结束信号应答信号
  • 主设备发送一段数据后,从设备需要回应一个ACK
  • 由IIC地址决定,8位地址,减去1位广播地址,是7位地址,2^7=128,但是地址0x00(广播地址)不用,那就是127个地址, 所以理论上可以挂127个从器件
  • 电阻计算:https://blog.csdn.net/sternlycore/article/details/85851669
  • SDA数据线上的每个字节必须是8位,每次传输的字节数量没有限制。每个字节后必须跟一个响应位(ACK)
  • 数据传输时:SCL在高电平时候,SDA要稳定。
  • ACK 应答是SDA为低电平,NACK 不应答SDA为是高电平
  • 一个7-bit的地址是从最高位(MSB) 开始发送的,这个地址后面会紧跟1-bit(R/W)的操作符1表示读操作,0表示写操作。 接下来的一个bit是NACK/ACK,

(详解+代码)协议部分

IIC部分

void IIC_Init(void); //初始化 IIC 的 IO 口
void IIC_Start(void); //发送 IIC 开始信号
void IIC_Stop(void); //发送 IIC 停止信号
void IIC_Send_Byte(u8 txd); //IIC 发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC 读取一个字节
u8 IIC_Wait_Ack(void); //IIC 等待 ACK 信号
void IIC_Ack(void); //IIC 发送 ACK 信号
void IIC_NAck(void); //IIC 不发送 ACK 信号
void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);

初始化 IIC 的 IO 口

默认SDA和SCL都是高电平

//初始化IIC
void IIC_Init(void)
{					     
 	RCC->APB2ENR|=1<<3;		//先使能外设IO PORTB时钟 							 
	GPIOB->CRL&=0X00FFFFFF;	//PB6/7 推挽输出
	GPIOB->CRL|=0X33000000;	   
	GPIOB->ODR|=3<<6;     	//PB6,7 输出高
}

IIC 开始信号

在这里插入图片描述

void IIC_Start(void)
{
	SDA_OUT();     //sda线输出
	IIC_SDA=1;	  	  
	IIC_SCL=1;
	delay_us(4);
 	IIC_SDA=0;//START:when CLK is high,DATA change form high to low 
	delay_us(4);
	IIC_SCL=0;//钳住I2C总线,准备发送或接收数据 
}	

图+代码说明:两根线都是高电平。SDA先拉低并且维持4us SCL也拉低4us并准备发送数据

两根线最后的状态:

  • SDA = 0 低电平。
  • SCL = 0 低电平。

IIC发送一个字节

在这里插入图片描述
//IIC发送一个字节
//返回从机有无应答

void IIC_Send_Byte(u8 txd)
{                        
    u8 t;   
	SDA_OUT(); 	    
    IIC_SCL=0;//拉低时钟开始数据传输
    for(t=0;t<8;t++)
    {              
        IIC_SDA=(txd&0x80)>>7;
        txd<<=1; 	  
		delay_us(2);   //对TEA5767这三个延时都是必须的
		IIC_SCL=1;
		delay_us(2); 
		IIC_SCL=0;	   
		delay_us(2);
    }	 
} 	

数据有效性:上述代码可以由下图来解释
在这里插入图片描述
注意:IC器件读取数据, SCL高电平,SDA必须要稳定。

两根线最后的状态:

  • SDA = 0 低电平。
  • SCL = 0 低电平。

IIC 读一个字节

//读1个字节,ack=1时,发送ACK,ack=0,发送nACK   
u8 IIC_Read_Byte(unsigned char ack)
{
		unsigned char i,receive=0;
		SDA_IN();//SDA设置为输入
    for(i=0;i<8;i++ )
		{
        IIC_SCL=0; 
        delay_us(2);
				IIC_SCL=1;
        receive<<=1;
        if(READ_SDA)receive++;   
				delay_us(1); 
    }					 
    if (!ack)
        IIC_NAck();//发送nACK
    else
        IIC_Ack(); //发送ACK   
    return receive;
}

响应ACK和非响应NACK

在这里插入图片描述

在这里插入图片描述
可以从红色的竖线 进行理解。应答是低电平,不应答是高电平

//产生ACK应答
void IIC_Ack(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}
//不产生ACK应答		    
void IIC_NAck(void)
{
	IIC_SCL=0;
	SDA_OUT();
	IIC_SDA=1;
	delay_us(2);
	IIC_SCL=1;
	delay_us(2);
	IIC_SCL=0;
}		

两根线最后的状态:
应答ACK:

  • SDA = 0 低电平。
  • SCL = 0 低电平。
    不应答NACK:
  • SDA = 1 高电平。
  • SCL = 0 低电平。

IIC 停止信号

在这里插入图片描述

void IIC_Stop(void)
{
	SDA_OUT();//sda线输出
	IIC_SCL=0;
	IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
 	delay_us(4);
	IIC_SCL=1; 
	IIC_SDA=1;//发送I2C总线结束信号
	delay_us(4);							   	
}

很简单一句话来概括:停止信号是SCL 在高电平的时候 ,SDA拉高。
而开始信号是 SDA先拉低,SCL紧接拉低。

实操部分 驱动AT24C01

打开芯片数据手册
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

主机写数据-从机接收

首先主机发送START条件+从机地址+R/W=0(写操作,设置为0),从机读取到该地址后回应ACK,主机将继续发送需要操作的寄存器地址,从机继续回应ACK,表示从机准备完毕。之后主机发送寄存器的数据(可能是1byte也可能是多个byte),每个byte从机都会回应ACK,发送完成后,主机发送STOP命令,将总线释放,完成写操作。如下图示意:
在这里插入图片描述


```c
void AT24CXX_WriteOneByte(u16 WriteAddr,u8 DataToWrite)
{				   	  	    																 
    IIC_Start();  
	if(EE_TYPE>AT24C16)
	{
		IIC_Send_Byte(0XA0);	    //发送写命令
		IIC_Wait_Ack();
		IIC_Send_Byte(WriteAddr>>8);//发送高地址	  
	}else IIC_Send_Byte(0XA0+((WriteAddr/256)<<1));   //发送器件地址0XA0,写数据 	 
	IIC_Wait_Ack();	   
    IIC_Send_Byte(WriteAddr%256);   //发送低地址
	IIC_Wait_Ack(); 	 										  		   
	IIC_Send_Byte(DataToWrite);     //发送字节							   
	IIC_Wait_Ack();  		    	   
    IIC_Stop();						//产生一个停止条件 
	delay_ms(10);	 				//EEPROM的写入速度比较慢,加入延迟
} 

主机读数据-从机发送

读数据与写数据相似,但读数据会多几个步骤。要想从从机读取数据,首先要知道从机地址以及寄存器地址,这两部需要进行读操作来实现,和读操作一致。读操作完成后,主机发送重复开始+从机地址+R/W=1(读操作,设置为1),从机返回ACK,此时主机释放SDA线转由从机控制,主机读取SDA总线进行数据接收,每发送1 byte数据,主机会响应ACK表示还需要再接收数据。当主机接收完想要的数据后,主机将会返回NACK,告诉从机释放SDA总线,随后主机发送STOP命令,将总线释放,完成读操作。如下图示意:
在这里插入图片描述

u8 AT24CXX_ReadOneByte(u16 ReadAddr)
{				  
	u8 temp=0;		  	    																 
    IIC_Start();  
	if(EE_TYPE>AT24C16)
	{
		IIC_Send_Byte(0XA0);	   //发送写命令
		IIC_Wait_Ack();
		IIC_Send_Byte(ReadAddr>>8);//发送高地址	    
	}else IIC_Send_Byte(0XA0+((ReadAddr/256)<<1));   //发送器件地址0XA0,写数据 	   
	IIC_Wait_Ack(); 
    IIC_Send_Byte(ReadAddr%256);   //发送低地址
	IIC_Wait_Ack();	    
	IIC_Start();  	 	   
	IIC_Send_Byte(0XA1);           //进入接收模式			   
	IIC_Wait_Ack();	 
    temp=IIC_Read_Byte(0);		   
    IIC_Stop();//产生一个停止条件	    
	return temp;
}

参考文档:https://blog.csdn.net/sternlycore/article/details/85759475?utm_medium=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant_t0.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

https://blog.csdn.net/sternlycore/article/details/85600668?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.nonecase

### USB驱动面试常见问题及答案 #### 一、USB驱动基础概念 USB驱动涉及多个层次的设计与实现,通常分为硬件层、主机控制器驱动层以及应用层。以下是几个常见的USB驱动相关问题及其解答。 1. **什么是USB挂起态?** 当USB总线供电设备在3毫秒内未检测到任何操作时,会自动进入挂起状态[^2]。在此状态下,设备的电流消耗不得超过280微安。这种机制旨在降低功耗并优化资源利用。 2. **USB驱动开发的主要组成部分有哪些?** USB驱动主要由以下几个部分组成: - 主机控制器驱动(HCD),负责管理USB主机控制器的操作。 - 设备类驱动,针对特定类型的USB设备提供功能支持。 - 总线枚举过程中的初始化逻辑,用于识别连接的新设备并加载相应的驱动程序。 #### 二、Linux环境下的USB驱动设计 在Linux系统中,USB驱动属于典型的字符设备或块设备范畴。以下是对这两种设备特性的描述: 1. **字符设备 vs 块设备的区别是什么?** 字符设备仅允许按顺序读取数据;而块设备则支持随机访问,并且其最小传输单位为固定大小的数据块(通常是512字节)。例如,硬盘和闪存都属于块设备类别[^3]。 2. **如何编写一个简单的IIC协议驱动来控制EEPROM?** 编写此类驱动通常需要遵循如下原则:定义好寄存器映射关系,通过软件模拟或者调用底层API完成实际通信流程。具体代码框架可能类似于下面这样: ```c static int eeprom_read(struct i2c_client *client, u8 reg_addr, u8 *data) { struct i2c_msg msgs[] = { { client->addr, 0, sizeof(reg_addr), &reg_addr }, { client->addr, I2C_M_RD, sizeof(*data), data } }; if (i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs)) != ARRAY_SIZE(msgs)) return -EIO; return 0; } ``` #### 三、Android平台上的特殊需求 对于专注于移动终端产品的公司而言,除了基本的USB功能外,还可能会关注其他无线技术领域的能力评估。 1. **为什么某些企业在招聘安卓开发者时更看重蓝牙或WiFi经验而非单纯USB?** 对于生产实物商品的企业来说,具备额外的技术专长能够显著增强候选人的竞争力。这是因为这些附加特性往往是现代智能设备不可或缺的一部分[^4]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值