STM32 倒车测距系统 —— STM32 超声波测距(HC-SR04)
STM32 —— HC-SR04 超声波测距
实验原理
这里采用 RT-Thread Nano 系统进行多线程开发,关于 RT-Thread Nano 系统的使用可以参考我的另一篇博客:
超声波模块采用 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 的时钟:
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有重复的定义
解决方法:
把下面这段代码注释掉即可