STM32F103 I2C硬件缺陷详述及解决方案

记得刚开始接触STM32单片机的时候,就从很多地方听说STM32F103的硬件I2C是有缺陷的,但是当时在网上查了很久也没有查到具体的缺陷是什么,后面在工作中正好有相关问题,在经过一段时间深入了解后,写下这篇文章来盘一盘硬件I2C的缺陷。

I2C的协议中规定,在传输数据时,接收方在接收到数据后都会向发送方发出一个响应信号ACK,如果传输双方有一个是主接收器,它必须在不产生时钟的最后一个字节发出一个NACK,来告知发送器此次数据传输过程结束,如下图所示:
在这里插入图片描述

I2C中的ACK信号为一个低电平,NACK信号为高电平,软件流程中,只要不是最后一个数据,在主接收器接收到数据后默认都会发出去一个ACK信号,那么问题来了,当主接收器在接受到最后一个字节后,I2C的主发送器发送出去一个ACK信号会导致什么样的结果发生呢?

此时,总线上的从发送器收到ACK,以为还要继续发送数据,所以将SDA拉住等待主机的时钟继续发送数据,而主接收器呢,因为自身已经接收到最后一个字节数据,所以不会继续向从机发送时钟,导致SDA一直被从机拉住无法释放,整个通信陷入死锁。从逻辑分析仪上抓取的波形表现可以很清楚的看到现象:
在这里插入图片描述

代码如下:

uint32_t I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)
{  
    
  while(I2C_GetFlagStatus(EEPROM_I2Cx, I2C_FLAG_BUSY));
  /* Send START condition */
  I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);
  
  /* Test on EV5 and clear it */
  while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT));
  
  /* Send EEPROM address for write */
  I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDRESS, I2C_Direction_Transmitter);

  /* Test on EV6 and clear it */
  while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));
    
  /* Clear EV6 by setting again the PE bit */
  I2C_Cmd(EEPROM_I2Cx, ENABLE);

  /* Send the EEPROM's internal address to write to */
  I2C_SendData(EEPROM_I2Cx, ReadAddr);  

  /* Test on EV8 and clear it */
  while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));
    
  /* Send STRAT condition a second time */  
  I2C_GenerateSTART(EEPROM_I2Cx, ENABLE);

  /* Test on EV5 and clear it */
  while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_MODE_SELECT));
    
  /* Send EEPROM address for read */
  I2C_Send7bitAddress(EEPROM_I2Cx, EEPROM_ADDRESS, I2C_Direction_Receiver);

  while(!I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));
  
  /* While there is data to be read */
  while(NumByteToRead)  
  {
    
		while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED)==0);
		
          
      /* Read a byte from the EEPROM */
      *pBuffer = I2C_ReceiveData(EEPROM_I2Cx);

      /* Point to the next location where the byte read will be saved */
      pBuffer++; 
      
      /* Decrement the read bytes counter */
      NumByteToRead--;        
      
  }


	/* Disable Acknowledgement */
	I2C_AcknowledgeConfig(EEPROM_I2Cx, DISABLE);
      
  /* Send STOP Condition */
  I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);

  /* Enable Acknowledgement to be ready for another reception */
  I2C_AcknowledgeConfig(EEPROM_I2Cx, ENABLE);
  
    return 1;
}
int main(void)
{ 
  LED_GPIO_Config();
 
  /* 串口初始化 */
	USART_Config();

	/* I2C 外设初(AT24C02)始化 */
	I2C_EE_Init();

  I2C_EE_BufferRead(I2c_Buf_Read, EEP_Firstpage, 4); 
  while (1)
  {      

  }
}

main函数里是读取4个eeprom的数据,从I2C的标准通信协议来看,这段代码在流程上是没有问题的,但是从波形上可以看到有两个很奇怪的现象:
1. NACK和STOP信号都没有发出去
2. 本来应该主接收器应该读取4个数据,却多发出去一个时钟,读到5个数据

从分析得出,总线的死锁是由于NACK和STOP信号没发出去导致的,那么为什么NACK和STOP没发出去呢?其实这个问题又是由于第二个问题的本身导致的。

STM32F103的I2C设计为了加快数据读取速度,在读取一个数据后,如果没有检测到NACK信号的话,会多读取一个数据,即多发送一个字节的时钟去读取数据,但是由于其中过程太快,如果想在接收到最后一个字节后再去发送NACK,那么下一个时钟已经出去了,导致NACK和STOP信号都没发出去,也就导致了总线死锁。

解决方法:

  1. 使用DMA或者中断,中断或DMA方式速度更快,可以让NACK可以及时在接收到最后一个字节之后发出去
  2. 如果还是要使用轮询方式的话,就需要修改代码流程,将发送NACK和STOP信号的时间提前到倒数第二个数据,具体如下:
  while(NumByteToRead)  
  {
    
		if(NumByteToRead == 1)
		{
			/* Disable Acknowledgement */
			I2C_AcknowledgeConfig(EEPROM_I2Cx, DISABLE);
					
			/* Send STOP Condition */
			I2C_GenerateSTOP(EEPROM_I2Cx, ENABLE);
		}
		
		while(I2C_CheckEvent(EEPROM_I2Cx, I2C_EVENT_MASTER_BYTE_RECEIVED)==0);
		
          
      /* Read a byte from the EEPROM */
      *pBuffer = I2C_ReceiveData(EEPROM_I2Cx);

      /* Point to the next location where the byte read will be saved */
      pBuffer++; 
      
      /* Decrement the read bytes counter */
      NumByteToRead--;        
      
  }

此时从逻辑分析仪上抓取到的波形显示正常:
在这里插入图片描述

  1. 降低I2C的传输速度到几十KHz,原理一致,I2C发送速度慢了以后,NACK就可以及时发出。
  • 27
    点赞
  • 117
    收藏
    觉得还不错? 一键收藏
  • 10
    评论
STM32F103是一款由STMicroelectronics开发的微控制器芯片,它具有强大的处理能力和丰富的外设功能。其中之一就是I2C(Inter-Integrated Circuit)硬件接口。 I2C是一种串行通信协议,用于连接微控制器与外部器件,如传感器、存储器、显示器等。STM32F103I2C硬件接口包含主模式和从模式两种运行模式,可以灵活地满足不同的应用需求。 在主模式下,STM32F103可以作为I2C总线的主设备,控制和管理多个从设备。主设备可以发送I2C起始信号、地址和数据,还可以接收从设备的响应和数据。I2C硬件接口中的寄存器和控制器可以实现自动重复启动、多字节传输、多主设备共享总线等功能,方便主设备与从设备之间进行高效的数据交换。 在从模式下,STM32F103可以作为I2C总线的从设备,响应主设备的命令和传输数据。从设备可以根据主设备的地址选择性地响应,还可以通过自动应答机制向主设备发送数据。I2C硬件接口中的寄存器和控制器还支持接收和发送缓冲区,以及中断和DMA传输方式,提升了从设备的灵活性和数据处理能力。 总之,STM32F103I2C硬件接口为嵌入式系统提供了高效、可靠的串行通信解决方案。它的主模式和从模式能够满足不同的通信要求,支持多主设备同时共享总线,并配备了丰富的功能和灵活的操作方式,使得它成为许多应用领域中的理想选择。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值