前言
在前面的教程中已经讲过GPIO口配置、中断控制系统NVIC配置、外部中断EXTI和定时器配置等内容。本文将通过一个简单的超声波传感器将之前所讲的内容都应用起来。
一、超声波传感器简介
1.超声波传感器特点
HC-SR04超声波测距模块可提供2cm-400cm的非接触式距离感测功能,测距精度可达高到3mm。超声波测距模块有4个引脚:
VCC:电源正,连接单片机的5V
GND:电源负,连接单片机的地(GND)
TRIG:触发控制信号输入引脚,给此引脚一个至少10us的高电平脉冲启动一次测距。
ECHO:回响信号输出引脚。模块测距完成后,此引脚会输出一个高电平,高电平的持续时间就是超声波从发射到返回的时间。
2.超声波传感器测距原理
当给TRIG引脚一个至少10us的高电平脉冲时,模块会自动发送8个40khz的方波,并自动检测是否有信号返回;当检测到信号返回,ECHO引脚输出高电平,高电平持续的时间就是超声波从发射到返回的时间。
探测距离=高电平时间*声速/2;
【注1】建议测量周期60ms以上,以防止发射信号对测量信号的影响。
【注2】声速取340m/s或34000cm/s。
二、超声波程序设计思路
STM32F407开发板的PA2引脚连接TRIG引脚,PA2引脚输出一个10us以上的高电平脉冲来触发测距。PA1引脚连接ECHO引脚,并设置为外部中断,同时检测上升沿和下降沿。当ECHO上升沿时,表示接收到返回信号,此时启动定时器开始计时;当ECHO下降沿时,表示信号结束,此时停止定时器计数,并计算距离。
1.程序设计步骤
(1)初始化TRIG引脚(PA2)为推挽输出,初始状态为低电平。
(2)初始化ECHO引脚(PA1)为浮空输入,并配置为外部中断,同时设置上升沿和下降沿触发。
(3)配置NVIC,设置通断分组、中断通断、抢占和响应优先级等
(4)配置定时器TIM3,使其1us计数一次。
(5)编写外部中断函数,判断是上升沿还是下降沿。上升沿时,启动定时器;下降沿时,停止定时器,读取计数器的值,并计算距离。
(6)编写延时函数、获取距离、发送触发信号等函数
2.程序如下(以下函数都定义在main.c中)
/*1-TRIG引脚配置-推挽输出*/void TRIG_Config(){/*1-定义结构体*/GPIO_InitTypeDef GPIO_InitStruct; //定义结构体/*2-开启GPIOF时钟*/RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);/*3-初始化相关参数*/GPIO_InitStruct.GPIO_Pin = GPIO_Pin_2; //选择A2引脚GPIO_InitStruct.GPIO_Mode = GPIO_Mode_OUT; //输出模式GPIO_InitStruct.GPIO_Speed = GPIO_Speed_100MHz; //输出速度GPIO_InitStruct.GPIO_OType = GPIO_OType_PP; //推挽输出GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_UP; // 上拉/*4-完成初始化*/GPIO_Init(GPIOA, &GPIO_InitStruct);GPIO_ResetBits(GPIOA, GPIO_Pin_2); // 初始化为低电平}
/*2-ECHO引脚配置-配置PA1为EXTI中断输入脚*/void ECHO_Config(){/*1-定义相关结构体*/NVIC_InitTypeDef NVIC_InitStructure;//NVIC结构体GPIO_InitTypeDef GPIO_InitStruct;//GPIO结构体EXTI_InitTypeDef EXTI_InitStructure;//EXTI结构体/*2-开相应时钟*/RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);//开时钟RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);/*3-PA1配置为输入引脚*/GPIO_InitStruct.GPIO_Pin = GPIO_Pin_1; //A1引脚GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN; //输入模式GPIO_InitStruct.GPIO_PuPd = GPIO_PuPd_NOPULL; //浮空模式GPIO_Init(GPIOA, &GPIO_InitStruct);/*4-设置中断线与IO引脚的映射关系*/SYSCFG_EXTILineConfig(EXTI_PortSourceGPIOA, EXTI_PinSource1);/*5-EXTI配置*/EXTI_InitStructure.EXTI_Line = EXTI_Line1;EXTI_InitStructure.EXTI_Mode = EXTI_Mode_Interrupt;//中断模式EXTI_InitStructure.EXTI_Trigger = EXTI_Trigger_Rising_Falling;//上升沿下降沿触发EXTI_InitStructure.EXTI_LineCmd = ENABLE;//使能EXTI中断EXTI_Init(&EXTI_InitStructure);/*6-NVCI配置*/NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);//设置中断分组NVIC_InitStructure.NVIC_IRQChannel = EXTI1_IRQn;//中断通道NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;//抢占优先级NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//响应优先级NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能中断NVIC_Init(&NVIC_InitStructure);}
/*3-TIM3配置*/void TIM3_Init(){/*1-定义TIM3结构体*/TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;/*2-开启TIM3时钟*/RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);//开启TIM2时钟/*3-时基配置*/TIM_TimeBaseStructure.TIM_Period = 0xffff;//自动重装载ARR值TIM_TimeBaseStructure.TIM_Prescaler = 84-1;//预分频PSC值,1MHzTIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;//1分频TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数模式TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure);//完成初始化/*4-使能定时器*///TIM_Cmd(TIM3,ENABLE);}
/*4-延时函数*///延时函数:微秒级别void delay_us(uint32_t nus){uint32_t tmp;while(nus--){SysTick->LOAD = 168000000/8/1000000; //计数初值,延时1usSysTick->VAL = 0;//清空当前值SysTick->CTRL |= 0x1; //开始计数do{tmp = SysTick->CTRL;//获取CTRL状态}while((tmp&0x1) && !(0x1<<16));SysTick->VAL = 0;//清空当前值SysTick->CTRL &= 0;//关闭计数}}//毫秒级别void delay_ms(uint32_t nms){uint32_t tmp;while(nms--){SysTick->LOAD = 168000000/8/1000;SysTick->VAL = 0;SysTick->CTRL |= 0x1;do{tmp = SysTick->CTRL;}while((tmp&0x1) && !(0x1<<16));SysTick->VAL = 0;SysTick->CTRL &= 0;}}
/*5-发送触发信号和测距函数*/// 发送10us触发脉冲void Ultrasonic_Trigger(void) {GPIO_SetBits(GPIOA, GPIO_Pin_2); // 高电平delay_us(15); // 保持15usGPIO_ResetBits(GPIOA, GPIO_Pin_2); // 拉低}//计算距离float Get_Distance(void) {// 距离 = (时间 * 声速) / 2// 声速340m/s = 34000cm/s = 0.034cm/usreturn (TIM3_Capture * 0.034) / 2.0;}
/*6-main.c*/// 全局变量uint32_t TIM3_Capture = 0;uint8_t Echo_State = 0; // 0:空闲 1:已捕获上升沿float distance = 0;//上述函数都定义在main函数之前int main(){LED_Init();TIM3_Init();TRIG_Config();ECHO_Config();while(1){Ultrasonic_Trigger(); // 发送触发信号delay_ms(100); // 每100ms测量一次distance = Get_Distance();if(distance < 10) LED0_ON();//简单用LED灯测试 ,可用OLED或串口显示数据else LED0_OFF();}}/*中断服务函数*/void EXTI1_IRQHandler(void) {if (EXTI_GetITStatus(EXTI_Line1) != RESET) {if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_1)) { // 上升沿TIM3->CNT = 0; // 计数器清零TIM_Cmd(TIM3, ENABLE); // 启动定时器Echo_State = 1; // 标记已捕获上升沿} else { // 下降沿if (Echo_State) {TIM_Cmd(TIM3, DISABLE); // 停止定时器TIM3_Capture = TIM3->CNT; // 获取计数值Echo_State = 0; // 重置状态}}EXTI_ClearITPendingBit(EXTI_Line1);}}

