使用LL库实现I2C从机,纠正START发送多次的BUG。

/ 2评 / 0

这是一个非常操蛋的BUG,在之前的程序会发现,就是i2cdetect不能连续执行两次,如果使用不正常的I2C访问时许会导致器件死锁。现在实现一个功能,有Read Only寄存器,也有普通寄存器。
主要逻辑代码是这样的。

const uint8_t BufferIndex_RO[0xFF] = {
0x00,0x00,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};
__IO uint8_t BufferState = 0;
__IO uint8_t BufferIndex = 0;
__IO uint8_t RxBuffer[0xFF]={
0x67,0xF7,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
};
__IO uint8_t BufferTmp = 0;
void I2C2_EV_IRQHandler(void)
{
  /* Check ADDR flag value in ISR register */
  if(LL_I2C_IsActiveFlag_ADDR(I2C2))
  {
    /* Verify the Address Match with the OWN Slave address */
    if(LL_I2C_GetAddressMatchCode(I2C2) == SLAVE_OWN_ADDRESS)
    {
      /* Verify the transfer direction, a read direction, Slave enters transmitter mode */
      if(LL_I2C_GetTransferDirection(I2C2) == LL_I2C_DIRECTION_READ)
      {
        /* Clear ADDR flag value in ISR register */
        LL_I2C_ClearFlag_ADDR(I2C2);
        /* Enable Transmit Interrupt */
        LL_I2C_EnableIT_TX(I2C2);
      }
      else
      {
				BufferState = I2C_SLAVE_START_WRITE_FIRST;
        /* Clear ADDR flag value in ISR register */
        LL_I2C_ClearFlag_ADDR(I2C2);
				LL_I2C_EnableIT_RX(I2C2);
      }
    }
    else
    {
      /* Clear ADDR flag value in ISR register */
      LL_I2C_ClearFlag_ADDR(I2C2);
      /* Call Error function */
      Error_Callback();
    }
  }
  /* Check NACK flag value in ISR register */
  else if(LL_I2C_IsActiveFlag_NACK(I2C2))
  {
    /* End of Transfer */
    LL_I2C_ClearFlag_NACK(I2C2);
  }
  /* Check TXIS flag value in ISR register */
  else if(LL_I2C_IsActiveFlag_TXIS(I2C2))
  {
    /* Call function Slave Ready to Transmit Callback */
    //Slave_Ready_To_Transmit_Callback();
		  LL_I2C_TransmitData8(I2C2, RxBuffer[BufferIndex++]);
		BufferState = I2C_SLAVE_INIT;
  }
  /* Check TXIS flag value in ISR register */
  else if(LL_I2C_IsActiveFlag_RXNE(I2C2))
  {
		if(BufferState == I2C_SLAVE_START_WRITE_FIRST){
			BufferIndex = LL_I2C_ReceiveData8(I2C2);
			BufferState = I2C_SLAVE_START_WRITE_DATA;
		}else if(BufferState == I2C_SLAVE_START_WRITE_DATA){
			if(BufferIndex_RO[BufferIndex] == 0xFF){ /* If not 0xFF,it is read only. */
				RxBuffer[BufferIndex] = LL_I2C_ReceiveData8(I2C2);
			}else{
				BufferTmp = LL_I2C_ReceiveData8(I2C2);
			}
			BufferState = I2C_SLAVE_INIT;
		}else{
		  BufferTmp = LL_I2C_ReceiveData8(I2C2);
			BufferState = I2C_SLAVE_INIT;
		}
  }
  /* Check STOP flag value in ISR register */
  else if(LL_I2C_IsActiveFlag_STOP(I2C2))
  {
    /* Clear STOP flag value in ISR register */
    LL_I2C_ClearFlag_STOP(I2C2);
    /* Check TXE flag value in ISR register */
    if(!LL_I2C_IsActiveFlag_TXE(I2C2))
    {
      /* Flush the TXDR register */
      LL_I2C_ClearFlag_TXE(I2C2);
    }
  }
  /* Check TXE flag value in ISR register */
  else if(!LL_I2C_IsActiveFlag_TXE(I2C2))
  {
    /* Do nothing */
    /* This Flag will be set by hardware when the TXDR register is empty */
    /* If needed, use LL_I2C_ClearFlag_TXE() interface to flush the TXDR register  */
  }
  else
  {
    /* Call Error function */
    Error_Callback();
  }
}

简单说下,BufferIndex_RO就说明哪些是RO寄存器,RxBuffer就算寄存器内容。

 

如果利用这个,STM32不断ADC采集DMA到这个数组,然后上面RPI这么访问,岂不是可以做一个外部ADC芯片?
程序放出来记录下。
I2C从机通信寄存器读写

  1. wang说道:

    非常感谢博主 去年开始学stm8 还是直接用寄存器在写 最近stm8资源不够 想改成stm32 首先就要用到iic从机 用hal库搞了两三天都没弄好 各种问题 直到看了这篇文章 如获至宝啊 用博主的例程一次就成功了 LL库也便于理解 棒!

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注