利用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

物联沃分享整理
物联沃-IOTWORD物联网 » 利用WT53L7RC-TTL的矩阵激光测距,实现STM32毫米级测距

发表回复