单片机交流
直播中

王军

7年用户 1750经验值
私信 关注
[问答]

怎么实现基于stm32最小系统的超声波测距的设计?

超声波测距原理是什么?
怎么实现基于STM32最小系统的超声波测距的设计?

回帖(1)

肖蕾

2021-11-8 10:49:31
学习目标
  

  • 理解超声波的原理
  • 实现基本测距
  • 通过串口在电脑上打印实际距离
  • 通过串口设置阈值,当距离太近,报警装置报警

    超声波测距原理
HC-SR04 超声波测距模块可提供 2cm-400cm 的非接触式距离感测功能,测距精度可达高到 3mm;模块包括超声波发射器、接收器与控制威廉希尔官方网站 。基本原理如下:
(1)采用IO口TRIG触发测距,给至少10us的高电平信号;
(2)模块自动发送8个40khz的方波,自动检测是否有信号返回;
(3)有信号返回,通过IO口ECHO输出一个高电平,高电平持续的时间就是超声波从发射到返回的时间。测试距离=(高电平时间*声速(340M/S))/2;
  
  

  

操作:初始化时将trig和echo端口都置低,首先向给 trig发送至少10 us的高电平脉冲(模块自动向外发送8个40K的方波),然后等待,捕捉 echo 端输出上升沿,捕捉到上升沿的同时,打开定时器开始计时,再次等待捕捉echo的下降沿,当捕捉到下降沿,读出计时器的时间,这就是超声波在空气中运行的时间(或采用输入捕获得出高电平时间),按照 测试距离=(高电平时间*声速(340M/S))/2 就可以算出超声波到障碍物的距离。
定时器
通过定时器向上计数到24ms(因为超声波模块的测距范围为2cm-400cm,通过400cm计算出时间大约为23.5ms,而对于距离过小时,个人觉得是由于本来就测不到2cm故而模块会一直发出而没有返回值直到溢出),向上溢出时进入到定时器中断服务函数,蜂鸣器报警。
串口通信
初始化之后,在主函数里,直接通过“printf”就可以将测得的数据发送到串口。
   
初始化部分


void CSB_Init(void)
{
        GPIO_InitTypeDef  GPIO_InitStructure;
  RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);//使能GPIOF时钟
  
        //GPIOF9初始化设置,超声波发出,trig
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_5;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化
       
        GPIO_ResetBits(GPIOF,GPIO_Pin_5);//trig对应引脚GPIOF9拉低
       
        //GPIOF10初始化设置,超声波接收,echo
        GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//普通输入模式
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_UP;//上拉
  GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化
       
        GPIO_ResetBits(GPIOF,GPIO_Pin_1);//echo对应引脚GPIOF10拉低
       
       
        //初始化蜂鸣器对应引脚GPIOF8
  GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
  GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//普通输出模式
  GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;//推挽输出
  GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//100MHz
  GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_DOWN;//下拉
  GPIO_Init(GPIOF, &GPIO_InitStructure);//初始化GPIO
       
  GPIO_ResetBits(GPIOF,GPIO_Pin_8);  //蜂鸣器对应引脚GPIOF8拉低,
}


void TIM3_Int_Init(u16 arr,u16 psc)
{
        TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
       
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);  ///使能TIM3时钟


  TIM_TimeBaseInitStructure.TIM_Period = arr;         //自动重装载值
        TIM_TimeBaseInitStructure.TIM_Prescaler=psc;  //定时器分频
        TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式
        TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
       
        TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE); //允许定时器3更新中断
        TIM_TimeBaseInit(TIM3,&TIM_TimeBaseInitStructure);//初始化TIM3
        TIM_Cmd(TIM3,DISABLE);
       
        NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn; //定时器3中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=0x03; //抢占优先级1
        NVIC_InitStructure.NVIC_IRQChannelSubPriority=0x03; //子优先级3
        NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;
        NVIC_Init(&NVIC_InitStructure);
}


void TIMEOPEN()
{
        TIM_SetCounter(TIM3,0);//清除计数t=0
        TIM_Cmd(TIM3,ENABLE); //使能定时器3
}       


void TIMECLOSE()
{
        TIM_Cmd(TIM3,DISABLE); //关闭定时器3
}


void TIM3_IRQHandler(void)       //定时器中断服务函数
{
        if(TIM_GetITStatus(TIM3,TIM_IT_Update)==SET) //溢出中断
        {
                GPIO_SetBits(GPIOF,GPIO_Pin_8);//蜂鸣
                delay_ms(300);  
                GPIO_ResetBits(GPIOF,GPIO_Pin_8);//停止蜂鸣
                delay_ms(300);
        }
        TIM_ClearITPendingBit(TIM3,TIM_IT_Update);  //清除中断标志位
}


主函数部分


u8 i=0;
u16 s=0;
u16 t=0;
u16 finals=0;
u16 alls=0;
int main()
{
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组2
        delay_init(168);                //延时初始化
        TIM3_Int_Init(239,8399);//定时器时钟84M,分频系数8400,所以84M/8400=10Khz的计数频率,计数10次为1ms,计数240次为24ms,产生溢出
        uart_init(115200);       
        CSB_Init();                                  //初始化与c***连接的硬件接口
       
        while(1)
        {                    // GPIO_ResetBits(GPIOF,GPIO_Pin_9);//trig对应引脚GPIOF9拉低
                alls=0;
                for(i=0;i<5;i++)
                {
                        GPIO_SetBits(GPIOF,GPIO_Pin_5);//触发trig发出信号
                        delay_us(15);//延时15us
                        GPIO_ResetBits(GPIOF,GPIO_Pin_5);
                        while(GPIO_ReadInputDataBit(GPIOF,GPIO_Pin_1)==0);
                        TIMEOPEN();       


                        while(GPIO_ReadInputDataBit(GPIOF,GPIO_Pin_1)==1);
                        TIMECLOSE();
                        t=TIM_GetCounter(TIM3);
                        s=(t*340)/200;//每次测量的距离cm
                        alls=alls+s;//总距离
                }
                finals=alls/5;//求出平均距离


                printf("rn距离为:(单位cm)rn");
                printf("%drn",finals);
        }


总结
1.在初始化端口啊什么的要仔细。
2.超声波模块在使用之前要先检查一下。
3.注意计算时的单位换算问题…
4.在测试时不能在在定时器打开前后用串口打印来验证是否检测到的问题,会消耗时间导致时间不准确。


                        while(GPIO_ReadInputDataBit(GPIOF,GPIO_Pin_1)==0);
                                        //printf("乌拉1:rn");                       
                        TIMEOPEN();       
                     // printf("乌拉2:rn");
                        while(GPIO_ReadInputDataBit(GPIOF,GPIO_Pin_1)==1);
                        TIMECLOSE();
举报

更多回帖

发帖
×
20
完善资料,
赚取积分