STM32软件模拟IIC时序实现与EEPROM的通信

                   IIC简介

 IIC物理层

用软件模拟IIC时序

        一、空闲状态(初始化):SCL 和SDA都保持高电平

        二、开始信号 :SCL为高电平期间,SDA由高电平变为低电平。

        三、停止信号:SCL为高电平期间,SDA由低电平变为高电平

        四、应答信号:

        五、发送一个字节

        六、读取一个字节

        七、IIC发送数据

        八、IIC读取数据

下面以EEPROM AT24C02为例

        AT24C02相关知识:

        向AT24C02中写数据

        从AT24C02中读数据​

软件模拟IIC时序过程中遇到的问题


注:

①此文章用来作为个人笔记使用,内容难免有不当之处,如有需要,仅供参考。

②以下内容基于正点原子精英版开发板(STM32F103ZET6)。

IIC简介

        IIC(Inter-Integrated Circuit)是一种两线式串行总线(SDA:数据线,SCL:时钟线)。属于同步通信(带同步时钟信号,即发送数据的同时还要发送时钟信号),传送方式为半双工(数据可以双向传输,但在某一时刻只能单向传输)。标准IIC总线传输速率一般为100kbps,高速IIC总线传输速率一般可达400kbps以上,且传输速率比SPI低。 所有要进行IIC通讯的设备直接挂到两条总线上即可。 

 IIC物理层

  • SDA(Serial data):数据线,用于传输数据。

  • SCL(Serial clock line):时钟线,用于发送时钟信号。

  • 上拉电阻:为了避免总线信号的混乱,各设备连接到总线的输出端时必须是漏极开路(OD)输出或集电极开路(OC)输出,如下图所示。那么设备只能输出低电平和高阻态两种状态,设备要想输出高电平必须接上拉电阻 。

  • IIC设备地址: 每一个IIC设备都有一个设备地址,来确保不同设备之间访问的准确性。有的器件地址在出厂时地址就设定好了,用户不可以更改。有的器件例如EEPROM,前四个地址已经确定为1010,后三个地址是由硬件链接确定的,所以IIC总线最多能连8个EEPROM芯片。

  • IIC的高阻态:IIC设备一般是开漏输出(OD),这种方式能输出低电平和高阻态两种电平。好处是当设备需要读取IIC总线上的数据时,不需要切换到输入模式,在高阻态模式下就能读取IIC总线上的数据。并且开漏输出支持线与功能(可简单理解为支持多个输出口接到一起),而正好IIC总线上需要挂多个设备。

 

 (对IIC总线上设备芯片开漏读写数据的理解:①芯片接收数据时,DATA1OUT和DATA2OUT为低电平,MOS管关闭(高阻态状态),此时DATA1IN和DATA2IN处读到的电平就是SDA线上的电平;②芯片发送数据时,通过DATA1OUT和DATA2OUT切换MOS管导通和关闭,配合上拉电阻,使SDA线切换为高低电平,即发出高低电平。当然,此时其他空闲设备都处于高阻态。)

 IIC设备可分为主设备和从设备 ,主设备一般是产生时钟信号、开始信号和停止信号的一方,主设备有时可能不止一个。

用软件模拟IIC时序:

STM32F103ZET6 作为主设备,AT24C02 EEPROM作为从设备

        注:依个人理解和总结,模拟IIC时序时其实只有三种情况:①开始信号:SCL为高电平期间,SDA由高电平变为低电平。②停止信号:SCL为高电平时,SDA由低电平变为高电平 ③除去前两种特殊信号,其他都可理解为是基于发送或接收一个位的数据: 

        单片机发送一个位的数据:发送数据都是在SCL为低电平时准备好数据,在SCL上升沿发送,并在SCL的高电平保持不变(一是防止产生停止信号或开始信号,二是使接受设备有足够时间读取到数据)。当然发送完意味着接收设备要接收一个位,接收设备也是收到一个SCL上升沿时认为发送设备发送了一位数据,因此会读取一次。
        单片机接收一个位的数据也就是从设备芯片需要发送一个位,因此需要提供给从设备芯片一个上升沿时钟信号。此时从设备芯片会主动发送一个位,我们用单片机读取总线上的电平作为一个位即可。对从设备而言,向主设备发送数据时需要收到主设备发送来的一个上升沿时钟信号,才发送一位数据。

数据有效性

        IIC信号在数据传输过程中,当SCL=1高电平时,数据线SDA必须保持稳定状态,不允许有电平跳变,只有在时钟线上的信号为低电平期间,数据线上的高电平或低电平状态才允许变化。因为SCL=1时 数据线SDA的任何电平变换会看做是总线的起始信号或者停止信号。

一、空闲状态(初始化):SCL 和SDA都保持高电平

//IIC总线 PB6:SCL  PB7:SDA
//软件模拟IIC时序可以使用任意两个GPIO口,即使单片机没有IIC功能,也是可以的。
void IIC_Init(void)
{
	GPIO_InitTypeDef GPIO_InitStructure;
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB,ENABLE);
	
	GPIO_InitStructure.GPIO_Mode =GPIO_Mode_Out_OD; //配置为开漏输出
	GPIO_InitStructure.GPIO_Pin =GPIO_Pin_6|GPIO_Pin_7;
	GPIO_InitStructure.GPIO_Speed =GPIO_Speed_50MHz;
	GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	GPIO_SetBits(GPIOB,GPIO_Pin_6|GPIO_Pin_7);
}

        用STM32软件模拟IIC时序时,用于模拟IIC总线的IO口可以配置成推挽输出或开漏输出两种情况。因原子例程中是配置成了推挽输出,因此在自己模拟时序时尝试配置成了开漏输出。两种配置方式的区别见文章末尾问题一。

二、开始信号 :SCL为高电平期间,SDA由高电平变为低电平。

在这里插入图片描述

//产生IIC开始信号
void IIC_Start(void)
{
	IIC_SCL=1; 
	IIC_SDA=1; 
	delay_us(5); 
	IIC_SDA=0; 
	delay_us(5); 
	IIC_SCL=0; 
}

       在起始条件产生后,总线处于忙状态,由本次数据传输的主从设备独占,其他I2C器件无法访问总线。

         有关开始信号的时序模拟遇到的问题见文章末尾问题三。

三、停止信号:SCL为高电平期间,SDA由低电平变为高电平

void IIC_Stop(void)
{
	IIC_SCL=0;
	IIC_SDA=0;
	delay_us(2);
	IIC_SCL=1;
	delay_us(5);
	IIC_SDA=1;
	delay_us(5);
}

        在停止条件产生后,本次数据传输的主从设备将释放总线,总线再次处于空闲状态。

四、应答信号:

        应答信号有应答(ACK)和不应答(NACK)两种。按方向又可以分成单片机对设备芯片应答(或不应答)和设备芯片对单片机应答(或不应答)两种情况。但是设备芯片对单片机的应答信号是芯片本身主动发送的,不需要我们编写代码,因此我们只需要编写三个代码:单片机对设备芯片应答、单片机对设备芯片不应答、以及接收设备芯片对单片机的应答信号。

        每当主机向从机发送完一个字节的数据,主机总是需要等待从机给出一个应答信号,以确认从机是否成功接收到了数据。

        应答信号为低电平时,规定为有效应答位(ACK,简称应答位),表示接收器已经成功地接收了该字节;
        应答信号为高电平时,规定为非应答位(NACK),一般表示接收器接收该字节没有成功。


        **每发送一个字节(8个bit)**在一个字节传输的8个时钟后的第九个时钟期间,接收器接收数据后必须回一个ACK应答信号给发送器,这样才能进行数据传输。

//单片机对设备芯片的应答ACK信号:可以理解为单片机向从设备发送一位值为0的数据
void IIC_ACK(void)
{
	IIC_SCL=0;
	IIC_SDA=0; //在SCL为0时准备好数据
	delay_us(5);
	IIC_SCL=1; //SCL的上升沿发送一位数据
	delay_us(5);//在SCL高电平期间保持不变
	IIC_SCL=0;
}

//单片机对设备芯片的非应答NADCK信号:可以理解为单片机向从设备发送一位值为1的数据
void IIC_NACK(void)
{
	IIC_SCL=0;
	IIC_SDA=1;  //在SCL为0时准备好数据
	delay_us(2);
	IIC_SCL=1;  //SCL的上升沿发送一位数据
	delay_us(5); //在SCL高电平期间保持不变
	IIC_SCL=0;
}

//单片机等待接收设备芯片的应答:相当于从设备向单片机发送一位数据
u8 IIC_WaitACK(void)
{
	IIC_SCL=0;  
	IIC_SDA=1;  //此处是为了让单片机处于高阻态,以便于单片机能读取SDA线上的电平。
                //但是会发现不设置成高阻态程序也能正常运行,原因是即使IIC_SDA=0时单片机不能 
                //读取到外部电平,此时单片机被MOS管拉低,读到的值会总是为0,单片机会误认为是从机 
                //发送的应答信号,因此程序也会正常运行。
	delay_us(2);
	IIC_SCL=1;
	delay_us(2); //以上是为了提供给从机一个上升沿时钟信号,从机就会主动发送一位数据
	if(READ_SDA==1) //下面判断从机发送过来的是1还是0,也就是判断从机发送的是不应答还是应答信号
	{
		IIC_Stop(); //如果从机发送过来的是不应答就停止传输数据
		return 1;
	}
	else
	{
		IIC_SCL=0;
		return 0; //如果从机发送过来的是应答信号就继续传输数据
	}
}

五、发送一个字节

       IIC协议要求数据传输是以字节为单位的(一次传输8位)。数据传送时,先传送最高位(MSB),每一个被传送的字节后面都必须跟随一位应答位(8位数据位加1位应答位称为一帧)。

//发送一个字节数据:相当于把8位数据一位一位发送过去
//此函数被用来发送设备芯片地址、数据在设备芯片中的存储地址、以及要发送的8位数据
void IIC_SendByte(u8 txd)
{
	u8 i;
	IIC_SCL=0;
	for(i=0;i<8;i++) //先传送高位,再传送低位
	{
		if(txd&0x80) IIC_SDA=1;
    else IIC_SDA=0;
    txd<<=1;
		delay_us(2);
		IIC_SCL=1;
		delay_us(2);
		IIC_SCL=0;
	}
}

六、读取一个字节

//读取一个字节数据:从设备会在主设备每发送一个时钟上升沿时向主设备发送一位数据
u8 IIC_ReadByte(void)
{
	u8 i;
	u8 receive=0;
	for(i=0;i<8;i++)
	{
		IIC_SCL=0;
		delay_us(2);
		IIC_SDA=1;
		IIC_SCL=1;
		receive<<=1;
		if(READ_SDA==1) receive++;
	}		
	return receive;
}

以上函数可以看做是IIC的底层函数,要实现某个功能直接调用以上函数即可。

七、IIC发送数据

 流程:开始信号->设备地址和写(7+1位)->应答->数据存放地址->应答->8位要发送的数据->应答->停止信号

1、主机首先产生START信号
2、 然后紧跟着发送一个从机地址,这个地址共有7位,紧接着的第8位是数据方向位(R/W),0表示主机发送数据(写),1表示主机接收数据(读)。这总共8位是作为一个字节一起发送的。
3、主机发送地址时,总线上的每个从机都将这7位地址码与自己的地址进行比较,若相同,则认为自己正在被主机寻址,根据R/T位将自己确定为发送器和接收器
4、这时候主机等待从机的应答信号
5、当主机收到应答信号时,发送要访问从机的那个地址, 继续等待从机的应答信号
6、当主机收到应答信号时,发送1个字节的数据,继续等待从机应答信号

(6''这里可以连续发送数据,即重复 “发送一个字节数据+等待从机应答信号”,就可以连续向后续字节地址写入数据(寄存器地址会自增),直到不再发送数据了就可以发送一个停止信号)
7、主机产生停止信号,结束传送过程。

//模拟完整发送一个字节数据时序
//write_addr:设备地址  write_reg:寄存器地址  write_data:要写入的数据
void IIC_SendOneByte(u8 write_addr,u8 write_reg,u8 write_data)
{
	IIC_Start();
	IIC_SendByte((write_addr<<1)|0);
	IIC_WaitACK();
	IIC_SendByte(write_reg);
	IIC_WaitACK();
	IIC_SendByte(write_data);
	IIC_WaitACK();
	IIC_Stop();
    delay_ms(5);
}

//IIC连续发送数据
//addr:设备地址  reg:寄存器地址  len:写入长度  buf:数据区
//返回值:0,正常;其他,错误代码  
u8 IIC_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
	u8 i; 
    IIC_Start(); 
	IIC_SendByte((addr<<1)|0);//发送设备地址+写命令	
    IIC_WaitACK();
    IIC_SendByte(reg);	//写寄存器地址
    IIC_WaitACK();();		//等待应答
	for(i=0;i<len;i++)
	{
		IIC_SendByte(buf[i]);	//发送数据
		IIC_WaitACK();		
	}    
    IIC_Stop();	 
	return 0;	
}

八、IIC读取数据

流程:开始信号->设备地址和写->应答->数据存储地址-> 应答->开始信号->设备地址和读->应答->要发送的8位数据->不应答->停止信号

1、主机首先产生START信号

2、然后紧跟着发送一个从机地址,注意此时该地址的第8位为0,表明是向从机写命令,
3、这时候主机等待从机的应答信号(ACK)
4、主机收到应答信号时,发送要访问的地址,继续等待从机的应答信号,
5、当主机收到应答信号后,主机要改变通信模式(主机将由发送变为接收,从机将由接收变为发送)所以主机重新发送一个开始start信号,然后紧跟着发送一个从机地址,注意此时该地址的第8位为1,表明将主机设 置成接收模式开始读取数据,

6、这时候主机等待从机的应答信号,当主机收到应答信号时,就可以接收1个字节的数据。当接收完成后,若主机发送应答信号,就可以继续读数据,如此循环“接收数据+发送应答信号”,就可以连续从后续字节地址读取数据(寄存器地址自增)。直到主机发送非应答信号,表示不在接收数据
7、主机进而产生停止信号,结束传送过程。

//模拟完整读取一个数据时序
//read_addr:设备地址  read_reg:寄存器地址
u8 IIC_ReadOneByte(u8 read_addr,u8 read_reg)
{
	u8 temp;
	IIC_Start();
    IIC_SendByte((addr<<1)|0);
	IIC_WaitACK();
	IIC_SendByte(read_reg);
	IIC_WaitACK();
	IIC_Start();
	IIC_SendByte((addr<<1)|1);
	IIC_WaitACK();
	temp=IIC_ReadByte();
	IIC_NACK();
	IIC_Stop();
	return temp;
}


//IIC连续读取数据 
//addr:设备地址  reg:要读取的寄存器地址  len:要读取的长度  buf:读取到的数据存储区
//返回值:0,正常;其他,错误代码
u8 IIC_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{ 
 	IIC_Start(); 
	IIC_SendByte((addr<<1)|0);//发送器件地址+写命令	
    IIC_WaitACK();
    IIC_SendByte(reg);	//写寄存器地址
    IIC_WaitACK();		//等待应答
    IIC_Start();
	IIC_SendByte((addr<<1)|1);//发送器件地址+读命令	
    IIC_WaitACK();		//等待应答 
	while(len)
	{
		if(len==1)
        {
            *buf=IIC_ReadByte();
            IIC_NACK();//读数据,发送nACK 
        }
            
		else 
        {
            *buf=IIC_ReadByte();		
            IIC_NCK();//读数据,发送ACK
        }
  
		len--;
		buf++; 
	}    
    IIC_Stop();	//产生一个停止条件 
	return 0;	
}

下面以EEPROM AT24C02为例

AT24C02相关知识:

        24C02是一个2K Bit的串行EEPROM存储器(掉电不丢失),内部含有256个字节。在24C02里面有一个8字节的页写缓冲器。

A0,A1,A2:硬件地址引脚
WP:写保护引脚,接高电平只读,接地允许读和写
SCL和SDA:IIC总线

VCC、GND:电源和地

        可以看出对于不同大小的24Cxx,具有不同的从器件地址。由于24C02为2k容量,也就是说只需要参考图中第一行的内容:

芯片的寻址
        AT24C设备地址为如下,前四位固定为1010,A2~A0由管脚电平决定。AT24CXX EEPROM Board模块中默认为接地。A2~A0为000,最后一位表示读写操作。所以AT24Cxx的读地址为0xA1,写地址为0xA0。

也就是说如果是
写24C02的时候,从器件地址为10100000(0xA0);
读24C02的时候,从器件地址为10100001(0xA1)。

片内地址寻址:

芯片寻址可对内部256B中的任一个进行读/写操作,其寻址范围为00~FF,共256个寻址单位。

硬件连接


向AT24C02中写数据

 

操作时序:

1、MCU先发送一个开始信号(START)启动总线

2、接着跟上首字节,发送设备地址和写(即0xA0)
3、等待应答信号(ACK)

4、发送数据的存储地址。24C02一共有256个字节的存储空间,地址从0x00~0xFF,想把数据存储在哪

个位置,此刻写的就是哪个地址。

5、等待应答信号(ACK)
6、发送要存储的一个字节的数据,并等待应答信号(ACK)。
7、发送结束信号(STOP)停止总线

注:给EEPROM发送数据时,数据是先发送到页写缓冲区,等5ms之后,才会被转移到非易失区域;在写数据的过程中,每成功写入一个字节,E2PROM存储空间的地址就会自动加1

        EEPROM的写数据分为字节写数据模式和页写数据模式。字节写就是一个地址一个数据的写(见下述代码段(1));页写是连续写数据,一个地址多个数据的写。但是页写不能跨页,跨页后超出的数据会覆盖原先写入的数据。

页写介绍:在向EEPROM写入多个字节的数据时,如果每写一个字节都要等5ms才能让数据从缓冲区转移到非易失区域,那效率会低太多。为此,出现了EEPROM的分页管理:24C01/02按8字节一个页,24C04/08/16按16个字节一页,24C32/64按32字节一页。这时在同一页连续写入多个字节后,再发送一个停止信号,EEPROM就会把一页的数据一次性从页缓冲区转移到非易失区域。这样就大大提高了效率。因此,如果在连续写数据过程中发生了跨页,就必须发送一个停止信号将一页的数据一次性转移到非易失区域。再重新进行后续数据的发送。

 //(1)字节写数据
void IIC_SendString(u8 *pBuffer,u8 addr_send_start)
{
	while(1)
	{
	IIC_SendOneByte(addr_send_start,*pBuffer);//每写入一个字节立马转移到非易失区域,并且需等 
                                              //待5ms(等待的5ms已经在此条函数中)。
	addr_send_start++;
	pBuffer++;
	if(*pBuffer=='\0') break;
	}
}

//(2)页写数据
void IIC_SendString(u8 *pBuffer,u8 addr_send_start)
{
	u8 addr;
	addr=addr_send_start;
	IIC_Start();
	IIC_SendByte(0xA0);
	IIC_WaitACK();
	IIC_SendByte(addr_send_start);
	IIC_WaitACK();
	while(1)
	{
		IIC_SendByte(*pBuffer);
		IIC_WaitACK();
		if(*pBuffer=='\0') break;
		pBuffer++;
		addr++;
		if(addr%8==0)  //判断要写入的地址是否跨页,如果跨页则需重新开始发送
		{
			IIC_Stop();
			delay_ms(10); //发送停止信号,并等待10ms.(发送停止信号后EEPROM开始将页缓冲区数据转 
                          //移到非易失区域,此过程需等待5ms以上时间)
			IIC_Start();
			IIC_SendByte(0xA0);
			IIC_WaitACK();
			IIC_SendByte(addr);
			IIC_WaitACK();
		}
	}
	IIC_Stop();
	delay_ms(10);
}

从AT24C02中读数据

操作时序:

1、MCU先发送一个开始信号(START)启动总线

2、接着跟上首字节,发送设备地址和写(即0xA0)
注意:这里写操作是为了要把所要读的数据的存储地址先写进去,告诉E2PROM要读取哪个地址的数据。

3、等待应答
4、发送要读取内存的地址(WORD ADDRESS),通知E2PROM读取要哪个地址的信息。

5、等待应答
6、重新发送开始信号(START)

7、发送设备地址和读 (即0xA1)

8、等待应答
9、EEPROM会自动向主机发送数据,主机读取从器件发回的数据,在读一个字节后,MCU如果回应一个应答信号(ACK)后,EEPROM会继续传输下一个地址的数据,MCU不断回应应答信号可以不断读取内存的数据
如果不想读了,就发送一个非应答位NACK。发送结束信号(STOP)停止总线

     与发送数据有点类似,从EEPROM读取数据页可以有两种方式。

//一个地址一个字节的读取
void IIC_ReadString(u8 *pBuffer,u8 addr_read_start)
{
		while(1)
	{
		*pBuffer=IIC_ReadOneByte(addr_read_start);	
		if(*pBuffer=='\0') break;
		pBuffer++;
		addr_read_start++;
	}
}


//(2)连续读取字符串,但不存在跨页问题,直接读取即可
void IIC_ReadString(u8 *pBuffer,u8 addr_read_start)
{
	IIC_Start();
    IIC_SendByte(0xA0);
	IIC_WaitACK();
	IIC_SendByte(addr_read_start);
	IIC_WaitACK();
	IIC_Start();
	IIC_SendByte(0xA1);
	IIC_WaitACK();
	while(1)
	{
	*pBuffer=IIC_ReadByte();
	if(*pBuffer=='\0') break;
	IIC_ACK();
		pBuffer++;
	}
	IIC_NACK();
	IIC_Stop();

软件模拟IIC时序过程中遇到的问题

一、用于模拟IIC总线的IO口配置成推挽输出和开漏输出的区别:

 

  上图为STM32 GPIO功能框图,黄色部分配置为推挽输出。③处有两个MOS管,  当上面的P-MOS管导通、下面的N-MOS关闭时,输出口被VDD拉成了高电平; 当上面的P-MOS管关闭、下面的N-MOS导通时,输出口被VSS拉低成低电平。因此GPIO输出模式中的推挽输出模式可以输出低电平和高电平两种电平。

         上图黄色部分配置为开漏输出(③处只有下面的N-MOS起作用,上面的P-MOS管可以理解成总是处于关闭状态)。当③处N-MOS管导通时,输出口为低电平;当③处N-MOS管关闭是,输出口被悬空,称之为高阻态,此时输出口的电平状态不确定,电平状态由下一级电路决定(也就是输出口后边接了什么)。因此GPIO输出模式中的开漏输出模式只能输出低电平和高阻态,而不能输出高电平。若要输出高电平需要外接上拉电阻。

        GPIO有一个特点是当配置为输出模式时,其输入通道仍然是打开的 ,TTL施密特触发器处于打开状态,此时可以通过输入数据寄存器读出输出口状态。但是当配置为推挽模式时,输出口状态由芯片内部的两个MOS管拉高或拉低,也就是输入数据寄存器中的值并非IO口外部电平状态;而配置为开漏输出的高阻态模式时,由于此时的IO口被悬空,当IO口输入高低电平时,输入数据寄存器中的值就是外部IO口的状态。也就是在开漏输出模式下的IO口也可同时作为输入口使用。

        因此,当初始化IO口为推挽模式时,如果单片机要读取(接收)从机发来的数据,就要提前把SDA接口配置成输入模式(发送数据时又要改回推挽模式);而初始化IO口为开漏输出时,则不需要来回切换输入输出模式,只需要输出高阻态即可。

二、既然推挽模式下不支持线与,为什么单片机能配置成推挽模式?

        首先来看为什么推挽模式下不支持线与:

         可见,当两个推挽输出的IO接到一起时,若上面的IO输出1,下边输出0,则电流直接由正极经过两个MOS管(忽略MOS管导通电阻相当于直接讲正负极接到一起),流到负极,中间不经过电阻,这样会产生很大的电流,从而烧坏芯片;上面的IO输出0,下边输出1,也是同样的情况;只有当上下同时输出0或上下同时输出1时才不会烧坏芯片。

再来看看为什么开漏输出时支持线与:

         当两个开漏输出接到一起时,只有当上下两个MOS管同时关闭时,输出口为高阻态(上图中外接了上拉电阻,所以时高电平);当任何一个MOS管导通时,输出口都被拉低成低电平。但不管怎么样,都不会产生大电流烧坏芯片。

那为什么在IIC总线中把单片机配置成推挽模式也是可以的呢:

①IIC总线中只有一个单片机(一个主设备)时:

对于SDA线:从设备芯片内部都是开漏输出(一个MOS管),其实也就是开漏和推挽接在了一起。并且单片机读取数据时(此时单片机配置成了输入模式,而非推挽模式),从设备芯片为写(开漏);单片机发送数据时(推挽模式),从设备芯片为读(开漏且为高阻态)。这两种情况都不会出现大电流烧坏芯片。

对于SCL线:单片机只发送(推挽),设备芯片只接收(开漏且高阻态)。也不会烧坏芯片。

②IIC总线有多个单片机(多个主设备)时:由于永远是一读一写,因此不会出现推挽与推挽接在一起的情况,也不会烧坏芯片。

综上,单片机配置成推挽输出模式也是可以的。

三、有关开始信号软件模拟遇到的问题如下:

        按理来说只有在IIC_SCL=0期间才能修改SDA 的值,为什么代码段(1)中先使IIC_SCL=1,再改变IIC_SDA=1也是可以的呢(如果IIC_SDA在上一刻值为0不就产生了一个停止信号吗)?

原因: 开始信号函只出现在发送数据的开头或读取数据的开头和中间(发送数据地址函数后的等待应答信号函数之后)两个地方。经实验,即使是刻意在开始信号前加一个停止信号,也不会影响数据传输。

本文有部分内容引用博主「Z小旋」的文章。
原文链接:https://blog.csdn.net/as480133937/article/details/105366932

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值