STM32 倒车测距系统(科目二) —— STM32 超声波测距(HC-SR04)

STM32 —— HC-SR04 超声波测距

实验原理

这里采用 RT-Thread Nano 系统进行多线程开发,关于 RT-Thread Nano 系统的使用可以参考我的另一篇博客:

STM32 —— RT-Thread Nono 移植

超声波模块采用 HC-SR04 进行测距,有关超声波的原理可以参考我前面发布的博客:

超声波 HC-SR04 模块入门

CubeMX 配置

配置 RT-Thread Nano 系统

首先我们先添加 RT-Thread Nano 系统,由于前面我们已经介绍过对于 RT-Thread 系统的使用,所以这里直接给出配置方法:

RCC 配置

时钟配置

UART1 配置

配置 HC-SR04 模块

这里我们需要配置超声波模块,这里超声波模块共有四个引脚,除了两个 VCC 和 GND 以为,还有 Trig 和 Echo 两个引脚,这里我们需要对这两个引脚进行配置,并且我们需要配置一个时钟,这里我们直接采用 TIM2 的 CH1 通道进行 Echo 信号处理,并配置一个引脚用于 Trig 信号输出:

引脚配置

代码设计

RT-Thread 进程设计

首先我们需要新建一个 app_rt_thread.c 源文件,用于进行进程设计,这里以串口发送 Hello World 为例,给出示例代码:

#include "rtthread.h"
#include "main.h"
#include "stdio.h"
#include "usart.h"
#include "gpio.h"

	// 重写 printf 函数,串口输出
	int fputc(int ch, FILE *f){
//		uint8_t temp[1] = {ch};
		HAL_UART_Transmit(&huart1,(uint8_t *)&ch,1,0xFFFF);
		while(__HAL_UART_GET_FLAG(&huart1,UART_FLAG_TC) != SET){
		}
		return ch;
	}
	
	struct rt_thread uart_out_thread;						// 设置线程
	rt_uint8_t rt_uart_out_thread_stack[256];		// 设置栈空间\
	
	// 任务函数
	void uart_out_task_entry(void *parameter);
	
	// 线程初始化函数
	void MX_RT_Thread_Init(void){
		// 初始化线程,参数如下:(&进程, “进程名称”, 进程函数, RT_NULL, &栈起始位置, 栈大小, 3, 20)
		rt_thread_init(&uart_out_thread, "uart_out",  uart_out_task_entry, RT_NULL, &rt_uart_out_thread_stack[0], sizeof(rt_uart_out_thread_stack),3,20);
		
		//
	}
	
	// 主任务,需要循环执行
	void MX_RT_Thread_Process(void){
		printf("开始运行");
		rt_thread_delay(2000);
	}
	
	// 实例化任务函数
	void uart_out_task_entry(void *parameter){
		printf("Hello Ppqppl!\n\r");
	}

然后我们需要在主函数中引用我们的 RT_Thread Nano 相关的头文件:

#include "rtthread.h"

下面我们需要修改两处系统默认函数才能正常运行:

超声波数据获取

首先我们需要考虑一点,如果我们想要超声波数据准确,我们就要考虑温度对声波传输速度的影响:

首先我们需要设置 72M 的时钟:

扫描二维码关注公众号,回复: 15368006 查看本文章
void delay_us(uint32_t us) // 主频72M
{
    uint32_t delay = (HAL_RCC_GetHCLKFreq() / 4000000 * us);
    while (delay--)
    {
        ;
    }
}

然后我们需要进行时钟下降沿捕获,这里设置位下降沿触发:

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) //
{

    if (TIM2 == htim->Instance) // 判断触发的中断的定时器为TIM2
    {
        switch (measure_Cnt)
        {
        case 1:
            measure_Buf[0] = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1);            // 获取当前的捕获值.
            __HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); // 设置为下降沿捕获。
            measure_Cnt++;
            break;
        case 2:
            measure_Buf[1] = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1); // 获取当前的捕获值.
            HAL_TIM_IC_Stop_IT(&htim2, TIM_CHANNEL_1);                         // 停止捕获   或者: __HAL_TIM_DISABLE(&htim5);
            measure_Cnt++;
        }
    }
}

然后我们需要进行数据的获取处理:

float SR04_GetData(void)
{
    switch (measure_Cnt)
    {
    case 0:
        TRIG_H;
        delay_us(30);
        TRIG_L;

        measure_Cnt++;
        __HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);
        HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); // 启动输入捕获       或者: __HAL_TIM_ENABLE(&htim5);
        break;
    case 3:
        high_time = measure_Buf[1] - measure_Buf[0]; // 高电平时间
        distant = (high_time * 0.034) / 2;           // 单位cm
        distances[n] = distant;
        n++;
				if(distant == 0 || distant >= 280){
					n--;
				}
        if (n >= 5)
        { // 采用1、4、6、4、1的高斯核进行滤波
            n = 0;
            distant = (distances[0] + 4 * distances[1] + 6 * distances[2] + 4 * distances[3] + distances[4]) / 16;
        }
				else{
				}
        measure_Cnt = 0; // 清空标志位
        TIM2->CNT = 0;   // 清空计时器计数
				return distant;
        break;
    }
}

我们在数据获取的过程中加入了高斯滤波

下面直接给出超声波测距的驱动源码:

SR04.c

#include "SR04.h"

float distant;                 // 测量距离
uint32_t measure_Buf[3] = {0}; // 存放定时器计数值的数组
float distances[5] = {0};
uint8_t measure_Cnt = 0; // 状态标志位
uint32_t high_time;      // 超声波模块返回的高电平时间
uint8_t n = 0;

//===============================================读取距离
void SR04_GetData(void)
{
    switch (measure_Cnt)
    {
    case 0:
        TRIG_H;
        delay_us(30);
        TRIG_L;

        measure_Cnt++;
        __HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1, TIM_INPUTCHANNELPOLARITY_RISING);
        HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); // 启动输入捕获       或者: __HAL_TIM_ENABLE(&htim5);
        break;
    case 3:
        high_time = measure_Buf[1] - measure_Buf[0]; // 高电平时间
        distant = (high_time * 0.034) / 2;           // 单位cm
        distances[n] = distant;
        n++;
        if (n >= 5)
        { // 采用1、4、6、4、1的高斯核进行滤波
            n = 0;
            distant = (distances[0] + 4 * distances[1] + 6 * distances[2] + 4 * distances[3] + distances[4]) / 16;
            printf("\r\n检测距离为:%.2f-cm-\r\n", distant);
        }
        // printf("\r\n检测距离为:%.2f-cm-\r\n",distant);
        measure_Cnt = 0; // 清空标志位
        TIM2->CNT = 0;   // 清空计时器计数
        break;
    }
}

//===============================================us延时函数
void delay_us(uint32_t us) // 主频72M
{
    uint32_t delay = (HAL_RCC_GetHCLKFreq() / 4000000 * us);
    while (delay--)
    {
        //;
    }
}

//===============================================中断回调函数
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) //
{

    if (TIM2 == htim->Instance) // 判断触发的中断的定时器为TIM2
    {
        switch (measure_Cnt)
        {
        case 1:
            measure_Buf[0] = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1);            // 获取当前的捕获值.
            __HAL_TIM_SET_CAPTUREPOLARITY(&htim2, TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING); // 设置为下降沿捕获。
            measure_Cnt++;
            break;
        case 2:
            measure_Buf[1] = HAL_TIM_ReadCapturedValue(&htim2, TIM_CHANNEL_1); // 获取当前的捕获值.
            HAL_TIM_IC_Stop_IT(&htim2, TIM_CHANNEL_1);                         // 停止捕获   或者: __HAL_TIM_DISABLE(&htim5);
            measure_Cnt++;
        }
    }
}

SR04.h:

#ifndef __SR04_H
#define __SR04_H
#include "main.h"
#include "tim.h"
#include "stdio.h"

#define TRIG_H HAL_GPIO_WritePin(Trig_GPIO_Port, Trig_Pin, GPIO_PIN_SET)
#define TRIG_L HAL_GPIO_WritePin(Trig_GPIO_Port, Trig_Pin, GPIO_PIN_RESET)

void delay_us(uint32_t us);
void SR04_GetData(void);

#endif

上板验证

接线图

烧录测试

报错解决方案

如果出现如下报错:

STM32_HC_SR04_CubeMX\STM32_HC_SR04_CubeMX.axf: Error: L6200E: Symbol HardFault_Handler multiply defined (by context_rvds.o and stm32f1xx_it.o).

报错的意思就是在context_rvds.c和stm32f10x_it.c文件中,HardFault_Handler有重复的定义

解决方法:

把下面这段代码注释掉即可

猜你喜欢

转载自blog.csdn.net/m0_59161987/article/details/130980589