嵌入式软件程序块,I2C?

3
我在为ST Microelectronics iNemo开发应用程序时遇到了一个非常奇怪的问题。我的应用程序包括以下内容:
  • SPI陀螺仪读取
  • I2C加速度计和磁力计(同一设备)读取
  • 姿态估计算法
  • PD函数
  • 使用USART接收数据,无DMA中断
  • 使用USART发送日志数据包
该循环通过100Hz的定时器触发。 程序工作正常(我已经使用一些USART调试打印进行过测试),直到我开始使用USART发送数据:我最初的猜测是,由于这个事实使得中断接收变得可能,所以它会导致I2C总线仲裁机制出现问题。我的猜测源于这样一个事实:当我通过USART打印成功调试出问题(这是时间相关的)时,我发现最后一次打印总是在加速度计或磁力计打印之前(这是我在代码中调用的第一个打印)。 此外,如果我通过USART启用详细的调试打印,问题就会更少地发生;而如果我禁用它并只发送日志数据包,问题则总是立即发生。有人能给我一个关于这个问题可能的原因的想法吗?谢谢。
编辑:我附上了我的I2C代码:
#define DMA_BUFFER_SIZE       196  
#define FORCE_CRITICAL_SEC
/**
 * @brief DMA initialization structure variable definition.
*/ 
DMA_InitTypeDef  I2CDMA_InitStructure;

/**
* @brief Volatile variable definition for I2C direction.
*/ 
__IO uint32_t I2CDirection = I2C_DIRECTION_TX;
void iNemoI2CInit(I2C_TypeDef* I2Cx, uint32_t I2CxSpeed)
{
  I2C_InitTypeDef  I2C_InitStructure;
  GPIO_InitTypeDef GPIO_InitStructure;

  /* Enable GPIO clocks */ 
 RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB|RCC_APB2Periph_AFIO, ENABLE);

 /* Configure I2C pins: SCL and SDA */
 if(I2Cx==I2C2)
 {
   RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C2, ENABLE);
   GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_10 | GPIO_Pin_11;
  }
  else
  {
    GPIO_PinRemapConfig(GPIO_Remap_I2C1,ENABLE);

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);
    GPIO_InitStructure.GPIO_Pin =  GPIO_Pin_8 | GPIO_Pin_9;
  }

  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
  GPIO_Init(GPIOB, &GPIO_InitStructure);


  /* I2C configuration */
  I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
  I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
  I2C_InitStructure.I2C_OwnAddress1 = 0x00;
  I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
  I2C_InitStructure.I2C_AcknowledgedAddress =    I2C_AcknowledgedAddress_7bit;
  I2C_InitStructure.I2C_ClockSpeed = I2CxSpeed;

  /* Apply I2C configuration after enabling it */
  I2C_Init(I2Cx, &I2C_InitStructure);

  /* I2C Peripheral Enable */
  I2C_Cmd(I2Cx, ENABLE);

  /* Enable DMA if required */
#if (defined(I2C1_USE_DMA_TX) || defined(I2C1_USE_DMA_RX))
 if (I2Cx==I2C1)
   iNemoI2CDMAInit(I2C1);
#endif

#if (defined(I2C2_USE_DMA_TX) || defined(I2C2_USE_DMA_RX))
 if (I2Cx==I2C2)
    iNemoI2CDMAInit(I2C2);
#endif 


}
void iNemoI2CDMAInit(I2C_TypeDef* I2Cx)
{
  /* Enable the DMA1 clock */
  RCC_AHBPeriphClockCmd(RCC_AHBPeriph_DMA1, ENABLE);

  /* I2C TX DMA Channel configuration */    
  I2CDMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)0;   /* This parameter will be configured durig communication */
  I2CDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;    /* This parameter will be configured durig communication */
  I2CDMA_InitStructure.DMA_BufferSize = 0xFFFF;            /* This parameter will be configured durig communication */
  I2CDMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
  I2CDMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
  I2CDMA_InitStructure.DMA_PeripheralDataSize = DMA_MemoryDataSize_Byte;
  I2CDMA_InitStructure.DMA_MemoryDataSize = DMA_MemoryDataSize_Byte;
  I2CDMA_InitStructure.DMA_Mode = DMA_Mode_Normal;
  I2CDMA_InitStructure.DMA_Priority = DMA_Priority_VeryHigh;
  I2CDMA_InitStructure.DMA_M2M = DMA_M2M_Disable;

  if(I2Cx==I2C2)
  {
    I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C2_DR_Address;

#ifdef I2C2_USE_DMA_TX
      DMA_DeInit(I2C2_DMA_CHANNEL_TX);
      DMA_Init(I2C2_DMA_CHANNEL_TX, &I2CDMA_InitStructure);
#endif

#ifdef I2C2_USE_DMA_RX
      /* I2C2 RX DMA Channel configuration */
      DMA_DeInit(I2C2_DMA_CHANNEL_RX);
      DMA_Init(I2C2_DMA_CHANNEL_RX, &I2CDMA_InitStructure);
#endif
  }

  if(I2Cx==I2C1)
  {
    I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C1_DR_Address;

#ifdef I2C1_USE_DMA_TX
      DMA_DeInit(I2C1_DMA_CHANNEL_TX);
      DMA_Init(I2C1_DMA_CHANNEL_TX, &I2CDMA_InitStructure);
#endif

#ifdef I2C1_USE_DMA_RX
      /* I2C1 RX DMA Channel configuration */
      DMA_DeInit(I2C1_DMA_CHANNEL_RX);
      DMA_Init(I2C1_DMA_CHANNEL_RX, &I2CDMA_InitStructure);
#endif

  }
void iNemoI2CDMAConfig(I2C_TypeDef* I2Cx, uint8_t* pBuffer, uint32_t lBufferSize, uint32_t lDirection)
{
  /* Initialize the DMA with the new parameters */
  if (lDirection == I2C_DIRECTION_TX)
  {
    /* Configure the DMA Tx Channel with the buffer address and the buffer size */
    I2CDMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)pBuffer;
    I2CDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralDST;
    I2CDMA_InitStructure.DMA_BufferSize = (uint32_t)lBufferSize;
    if(I2Cx==I2C2)
    {
      I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C2_DR_Address;
      DMA_Cmd(I2C2_DMA_CHANNEL_TX, DISABLE);
      DMA_Init(I2C2_DMA_CHANNEL_TX, &I2CDMA_InitStructure);
      DMA_Cmd(I2C2_DMA_CHANNEL_TX, ENABLE);
    }
    else
    {
      I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C1_DR_Address;
      DMA_Cmd(I2C1_DMA_CHANNEL_TX, DISABLE);
      DMA_Init(I2C1_DMA_CHANNEL_TX, &I2CDMA_InitStructure);
      DMA_Cmd(I2C1_DMA_CHANNEL_TX, ENABLE);
    }
  }
  else /* Reception */
  {
    /* Configure the DMA Rx Channel with the buffer address and the buffer size */
    I2CDMA_InitStructure.DMA_MemoryBaseAddr = (uint32_t)pBuffer;
    I2CDMA_InitStructure.DMA_DIR = DMA_DIR_PeripheralSRC;
    I2CDMA_InitStructure.DMA_BufferSize = (uint32_t)lBufferSize;

    if(I2Cx==I2C2)
    {
      I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C2_DR_Address;
      DMA_Cmd(I2C2_DMA_CHANNEL_RX, DISABLE);
      DMA_Init(I2C2_DMA_CHANNEL_RX, &I2CDMA_InitStructure);
      DMA_Cmd(I2C2_DMA_CHANNEL_RX, ENABLE);
    }
    else
    {
      I2CDMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)I2C1_DR_Address;
      DMA_Cmd(I2C1_DMA_CHANNEL_RX, DISABLE);
      DMA_Init(I2C1_DMA_CHANNEL_RX, &I2CDMA_InitStructure);
      DMA_Cmd(I2C1_DMA_CHANNEL_RX, ENABLE);
   }
  }
}

void iNemoI2CBufferReadDma(I2C_TypeDef* I2Cx, uint8_t cAddr, uint8_t*    pcBuffer, uint8_t cReadAddr, uint8_t cNumByteToRead)
{

__IO uint32_t temp = 0;
__IO uint32_t Timeout = 0;

/* Enable I2C errors interrupts */
I2Cx->CR2 |= I2C_IT_ERR;

/* Set the MSb of the register address in case of multiple readings */
if(cNumByteToRead>1)
  cReadAddr |= 0x80;

#ifdef FORCE_CRITICAL_SEC
    __disable_irq();
#endif    

#ifdef USART_DEBUG2
    USART1_Printf("FLAG BUSY\r\n");
#endif

Timeout = 0xFFFF;
/* While the bus is busy */
while(I2C_GetFlagStatus(I2Cx, I2C_FLAG_BUSY)){
    if (Timeout-- == 0)
        return;
}

/* Send START condition */
I2C_GenerateSTART(I2Cx, ENABLE);

#ifdef USART_DEBUG2
    USART1_Printf("MASTER MODE\r\n");
#endif

Timeout = 0xFFFF;
/* Test on EV5 and clear it */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT)){
    if (Timeout-- == 0)
        return;
}

/* Send LSM303DLH address for read */
I2C_Send7bitAddress(I2Cx, cAddr, I2C_Direction_Transmitter);

Timeout = 0xFFFF;
/* Test on EV6 and clear it */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED)){
    if (Timeout-- == 0)
        return;
}

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

/* Send the LSM303DLH_Magn's internal address to write to */
I2C_SendData(I2Cx, cReadAddr);

#ifdef USART_DEBUG2
    USART1_Printf("BYTE TRANSMITTED\r\n");
#endif

Timeout = 0xFFFF;

/* Test on EV8 and clear it */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED)){
    if (Timeout-- == 0)
        return;
}

/* Configure I2Cx DMA channel */
iNemoI2CDMAConfig(I2Cx, pcBuffer, cNumByteToRead, I2C_DIRECTION_RX);

/* Set Last bit to have a NACK on the last received byte */
I2Cx->CR2 |= 0x1000;

/* Enable I2C DMA requests */
I2C_DMACmd(I2Cx, ENABLE);
Timeout = 0xFFFF;

/* Send START condition */
I2C_GenerateSTART(I2Cx, ENABLE);

Timeout = 0xFFFF;

/* Wait until SB flag is set: EV5  */
while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
{
  if (Timeout-- == 0)
    return;
}
Timeout = 0xFFFF;

/* Send LSM303DLH address for read */
I2C_Send7bitAddress(I2Cx, cAddr, I2C_Direction_Receiver);

Timeout = 0xFFFF;

    /* Wait until ADDR is set: EV6 */
    while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
   {
      if (Timeout-- == 0)
        return;
    }
    /* Clear ADDR flag by reading SR2 register */
    temp = I2Cx->SR2;



    if(I2Cx == I2C2)
    {
      Timeout = 0xFFFF;
      /* Wait until DMA end of transfer */
      while (!DMA_GetFlagStatus(DMA1_FLAG_TC5)){
        if (Timeout-- == 0)
            return;
    }
      /* Disable DMA Channel */
      DMA_Cmd(I2C2_DMA_CHANNEL_RX, DISABLE);

      /* Clear the DMA Transfer Complete flag */
      DMA_ClearFlag(DMA1_FLAG_TC5);
    }
    else
    {
      /* Wait until DMA end of transfer */
    #ifdef USART_DEBUG2
        USART1_Printf("END TRANSFER\r\n");
    #endif
      Timeout = 0xFFFF;
      while (!DMA_GetFlagStatus(DMA1_FLAG_TC7)){
       if (Timeout-- == 0)
            return;
    }
      /* Disable DMA Channel */
      DMA_Cmd(I2C1_DMA_CHANNEL_RX, DISABLE);

      /* Clear the DMA Transfer Complete flag */
      DMA_ClearFlag(DMA1_FLAG_TC7);
    }


    /* Disable Ack for the last byte */
    I2C_AcknowledgeConfig(I2Cx, DISABLE);

    /* Send STOP Condition */
    I2C_GenerateSTOP(I2Cx, ENABLE);

    #ifdef USART_DEBUG2
        USART1_Printf("STOP BIT\r\n");
   #endif
   Timeout = 0xFFFF;
   /* Make sure that the STOP bit is cleared by Hardware before CR1 write access */
   while ((I2Cx->CR1 & 0x0200) == 0x0200){
       if (Timeout-- == 0)
        return;
   }

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

#ifdef FORCE_CRITICAL_SEC
    __enable_irq();
#endif

}
void iNemoI2CBufferWriteDma(I2C_TypeDef* I2Cx, uint8_t cAddr, uint8_t* pcBuffer, uint8_t cWriteAddr, uint8_t cNumByteToWrite)
{

  __IO uint32_t temp = 0;
  __IO uint32_t Timeout = 0;

  static uint8_t pcDmaBuffer[DMA_BUFFER_SIZE+1];

  /* Set to 1 the MSb of the register address in case of multiple byte writing */
  if(cNumByteToWrite>1)
    cWriteAddr |= 0x80;

  pcDmaBuffer[0]=cWriteAddr;
  memcpy(&pcDmaBuffer[1],pcBuffer,cNumByteToWrite);

  /* Enable Error IT  */
  I2Cx->CR2 |= I2C_IT_ERR;

  Timeout = 0xFFFF;
  /* Configure the DMA channel for I2Cx transmission */
  iNemoI2CDMAConfig(I2Cx, pcDmaBuffer, cNumByteToWrite+1, I2C_DIRECTION_TX);

  /* Enable DMA for I2C */
  I2C_DMACmd(I2Cx, ENABLE);

  /* Send START condition */
  I2C_GenerateSTART(I2Cx, ENABLE);


  /* Wait until SB flag is set: EV5 */
  while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_MODE_SELECT))
  {
    if (Timeout-- == 0)
      return;
  }

  Timeout = 0xFFFF;

  /* Send LSM303DLH address for write */
  I2C_Send7bitAddress(I2Cx, cAddr, I2C_Direction_Transmitter);

  /* Wait until ADDR is set: EV6 */
  while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
  {
    if (Timeout-- == 0)
      return;
  }

  /* Clear ADDR flag by reading SR2 register */
  temp = I2Cx->SR2;


  /* Disable the DMA1 channel */
  if(I2Cx == I2C2)
  {
    /* Wait until DMA end of transfer */
    while (!DMA_GetFlagStatus(DMA1_FLAG_TC4));
    /* Disable DMA Channel */
    DMA_Cmd(I2C2_DMA_CHANNEL_TX, DISABLE);

    /* Clear the DMA Transfer complete flag */
    DMA_ClearFlag(DMA1_FLAG_TC4);
  }
  else
  {
    /* Wait until DMA end of transfer */
    while (!DMA_GetFlagStatus(DMA1_FLAG_TC6));
    /* Disable DMA Channel */
    DMA_Cmd(I2C1_DMA_CHANNEL_TX, DISABLE);

    /* Clear the DMA Transfer complete flag */
    DMA_ClearFlag(DMA1_FLAG_TC6);
  }


  /* EV8_2: Wait until BTF is set before programming the STOP */
  while(!I2C_CheckEvent(I2Cx, I2C_EVENT_MASTER_BYTE_TRANSMITTED));

  /* Send STOP Condition */
  I2C_GenerateSTOP(I2Cx, ENABLE);

  /* Make sure that the STOP bit is cleared by Hardware before CR1 write access */
  while ((I2Cx->CR1 & 0x0200) == 0x0200);

}

你的I2C读取出现了问题。可能你没有正确使用I2C API或者硬件设置不正确。 - Eugene Sh.
1
检查您在DSO上的I2C波形,确保您的位模式是正确的。 - Amol Saindane
@EugeneSh。我已经添加了代码,我认为它没问题,因为它是从ST的一个示例中取出的,但我在这里附上了它,请问您对此有什么意见? - Daniel
@Amol,我做不到,I2C连接嵌入在一个13x13mm的封装(iNemo)中,它集成了stmf103和传感器。通信是内部的。 - Daniel
1个回答

3
我看到你的一些while循环有超时限制,但有些却没有:

while ((I2Cx->CR1 & 0x0200) == 0x0200);

请所有循环都加上超时限制,并在出现错误条件时做个记录(需要进行调查,如果你不知道原因,它将在以后找回来困扰你)。

硬件有时可能会出现一些问题,所以完全有可能你在正确操作一切,但仍然无法正常工作。检查errata(适用于STM32 I2C和你的I2C从设备)以了解已记录的错误。

几年前我遇到过一个问题,I2C线路会保持低电平状态,我不得不将引脚重新配置为GPIO模式,使用bit-bang模式输出一些位,然后才能切换回I2C操作。

1
此外,将这种循环的分号写在同一行是不好的编程习惯。读者将无法确定缺少循环体是有意为之还是只是手指失误放置了分号。 - Lundin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接