🎀 文章作者:二土电子
🌸 关注公众号获取PCB与原理图!
🐸 期待大家一起学习交流!
一、项目简介
本项目基于STM32设计了一个双路激光测距系统,能够实现双路测距、测量结果显示、按键控制测量开关、超阈值声光报警功能,并且能够通过串口输出测量结果帧,方便上位机处理并进行后续操作。
🔧软件工具:Keil5
二、硬件选型
- 主控:STM32F103C8T6核心板
- 显示模块:OLED
- VL53L0X 激光测距模块
- 有源低电平触发蜂鸣器
- 按键、LED
三、硬件简介
这里主要是介绍之前没有出现过的VL53L0X 激光测距传感器
VL53L0X 是一款基于飞行时间 (Time of Flight, ToF) 技术的激光测距传感器,能够精确测量目标物体的距离。该模块具有高精度、低功耗以及快速响应的特点。
VL53L0X 使用脉冲激光发射器向被测对象发送短促而强烈的光脉冲,并通过内置接收器捕捉返回来的散射光信号。利用这些数据计算出发射与接收之间的时间差 Δt ,再乘以光速 c 并除以两倍得到实际距离 d=Δtc/2 。为了提高准确性,设备内部还进行了复杂的算法处理来消除环境干扰等因素的影响。
VL53L0X 通过IIC总线与MCU通信实现数据交互,默认地址为0x52。本设计使用两路激光测距,如果两个VL53L0X 共用一个IIC总线会涉及到地址冲突问题,因为本设计IO资源比较充足,所以博主选择使用两路模拟IIC实现两个VL53L0X 的通信,从而实现两路激光测距,而不需要考虑地址冲突问题。
四、程序设计
4.1 IIC程序设计
像上面所说的,我们使用两路模拟IIC来实现两个VL53L0X 的通信,所以在设计IIC程序时需要分两种情况,这里以II起始信号函数设计为例来解释一下具体设计思路
uint8_t i2c_start(void)
{
if (VL53L0X_Index == 1)
{
I2C_SDA_HIGH();
I2C_SCL_HIGH();
i2c_delay();
//i2c_delay();
if(!I2C_SDA_STATE)
return 1;
I2C_SDA_LOW();
i2c_delay();
if(I2C_SDA_STATE)
return 1;
I2C_SCL_LOW();
i2c_delay();
}
else if (VL53L0X_Index == 2)
{
I2C_SDA_HIGH2();
I2C_SCL_HIGH2();
i2c_delay();
//i2c_delay();
if(!I2C_SDA_STATE2)
return 1;
I2C_SDA_LOW2();
i2c_delay();
if(I2C_SDA_STATE2)
return 1;
I2C_SCL_LOW2();
i2c_delay();
}
return 0;
}
其中的VL53L0X_Index
是用来控制选择的是第几路VL53L0X传感器,通过修改VL53L0X_Index的值来控制当前使用的是那个IIC总线,其他的IIC程序设计和起始信号的设计思路相同。
4.2 测量业务函数
下面介绍一下整体的业务函数设计,老规矩,先贴代码。
void App_Measure_Task (void)
{
u8 keyValue = 0;
u8 string[5];
u16 distance1 = 0;
u16 distance2 = 0;
u8 mode = 0;
u8 sendData[6];
VL53L0X_Index = 1;
vl53l0x_init();
VL53L0X_Index = 2;
vl53l0x_init();
while (1)
{
keyValue = Med_KeyScan();
if (keyValue == 0xAA)
{
Med_Oled_Clear(); // 清屏
mode = 1;
}
else if (keyValue == 0x55)
{
Med_Oled_Clear(); // 清屏
mode = 0;
}
if (mode == 0)
{
Med_Oled_ShowCHinese16x16(30,3,"激光测距");
}
else if (mode == 1)
{
keyValue = Med_KeyScan();
if (keyValue == 0x55)
{
Med_Oled_Clear(); // 清屏
mode = 0;
}
// 开启测距
Med_Oled_ShowCHinese16x16(30,1,"激光测距");
Med_Oled_ShowCHinese16x16(20,3,"第一路");
Med_Oled_ShowCHinese16x16(20,5,"第二路");
// 开启双路激光测距
if (VL53L0X_Index == 1)
{
if(vl53l0x_status == VL53L0X_ERROR_NONE)
{
vl53l0x_start_single_test(&vl53l0x_dev, &vl53l0x_data); //输出数据为mm
distance1 = gDistance;
gDistance = 0;
VL53L0X_Index = 2;
}
}
else if (VL53L0X_Index == 2)
{
if(vl53l0x_status == VL53L0X_ERROR_NONE)
{
vl53l0x_start_single_test(&vl53l0x_dev, &vl53l0x_data); //输出数据为mm
distance2 = gDistance;
gDistance = 0;
VL53L0X_Index = 1;
}
// OLED显示测量结果
sprintf((char*)string,"%dcm ",distance1);
Med_Oled_ShowString(70,3,string,16);
sprintf((char*)string,"%dcm ",distance2);
Med_Oled_ShowString(70,5,string,16);
// 串口输出测量数据
sendData[0] = 0xD1;
sendData[1] = (distance1 >> 8) & 0xFF;
sendData[2] = distance1 & 0xFF;
sendData[3] = 0xD2;
sendData[4] = (distance2 >> 8) & 0xFF;
sendData[5] = distance2 & 0xFF;
USART1_Send(sendData,6);
// 距离在10cm内报警
if ((distance1 > 0 && distance2 > 0) && (distance1 <= 10 | distance2 <= 10))
{
LED = 1;
BEEP = 0;
}
else
{
LED = 0;
BEEP = 1;
}
}
}
}
}
我们通过在业务函数中修改VL53L0X_Index
的值,实现两路测距之间的切换,使用两个变量distance1
和distance2
来存储测量结果,除了将测量结果显示到OLED上之外还会将测量结果存储到sendData
数组中,通过串口1发送出去,方便上位机进行接收处理。
下面简单介绍一下帧格式
D1 Dis1_H Dis1_L D2 Dis2_H Dis2_L
其中D1
和D2
用来指示后面的测量结果是哪一路的测量结果,紧接着后面跟着两字节测量结果。
4.3 OLED显示程序设计
关于OLED显示程序设计就不再做过多的介绍了,大家可以到外设系列查看,或者直接关注公众号,在STM32专栏中找到OLED即可在文末获取源码。
五、问题总结
简单说一下这次设计中遇到的小问题,调试过程中发现即使贴着VL53L0X传感器,测量距离最小只是4cm,也就是说4cm一下没有测量出来,另外VL53L0X的输出结果都是整数值,没有小数值。