STM32F4+VL53L0激光测距cubemx实现
前言
提示:本程序测试主控使用STM32F427VGT6系列单片机,使用cubemx作为开发工具,串口波特率为115200,使用VL530L0模块,使用IIC通信,可移植到相关STM32F4型号单片机上
一、STM32cubemx配置
a.配置RCC
RCC设置,选择HSE(外部高速时钟)为Crystal/Ceramic Resonator(晶振/陶瓷谐振器)
b.配置SYS
##c, 配置USART
这里使用串口1,波特率设为115200,开启中断,用于接收VL530的数据
c.配置VL530使能口
这里使用PB8,PB9作为SDA,SCL配置口,这里设为输出模式,电平拉高
d.时钟线配置
配置系统目录为180M最高速度,只用调节SYSCLK其它即可自动配置,时钟配置不同型号单片机可能不同,这里我设置为180MHZ
二、STM32F4 keil配置
1.引入库
a.引入IIC口配置
myiic.c配置
#include "myiic.h"
#include "delay.h"
//初始化IIC
void IIC_Init(void)
{
GPIO_InitTypeDef GPIO_InitStructure;
IIC_SCL=1;
IIC_SDA=1;
}
//产生IIC起始信号
void IIC_Start(void)
{
SDA_OUT(); //sda线输出
IIC_SDA=1;
IIC_SCL=1;
delay_us(4);
IIC_SDA=0;//START:when CLK is high,DATA change form high to low
delay_us(4);
IIC_SCL=0;//钳住I2C总线,准备发送或接收数据
}
//产生IIC停止信号
void IIC_Stop(void)
{
SDA_OUT();//sda线输出
IIC_SCL=0;
IIC_SDA=0;//STOP:when CLK is high DATA change form low to high
delay_us(4);
IIC_SCL=1;
IIC_SDA=1;//发送I2C总线结束信号
delay_us(4);
}
//等待应答信号到来
//返回值:1,接收应答失败
// 0,接收应答成功
u8 IIC_Wait_Ack(void)
{
u8 ucErrTime=0;
SDA_IN(); //SDA设置为输入
IIC_SDA=1;delay_us(1);
IIC_SCL=1;delay_us(1);
while(READ_SDA)
{
ucErrTime++;
if(ucErrTime>250)
{
IIC_Stop();
return 1;
}
}
IIC_SCL=0;//时钟输出0
return 0;
}
//产生ACK应答
void IIC_Ack(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=0;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//不产生ACK应答
void IIC_NAck(void)
{
IIC_SCL=0;
SDA_OUT();
IIC_SDA=1;
delay_us(2);
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
}
//IIC发送一个字节
//返回从机有无应答
//1,有应答
//0,无应答
void IIC_Send_Byte(u8 txd)
{
u8 t;
SDA_OUT();
IIC_SCL=0;//拉低时钟开始数据传输
for(t=0;t<8;t++)
{
IIC_SDA=(txd&0x80)>>7;
txd<<=1;
delay_us(2); //对TEA5767这三个延时都是必须的
IIC_SCL=1;
delay_us(2);
IIC_SCL=0;
delay_us(2);
}
}
//读1个字节,ack=1时,发送ACK,ack=0,发送nACK
u8 IIC_Read_Byte(unsigned char ack)
{
unsigned char i,receive=0;
SDA_IN();//SDA设置为输入
for(i=0;i<8;i++ )
{
IIC_SCL=0;
delay_us(2);
IIC_SCL=1;
receive<<=1;
if(READ_SDA)receive++;
delay_us(1);
}
if (!ack)
IIC_NAck();//发送nACK
else
IIC_Ack(); //发送ACK
return receive;
}
myiic.h配置
#ifndef __MYIIC_H
#define __MYIIC_H
#include "sys.h"
#include "stdio.h"
#include "main.h"
#include "string.h"
#include "sys.h"
//IO方向设置
#define SDA_IN() {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=0<<9*2;} //PB9输入模式
#define SDA_OUT() {GPIOB->MODER&=~(3<<(9*2));GPIOB->MODER|=1<<9*2;} //PB9输出模式
//IO操作函数
#define IIC_SCL PBout(8) //SCL
#define IIC_SDA PBout(9) //SDA
#define READ_SDA PBin(9) //输入SDA
//IIC所有操作函数
void IIC_Init(void); //初始化IIC的IO口
void IIC_Start(void); //发送IIC开始信号
void IIC_Stop(void); //发送IIC停止信号
void IIC_Send_Byte(u8 txd); //IIC发送一个字节
u8 IIC_Read_Byte(unsigned char ack);//IIC读取一个字节
u8 IIC_Wait_Ack(void); //IIC等待ACK信号
void IIC_Ack(void); //IIC发送ACK信号
void IIC_NAck(void); //IIC不发送ACK信号
void IIC_Write_One_Byte(u8 daddr,u8 addr,u8 data);
u8 IIC_Read_One_Byte(u8 daddr,u8 addr);
#endif
b.引入VL530底层配置
VL530L0.c配置:
#include "VL53L0.h"
#include "myiic.h"
uint16_t bswap(u8 b[])
{
uint16_t val = ((b[0]<< 8) & b[1]);
return val;
}
uint16_t VL53L0X_decode_vcsel_period(short vcsel_period_reg)
{
uint16_t vcsel_period_pclks = (vcsel_period_reg + 1) <<1;
return vcsel_period_pclks;
}
uint16_t makeuint16(int lsb, int msb)
{
return ((msb & 0xFF) << 8) | (lsb & 0xFF);
}
//IIC连续写
//addr:器件地址
//reg:寄存器地址
//len:写入长度
//buf:数据区
//返回值:0,正常
// 其他,错误代码
u8 VL53L0X_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
u8 i;
IIC_Start();
IIC_Send_Byte((addr<<1)|0);//发送器件地址+写命令
if(IIC_Wait_Ack()) //等待应答
{
IIC_Stop();
return 1;
}
IIC_Send_Byte(reg); //写寄存器地址
IIC_Wait_Ack(); //等待应答
for(i=0;i<len;i++)
{
IIC_Send_Byte(buf[i]); //发送数据
if(IIC_Wait_Ack()) //等待ACK
{
IIC_Stop();
return 1;
}
}
IIC_Stop();
return 0;
}
//IIC连续读
//addr:器件地址
//reg:要读取的寄存器地址
//len:要读取的长度
//buf:读取到的数据存储区
//返回值:0,正常
// 其他,错误代码
u8 VL53L0X_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf)
{
IIC_Start();
IIC_Send_Byte((addr<<1)|0);//发送器件地址+写命令
if(IIC_Wait_Ack()) //等待应答
{
IIC_Stop();
return 1;
}
IIC_Send_Byte(reg); //写寄存器地址
IIC_Wait_Ack(); //等待应答
IIC_Start();
IIC_Send_Byte((addr<<1)|1);//发送器件地址+读命令
IIC_Wait_Ack(); //等待应答
while(len)
{
if(len==1)*buf=IIC_Read_Byte(0);//读数据,发送nACK
else *buf=IIC_Read_Byte(1); //读数据,发送ACK
len--;
buf++;
}
IIC_Stop(); //产生一个停止条件
return 0;
}
//IIC写一个字节
//reg:寄存器地址
//data:数据
//返回值:0,正常
// 其他,错误代码
u8 VL53L0X_Write_Byte(u8 reg,u8 data)
{
IIC_Start();
IIC_Send_Byte((VL53L0X_Add<<1)|0);//发送器件地址+写命令
if(IIC_Wait_Ack()) //等待应答
{
IIC_Stop();
return 1;
}
IIC_Send_Byte(reg); //写寄存器地址
IIC_Wait_Ack(); //等待应答
IIC_Send_Byte(data);//发送数据
if(IIC_Wait_Ack()) //等待ACK
{
IIC_Stop();
return 1;
}
IIC_Stop();
return 0;
}
//IIC读一个字节
//reg:寄存器地址
//返回值:读到的数据
u8 VL53L0X_Read_Byte(u8 reg)
{
u8 res;
IIC_Start();
//IIC_Send_Byte((VL53L0X_Add<<1)|0);//发送器件地址+写命令
IIC_Send_Byte(0x52);//发送器件地址+写命令
IIC_Wait_Ack(); //等待应答
IIC_Send_Byte(reg); //写寄存器地址
IIC_Wait_Ack(); //等待应答
IIC_Start();
IIC_Send_Byte(0x53 );//发送器件地址+读命令
IIC_Wait_Ack(); //等待应答
res=IIC_Read_Byte(0);//读取数据,发送nACK
IIC_Stop(); //产生一个停止条件
return res;
}
VL53L0.h:
#ifndef _VL53L0_H
#define _VL53L0_H
#define VL53L0X_REG_IDENTIFICATION_MODEL_ID 0xc0
#define VL53L0X_REG_IDENTIFICATION_REVISION_ID 0xc2
#define VL53L0X_REG_PRE_RANGE_CONFIG_VCSEL_PERIOD 0x50
#define VL53L0X_REG_FINAL_RANGE_CONFIG_VCSEL_PERIOD 0x70
#define VL53L0X_REG_SYSRANGE_START 0x00
#define VL53L0X_REG_RESULT_INTERRUPT_STATUS 0x13
#define VL53L0X_REG_RESULT_RANGE_STATUS 0x14
#define VL53L0X_Add 0x29
#include "sys.h"
u8 VL53L0X_Write_Len(u8 addr,u8 reg,u8 len,u8 *buf);//连续写
u8 VL53L0X_Read_Len(u8 addr,u8 reg,u8 len,u8 *buf);//连续读
u8 VL53L0X_Write_Byte(u8 reg,u8 data);//写一个字节
u8 VL53L0X_Read_Byte(u8 reg);//读一个字节
uint16_t bswap(u8 b[]);
uint16_t VL53L0X_decode_vcsel_period(short vcsel_period_reg);
uint16_t makeuint16(int lsb, int msb);
#endif
2.读取数据
a.初始化配置
主函数初始化配置
#include "main.h"
#include "tim.h"
#include "usart.h"
#include "gpio.h"
#include "fmc.h"
#include "myiic.h" //测距初始化
#include "VL53L0.h" //测距初始化
定义相关变量
float data[2],i;
uint8_t pcStr[20];
uint8_t aTxStartMessage;//储存接收的数据
uint8_t USART_RX_STA;
int z1,js,bzpd=0,bzpd1=0;
int temp0,temp1,temp2;
static int x,y,z,xz,yz=0; //串口接收到的横纵坐标
extern uint16_t msHcCount;
float length=0;
extern int jd1,asd;
u8 val = 0; //测距函数定义
u8 gbuf[16];
u8 DeviceRangeStatusInternal;
在mian函数中添加声明初始化
SystemClock_Config();
IIC_Init(); //使能定义,已经移植到cube
MX_USART1_UART_Init();
uint32_t cnt = 0;
uint16_t count[3];
MX_GPIO_Init();
MX_FMC_Init();
b.获取数据
在while(1)中添加如下代码
while(cnt < 100)
{
HAL_Delay(10);
val = VL53L0X_Read_Byte(VL53L0X_REG_RESULT_RANGE_STATUS);
if( val & 0x01) break;
cnt++;
}
VL53L0X_Read_Len(VL53L0X_Add, 0x14 , 12, gbuf);
count[0] = makeuint16(gbuf[7], gbuf[6]);
count[1] = makeuint16(gbuf[9], gbuf[8]);
count[2] = makeuint16(gbuf[11], gbuf[10]);
DeviceRangeStatusInternal = ((gbuf[0] & 0x78) >> 3);
printf("\r\n ambient count = %4d signal count = %4d distance = %4d status = %d ",count[0],count[1],count[2],DeviceRangeStatusInternal);
// data_analy(count[2]);
HAL_Delay(200);
c.结果展示
连接串口线到电脑,设置波特率为115200,即可看到相关数据
3.总结
数据中第一个是环境统计,第二个是信号数统计,如果前方物体越平滑,值越小趋近1,如果前方物体有多个凹凸面,或者检查到多个物体玛者信号数越大,第三个就是相对距离,单位是mm,我用的是一个简单的测距模块,测试精度为20mm—3m,如果高低于这个范围,则会显示20或者8190,一般在1m中效果最佳,准确度比HC05.06要高很多,适合近距离测距,本文章只粘贴了一部分主要程序,如果有需要完整工程可以在下面链接下载,比赛准备用,有缺陷之处,还望请各位大佬指教
三.程序下载链接
https://download.csdn.net/download/weixin_44984773/20357255?spm=1001.2014.3001.5501