Найти тему

STM32 + I2C (STM32CubeIDE). Краткое сравнение с Wire(Arduino)

Оглавление

Вторая часть про настройку NUCLEO-F746ZG для работы по интерфейсу I2C. В предыдущей части был создан, настроен проект в STM32CubeMX, после чего сгенерирован исходный код для STM32CubeIDE. Основные файлы хранятся в этом репозитории.

Основной файл, с которым будем работать, называется main.c.

В этой части будет решены следующие задачи:

1. Поиск устройств на шине I2C

2. Отправка данных slave-устройству

3. Получение данных от slave-устройства

Формальная организация

На этапе создания проекта в Кубе мы оставили галку на "Keep User Code when re-generating ". Однако, чтобы написанный код сохранялся, нужно его размещать внутри блоков:

/* USER CODE BEGIN I2C2_Init 2 */
/* USER CODE END I2C2_Init 2 */

, где вместо I2C2_Init 2 может быть другое название другого блока кода.

Поиск устройств по шине

Следующая команда проверяет наличие устройств на шине I2C. Следует помнить, что значение искомого адреса перед отправкой нужно сдвинуть влево согласно даташиту.

* @param DevAddress Target device address: The device 7 bits address value
* in datasheet must be shifted to the left before calling the interface

Адрес у нас 7 битный, а значит количество возможных адресов у нас ограничено 127. В результате получаем следующий алгоритм:

for (uint16_t i = 0x00; i < 127; i++)
{
if(HAL_I2C_IsDeviceReady(&hi2c2,(i<<1),1,100) == HAL_OK)
Что-то делаем, если нашли
else
Нужно что-то делать, если не нашли
}
1. hi2c2 -- структура управления I2C
Примечание: (i<<1) -- это сдвиг влево адреса

Интерфейс I2C использует сигнал Acknowledgement (Подтверждение) для идентификации наличия устройства на опрашиваемом адресе. Ниже приведены примеры отсутствия и наличия ответа от опрашиваемого устройства

Подтверждение устройства по адресу 0x58 не было получено
Подтверждение устройства по адресу 0x58 не было получено

Найдено устройство по адресу 0x5A
Найдено устройство по адресу 0x5A

Отправка данных устройству

Если мы нашли что-либо на шине устройства, то следующим шагом будет передача данных на это устройство.

HAL_I2C_Master_Transmit(&hi2c2, (TargetI2Cdevice<<1), ptI2Cbuffer2transmit, 4, 10);
1. TargetI2Cdevice -- адрес устройства
2. ptI2Cbuffer2transmit -- буфер с 4 значениями, которые необходимо передать
3. Последнее значение, равное 10, указывает промежуток ожидания между отправкой данных

Результат отправки данных приведен ниже: сначала отправлен адрес, а потом четыре байта данных. На каждую итерацию отправки информации было получено подтверждение.

Верхний график иллюстрирует работу по линии SDA, нижний график по линии SCL. Отчетливо видны сигналы тактирования при передаче данных.
Верхний график иллюстрирует работу по линии SDA, нижний график по линии SCL. Отчетливо видны сигналы тактирования при передаче данных.

Получение данных от устройства

Чтобы получить данные, нужна похожая команда:

HAL_I2C_Master_Receive(&hi2c2, (TargetI2Cdevice<<1), ptI2Cbuffer4receive, I2C_RECEIVE_CNT, 10);
1. ptI2Cbuffer4receive -- буффер, в который будут записаны данные
2. I2C_RECEIVE_CNT -- макрос, указывающий количество данных для получения

В моем случае, я получаю 80 значений по шине, поэтому график получается немного ненаглядный.

Тактирование на графике слилось в сплошную полосу из-за масштаба, зато заметно, что для передачи 80 значений потребовалось около 2 миллисекунд.
Тактирование на графике слилось в сплошную полосу из-за масштаба, зато заметно, что для передачи 80 значений потребовалось около 2 миллисекунд.

Стоит отметить, что устройство предполагает отправку большего количества данных. Поэтому последний байт сопровождается отправкой NACK.

-5

На самом в устройстве этот разрыв приведен к ошибке типа Acknowledge Failure.

Немного о скорости стандартной библиотеки HAL и I2C. Изначально, поставим задачу: сколько можно передать данных за 10 мс, иначе при частоте отправке пачек 100 Гц?

Опытным путем было получено, что наиболее стабильная передача данных получается на данной частоте при следующих характеристиках:

1. Режим I2C: Fast mode (400 кГц)

2. Количество данных в пачке: 390

2. Промежуток ожидания(последний параметр функции) 10

Возникает вопрос: Можно ли ускорить передачу данных, запрограммировав на регистрах?

Визуализация передачи при таких характеристиках можно наблюдать ниже.

Общая длительность
Общая длительность

Уточненное время передачи равно 9.7 мс.

Увеличение на конечных данных
Увеличение на конечных данных

Сравнение с Arduino

Часто к преимуществам Arduino относят минимальное количество кода, которого надо написать для решения задачи. Рассмотрим количество кода на примере I2C (функция передачи данных).

Arduino

Wire.begin();
Wire.beginTransmission(address);
for(int j = 0; j < 3; j++)
{
Wire.write(value);
}
Wire.endTransmission(true);

STM32 HAL

HAL_I2C_Master_Transmit(&hi2c2, (TargetI2Cdevice<<1), ptI2Cbuffer2transmit, 4, 10);

В обоих случаях, это ровно то, что необходимо написать для успешной отправки данных. В Ардуино получается даже немного больше. В целом, сейчас все преимущество Ардуино это связка расширений и библиотек. Это лично мое мнение.

Более того, я считаю, что графический интерфейс Куба гораздо лучше того, чего предлагает Ардуино (а Ардуино предложить собственно и нечего).

Лайк?