О том, как подключить 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 и получать расстояние до объектов, которое изменяется в реальном времени.
