开发板:秉火-霸道V1
芯片:STM32F103ZET6
定时器:TIM2
通道: 3
模块工作原理
- 采用 IO 触发测距,给 Trig 引脚至少 10us 的高电平信号;
- 模块自动发送 8 个 40khz 的方波,自动检测是否有信号返回;
- 有信号返回,通过引脚 Echo 输出一高电平,高电平持续的时间就是 超声波从发射到返回的时间.
- 测试距离 = (高电平时间 * 声速(340M/S)) / 2;
时序图
我们可以借助通用定时器的 输入捕获 来测量 Echo 引脚高电平持续的时间
HC_SR04.h
#ifndef _BSP_HCSR04_H
#define _BSP_HCSR04_H
#include"stm32f10x.h"
#include"stm32f10x_tim.h"
//TIM2
#define GENERAL_TIMx TIM2
#define GENERAL_TIMx_CLK_FUN RCC_APB1PeriphClockCmd
#define GENERAL_TIMx_CLK RCC_APB1Periph_TIM2
//Trig引脚定义---PA10
#define HCSR04_GPIO_CLK_FUN RCC_APB2PeriphClockCmd
#define HCSR04_GPIO_CLK RCC_APB2Periph_GPIOA
#define HCSR04_GPIO_TRIG_PORT GPIOA
#define HCSR04_GPIO_TRIG_PIN GPIO_Pin_10
#define HCSR04_TRIG_Hight HCSR04_GPIO_TRIG_PORT->BSRR = HCSR04_GPIO_TRIG_PIN
#define HCSR04_TRIG_Low HCSR04_GPIO_TRIG_PORT->BRR = HCSR04_GPIO_TRIG_PIN
//TIM引脚定义
#define GENERAL_TIMx_CH3_CLK_FUN RCC_APB2PeriphClockCmd
#define GENERAL_TIMx_CH3_CLK RCC_APB2Periph_GPIOA
#define GENERAL_TIMx_CH3_PORT GPIOA
#define GENERAL_TIMx_CH3_PIN GPIO_Pin_2
#define GENERAL_TIMx_CHANNEL_x TIM_Channel_3
//TIM2中断
#define GENERAL_TIMx_IRQn TIM2_IRQn
#define GENERAL_TIMx_IRQHandler TIM2_IRQHandler
typedef struct {
uint8_t Capture_FinishFlag; // 捕获结束标志位
uint8_t Capture_StartFlag; // 捕获开始标志位
uint16_t Capture_CcrValue; // 捕获寄存器的值
uint16_t Capture_Period; // 自动重装载寄存器更新标志
} TIM_ICUserValueTypeDef;
extern TIM_ICUserValueTypeDef TIM_ICUserValueStruct;
void TIM_Config(void);
void HCSR04_GPIO_Config(void);
void HCSR04_TRIG_Send(void);
#endif /*_BSP_HCSR04_H*/
HC_SR04.c
/*
Trig: PA2
Echo: PA10
*/
#include"bsp_HCSR04.h"
#include "bsp_delay.h"
TIM_ICUserValueTypeDef TIM_ICUserValueStruct = {0,0,0,0};
//HCSR04初始化引脚
void HCSR04_GPIO_Config(void)
{
//定义GPIO结构体变量
GPIO_InitTypeDef GPIO_InitStruct;
//打开时钟
HCSR04_GPIO_CLK_FUN(HCSR04_GPIO_CLK,ENABLE);
//Trig引脚
GPIO_InitStruct.GPIO_Pin = HCSR04_GPIO_TRIG_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(HCSR04_GPIO_TRIG_PORT,&GPIO_InitStruct);
}
void HCSR04_TRIG_Send(void)
{
HCSR04_TRIG_Hight;
Delay_Us(15);
HCSR04_TRIG_Low;
}
static void GENERAL_TIMx_GPIO_Config(void)
{
GPIO_InitTypeDef GPIO_InitStruct;
GENERAL_TIMx_CH3_CLK_FUN(GENERAL_TIMx_CH3_CLK,ENABLE);
GPIO_InitStruct.GPIO_Pin = GENERAL_TIMx_CH3_PIN;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_Init(GENERAL_TIMx_CH3_PORT,&GPIO_InitStruct);
}
//中断初始化
static void GENERAL_TIMx_NVIC_Config(void)
{
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_0);
//设置中断通道
NVIC_InitStruct.NVIC_IRQChannel = GENERAL_TIMx_IRQn;
//设置子优先级
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
//设置抢占优先级
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
//中断通道使能
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStruct);
}
static void GENERAL_TIMx_Mode_Config(void)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_ICInitTypeDef TIM_ICInitStruct;
GENERAL_TIMx_CLK_FUN(GENERAL_TIMx_CLK,ENABLE);
//初始化时基
TIM_TimeBaseInitStruct.TIM_Prescaler = (72-1);
TIM_TimeBaseInitStruct.TIM_Period = 0xFFFF;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(GENERAL_TIMx,&TIM_TimeBaseInitStruct);
//初始化 IC
TIM_ICInitStruct.TIM_Channel = GENERAL_TIMx_CHANNEL_x;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInitStruct.TIM_ICFilter = 0;
TIM_ICInit(GENERAL_TIMx,&TIM_ICInitStruct);
//清楚更新中断标志
TIM_ClearFlag(GENERAL_TIMx,TIM_FLAG_Update | TIM_FLAG_CC3);
//中断初始化
TIM_ITConfig(GENERAL_TIMx,TIM_FLAG_Update | TIM_FLAG_CC3,ENABLE);
//使能TIM
TIM_Cmd(GENERAL_TIMx,ENABLE);
}
void TIM_Config(void)
{
GENERAL_TIMx_GPIO_Config();
GENERAL_TIMx_NVIC_Config();
GENERAL_TIMx_Mode_Config();
}
TIM2中断函数
详细过程参考野火例徎 32-TIM—高级定时器
//TIM2中断函数
void GENERAL_TIMx_IRQHandler(void)
{
//判断是否超出Period 检测位为更新中断
//若是更新,改变Capture_Period值
if(TIM_GetITStatus(GENERAL_TIMx,TIM_IT_Update) != RESET)
{
TIM_ICUserValueStruct.Capture_Period++;
//清除中断等待
TIM_ClearITPendingBit(GENERAL_TIMx,TIM_IT_Update);
}
//捕获边沿
if(TIM_GetITStatus(GENERAL_TIMx,TIM_IT_CC3) != RESET)
{
//表示第一次捕获 清除CCR
if(TIM_ICUserValueStruct.Capture_StartFlag == 0)
{
TIM_SetCounter(GENERAL_TIMx,0); //从0开始计数
TIM_ICUserValueStruct.Capture_CcrValue = 0; //捕获值为0
// 自动重装载寄存器更新标志清0
TIM_ICUserValueStruct.Capture_Period = 0;
//改变捕获极性
TIM_OC3PolarityConfig(GENERAL_TIMx,TIM_ICPolarity_Falling);
TIM_ICUserValueStruct.Capture_StartFlag = 1;
}
//第二次捕获
else
{
//得到CCR的值
TIM_ICUserValueStruct.Capture_CcrValue = TIM_GetCapture3(GENERAL_TIMx);
//改变捕获极性
TIM_OC3PolarityConfig(GENERAL_TIMx,TIM_ICPolarity_Rising);
//改变标志
TIM_ICUserValueStruct.Capture_StartFlag = 0;
TIM_ICUserValueStruct.Capture_FinishFlag = 1;
}
TIM_ClearITPendingBit (GENERAL_TIMx,TIM_IT_CC3);
}
}
delay函数
#include "bsp_delay.h"
void Delay_Us( __IO uint32_t us)
{
uint32_t i;
SysTick_Config(SystemCoreClock/1000000);
for (i=0; i<us; i++)
{
// 当计数器的值减小到 0 的时候,CRTL 寄存器的位 16 会置 1
while ( !((SysTick->CTRL)&(1<<16)) );
}
// 关闭 SysTick 定时器
SysTick->CTRL &=~SysTick_CTRL_ENABLE_Msk;
}
main.c
/*
按下 K1 启动测量
*/
#include"bsp_HCSR04.h"
#include"bsp_usart.h"
#include"bsp_key.h"
int main()
{
uint32_t time;
// TIM 计数器的驱动时钟
uint32_t TIM_PscCLK = 72000000 / (71+1);
float d = 0;
USART_Config();
HCSR04_GPIO_Config();
TIM_Config();
KEY_GPIO_Config();
printf("\rHC_SR04实验\n");
while (1)
{
if(Key_Scan(KEY1_GPIO_PORT,KEY1_GPIO_PIN)== KEY_ON)
HCSR04_TRIG_Send();
if(TIM_ICUserValueStruct.Capture_FinishFlag == 1)
{
// 计算高电平时间的计数器的值
time = TIM_ICUserValueStruct.Capture_Period * (0xFFFF+1) + (TIM_ICUserValueStruct.Capture_CcrValue+1);
printf ( "\r\n测得高电平脉宽时间:%ld.%ld s\r\n",time/TIM_PscCLK,time%TIM_PscCLK );
d = (float)time * 170 / 10000;
printf("距离为:");
Print_Float(d);
TIM_ICUserValueStruct.Capture_FinishFlag = 0;
}
}
}
上述部分代码移植了野火的例程代码