STM32F4使用DMA的I2C无法工作。

5
我正在使用STM32F4并希望与LSM303加速度计通信。为此,我使用I2C,仅使用I2C时一切正常,但当我尝试将DMA与之结合使用时,它停止工作。
当我使用HAL_I2C_Master_Transmit_DMA时,它可以工作并成功获得IRQHandler。但是,在此之后,当我想要使用HAL_I2C_Master_Receive_DMA时,它显示I2C状态不可用...
我读到了STM32FX的I2C有点混乱,但我不明白为什么不使用DMA时一切正常。
还有,当它触发Master_Transmit_DMA的回调函数I2C_DMAXferCplt时,它显示I2C_HandleTypeDef的CurrentState仍然等于HAL_I2C_STATE_BUSY_TX,因此它不会将状态设置回READY。这就是为什么在我调用Master_Receive_DMA时它无法接收任何数据。
以下是我的I2C初始化代码:
    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);
          }
        }

你有没有想过为什么在使用DMA和I2C时它不能正常工作?

谢谢,

维克多


如果你使用while (HAL_I2C_GetState(&hi2c2) != HAL_I2C_STATE_READY) { vTaskDelay(1); }等待它准备好会怎样呢?看起来从设备接收命令后,从设备不会释放线路。你使用了什么上拉电阻? - Bulkin
我试图等待状态为READY,但只是一个无限循环。对于我的上拉电阻,我在SDA上使用4.7k,在SCL上也是同样的值。但我真正不理解的是,为什么当我使用阻塞模式下的接收和传输函数时它能正常工作,而当我使用带DMA的非阻塞函数时却不能正常工作。这不会改变I2C的工作方式,对吧? - Victor Douet
我没有看到I2C的INT?你启用了吗? - Bulkin
我正在使用旧版的ST标准外设库而不是HAL,并且遇到了完全相同的问题。你能找出问题在哪里吗?也许这更适合在电子堆栈交换上讨论。 - Sohail
同样的问题,我也在使用STM32f7,但无法处理DMA,但在阻塞I2C上运行良好!!! @Bulkin INT是什么? - mohammadsdtmnd
3个回答

5
当我在DMA中断之上启用I2C事件中断时,它对我起作用了,请参见下面生成的代码和CubeMX配置。
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在选择DMA时没有自动检查I2C1事件全局中断,我认为它应该这样做(STmicro请修复此问题),因为我不知道它如何在没有这个中断的情况下工作。

1
我曾经遇到过相同的问题。我通过降低频率来解决它。
ST勘误文件指出,您必须将I2C频率降至88kHz才能解决其他问题。
我知道这并没有解释为什么这个错误在阻塞模式下不会发生,但在DMA模式下会发生,但我希望它有所帮助。

0

我一直在处理关于 STM32F407I2C1 的同样问题。

在搜索程序流程中可能存在的错误后,我发现函数 HAL_I2C_Master_Transmit_DMA 导致了以下行:

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

在第一次传输后,这个函数不会返回HAL_OK,而这对于继续传输是必要的。

因此,我的解决方案是在回调函数中中止先前的DMA中断,该函数在传输完成后被调用。对于HAL_I2C_Master_Receive_DMA也可以进行同样的操作。为了解决这个问题,我在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);
    }
}

请注意,这只是一个解决方法。如果有人找到了更深层次的原因,请告诉我。

但是,CUBE在I2C和DMA上生成的中断处理程序(如jeluu所提到的)必须处理这个问题,不是吗? - mohammadsdtmnd

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