Вопрос или проблема
Я использую 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
, что указывает на проблемы с передачей данных.
Возможные причины проблемы
-
Проблемы с признаками завершения передачи: I2C требует, чтобы передатчик получил подтверждение от ведомого устройства. Если подтверждение не было получено (например, если ведомое устройство не готово или не отвечает), состояние может застрять.
-
Неправильные настройки шин: Проверьте, что все ведомые устройства правильно подключены к шине I2C, и что подтягивающие резисторы (51K) подходят для рабочих условий. Иногда сильно большие значения могут привести к недостаточному уровню сигнала.
-
Однако, стоит проверить и ошибки в коде и конфигурации. Например, использование
HAL_I2C_Master_Seq_Transmit_DMA
иHAL_I2C_Master_Seq_Receive_DMA
без правильной настройки DMA может привести к конфликтам. -
Взаимодействие с другими перифериями: Важно удостовериться, что другие периферии, такие как UART1, не мешают работе DMA. Обратите внимание на распределение каналов DMA.
Рекомендации по исправлению
-
Убедитесь в корректной конфигурации шины I2C: Проверьте правильные значения для подтягивающих резисторов и подключения устройств.
-
Увеличьте время ожидания: Добавьте некоторый тайм-аут в ваши циклы
while
, чтобы избежать бесконечных зависаний на этапе передачи. -
Используйте колбэки или прерывания: Вместо активного ожидания состояния I2C, рассмотрите возможность использования прерываний или колбэков, которые будут уведомлять вас о завершении передачи данных.
-
Профилирование производительности: Попробуйте следить за состоянием DMA в реальном времени с помощью отладчика или дополнительных логов для понимания сути проблемы.
-
Избегайте использования DMA для ненадежных периферий: Если у вас возникают постоянные проблемы с DMA на UART, попробуйте временно отключить его и использовать блокирующие функции, пока проблема не будет решена.
Заключение
Зависание I2C на STM32F103 может вызываться несколькими факторами, и ваша задача — выявить первопричину. Используя предложенные рекомендации и систематическое тестирование, вы сможете значительно улучшить стабильность вашей программы. Не забывайте также про документацию ST, где можно найти множество примеров и советов по работе с HAL библиотекой.