• 基于stm32的超声波测距报警系统(附源码和连接方式;串口显示)


    之前自己写过51 单片机的超声波测距代码,最近学了stm32,就想着也用stm32来写一个。想着写出来分享给大家吧。相比之下,51和stm32自己还是偏喜欢stm32的,主要是基于固件库来进行开发的
    自己也很喜欢调用固件库的感觉,所以就有了这篇文章。
    首先讲一下HC-SR04超声波模块的工作原理吧!
    原理:
    给脉冲触发引脚(trig)输入一个长为20us的高电平方波,输入方波后,模块会自动发射8个40KHz的声波,与此同时回波引脚(echo)端的电平会由0变为1;(此时应该启动定时器计时),当超声波返回被模块接收到时,回波引 脚端的电平会由1变为0;(此时应该停止定时器计数),定时器记下的这个时间即为超声波由发射到返回的总时长。根据声音在空气中的速度为344米/秒,即可计算出所测的距离。
    超声波工作时序图

    **实验器材:**HC-SR04超声波;STM32C8T6;无源蜂鸣器;CH340下载器

    //超声波测距
    
    #include "hcsr04.h"
     
    #define HCSR04_PORT     GPIOB
    #define HCSR04_CLK      RCC_APB2Periph_GPIOB
    #define HCSR04_TRIG     GPIO_Pin_4
    #define HCSR04_ECHO     GPIO_Pin_5
    #define BEEP_PIN                 GPIO_Pin_6
    
    #define TRIG_Send  PBout(4) 
    #define ECHO_Reci  PBin(5)
    #define  BEEP PBout(6) 
    
    u16 msHcCount = 0;//ms计数
    
    void Hcsr04Init()
    {  
        TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;     //生成用于定时器设置的结构体
        GPIO_InitTypeDef GPIO_InitStructure;
        RCC_APB2PeriphClockCmd(HCSR04_CLK, ENABLE);
         
            //IO初始化
        GPIO_InitStructure.GPIO_Pin =HCSR04_TRIG;       //发送电平引脚
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
        GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);
        GPIO_ResetBits(HCSR04_PORT,HCSR04_TRIG);
         
        GPIO_InitStructure.GPIO_Pin =   HCSR04_ECHO;     //返回电平引脚
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;//浮空输入
        GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);  
            GPIO_ResetBits(HCSR04_PORT,HCSR04_ECHO);   
            //蜂鸣器引脚初始化 
         GPIO_InitStructure.GPIO_Pin =BEEP_PIN ;       //发送电平引脚
        GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
        GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;//推挽输出
        GPIO_Init(HCSR04_PORT, &GPIO_InitStructure);
        GPIO_SetBits(HCSR04_PORT,BEEP_PIN );
        
                //定时器初始化 使用基本定时器TIM6
            RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM6, ENABLE);   //使能对应RCC时钟
            //配置定时器基础结构体
            TIM_DeInit(TIM2);
            TIM_TimeBaseStructure.TIM_Period = (1000-1); //设置在下一个更新事件装入活动的自动重装载寄存器周期的值         计数到1000为1ms
            TIM_TimeBaseStructure.TIM_Prescaler =(72-1); //设置用来作为TIMx时钟频率除数的预分频值  1M的计数频率 1US计数
            TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;//不分频
            TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
            TIM_TimeBaseInit(TIM6, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位         
            
            TIM_ClearFlag(TIM6, TIM_FLAG_Update);   //清除更新中断,免得一打开中断立即产生中断
            TIM_ITConfig(TIM6,TIM_IT_Update,ENABLE);    //打开定时器更新中断
            hcsr04_NVIC();
        TIM_Cmd(TIM6,DISABLE);     
    }
    
    
    //tips:static函数的作用域仅限于定义它的源文件内,所以不需要在头文件里声明
    static void OpenTimerForHc()        //打开定时器
    {
            TIM_SetCounter(TIM6,0);//清除计数
            msHcCount = 0;
            TIM_Cmd(TIM6, ENABLE);  //使能TIMx外设
    }
     
    static void CloseTimerForHc()        //关闭定时器
    {
            TIM_Cmd(TIM6, DISABLE);  //使能TIMx外设
    }
     
     
     //NVIC配置
    void hcsr04_NVIC()
    {
                NVIC_InitTypeDef NVIC_InitStructure;
                NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
        
                NVIC_InitStructure.NVIC_IRQChannel = TIM6_IRQn;             //选择串口1中断
                NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //抢占式中断优先级设置为1
                NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;         //响应式中断优先级设置为1
                NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;        //使能中断
                NVIC_Init(&NVIC_InitStructure);
    }
    
    
    //定时器6中断服务程序
    void TIM6_IRQHandler(void)   //TIM3中断
    {
            if (TIM_GetITStatus(TIM6, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
            {
                    TIM_ClearITPendingBit(TIM6, TIM_IT_Update  );  //清除TIMx更新中断标志 
                    msHcCount++;
            }
    }
     
    
    //获取定时器时间
    u32 GetEchoTimer(void)
    {
            u32 t = 0;
            t = msHcCount*1000;//得到MS
            t += TIM_GetCounter(TIM6);//得到US
              TIM6->CNT = 0;  //将TIM2计数寄存器的计数值清零
                    Delay_Ms(50);
            return t;
    }
     
    
    //一次获取超声波测距数据 两次测距之间需要相隔一段时间,隔断回响信号
    //为了消除余震的影响,取五次数据的平均值进行加权滤波。
    float Hcsr04GetLength(void )
    {
            u32 t = 0;
            int i = 0;
            float lengthTemp = 0;
            float sum = 0;
            while(i!=5)
            {
            TRIG_Send = 1;      //发送口高电平输出
            Delay_Us(20);
            TRIG_Send = 0;
            while(ECHO_Reci == 0);      //等待接收口高电平输出
                OpenTimerForHc();        //打开定时器
                i = i + 1;
                while(ECHO_Reci == 1);
                CloseTimerForHc();        //关闭定时器
                t = GetEchoTimer();        //获取时间,分辨率为1US
                lengthTemp = ((float)t/58.0);//cm
                sum = lengthTemp + sum ;
            
        }
            lengthTemp = sum/5.0;
            return lengthTemp;
    }
    
    
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    ** 函数名称: Delay_Ms_Ms
    ** 功能描述: 延时1MS (可通过仿真来判断他的准确度)            
    ** 参数描述:time (ms) 注意time<65535
    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    void Delay_Ms(uint16_t time)  //延时函数
    { 
        uint16_t i,j;
        for(i=0;i<time;i++)
              for(j=0;j<10260;j++);
    }
    /*:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
    ** 函数名称: Delay_Ms_Us
    ** 功能描述: 延时1us (可通过仿真来判断他的准确度)
    ** 参数描述:time (us) 注意time<65535                 
    :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*/
    void Delay_Us(uint16_t time)  //延时函数
    { 
        uint16_t i,j;
        for(i=0;i<time;i++)
              for(j=0;j<9;j++);
    }
    

    主函数:

    int main()
    {
        
            float length;
            
            GPIO_cfg();
          NVIC_cfg();
            USART_cfg();    
            printf("串口初始化成功!
    ");
        
            Hcsr04Init();    
            printf("超声波初始化成功!
    ");//测试程序是否卡在下面两句上面
    
            length = Hcsr04GetLength();
            printf("距离为:%.3f
    ",length);
            if(length<30){BEEP=!BEEP;Delay_Us(100);}//如果距离小于30cm则蜂鸣器报警提示
        
    }
    

    到此就结束了,希望可以帮到你们。但是在测量过程中,我们会发现有时候这个测距很不准,其实这个叫做**余震现象,那么,我们怎么来消除这个现象使得测量的精准度提高呢?
    我们可以去多次测量的结果取平均值,这样子测量就准确很多了
    。具体代码实现如下:

            int i = 0;
            float lengthTemp = 0;
            float sum = 0;
            while(i!=5)
            {
            TRIG_Send = 1;      //发送口高电平输出
            Delay_Us(20);
            TRIG_Send = 0;
            while(ECHO_Reci == 0);      //等待接收口高电平输出
                OpenTimerForHc();        //打开定时器
                i = i + 1;
                while(ECHO_Reci == 1);
                CloseTimerForHc();        //关闭定时器
                t = GetEchoTimer();        //获取时间,分辨率为1US
                lengthTemp = ((float)t/58.0);//cm
                sum = lengthTemp + sum ;
            
        }
            lengthTemp = sum/5.0;
            return lengthTemp;
    

    好的,结束。

    一键三连呀!
  • 相关阅读:
    java 在线网络考试系统源码 springboot mybaits vue.js 前后分离跨域
    springboot 整合flowable 项目源码 mybiats vue.js 前后分离 跨域
    flowable Springboot vue.js 前后分离 跨域 有代码生成器 工作流
    Flowable 工作流 Springboot vue.js 前后分离 跨域 有代码生成器
    java 企业 网站源码 后台 springmvc SSM 前台 静态化 代码生成器
    java 进销存 商户管理 系统 管理 库存管理 销售报表springmvc SSM项目
    基于FPGA的电子计算器设计(中)
    基于FPGA的电子计算器设计(上)
    FPGA零基础学习:SPI 协议驱动设计
    Signal tap 逻辑分析仪使用教程
  • 原文地址:https://www.cnblogs.com/jee-cai/p/14095396.html
Copyright © 2020-2023  润新知