4
votes

I'm using one STM32F4 and I want to communicate with my LSM303 accelerometer. For that I'm using I2C, and just using I2C works fine but when I try to use DMA with it, it stops working. When I use HAL_I2C_Master_Transmit_DMA it works and I got the IRQHandler and . But when after that I want to use HAL_I2C_Master_Receive_DMA it says that the State of the I2C is not ready... I read that the I2C was kind of messed up with the STM32FX but I don't understand why it's working fine without DMA.

Also when it hits the callback I2C_DMAXferCplt for the Master_Transmit_DMA it says that the CurrentState of the I2C_HandleTypeDef is still equal to HAL_I2C_STATE_BUSY_TX and therefor it does not put the state back to READY. That why it does not receive anything when I call the Master_Receive_DMA.

Here's my I2C init :

    void MX_I2C2_Init(void)
      {
          I2C_ST_INS.Instance = I2C2;
          I2C_ST_INS.Init.ClockSpeed = 400000;
           I2C_ST_INS.Init.DutyCycle = I2C_DUTYCYCLE_2;
           I2C_ST_INS.Init.OwnAddress1 = 0;
          I2C_ST_INS.Init.AddressingMode = I2C_ADDRESSINGMODE_7BIT;
          I2C_ST_INS.Init.DualAddressMode = I2C_DUALADDRESS_DISABLED;
          I2C_ST_INS.Init.OwnAddress2 = 0;
          I2C_ST_INS.Init.GeneralCallMode = I2C_GENERALCALL_DISABLED;
          I2C_ST_INS.Init.NoStretchMode = I2C_NOSTRETCH_DISABLED;

          HAL_I2C_Init(&I2C_ST_INS);

        }

   void HAL_I2C_MspInit(I2C_HandleTypeDef* i2cHandle)
   {
          GPIO_InitTypeDef GPIO_InitStruct;
          if(i2cHandle->Instance==I2C1)
          {
              //Not useful for this post
          }
          else if(i2cHandle->Instance==I2C2)
          {

            GPIO_InitStruct.Pin = MASTER_IMUB_I2C_SDA_Pin|MASTER_IMUB_I2C_SCL_Pin;
            GPIO_InitStruct.Mode = GPIO_MODE_AF_OD;
            GPIO_InitStruct.Pull = GPIO_PULLUP;
            GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
            GPIO_InitStruct.Alternate = GPIO_AF4_I2C2;
            HAL_GPIO_Init(GPIOF, &GPIO_InitStruct);

        __HAL_RCC_I2C2_CLK_ENABLE();

            /* DMA controller clock enable */
            __HAL_RCC_DMA1_CLK_ENABLE();
            hdma_i2c2_rx.Instance = DMA1_Stream2;
            hdma_i2c2_rx.Init.Channel = DMA_CHANNEL_7;
            hdma_i2c2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY;
            hdma_i2c2_rx.Init.PeriphInc = DMA_PINC_DISABLE;
            hdma_i2c2_rx.Init.MemInc = DMA_MINC_ENABLE;
            hdma_i2c2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
            hdma_i2c2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
            hdma_i2c2_rx.Init.Mode = DMA_NORMAL;
            hdma_i2c2_rx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
            hdma_i2c2_rx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
            hdma_i2c2_rx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
            hdma_i2c2_rx.Init.MemBurst = DMA_MBURST_SINGLE;
            hdma_i2c2_rx.Init.PeriphBurst = DMA_PBURST_SINGLE;
            if (HAL_DMA_Init(&hdma_i2c2_rx) != HAL_OK)
            {
              Error_Handler();
            }
            __HAL_LINKDMA(i2cHandle,hdmarx,hdma_i2c2_rx);

            HAL_NVIC_SetPriority(DMA1_Stream2_IRQn, 0, 0);
           HAL_NVIC_EnableIRQ(DMA1_Stream2_IRQn);


            hdma_i2c2_tx.Instance = DMA1_Stream7;
            hdma_i2c2_tx.Init.Channel = DMA_CHANNEL_7;
            hdma_i2c2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH;
            hdma_i2c2_tx.Init.PeriphInc = DMA_PINC_DISABLE;
            hdma_i2c2_tx.Init.MemInc = DMA_MINC_ENABLE;
            hdma_i2c2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE;
            hdma_i2c2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE;
            hdma_i2c2_tx.Init.Mode = DMA_NORMAL;
            hdma_i2c2_tx.Init.Priority = DMA_PRIORITY_VERY_HIGH;
            hdma_i2c2_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE;
            hdma_i2c2_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL;
            hdma_i2c2_tx.Init.MemBurst = DMA_MBURST_SINGLE;
            hdma_i2c2_tx.Init.PeriphBurst = DMA_PBURST_SINGLE;
            if (HAL_DMA_Init(&hdma_i2c2_tx) != HAL_OK)
            {
              Error_Handler();
            }

            __HAL_LINKDMA(i2cHandle,hdmatx,hdma_i2c2_tx);

           HAL_NVIC_SetPriority(DMA1_Stream7_IRQn, 0, 0);
           HAL_NVIC_EnableIRQ(DMA1_Stream7_IRQn);
          }
        }

Do you have any ideas why it does not work when I'm using DMA with I2C ?

Thanks,

Victor

3
What if you will wait till it ready with while (HAL_I2C_GetState(&hi2c2) != HAL_I2C_STATE_READY) { vTaskDelay(1); }? Looks like slave wont release line after command recieve. What PullUp resistors do you use?Bulkin
I tried to wait till state READY but it's just an endless loop. For my pull-up resistors I use 4,7k on SDA and same on SCL. But the thing I really don't understand is why does it works fine when I use the receive and transmit functions in blocking mode and why it does not work when I'm using non-blocking function with DMA. It does not change the way I2C works right ?Victor Douet
I do not see INT for I2C? Did you enable it?Bulkin
I'm using the older ST Standard Peripheral Library rather than the HAL and have the exact same problem. Could you find what's wrong? Perhaps this is better suited for the electronics stackexchange.Sohail

3 Answers

2
votes

It worked for me when I enabled the I2C_event interrupt on top of the DMA interrupt, see generated code and CubeMX config below

HAL_NVIC_SetPriority(I2C1_EV_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(I2C1_EV_IRQn);

My CubeMX configuration, notice that the event interrupt is checked

CubeMX does not automatically check the I2C1 event global interrupt when selecting DMA, I think it should (STmicro please fix this) as I dont see how it can work without it.

1
votes

I've had the same issue. I've solved it by lowering the frequency.

ST Errata document says you have to step down the I2C frequency to 88kHz to fix some other problem.

I know it doesn't explain why this error doesn't occur in blocking mode but happens with DMA, but I hope it helps.

0
votes

I had been struggling with the same problem on STM32F407 and I2C1.

After searching for potential bugs in the program flow, I found out that the function HAL_I2C_Master_Transmit_DMA leads to following line:

dmaxferstatus = HAL_DMA_Start_IT(hi2c->hdmatx, (uint32_t)hi2c->pBuffPtr, (uint32_t)&hi2c->Instance->DR, hi2c->XferSize);

After the first transfer, this won't return HAL_OK, which is necessary for the transmission to continue.

So my solution was simply abort the previous DMA interrupt in the callback function which is called after the transmission has completed. The same can be implied with HAL_I2C_Master_Receive_DMA. To resolve the problem, I added the following callback functions in main.c:

void HAL_I2C_MasterTxCpltCallback(I2C_HandleTypeDef *hi2c)
{
    if (hi2c->Instance==hi2c1.Instance)
    {
        HAL_DMA_Abort_IT(hi2c->hdmatx);
    }
}

void HAL_I2C_MasterRxCpltCallback(I2C_HandleTypeDef *hi2c)
{
    if (hi2c->Instance==hi2c1.Instance)
    {
        HAL_DMA_Abort_IT(hi2c->hdmarx);
    }
}

Please consider this is only a workaround. If someone finds out, I would like to know more about the underlying reason for this bug.