基于HAL库的STM32F103超声波测距程序设计与实现(电平模式)
STM32F103系列基于ARM Cortex-M3内核,是一款高性能、低成本、低功耗的32位微控制器,广泛应用于工业控制、智能仪表、物联网等领域。其架构采用哈佛结构,支持三级流水线,具备高效的指令执行能力。芯片内置Flash存储器与SRAM,并集成丰富的外设资源,如GPIO、定时器、ADC、SPI、I2C等,便于实现复杂功能的嵌入式系统设计。其支持多种低功耗模式,适应不同应用场景的需求。掌握其架
简介:本项目基于STM32F103微控制器,使用HAL库开发了一款超声波测距程序,采用电平模式实现高精度距离测量。程序利用定时器的输入捕捉功能,结合超声波发射与回波检测的时间差,通过声速计算目标距离。适用于智能家居、自动安防等嵌入式应用场景,帮助开发者掌握HAL库开发、定时器配置与传感器数据处理的核心技能。
1. STM32F103微控制器架构概述
STM32F103系列基于ARM Cortex-M3内核,是一款高性能、低成本、低功耗的32位微控制器,广泛应用于工业控制、智能仪表、物联网等领域。其架构采用哈佛结构,支持三级流水线,具备高效的指令执行能力。芯片内置Flash存储器与SRAM,并集成丰富的外设资源,如GPIO、定时器、ADC、SPI、I2C等,便于实现复杂功能的嵌入式系统设计。其支持多种低功耗模式,适应不同应用场景的需求。掌握其架构特性,有助于理解后续HAL库开发与外设驱动实现的底层机制。
2. HAL库开发环境搭建与配置
在嵌入式系统开发中,开发环境的搭建是至关重要的第一步。HAL(Hardware Abstraction Layer)库为STM32系列微控制器提供了标准化的硬件操作接口,大大降低了开发者对底层寄存器的操作难度。本章将详细讲解如何搭建基于HAL库的开发环境,涵盖开发工具链的选择与配置、工程创建与外设初始化流程,以及调试与下载环境的建立。通过本章内容,开发者将能够快速构建一个高效、稳定的STM32F103开发平台。
2.1 开发工具链的搭建
在开始STM32 HAL库开发之前,首先需要完成开发工具链的搭建。这包括STM32CubeMX的安装与配置、IDE的选择与设置,以及相关编译器和调试工具的安装。下面将分别介绍这些工具的功能及其配置方式。
2.1.1 STM32CubeMX的安装与功能介绍
STM32CubeMX 是 ST 官方提供的图形化配置工具,用于快速生成初始化代码,支持STM32全系列芯片。它集成了时钟树配置、外设引脚映射、中间件选择、代码生成等功能,极大提升了开发效率。
安装步骤
- 访问 ST官网 下载 STM32CubeMX 。
- 双击安装包运行安装程序,选择安装路径。
- 安装完成后,打开 STM32CubeMX,首次运行会提示更新芯片数据库(建议联网更新)。
核心功能介绍
- 芯片选择 :支持从型号搜索到引脚数、封装类型等多维度筛选。
- 引脚配置 :图形化界面中可直接拖动外设到对应引脚,自动完成冲突检测。
- 时钟配置 :提供系统时钟树图形化配置,自动生成各外设时钟频率。
- 中间件支持 :可启用RTOS、USB、FatFS、LwIP等中间件模块。
- 代码生成 :支持生成 Keil MDK-ARM 、 IAR 、 STM32CubeIDE 、 Makefile 等多种项目模板。
示例:使用STM32CubeMX配置一个使用HAL库的GPIO输出项目。
STM32CubeMX配置流程图
graph TD
A[打开STM32CubeMX] --> B[选择芯片型号 STM32F103C8Tx]
B --> C[配置引脚功能]
C --> D[选择PC13为GPIO_Output]
D --> E[配置系统时钟]
E --> F[生成代码]
F --> G[选择项目类型为Keil MDK-ARM]
G --> H[保存并打开工程]
2.1.2 Keil MDK-ARM与STM32CubeIDE的配置对比
在选择开发环境时,常见的两种方案是 Keil MDK-ARM 和 STM32CubeIDE 。它们各有优势,适用于不同开发需求。
Keil MDK-ARM 配置流程
Keil 是经典的嵌入式开发IDE,具有强大的调试功能和成熟的编译器。
- 下载并安装 Keil MDK-ARM 。
- 在 STM32CubeMX 中选择目标为 Keil MDK-ARM。
- 生成代码后,打开 Keil 工程。
- 检查
Startup文件和system_stm32f1xx.c是否正确包含。 - 编译工程并下载至目标板。
STM32CubeIDE 配置流程
STM32CubeIDE 是 ST 官方推出的集成开发环境,基于 Eclipse,内置 STM32CubeMX 插件,支持代码生成、调试、烧录一体化操作。
- 下载并安装 STM32CubeIDE 。
- 打开 STM32CubeIDE,创建 STM32 Project。
- 在配置界面中选择芯片型号并配置外设。
- 生成代码后,系统自动创建工程。
- 编译、调试、下载均可在同一个界面完成。
对比表格
| 特性 | Keil MDK-ARM | STM32CubeIDE |
|---|---|---|
| 开发界面 | 经典嵌入式IDE | 基于Eclipse的现代界面 |
| 调试器支持 | 支持J-Link、ST-Link等 | 内置ST-Link调试器支持 |
| 代码生成 | 需STM32CubeMX配合 | 内置STM32CubeMX插件 |
| 免费版本 | 需注册,代码大小受限 | 免费完整版 |
| 插件生态 | 丰富 | 逐步完善 |
| 适合人群 | 资深嵌入式工程师 | 初学者与进阶用户 |
小贴士:若你已经熟悉 Keil,且项目复杂度高,推荐继续使用 Keil MDK-ARM;若你希望一站式开发,或为初学者,推荐使用 STM32CubeIDE。
2.2 HAL库的工程创建与外设初始化
在开发环境搭建完成后,下一步是创建工程并配置外设。STM32CubeMX 可以自动生成基于 HAL 库的初始化代码,包括系统时钟、GPIO、定时器等常用外设。
2.2.1 使用STM32CubeMX生成初始化代码
以配置一个 LED 灯闪烁的工程为例,说明如何使用 STM32CubeMX 生成初始化代码。
步骤:
- 打开 STM32CubeMX,选择芯片 STM32F103C8Tx。
- 在 Pinout 页面,将 PC13 引脚设置为 GPIO_Output。
- 在 Clock Configuration 页面,配置系统时钟为 72 MHz。
- 在 Project Manager 页面,选择 Target 为 STM32F103C8,IDE 为 STM32CubeIDE。
- 点击 Generate Code,生成工程代码。
生成的代码结构
生成的工程包含以下核心文件:
main.c:主程序入口。stm32f1xx_hal_msp.c:底层驱动支持。stm32f1xx_it.c:中断服务程序。gpio.c:GPIO初始化代码。tim.c(如有定时器配置):定时器初始化代码。
示例代码片段:GPIO初始化
void MX_GPIO_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
/* GPIO Ports Clock Enable */
__HAL_RCC_GPIOC_CLK_ENABLE();
/*Configure GPIO pin Output Level */
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
/*Configure GPIO pin : PC13 */
GPIO_InitStruct.Pin = GPIO_PIN_13;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; // 推挽输出模式
GPIO_InitStruct.Pull = GPIO_NOPULL; // 无上拉/下拉
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; // 输出速度低
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}
代码逐行分析
-
GPIO_InitTypeDef GPIO_InitStruct = {0};
定义一个 GPIO 初始化结构体,并初始化为 0。 -
__HAL_RCC_GPIOC_CLK_ENABLE();
使能 GPIOC 的时钟,这是操作任何外设前的必要步骤。 -
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
设置 PC13 初始电平为低电平(LED熄灭)。 -
GPIO_InitStruct.Pin = GPIO_PIN_13;
设置要配置的引脚为 PC13。 -
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
设置为推挽输出模式,适用于驱动LED等负载。 -
GPIO_InitStruct.Pull = GPIO_NOPULL;
不启用上拉或下拉电阻。 -
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
设置输出速度为低频模式,减少功耗和干扰。 -
HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
调用 HAL 函数完成 GPIO 初始化。
2.2.2 HAL库中GPIO、定时器等外设的基本配置方法
除了 GPIO,定时器是嵌入式开发中最常用的外设之一。HAL库提供了丰富的定时器函数,便于实现精准的延时、PWM 输出、输入捕获等功能。
定时器初始化示例
以下代码展示如何使用 HAL 库初始化 TIM2 定时器,实现 1 秒钟的定时中断。
TIM_HandleTypeDef htim2;
void MX_TIM2_Init(void)
{
__HAL_RCC_TIM2_CLK_ENABLE(); // 启用定时器时钟
htim2.Instance = TIM2;
htim2.Init.Prescaler = 71; // 预分频值,72MHz / 72 = 1MHz
htim2.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数模式
htim2.Init.Period = 9999; // 自动重载值,1MHz / 10000 = 100Hz
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
HAL_TIM_Base_Init(&htim2); // 初始化定时器
HAL_TIM_Base_Start_IT(&htim2); // 启动定时器中断
}
参数说明
Prescaler = 71:系统时钟为 72MHz,除以 72 得到 1MHz 的定时器时钟。Period = 9999:计数到 9999 时触发中断,即每 10000 次计数为 10ms。TIM_COUNTERMODE_UP:向上计数模式,计数器从0递增到Period。TIM_AUTORELOAD_PRELOAD_DISABLE:关闭自动重载预装载,简化操作。
中断服务函数
在 stm32f1xx_it.c 中,需要添加如下中断服务函数:
void TIM2_IRQHandler(void)
{
HAL_TIM_IRQHandler(&htim2); // 调用HAL库处理中断
}
回调函数
在 main.c 中,添加如下回调函数:
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim == &htim2) {
HAL_GPIO_TogglePin(GPIOC, GPIO_PIN_13); // 每次中断翻转LED状态
}
}
该回调函数会在定时器溢出时被调用,从而实现 LED 每秒闪烁一次的功能。
2.3 调试与下载环境的建立
开发过程中,调试和下载是验证程序是否正常运行的关键步骤。本节将介绍如何使用 ST-Link 调试器进行程序下载和调试。
2.3.1 ST-Link调试器的连接与使用
ST-Link 是 ST 官方提供的调试工具,广泛用于 STM32 系列芯片的调试。
连接方式
| ST-Link引脚 | STM32F103C8Tx引脚 |
|---|---|
| SWCLK | PA14 |
| SWDIO | PA13 |
| GND | GND |
| 3.3V | 3.3V |
注意:部分开发板已内置 ST-Link,可直接通过 USB 接口连接电脑进行调试。
在 STM32CubeIDE 中使用 ST-Link
- 连接好 ST-Link 和目标板。
- 打开 STM32CubeIDE,点击 Run → Debug As → STM32 Cortex-M C/C++ Application 。
- 首次运行会提示选择调试器,选择 ST-Link。
- 成功连接后,可以设置断点、查看寄存器、内存等信息。
2.3.2 程序下载与调试流程详解
下载流程
- 在 STM32CubeIDE 中,点击 Run → Run As → STM32 Cortex-M C/C++ Application 。
- 程序会自动编译并下载到芯片 Flash。
- 若连接正常,程序会立即运行,LED开始闪烁。
调试流程
- 设置断点:在代码行号左侧单击,设置断点。
- 启动调试:点击 Run → Debug As 。
- 单步执行:使用 Step Over、Step Into 等按钮逐步执行代码。
- 查看变量:在 Variables 窗口查看变量值。
- 查看寄存器:在 Register 窗口查看外设寄存器状态。
调试流程图
graph TD
A[连接ST-Link调试器] --> B[打开工程并编译]
B --> C[启动调试模式]
C --> D[设置断点]
D --> E[单步执行/运行到断点]
E --> F[查看变量/寄存器/内存]
F --> G[分析程序行为]
通过本章内容,开发者已经掌握了基于 HAL 库的 STM32 开发环境搭建、工程创建与外设初始化、以及调试与下载的基本流程。下一章将深入讲解超声波测距的原理与信号检测机制,为后续项目开发打下坚实基础。
3. 超声波测距原理与信号检测机制
在嵌入式系统中,超声波测距是一种常见且高效的非接触式距离测量方式。它广泛应用于机器人避障、无人机高度测量、智能停车系统、工业自动化等多个领域。本章将从物理原理出发,深入解析超声波测距的基本机制,并结合STM32微控制器的硬件特性,分析如何在嵌入式系统中实现对超声波信号的检测与处理。
3.1 超声波测距的物理原理
超声波测距依赖于声波的发射与回波接收,通过测量声波往返时间来计算距离。本节将从声波传播的基础物理特性入手,推导测距公式,并分析温度对声速的影响及补偿方法。
3.1.1 声波传播特性与测距公式
超声波是频率高于20kHz的机械波,通常在空气中传播。在标准大气压和20℃环境下,声波在空气中的传播速度约为343 m/s。其传播速度 $ v $ 可由下式估算:
v = 331 + 0.6T
其中:
- $ v $:声速(单位:m/s)
- $ T $:环境温度(单位:℃)
测距的基本公式为:
d = \frac{v \cdot t}{2}
其中:
- $ d $:被测物体与传感器之间的距离(单位:m)
- $ t $:超声波发射到回波接收的时间差(单位:s)
由于超声波在空气中传播具有方向性强、不易受光线影响、成本低等优点,使其在工业测距中广受欢迎。
示例:测距计算
假设测得回波时间 $ t = 5.88ms $,当前温度为25℃。
-
计算声速:
$$
v = 331 + 0.6 \times 25 = 346 m/s
$$ -
计算距离:
$$
d = \frac{346 \times 0.00588}{2} \approx 1.016 \text{m}
$$
3.1.2 温度对声速的影响及补偿机制
温度变化会导致空气密度变化,从而影响声速。为提高测距精度,必须引入温度补偿机制。常见的做法是使用数字温度传感器(如DS18B20或STM32内部温度传感器)获取环境温度,并实时计算声速。
| 温度(℃) | 声速(m/s) | 计算公式 |
|---|---|---|
| 0 | 331 | 基准值 |
| 10 | 337 | 331 + 0.6×10 |
| 20 | 343 | 331 + 0.6×20 |
| 30 | 349 | 331 + 0.6×30 |
通过温度传感器获取当前环境温度,嵌入式系统可以动态更新声速值,从而提高测距精度。
3.2 电平模式下的信号检测
超声波模块(如HC-SR04)通常通过高低电平进行触发和响应。本节将详细分析其工作时序,并讨论如何在STM32平台上测量回波脉冲宽度。
3.2.1 高低电平触发与响应的时序分析
以HC-SR04为例,其工作流程如下:
sequenceDiagram
participant MCU
participant HC_SR04
MCU->>HC_SR04: Trig引脚拉高10μs
HC_SR04-->>HC_SR04: 触发8个40kHz脉冲
HC_SR04->>MCU: Echo引脚拉高,持续时间等于回波时间
MCU->>MCU: 捕获Echo高电平持续时间
该时序流程说明了超声波模块如何通过GPIO控制进行测距。MCU首先在Trig引脚上发送一个10μs的高电平脉冲,触发超声波发射。随后,Echo引脚被拉高,持续时间即为超声波往返时间。
3.2.2 回波信号的脉宽测量原理
在STM32平台中,可以使用定时器的输入捕捉功能(Input Capture)或简单的GPIO中断来测量Echo信号的脉宽。
以下是一个使用GPIO中断方式测量回波时间的示例代码(基于HAL库):
// 定义全局变量用于记录时间
uint32_t rising_time = 0;
uint32_t falling_time = 0;
uint32_t echo_width = 0;
// Echo引脚中断回调函数
void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin)
{
if (GPIO_Pin == ECHO_PIN) {
if (HAL_GPIO_ReadPin(ECHO_PORT, ECHO_PIN) == GPIO_PIN_SET) {
// 上升沿,记录时间
rising_time = __HAL_TIM_GET_COUNTER(&htim);
} else {
// 下降沿,计算宽度
falling_time = __HAL_TIM_GET_COUNTER(&htim);
echo_width = falling_time - rising_time;
}
}
}
代码分析与参数说明:
rising_time和falling_time:记录Echo信号上升沿和下降沿的时间戳。__HAL_TIM_GET_COUNTER(&htim):获取当前定时器计数值,用于精确测量时间间隔。HAL_GPIO_EXTI_Callback:外部中断回调函数,用于处理Echo引脚的状态变化。
该方式适用于低频测距场景。若需更高精度,推荐使用定时器输入捕捉模式(详见第四章)。
3.3 嵌入式系统中的硬件接口设计
在嵌入式系统中,将超声波模块与STM32F103正确连接并设计抗干扰电路,是实现稳定测距的关键。
3.3.1 超声波模块与STM32的引脚连接方式
以HC-SR04为例,其与STM32的连接如下:
| 模块引脚 | 功能说明 | STM32引脚(示例) |
|---|---|---|
| VCC | 电源(5V) | 5V电源引脚 |
| GND | 接地 | GND |
| Trig | 触发信号输入 | PA1(GPIO输出) |
| Echo | 回波信号输出 | PA2(GPIO输入/中断) |
注意:STM32F103的GPIO引脚耐压为3.3V,而Echo输出为5V信号。因此,建议在Echo引脚接入一个10kΩ电阻与3.3V稳压二极管组成限压电路,或使用电平转换器。
3.3.2 信号调理电路与抗干扰设计
为了提升测距稳定性,应考虑以下信号调理与抗干扰措施:
- 上拉电阻 :Echo引脚可能输出高阻态,建议在MCU端添加10kΩ上拉电阻。
- RC滤波 :在Echo信号线上并联RC滤波电路(如100Ω + 100nF),以抑制高频噪声。
- 屏蔽线缆 :使用屏蔽线缆连接超声波模块,防止电磁干扰。
- 消隐时间(Blanking Time)设置 :软件中加入消隐时间(如2ms),防止因超声波模块未准备好而误触发。
示例:RC滤波电路设计
graph TD
A[Echo输出] --> B[RC滤波]
B --> C[STM32 GPIO]
D[100Ω电阻] --> B
E[100nF电容] --> F[GND]
该电路能有效滤除高频噪声,防止误触发中断。
本章从超声波测距的物理基础入手,详细分析了其工作原理,并结合STM32平台,介绍了信号检测的硬件连接方式和软件实现逻辑。通过引入温度补偿机制和信号调理电路设计,进一步提升了测距系统的稳定性和精度,为后续软件开发与优化奠定了坚实基础。
4. STM32定时器输入捕捉模式与HAL库实现
在嵌入式系统中,时间测量是许多应用的核心功能之一。STM32F103系列微控制器提供了丰富的定时器资源,其中输入捕捉(Input Capture)模式是实现高精度时间测量的重要手段。本章将深入探讨STM32定时器的输入捕捉机制,结合HAL库的使用方法,详细解析如何通过定时器实现信号时间差的精确测量,为后续超声波测距系统打下坚实基础。
4.1 定时器输入捕捉模式原理
STM32F103系列的通用定时器(如TIM2-TIM5)均支持输入捕捉模式。该模式可用于捕获外部信号的上升沿或下降沿发生时刻,并将当前定时器计数值保存到寄存器中,从而实现时间差的测量。输入捕捉常用于测量脉冲宽度、周期长度、信号频率等场景。
4.1.1 输入捕捉的工作机制与寄存器配置
输入捕捉的基本机制如下:
- 输入信号检测 :通过定时器通道引脚(如TIMx_CH1~CH4)连接外部信号源;
- 边沿触发配置 :可配置为上升沿触发、下降沿触发或双边沿触发;
- 计数器捕获 :当指定边沿发生时,当前定时器计数值(TIMx_CNT)被复制到捕获/比较寄存器(TIMx_CCRx);
- 中断通知 :若使能中断,将触发捕获中断标志(CCxIF),并可进入中断服务程序;
- 数据读取与处理 :通过读取捕获寄存器的值,结合定时器时钟频率,计算出时间差。
相关的寄存器包括:
| 寄存器名称 | 功能说明 |
|---|---|
| TIMx_CR1 | 控制定时器的计数方向、使能状态等 |
| TIMx_DIER | 中断使能寄存器,用于启用捕获中断 |
| TIMx_CCER | 捕获/比较使能寄存器,控制通道的极性与使能 |
| TIMx_CCMR1/2 | 通道模式寄存器,设置输入捕获的边沿类型与滤波 |
| TIMx_CNT | 当前计数值寄存器 |
| TIMx_CCRx | 捕获比较寄存器,用于存储捕获的计数值 |
例如,要配置TIM2的通道1为上升沿触发的输入捕捉模式,需要设置TIMx_CCMR1寄存器的CC1S位为01(输入模式),设置IC1PSC为00(无预分频),并设置CC1P为0(上升沿触发)。
4.1.2 捕捉事件与时间测量的实现方法
输入捕捉的核心应用是测量两个信号边沿之间的时间差。例如,在超声波测距中,可以通过测量触发信号和回波信号之间的时间差来计算距离。
实现步骤如下:
- 初始化定时器为输入捕捉模式 :配置通道为输入模式,选择触发边沿;
- 启动定时器 :使能定时器,开始计数;
- 等待第一次捕捉事件 :当第一个边沿到来时,记录捕获值;
- 等待第二次捕捉事件 :当第二个边沿到来时,再次记录捕获值;
- 计算时间差 :两次捕获值之差乘以定时器的时钟周期,即可得到时间差。
假设系统时钟为72MHz,定时器预分频器为71,则定时器时钟为1MHz(即每计数一次为1μs),若两次捕获值分别为5000和15000,则时间差为10000μs = 10ms。
4.2 HAL库中的定时器配置函数
HAL库(Hardware Abstraction Layer)为开发者提供了标准化的接口,简化了底层寄存器的配置。在使用STM32CubeMX生成初始化代码后,可以通过HAL库提供的API快速配置输入捕捉模式。
4.2.1 HAL_TIM_IC_Init函数的功能与参数详解
函数原型如下:
HAL_StatusTypeDef HAL_TIM_IC_Init(TIM_HandleTypeDef *htim);
- 参数说明 :
htim:指向TIM_HandleTypeDef结构体的指针,包含定时器的基本配置信息,如定时器实例、时钟源、计数模式等。
此函数主要完成以下任务:
- 初始化定时器基本参数 :如时钟源、计数方向、周期等;
- 配置定时器为输入捕捉模式 ;
- 设置默认中断回调函数 ;
- 调用底层
HAL_TIM_IC_MspInit函数进行GPIO与中断配置 (由用户实现)。
示例代码如下:
TIM_HandleTypeDef htim2;
void MX_TIM2_Init(void)
{
htim2.Instance = TIM2;
htim2.Init.Prescaler = 71; // 预分频器值,72MHz / 72 = 1MHz
htim2.Init.CounterMode = TIM_COUNTERMODE_UP; // 向上计数
htim2.Init.Period = 0xFFFFFFFF; // 最大计数值
htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim2.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_IC_Init(&htim2) != HAL_OK)
{
Error_Handler();
}
}
- 执行逻辑分析 :
- 第3行:指定使用TIM2定时器;
- 第4~7行:配置定时器的预分频、计数方向、周期等;
- 第9行:调用
HAL_TIM_IC_Init完成初始化; - 若初始化失败,进入错误处理函数。
4.2.2 定时器通道配置与中断使能设置
配置定时器通道为输入捕捉模式需要调用 HAL_TIM_IC_ConfigChannel 函数:
HAL_StatusTypeDef HAL_TIM_IC_ConfigChannel(TIM_HandleTypeDef *htim,
TIM_IC_InitTypeDef *sConfig,
uint32_t Channel);
- 参数说明 :
htim:指向定时器句柄;sConfig:指向输入捕捉配置结构体,定义边沿触发类型、滤波设置等;Channel:指定通道(如TIM_CHANNEL_1)。
示例代码如下:
TIM_IC_InitTypeDef sConfigIC;
sConfigIC.ICPolarity = TIM_ICPOLARITY_RISING; // 上升沿触发
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI; // 选择通道输入
sConfigIC.ICPrescaler = TIM_ICPSC_DIV1; // 无预分频
sConfigIC.ICFilter = 0x0; // 不启用滤波
if (HAL_TIM_IC_ConfigChannel(&htim2, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
Error_Handler();
}
- 执行逻辑分析 :
- 第3行:设置为上升沿触发;
- 第4行:选择为直连输入模式(即不使用交叉通道);
- 第5行:不对输入信号进行分频;
- 第6行:禁用输入滤波;
- 第8~11行:配置通道1为输入捕捉模式。
接下来,需要使能捕获中断并启动定时器:
HAL_TIM_IC_Start_IT(&htim2, TIM_CHANNEL_1); // 启动通道1的输入捕捉中断
该函数将自动使能捕获中断(CC1IE位),并启动定时器计数。
4.3 回调函数与中断服务程序
在HAL库中,输入捕捉的中断处理通过回调函数机制实现,开发者可以通过重写回调函数来处理捕获事件。
4.3.1 HAL_TIM_IC_CaptureCallback回调函数的使用
当发生捕获事件时,HAL库会调用以下回调函数:
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);
开发者需要在用户代码中重写该函数,以处理捕获的数据。例如:
uint32_t IC_Val1 = 0;
uint32_t IC_Val2 = 0;
uint8_t Is_First_Captured = 0;
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
if (htim == &htim2)
{
if (Is_First_Captured == 0) // 第一次捕获
{
IC_Val1 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
Is_First_Captured = 1;
htim->Instance->CNT = 0; // 重置计数器
}
else if (Is_First_Captured == 1) // 第二次捕获
{
IC_Val2 = HAL_TIM_ReadCapturedValue(htim, TIM_CHANNEL_1);
Is_First_Captured = 0;
// 计算时间差
uint32_t duration = IC_Val2 - IC_Val1;
}
}
}
- 执行逻辑分析 :
- 第8行:读取当前捕获值;
- 第9行:标记为第一次捕获;
- 第11行:将计数器清零,以便下一次测量;
- 第13~16行:第二次捕获后计算时间差;
- 使用
HAL_TIM_ReadCapturedValue获取捕获寄存器的值。
4.3.2 多次捕捉与时间差计算的逻辑实现
在某些应用中,可能需要进行多次捕捉以提高测量精度或实现周期测量。例如,测量脉冲周期时,可以连续捕获多个上升沿的时间差。
流程图如下:
graph TD
A[开始] --> B[第一次捕获]
B --> C{是否已捕获?}
C -->|否| B
C -->|是| D[记录时间1]
D --> E[等待第二次捕获]
E --> F{是否已捕获?}
F -->|否| E
F -->|是| G[记录时间2]
G --> H[计算时间差]
H --> I[是否继续测量?]
I -->|是| B
I -->|否| J[结束]
该流程图展示了从初始化到多次测量的完整逻辑。通过在回调函数中维护状态标志(如 Is_First_Captured ),可以实现连续的时间测量。
此外,为避免溢出,应确保定时器周期足够大。若使用32位定时器(如TIM2、TIM5),最大计数值为0xFFFFFFFF,对应时间可达4294秒(约1.2小时),足以满足绝大多数应用需求。
通过本章的深入分析,我们掌握了STM32定时器输入捕捉模式的原理、寄存器配置方法,以及HAL库中的实现流程。结合回调函数与中断处理机制,可以高效实现时间测量功能,为后续超声波测距系统的开发提供了坚实的技术基础。
5. 超声波测距系统的软件实现与优化
5.1 超声波发射与接收控制流程
在STM32F103平台上,超声波测距的实现依赖于GPIO和定时器模块的协同工作。以下为超声波发射和接收的基本流程:
5.1.1 GPIO驱动超声波触发信号的生成
以HC-SR04超声波模块为例,其触发信号为一个持续10μs的高电平脉冲。我们使用GPIO端口(例如GPIO_PIN_5)来输出该信号。
// 触发超声波模块
void Trigger_Ultrasonic(void) {
HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_SET); // 拉高电平
HAL_Delay(1); // 延时1μs
HAL_GPIO_WritePin(TRIG_GPIO_Port, TRIG_Pin, GPIO_PIN_RESET); // 拉低电平
}
参数说明:
-TRIG_GPIO_Port:触发引脚所属的GPIO端口。
-TRIG_Pin:触发引脚编号(例如GPIO_PIN_5)。
-HAL_Delay(1):微秒级延时函数,需配置系统时钟或使用SysTick实现更高精度延时。
该触发信号发送后,超声波模块将发出8个40kHz的超声波脉冲,并等待回波信号。
5.1.2 接收端信号的等待与超时处理机制
回波信号由模块的Echo引脚输出,高电平持续时间即为超声波从发射到返回的时间。我们通过定时器输入捕捉模式获取该时间。
为避免系统死锁,应设置合理的超时时间(如60ms,对应最大测距约10米):
// 等待回波信号上升沿触发
uint32_t wait_echo_rising(void) {
uint32_t timeout = HAL_GetTick() + 60; // 设置60ms超时
while (HAL_GPIO_ReadPin(ECHO_GPIO_Port, ECHO_Pin) == GPIO_PIN_RESET) {
if (HAL_GetTick() > timeout) {
return 0; // 超时返回0
}
}
return 1; // 成功检测到上升沿
}
参数说明:
-ECHO_GPIO_Port:接收引脚所属GPIO端口。
-ECHO_Pin:接收引脚编号。
-HAL_GetTick():获取系统当前时间(毫秒级)。
该机制确保即使无回波信号,系统也能及时退出等待,提升鲁棒性。
5.2 时间测量与距离换算算法
5.2.1 捕获时间的单位换算与误差分析
通过定时器输入捕捉模式获取到的高电平持续时间( high_time ),单位为定时器时钟周期数(如72MHz系统时钟下,一个周期为1/72μs)。
计算公式如下:
distance = (high_time * speed_of_sound / 2) / (SystemCoreClock / 1000000)
其中:
speed_of_sound:声速(常温下约为343m/s)SystemCoreClock:系统主频(如72MHz)
示例代码如下:
float calculate_distance(uint32_t high_time, float speed_of_sound) {
float us = high_time / (SystemCoreClock / 1000000); // 微秒级时间
return (us * speed_of_sound / 2.0f) / 10000.0f; // 转换为厘米
}
误差来源分析:
- 定时器精度受限于系统时钟分频。
- 回波信号抖动可能导致捕捉误差。
- 温度变化影响声速,未补偿时误差可达±3%。
5.2.2 基于温度传感器的声速动态补偿算法
使用DS18B20等温度传感器获取环境温度,动态计算声速:
speed_of_sound = 331.3 + 0.606 * temperature
示例代码片段:
float get_sound_speed(float temperature) {
return 331.3f + 0.606f * temperature;
}
将该函数集成到主循环中,可实时更新声速值,提高测距精度。
5.3 系统稳定性与抗干扰优化
5.3.1 消隐时间的设置与多回波干扰处理
超声波模块在发射后存在短暂的“盲区”,通常为200μs。在此期间Echo引脚可能产生虚假高电平,应设置消隐时间避免误触发:
// 消隐时间延时
void delay_us(uint32_t us) {
// 使用SysTick或定时器实现微秒延时
}
void handle_echo_signal(void) {
delay_us(200); // 消隐时间
if (wait_echo_rising()) {
// 启动定时器捕捉回波下降沿
}
}
此外,对于多回波干扰(如墙角反射),可通过检测回波宽度,仅保留第一个脉冲进行计算。
5.3.2 测距结果的滤波算法与异常值剔除
为提高测距稳定性,采用滑动平均滤波与中位值滤波结合的方式:
#define SAMPLES 5
float distance_samples[SAMPLES];
float get_filtered_distance(float new_distance) {
for (int i = 0; i < SAMPLES - 1; i++) {
distance_samples[i] = distance_samples[i + 1];
}
distance_samples[SAMPLES - 1] = new_distance;
// 冒泡排序获取中位值
float temp[SAMPLES];
memcpy(temp, distance_samples, sizeof(temp));
for (int i = 0; i < SAMPLES - 1; i++) {
for (int j = 0; j < SAMPLES - i - 1; j++) {
if (temp[j] > temp[j + 1]) {
float swap = temp[j];
temp[j] = temp[j + 1];
temp[j + 1] = swap;
}
}
}
return temp[SAMPLES / 2]; // 返回中位值
}
该方法有效抑制瞬时干扰,提高测距稳定性。
5.4 测距结果显示与系统扩展
5.4.1 LCD显示屏的驱动与数据可视化实现
使用如OLED或TFT-LCD模块(如SSD1306)显示测距结果,需先初始化SPI或I2C接口,并加载字体库。
示例代码片段(基于SSD1306 OLED):
char buffer[20];
sprintf(buffer, "Distance: %.2f cm", distance);
SSD1306_Clear();
SSD1306_SetCursor(0, 0);
SSD1306_WriteString(buffer, Font_11x18, White);
SSD1306_UpdateScreen();
功能说明:
-SSD1306_Clear():清屏操作。
-SSD1306_SetCursor(x, y):设置光标位置。
-SSD1306_WriteString():显示字符串。
-SSD1306_UpdateScreen():刷新屏幕。
5.4.2 数据通过串口上传与智能家居集成应用展望
使用USART模块将测距数据上传至上位机或网关设备,便于远程监控:
char uart_buffer[50];
sprintf(uart_buffer, "Distance: %.2f cm\r\n", distance);
HAL_UART_Transmit(&huart2, (uint8_t*)uart_buffer, strlen(uart_buffer), HAL_MAX_DELAY);
扩展应用:
- 与Wi-Fi模块(如ESP8266)结合,实现远程测距监控。
- 接入MQTT协议,上传数据至IoT平台(如阿里云、ThingsBoard)。
- 应用于智能家居门禁、自动泊车、机器人避障等场景。
简介:本项目基于STM32F103微控制器,使用HAL库开发了一款超声波测距程序,采用电平模式实现高精度距离测量。程序利用定时器的输入捕捉功能,结合超声波发射与回波检测的时间差,通过声速计算目标距离。适用于智能家居、自动安防等嵌入式应用场景,帮助开发者掌握HAL库开发、定时器配置与传感器数据处理的核心技能。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)