stm32-I2C

看寄存器文档写了一天硬件I2C,写了好几遍都不对,最后还是用库了。。。。。。。。看了下火哥的代码,发现其实stm32固件库已经把每一步需要检测的标志位封装好了,我们要做的只是根据文档来检测这些标志,并采取下一步动作。下面是STM32和AT24C02的硬件I2C分析

硬件I2C主发送

下图是stm32主发送时序图和AT24C02的写字节操作流程图,其中EV5、EV6、EV8_1、EV8、EV8_2、EV9这些事件标志在stm32的官方固件库都有详细编写出来(stm32f10x_i2c.h),我们只需调用检测即可。野火的硬件I2C也就是在这两个图和官方固件库的基础上编写的。


有详细的英文注释,很好理解

void I2C_EE_ByteWrite(u8* pBuffer, u8 WriteAddr)//将pBuffer地址内的数据写入AT24C02的指定地址中
{
  /* Send STRAT condition */
  I2C_GenerateSTART(I2C2, ENABLE);

  /* Test on EV5 and clear it */
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));  

  /* Send EEPROM address for write */
  I2C_Send7bitAddress(I2C2, EEPROM_ADDRESS, I2C_Direction_Transmitter);

  /* Test on EV6 and clear it */
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

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

  /* Test on EV8 and clear it */
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

  /* Send the byte to be written */
  I2C_SendData(I2C2, *pBuffer); 

  /* Test on EV8 and clear it */
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

  /* Send STOP condition */
  I2C_GenerateSTOP(I2C2, ENABLE);
}  

硬件I2C主接收

和发送一样,这些事件标志在官方库中都有写出,但是需要注意由于是操作AT24C02所以根据它的要求在读之前会需要先写入需要读出的数据所在地址,所以接收数据前还需要先写数据。


以下是野火的AT2402读操作函数:

void I2C_EE_BufferRead(u8* pBuffer, u8 ReadAddr, u16 NumByteToRead)//从ReadAddr处读出
{  
  //*((u8 *)0x4001080c) |=0x80; 
    while(I2C_GetFlagStatus(I2C2, I2C_FLAG_BUSY)); // Added by Najoua 27/08/2008


  /* Send START condition */
  I2C_GenerateSTART(I2C2, ENABLE);
  //*((u8 *)0x4001080c) &=~0x80;

  /* Test on EV5 and clear it */
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));

  /* Send EEPROM address for write */
  I2C_Send7bitAddress(I2C2, EEPROM_ADDRESS, I2C_Direction_Transmitter);

  /* Test on EV6 and clear it */
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED));

  /* Clear EV6 by setting again the PE bit */
  I2C_Cmd(I2C2, ENABLE);

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

  /* Test on EV8 and clear it */
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

        /*以上都还为写操作阶段,接下来准备读*/

  /* Send STRAT condition a second time */  
  I2C_GenerateSTART(I2C2, ENABLE);

  /* Test on EV5 and clear it */
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_MODE_SELECT));

  /* Send EEPROM address for read */
  I2C_Send7bitAddress(I2C2, EEPROM_ADDRESS, I2C_Direction_Receiver);

  /* Test on EV6 and clear it */
  while(!I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED));

  /* While there is data to be read */
  while(NumByteToRead)  
  {
    if(NumByteToRead == 1)
    {
      /* Disable Acknowledgement */
      I2C_AcknowledgeConfig(I2C2, DISABLE);

      /* Send STOP Condition */
      I2C_GenerateSTOP(I2C2, ENABLE);
    }

    /* Test on EV7 and clear it */
    if(I2C_CheckEvent(I2C2, I2C_EVENT_MASTER_BYTE_RECEIVED))  
    {      
      /* Read a byte from the EEPROM */
      *pBuffer = I2C_ReceiveData(I2C2);

      /* Point to the next location where the byte read will be saved */
      pBuffer++; 

      /* Decrement the read bytes counter */
      NumByteToRead--;        
    }   
  }

  /* Enable Acknowledgement to be ready for another reception */
  I2C_AcknowledgeConfig(I2C2, ENABLE);
}  

硬件I2C主发送操作后的检测函数

火哥他对寄存器文档把握很准,需要什么不需要什么都很了解,当然也很熟悉官方固件库的内容,绝逼是老手了。下面一个函数是再次写入设备地址,用于检测从设备是否有响应,判断是否从设备已完成了以上数据写入操作。

void I2C_EE_WaitEepromStandbyState(void)      
{
  vu16 SR1_Tmp = 0;

  do
  {
    /* Send START condition */
    I2C_GenerateSTART(I2C2, ENABLE);
    /* Read I2C2 SR1 register */
    SR1_Tmp = I2C_ReadRegister(I2C2, I2C_Register_SR1);
    /* Send EEPROM address for write */
    I2C_Send7bitAddress(I2C2, EEPROM_ADDRESS, I2C_Direction_Transmitter);
  }while(!(I2C_ReadRegister(I2C2, I2C_Register_SR1) & 0x0002));

  /* Clear AF flag */
  I2C_ClearFlag(I2C2, I2C_FLAG_AF);
    /* STOP condition */    
    I2C_GenerateSTOP(I2C2, ENABLE); 
}  

模拟I2C

对于模拟I2C这里只记录几点重要的地方,代码就不贴出来了。

  1. 开始信号:SDA、SCL都为高的前提下,SDA跳为低电平。
  2. 停止信号:SCL为高时,SDA由低电平跳为高电平。
  3. 数据变化:只有当SCL为低电平的时候SDA线上的数据(高低电平)才可以变化。
  4. 等待ACK时,需要将SDA的GPIO引脚变为浮空输入。