STM32F4 I2C с DMA не работает

Я использую один STM32F4 и хочу связаться с моим акселерометром LSM303. Для этого я использую I2C, и просто использование I2C работает нормально, но когда я пытаюсь использовать DMA с ним, он перестает работать. Когда я использую HAL_I2C_Master_Transmit_DMA, это работает, и я получил IRQHandler и. Но когда после этого я хочу использовать HAL_I2C_Master_Receive_DMA, он говорит, что состояние I2C не готово... Я читал, что I2C был отчасти испорчен с STM32FX, но я не понимаю, почему он работает нормально без DMA.

Также, когда он достигает обратного вызова I2C_DMAXferCplt для Master_Transmit_DMA, он говорит, что CurrentState I2C_HandleTypeDef по-прежнему равен 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?

Спасибо,

Виктор

0 ответов

У меня это сработало, когда я включил прерывание I2C_event поверх прерывания DMA, см. Сгенерированный код и конфигурацию CubeMX ниже

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

CubeMX не проверяет автоматически глобальное прерывание события I2C1 при выборе DMA, я думаю, что должен (STmicro, пожалуйста, исправьте это), поскольку я не вижу, как он может работать без него.

У меня была такая же проблема. Решил понижением частоты.

В документе ST Errata говорится, что вам нужно понизить частоту I2C до 88 кГц, чтобы исправить другую проблему.

Я знаю, что это не объясняет, почему эта ошибка не возникает в режиме блокировки, а происходит с DMA, но я надеюсь, что это поможет.

Я боролся с той же проблемой на STM32F407 и I2C1.

После поиска потенциальных ошибок в потоке программы я обнаружил, что функция 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);
    }
}

Учтите, что это только обходной путь. Если кто-то узнает, я хотел бы больше узнать об основной причине этой ошибки.

Другие вопросы по тегам