Embedded programming block, I2C?

I am having a very strange problem while developing my ST Microelectronics iNemo application. My applications are as follows:

  • Reading gyroscope with SPI
  • Accelerometer and magnetometer (in the same device) with I2C
  • Algorithm for evaluating the ratio
  • PD functions
  • Receiving data using USART with interrupt without DMA
  • Sending a Logging Package Using USART

The cycle is started by a timer at a frequency of 100 Hz. The program works well (I tested it with some USART debug prints) until I start sending data using the USART: my initial guess was that since this fact allows interrupts to be received, it causes problems with the I2C bus arbitration mechanism. My guess is that when I was able to debug the problem (time-dependent) when printing the USART, I found that the last print is always before the accelerometer of the magnetometer fingerprints (the first one I call in my code). Also, if I enable verbose debug prints via the USART that I mentioned, the problem occurs in fewer cases, and if I disable it and I only send the logging protocol, the problem always occurs immediately. Can anyone give me an idea of ​​howwhat could be causing this problem? Thanks to

EDIT: I am attaching my I2C code:

#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 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);

}

      

+3


source to share


1 answer


I see that for some loops you have a timeout, but for some you don't:

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

Do all the timeouts of your loops and also note where the error condition occurs (it needs to be investigated - if you don't know the cause, it will reappear to haunt you).



The hardware can be a bit buggy sometimes, so it is entirely possible that you are doing everything right, but it still doesn't work. Check errors (for STM32 I2C and your I2C slaves) for documented errors.

A few years ago I ran into a problem where the I2C lines stayed low and I had to reconfigure the pins as GPIOs, bit-beat a few bits, and then I was able to get back to I2C working.

+3


source







All Articles