I2C зависает на STM32F103

Вопрос или проблема

Я использую FreeRTOS для связи с 10 подчинёнными устройствами по I2C в нормальном режиме DMA на кастомной плате PCB. Код работает некоторое время, но I2C иногда зависает, прерывая связь.

Изначально мой код был следующим:

static void vMaster_Slave_i2c_task(void* parameters)
{
   vTaskDelay(100); 

   for(;;)
   {
      for(uint8_t addr_pos=0;addr_pos<NUM_OF_SLAVES;addr_pos++)
      {
         HAL_I2C_Master_Seq_Transmit_DMA(&hi2c1, (uint16_t)(addr_pos<<1), (uint8_t*)&(_slave_params[addr_pos].slave_param), 1,I2C_FIRST_AND_LAST_FRAME) ;
                                                                                                
         vTaskDelay(10);
                        
         HAL_I2C_Master_Seq_Receive_DMA(&hi2c1, (uint16_t)(addr_pos<<1), rec_data, 4,I2C_FIRST_AND_LAST_FRAME);
                                                                              
         vTaskDelay(10);
      }                 
   }
}

Когда он начал зависать где-то внутри api HAL_I2C_Master_Seq_Transmit_DMA, я изменил код на следующий, как описано в примерах проектов ST:

static void vMaster_Slave_i2c_task(void* parameters)
{
   vTaskDelay(100); 

   for(;;)
   {
      for(uint8_t addr_pos=0;addr_pos<NUM_OF_SLAVES;addr_pos++)
      {
         while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY){}

         while(HAL_I2C_Master_Seq_Transmit_DMA(&hi2c1, (uint16_t)(addr_pos<<1), (uint8_t*)&(_slave_params[addr_pos].slave_param), 1,I2C_FIRST_AND_LAST_FRAME) != HAL_OK) {}
                                                                                                
         vTaskDelay(10);
    
         while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY){}
                
         while(HAL_I2C_Master_Seq_Receive_DMA(&hi2c1, (uint16_t)(addr_pos<<1), rec_data, 4,I2C_FIRST_AND_LAST_FRAME) != HAL_OK) {}
                                                                              
         vTaskDelay(10);

      }             
   }
}

Он ведёт себя так же, но теперь зависает на первом while (HAL_I2C_GetState(&hi2c1) != HAL_I2C_STATE_READY){}, пока я не отключу и не включу питание. Я проверил осциллографом, и когда он зависает, нет импульсов SCL. Я использую подтягивающие резисторы 51K.

Когда я ставлю точку останова и захожу внутрь, я вижу состояние I2C HAL_TRANSMIT_BUSY. Я не понимаю, почему это происходит. Я уже убедился, что ни одно другое периферийное устройство не использует каналы DMA1 6 и 7.

Кроме того… я использую HAL_I2C_Master_Seq_Receive_DMA и HAL_I2C_Master_Seq_Transmit_DMA просто потому, что HAL_I2C_Master_Transmit_DMA и HAL_I2C_Master_Receive_DMA вообще не работают по каким-то причинам.

В проекте также настроен UART1, который использует каналы DMA1 4,5. Я могу использовать UART1 с DMA только в режиме приёма. Для передачи DMA не работает (вызов API просто игнорируется компилятором! ), поэтому я использую блокирующий режим.

Любые советы от вас будут полезны.

Ответ или решение

Решение проблемы зависания I2C на STM32F103

Введение

При использовании I2C в стандартном режиме DMA на микроконтроллере STM32F103 иногда возникают случаи зависания, что приводит к прерыванию связи с несколькими ведомыми устройствами. Эта проблема касается вашего кода, написанного для задач FreeRTOS, и требует внимательного анализа.

Описание проблемы

На начальном этапе вы использовали следующий код:

static void vMaster_Slave_i2c_task(void* parameters) {
   vTaskDelay(100); 

   for(;;) {
      for(uint8_t addr_pos=0; addr_pos<NUM_OF_SLAVES; addr_pos++) {
         HAL_I2C_Master_Seq_Transmit_DMA(&hi2c1, (uint16_t)(addr_pos<<1), 
                                          (uint8_t*)&(_slave_params[addr_pos].slave_param), 1,
                                          I2C_FIRST_AND_LAST_FRAME);
         vTaskDelay(10);

         HAL_I2C_Master_Seq_Receive_DMA(&hi2c1, (uint16_t)(addr_pos<<1), rec_data, 4,
                                         I2C_FIRST_AND_LAST_FRAME);
         vTaskDelay(10);
      }                 
   }
}

После появления зависаний в HAL_I2C_Master_Seq_Transmit_DMA вы изменили код на более "защищённый", добавив проверки состояния I2C. Однако ваша программа по-прежнему зависает, и состояние I2C остается HAL_TRANSMIT_BUSY, что указывает на проблемы с передачей данных.

Возможные причины проблемы

  1. Проблемы с признаками завершения передачи: I2C требует, чтобы передатчик получил подтверждение от ведомого устройства. Если подтверждение не было получено (например, если ведомое устройство не готово или не отвечает), состояние может застрять.

  2. Неправильные настройки шин: Проверьте, что все ведомые устройства правильно подключены к шине I2C, и что подтягивающие резисторы (51K) подходят для рабочих условий. Иногда сильно большие значения могут привести к недостаточному уровню сигнала.

  3. Однако, стоит проверить и ошибки в коде и конфигурации. Например, использование HAL_I2C_Master_Seq_Transmit_DMA и HAL_I2C_Master_Seq_Receive_DMA без правильной настройки DMA может привести к конфликтам.

  4. Взаимодействие с другими перифериями: Важно удостовериться, что другие периферии, такие как UART1, не мешают работе DMA. Обратите внимание на распределение каналов DMA.

Рекомендации по исправлению

  1. Убедитесь в корректной конфигурации шины I2C: Проверьте правильные значения для подтягивающих резисторов и подключения устройств.

  2. Увеличьте время ожидания: Добавьте некоторый тайм-аут в ваши циклы while, чтобы избежать бесконечных зависаний на этапе передачи.

  3. Используйте колбэки или прерывания: Вместо активного ожидания состояния I2C, рассмотрите возможность использования прерываний или колбэков, которые будут уведомлять вас о завершении передачи данных.

  4. Профилирование производительности: Попробуйте следить за состоянием DMA в реальном времени с помощью отладчика или дополнительных логов для понимания сути проблемы.

  5. Избегайте использования DMA для ненадежных периферий: Если у вас возникают постоянные проблемы с DMA на UART, попробуйте временно отключить его и использовать блокирующие функции, пока проблема не будет решена.

Заключение

Зависание I2C на STM32F103 может вызываться несколькими факторами, и ваша задача — выявить первопричину. Используя предложенные рекомендации и систематическое тестирование, вы сможете значительно улучшить стабильность вашей программы. Не забывайте также про документацию ST, где можно найти множество примеров и советов по работе с HAL библиотекой.

Оцените материал
Добавить комментарий

Капча загружается...