О том, как подключить VL53L0 к STM32F103C8T6 и сделать так, чтобы всё работало.
Для чего нужны ToF-датчики?
ToF-датчики (Time-of-Flight) используются для точного измерения расстояния до объектов на основе времени прохождения светового сигнала. Они применяются в различных сферах, включая робототехнику, где помогают избегать препятствий, в смартфонах, а также в системах дополненной реальности для точного позиционирования виртуальных объектов. В промышленности ToF-датчики используют для контроля качества и автоматизации процессов, а в умных домах — для обнаружения присутствия людей и управления освещением. Их ключевые преимущества — высокая точность, быстродействие и стабильная работа в разных условиях освещенности.
Практическая часть
Изначально я хотел использовать времяпролетный датчик VL53L5, но нашел VL53L0, который заметно дешевле и попроще. Затем начал искать для него библиотеку и нашел её на официальном сайте STMicroelectronics, однако запустить VL53L0 с ней не вышло. В итоге, отыскал рабочую библиотеку на GitHub с использованием STM32F401 и переделал её для работы с STM32F103.
Прежде всего, запускаем CubeIDE и настраиваем проект
Во вкладке RCC выбираю Crystal/Ceramic Resonator
Во вкладке SYS выбираю Serial Wire
Во вкладке Connectivity включаю I2C1 и USART2
Включение I2C1 Включение USART2 Выход PB9 устанавливаю, как GPIO_EXTI9
Затем перехожу во вкладку NVIC и ставлю галочку на EXTI line interrupts
Во вкладе GPIO ставлю следующие настройки
Выставляю максимальную частоту 72 Гц
И создаю проект
Затем необходимо добавить саму библиотеку для VL53L0. Для этого:
Нужно скачать библиотеку с GitHub - https://github.com/lamik/VL53L0X_API_INT_STM32_HAL
Заходим в архив VL53L0X_API_INT_STM32_HAL, каталог Drivers и копируем папку VL53L0X.
Затем находим папку с проектом и переходим в каталог Drivers и помещаем в него скопированную папку VL53L0X.
В папке VL53L0X переходим в каталог platform, потом в папку src, открываем файл vl53l0x_platform.c, например, в блокноте или любом другом редакторе текста и меняем строку #include "stm32f4xx_hal.h" на #include "stm32f1xx_hal.h". После этого, возвращаемся в каталог platform и в папке inc открываем файл vl53l0x_platform.h и также меняем строку #include "stm32f4xx_hal.h" на #include "stm32f1xx_hal.h".
Изменение файла vl53l0x_platform.c Изменение файла vl53l0x_platform.h Теперь переключаемся в CubeIDE. В боковой панеле справа один раз нажимаем на наш проект и жмем сочетание клавиш Alt + Enter или нажимаем на File в верхнем левом углу и жмем Properties
Раскрываем список, нажимая на галочку возле C/C++ General, и из выпадающего списка выбираем пункт Paths and Symbols
Нажимаем на кнопку Add справа и добавляем путь до папки Inc, находящейся в каталоге Drivers, в папке VL53L0, в каталоге Core
Жмем на File system Нажимаем ОК То же самое проделываем для папки Inc, только находящейся в каталоге platform
Добавление папки Inc из каталога platform Результат Жмём Apply.
Теперь нажмем на галочку возле C/C++ Build и в выпадающем списке нажмем на Settings. Затем перейдем в MCU/MPU Settings и поставим галочку возле Use float with printf from newlib-nano. Также нам нужен hex-файл, для этого перейдем в MCU/MPU Post build outputs и поставим галочку возле Convert to Intel Hex file
Жмем Apply и Apply and Close
Наконец, переходим к коду.
Подключаем библиотеку для работы с VL53L0
/* USER CODE BEGIN Includes */
#include "vl53l0x_api.h"
#include "stdio.h"
/* USER CODE END Includes */
Инициализируем переменные для вывода измерений и работы датчика
/* USER CODE BEGIN PV */
uint8_t Message[64];
uint8_t MessageLen;
VL53L0X_RangingMeasurementData_t RangingData;
VL53L0X_Dev_t vl53l0x_c; // center module
VL53L0X_DEV Dev = &vl53l0x_c;
volatile uint8_t TofDataRead;
/* USER CODE END PV */
Добавил собственные функции для вывода данных через UART
/* USER CODE BEGIN 0 */
void String_write(char sx[])
{
HAL_UART_Transmit(&huart2, (uint8_t *)sx, strlen(sx), 100);
}
void String_writeln(char sx[])
{
String_write(sx);
char cr[] = "\r\n";
HAL_UART_Transmit(&huart2, (uint8_t *)cr, strlen(cr), 100);
}
void Int_write(int32_t x)
{
char buf[BUFSIZ];
sprintf(buf, "%ld", x);
String_write(buf);
}
void Int_writeln(int32_t x)
{
Int_write(x);
char cr[] = "\r\n";
HAL_UART_Transmit(&huart2, (uint8_t *)cr, strlen(cr), 100);
}
void Float_write(float x)
{
char buf[BUFSIZ];
sprintf(buf, "%f", x);
String_write(buf);
}
void Float_writeln(float x)
{
Float_write(x);
char cr[] = "\r\n";
HAL_UART_Transmit(&huart2, (uint8_t *)cr, strlen(cr), 100);
}
/* USER CODE END 0 */
Инициализация переменных
/* USER CODE BEGIN 1 */
uint32_t refSpadCount;
uint8_t isApertureSpads;
uint8_t VhvSettings;
uint8_t PhaseCal;
/* USER CODE END 1 */
Вывод сообщения через UART и объявление функций для начала измерений
/* USER CODE BEGIN 2 */
MessageLen = sprintf((char*)Message, "msalamon.pl VL53L0X Continuous mode\n\r");
HAL_UART_Transmit(&huart2, Message, MessageLen, 100);
Dev->I2cHandle = &hi2c1;
Dev->I2cDevAddr = 0x52;
HAL_NVIC_DisableIRQ(EXTI9_5_IRQn);
VL53L0X_WaitDeviceBooted( Dev );
VL53L0X_DataInit( Dev );
VL53L0X_StaticInit( Dev );
VL53L0X_PerformRefCalibration(Dev, &VhvSettings, &PhaseCal);
VL53L0X_PerformRefSpadManagement(Dev, &refSpadCount, &isApertureSpads);
VL53L0X_SetDeviceMode(Dev, VL53L0X_DEVICEMODE_CONTINUOUS_RANGING);
VL53L0X_StartMeasurement(Dev);
HAL_NVIC_EnableIRQ(EXTI9_5_IRQn);
/* USER CODE END 2 */
Вывод измерений через UART в мм на одной строке
/* USER CODE BEGIN WHILE */
while (1)
{
if(TofDataRead == 1)
{
/*MessageLen = sprintf((char*)Message, "Measured distance: %i\n\r", RangingData.RangeMilliMeter);
HAL_UART_Transmit(&huart2, Message, MessageLen, 100);*/
String_write("Measured distance: ");
Int_write(RangingData.RangeMilliMeter);
String_write(" mm \r");
TofDataRead = 0;
HAL_Delay(50);
}
/* USER CODE END WHILE */
Вывод измерений в реальном времени при помощи прерываний
/* USER CODE BEGIN 4 */
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if(GPIO_Pin == GPIO_PIN_9)
{
VL53L0X_GetRangingMeasurementData(Dev, &RangingData);
VL53L0X_ClearInterruptMask(Dev, VL53L0X_REG_SYSTEM_INTERRUPT_GPIO_NEW_SAMPLE_READY);
TofDataRead = 1;
}
}
/* USER CODE END 4 */
Нажимаем на молоток на верхней панели. В консоли должна быть похожая картина

Схема подключения

Теперь надо загрузить программу в микроконтроллер, для этого буду использовать CubeProgrammer. Подключаем STM32F103C8T6 при помощи st-link к компьютеру

Укажем путь до hex-файла, который находится в папке Debug. Перед этим переведем джампер на плате в положение boot 1 и нажмем на кнопку.
Нажимаем на кнопку Start Programming

Для того, чтобы увидеть результат измерений понадобится программа Putty. Подключаем UART к ПК и заходим в диспетчер устройств и смотрим, под каким com-портом он определился, в моем случае COM7

Жму на него два раза и выставляю, чтобы значение Bits per second было 115200.

Затем, захожу в Putty. Выбираю Connection type: serial, указываю com-порт и скорость. Нажимаю open.

Получаю расстояние в миллиметрах, которое изменяется в реальном времени

Вот таким образом можно подключить VL53L0 к STM32F103 и получать расстояние до объектов, которое изменяется в реальном времени.