利用WT53L7RC-TTL的矩阵激光测距,实现STM32毫米级测距
一、简介
WT53L7RC-TTL最多可以对8*8的矩阵区域进行测距,每个矩阵本质上是用vl53l0x进行测距。单个vl53l0x传感器的数值跳动在1cm左右,就算加上各种滤波,对于有毫米级测距精度要求的项目也不太好用。用WT53L7RC-TTL的8*8的矩阵测距的特点,对每次64个测距值进行平均值计算,可以得到一个能用的距离值。实际测下来,不采用任何滤波,静止情况下数值在1毫米内跳动。
详细的硬件资料参见:WT53L7RC-TTL产品说明书 · 深圳维特智能科技有限公司
二、硬件模块
本项目采用STM32F407和WT53L7RC-TTL连接,采用串口通讯(uart6)的方式读取数据。
接线说明:
绿色:RX–>PC6
黄色:TX–>PC7
黑色:GND–>共地
红色:VCC–>5V
三、代码
1.WT53L7RC-TTL模块协议
WT53L7RC-TTL模块并不是自动发送数据,需要单片机给模块发送读取命令后才能获取数据。单片机串口发送读取命令{0x50, 0x03, 0x00, 0x0F, 0x00, 0x43, 0x39, 0xB9}后,串口接收一串十六进制数据。
50 03 86 00 00 00 00 00 28 00 2E 00 2E 00 28 00 2D 00 2A 00 2F 00 29 00 2A 00 32 00 2A 00 2F 00 2D 00 2D 00 2C 00 29 00 2B 00 30 00 2E 00 2D 00 2D 00 2D 00 2E 00 28 00 2B 00 2E 00 30 00 32 00 2C 00 29 00 2F 00 2B 00 2E 00 2E 00 30 00 2D 00 30 00 2F 00 33 00 2D 00 2A 00 2F 00 30 00 35 00 2F 00 2E 00 31 00 2C 00 2D 00 32 00 30 00 2E 00 30 00 2E 00 2D 00 2B 00 2B 00 2F 00 31 00 2C 00 2E 00 28 00 2C 00 2E 00 28 2E FD
其中,0x50为Modbus地址,0x03为读命令,0x02为数据长度。后面跟着的一串00 00 00 00是指8*8矩阵中出现最小距离值的坐标,再后面的00 28是最小距离。
我们关心的是从第十位后的64个两字节的十六进制数,它们就是每个矩阵的距离值。最后两位 2E FD 为CRC校验。
需要注意的是,每个两字节数的最高位如果是1,数据不可信,编写代码时需要丢弃。
2.串口代码
.c代码,接收缓存长度为140,采用定时器定时发送读取命令{0x50, 0x03, 0x00, 0x0F, 0x00, 0x43, 0x39, 0xB9},发送频率为5Hz。接收中断中,缓存区达到最大长度后,开始解析数据,取平均值过程中剔除了错误数据。
#include "stm32f4xx.h" // Device header
#define BUFFER_SIZE 140
char receiveBuffer[BUFFER_SIZE];
uint16_t receiveIndex = 0;
uint8_t test = 0;
float avg;
void USART6_Init(void)
{
// 使能GPIO时钟
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOC, ENABLE);
// 配置USART6的引脚
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;
GPIO_Init(GPIOC, &GPIO_InitStructure);
// 配置USART6的引脚复用功能
GPIO_PinAFConfig(GPIOC, GPIO_PinSource6, GPIO_AF_USART6);
GPIO_PinAFConfig(GPIOC, GPIO_PinSource7, GPIO_AF_USART6);
// 使能USART6时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART6, ENABLE);
// 配置USART6
USART_InitTypeDef USART_InitStructure;
USART_InitStructure.USART_BaudRate = 115200;
USART_InitStructure.USART_WordLength = USART_WordLength_8b;
USART_InitStructure.USART_StopBits = USART_StopBits_1;
USART_InitStructure.USART_Parity = USART_Parity_No;
USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
USART_Init(USART6, &USART_InitStructure);
//配置嵌套向量表
NVIC_InitTypeDef NVIC_InitTypeDef_init;
NVIC_InitTypeDef_init.NVIC_IRQChannel = USART6_IRQn; //中断通道都不一样,一定要记得去中断向量表找,具体的中断向量表如何寻找,可查看以前中断配置
NVIC_InitTypeDef_init.NVIC_IRQChannelPreemptionPriority = 2;//抢占优先级
NVIC_InitTypeDef_init.NVIC_IRQChannelSubPriority = 2;//响应优先级
NVIC_InitTypeDef_init.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitTypeDef_init);
USART_ITConfig(USART6,USART_IT_RXNE,ENABLE);
// 使能USART6
USART_Cmd(USART6, ENABLE);
}
float ProcessReceivedData(void)
{
int Total = 64;
uint32_t sum = 0;
// 检查数组开始的三位是否固定不变
if ( (receiveBuffer[0] == 0x50) && (receiveBuffer[1] == 0x03) && (receiveBuffer[2] == 0x86) )
{
// 提取64个16位十六进制数
uint16_t values[64];
for (int i = 0; i < 64; i++)
{
values[i] = (receiveBuffer[9 + 2 * i] << 8) | receiveBuffer[10 + 2 * i];
if( (values[i] & 0x8000) == 0 )
{
sum += values[i];
}else
{
Total--;
}
}
// 计算平均值
float average =( (float)sum / Total )/10.0;
return average;
}
else
{
return -1;
}
}
void USART6_SendChar(uint8_t Byte)
{
// 等待发送缓冲区为空
while (USART_GetFlagStatus(USART6, USART_FLAG_TXE) == RESET);
// 发送字符
USART_SendData(USART6, Byte);
}
void USART6_SendString(uint8_t *Array, uint16_t Length)
{
uint16_t i;
for (i = 0; i < Length; i ++)
{
USART6_SendChar(Array[i]);
}
}
void USART6_IRQHandler(void)
{
if (USART_GetITStatus(USART6, USART_IT_RXNE) != RESET)
{
// 读取接收到的字符
char receivedChar = USART_ReceiveData(USART6);
// 存储到接收缓冲区
if (receiveIndex < BUFFER_SIZE - 1)
{
receiveBuffer[receiveIndex] = receivedChar;
receiveIndex++;
}
// avg=ProcessReceivedData();
if(receiveIndex >= BUFFER_SIZE-1)
{
receiveIndex = 0; // 清除接收缓冲区
avg=ProcessReceivedData();
}
// 清除中断标志
USART_ClearITPendingBit(USART6, USART_IT_RXNE);
}
}
void TIM2_Init(void)
{
// 使能TIM2时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
// 配置TIM2
TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = 1999; // 自动重装值
TIM_TimeBaseStructure.TIM_Prescaler = 8399; // 预分频器
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
// 使能TIM2更新中断
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
// 使能TIM2中断
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
// 启动TIM2
TIM_Cmd(TIM2, ENABLE);
}
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET)
{
// 清除中断标志
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
// 发送数组
uint8_t sendArray[] = {0x50, 0x03, 0x00, 0x0F, 0x00, 0x43, 0x39, 0xB9};
for (int i = 0; i < sizeof(sendArray); i++)
{
USART6_SendChar(sendArray[i]);
}
}
}
.h代码,引出了avg,即处理后得到的测距值。
#ifndef __WT53_H_
#define __WT53_H_
extern float avg;
extern uint8_t test ;
void USART6_Init(void);
float ProcessReceivedData(void);
void USART6_SendChar(uint8_t Byte);
void USART6_SendString(uint8_t *Array, uint16_t Length);
void TIM2_Init(void);
#endif
main函数,OLED模块用的是江科大的代码,用串口输出也可以。
#include "stm32f4xx.h"
#include "math.h"
#include "stdio.h"
#include "OLED.h"
#include "WT53.h"
int main(void)
{
USART6_Init();
TIM2_Init();
OLED_Init();
delay_ms(50);
OLED_ShowString(40,1,"cm",OLED_6X8);
while(1)
{
OLED_ShowFloatNum(1,1,avg, 2, 2, OLED_6X8);
OLED_Update();
}
}
四、效果
串口输出:
OLED显示屏:
作者:g8e2233