STM32F103与四路超声波传感器使用中的坑HC-SR04

STM32F103与四路超声波传感器使用中的坑HC-SR04

首先在此声明,本人是菜鸟一个,学习stm32也没有多长时间,最近因为做比赛,所以临时学习了一些东西,也遇到了一些坑,在此希望能够记录一下,作为自己的学习笔记,也希望能够跟大家分享,如果有不对的地方,希望各位大佬批评指正。

HC-SR04不再多说,淘宝满天飞的东西,我直接分析代码

首先是头文件,这个都没有问题

#include "hcsr04.h"
#include "stm32f10x_gpio.h"

其次,我在开头宏定义了我的HCSR04的管脚,这样方便以后修改,不用每个都去修改,只需要修改开头的宏定义就可以了,比较方便

//   0#   hscr04
#define HCSR04_0_PORT GPIOB
#define HCSR04_0_CLK RCC_APB2Periph_GPIOB
#define HCSR04_0_TRIG GPIO_Pin_5
#define HCSR04_0_ECHO GPIO_Pin_6

其余的也一样,在这里我就不再复制黏贴,大家直接改好就行,例如我定义的

//   1#   hscr04
#define HCSR04_1_PORT GPIOB
#define HCSR04_1_CLK RCC_APB2Periph_GPIOB
#define HCSR04_1_TRIG GPIO_Pin_7
#define HCSR04_1_ECHO GPIO_Pin_8

同样的道理,剩下的管脚应该都会定义了。

定义完了管脚,先定义一个整型变量,用于计数

u16 msHcCount = 0;

然后是GPIO的配置,这里以0#传感器为例

//hscr04  0# GPIO管脚初始化
void  Hcsr04_0_gpio_Init()
{      
    GPIO_InitTypeDef GPIO_InitStructure;  
    RCC_APB2PeriphClockCmd(HCSR04_0_CLK,ENABLE);  
       
    // IO初始化  
    GPIO_InitStructure.GPIO_Pin = HCSR04_0_TRIG;       //发送电平引脚  
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; //推挽输出  
    GPIO_Init(HCSR04_0_PORT, &GPIO_InitStructure);  
    GPIO_ResetBits(HCSR04_0_PORT, HCSR04_0_TRIG);  
       
    GPIO_InitStructure.GPIO_Pin = HCSR04_0_ECHO;     //返回电平引脚  
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; //浮空输入  
    GPIO_Init(HCSR04_0_PORT, &GPIO_InitStructure);    
    GPIO_ResetBits(HCSR04_0_PORT, HCSR04_0_ECHO);
}

其余的也一样,无非改改名称
配置完GPIO,我们配置TIM定时器

   //定时器初始化使用基本定时器TIM2
   //所有超声波均使用TIM2计时
   void  Hcsr04_tim_Init()
      {
            TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;     //生成用于定时器设置的结构体
            RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,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(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMX的时间基数单位         
      
    TIM_ClearFlag(TIM2, TIM_FLAG_Update);   //清除更新中断,免得一打开中断立即产生中断  
    TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);    //打开定时器更新中断  
    hcsr04_NVIC();  
    TIM_Cmd(TIM2,ENABLE); 

然后定义两个静态的函数,分别负责打开和关闭TIM计数器
注意,静态函数不需要再头文件里面声明

//提示:静态函数的作用域仅限于定义它的源文件内,所以不需要在头文件里声明  
static void  OpenTimerForHc()         //打开定时器   
{  
    TIM_SetCounter(TIM2,0); //清除计数  
    msHcCount = 0;  
    TIM_Cmd(TIM2, ENABLE);  //使能TIMX外设  
}  
   
static void  CloseTimerForHc()       //关闭定时器   
{
    TIM_Cmd(TIM2, DISABLE);  //使能TIMX外设  
}

我只用了一个TIM,分时为各个超声波计数,这里配置NVIC,在中断中处理
配置NVIC

// NVIC配置  
//TIM2的NVIC中断配置,所有的超声波均使用TIM2,跳这个NVIC中断
void hcsr04_NVIC()
{  
    NVIC_InitTypeDef NVIC_InitStructure;  
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); 
      
    NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;             //选择串口1个中断  
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //抢占式中断优先级设置为1  
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;         //响应式中断优先级设置为1  
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;        //使能中断  
    NVIC_Init(&NVIC_InitStructure);  
}

中断处理函数,在中断中计数,用来计时间

//定时器2中断服务程序  
void  TIM2_IRQHandler(void)   // TIM2中断  
{  
    if(TIM_GetITStatus(TIM2, TIM_IT_Update)!= RESET)   //检查TIM3更新中断发生与否  
    {  
        TIM_ClearITPendingBit(TIM2, TIM_IT_Update);  //清除TIMX更新中断标志   
        msHcCount ++;
    }  
}

这样就得到了时间
时间获取函数

//获取定时器时间  
u32 GetEchoTimer(void)
{  
    u32 t = 0;  
    t = msHcCount * 1000; //得到MS  
    t += TIM_GetCounter(TIM2); 
    TIM2-> CNT = 0;  //将TIM2计数寄存器的计数值清零  
    Delay_Ms(50);  
    return t;
}

测距采用五次取平均,这样精度高一些

//一次获取超声波测距数据两次测距之间需要相隔一段时间,隔断回响信号  
//为了消除余震的影响,取五次数据的平均值进行加权滤波。  
//返回0#超声波的距离
float  Hcsr04_0_GetLength(void)
{
    u32 t = 0;
    int  i = 0;  
    float  lengthTemp = 0;  
    float  sum = 0;  
    while (i != 5)  
    {
        GPIO_SetBits(HCSR04_0_PORT, HCSR04_0_TRIG);      //发送口高电平输出  
        Delay_Us(15);  
        GPIO_ResetBits(HCSR04_0_PORT, HCSR04_0_TRIG);  
        while(GPIO_ReadInputDataBit(HCSR04_0_PORT, HCSR04_0_ECHO) == 0);   //等待接收口高电平输出  
        OpenTimerForHc();        //打开定时器  
        i = i + 1;
        while((GPIO_ReadInputDataBit(HCSR04_0_PORT, HCSR04_0_ECHO)) == 1);  
        CloseTimerForHc();        //关闭定时器  
        t = GetEchoTimer();        //获取时间,分辨率为1US  
        lengthTemp =((float)t / 58.0); //厘米
        sum = lengthTemp + sum;
    }  
    lengthTemp = sum / 5.0;
    return lengthTemp;
}

还是以0#为例,其余的改改名字,加个函数就好

下面是粗略的延时函数

//延时函数
void  Delay_Ms(uint16_t time)
{   
    uint16_t i,j;  
    for(i = 0; i 

这样,就可以在主函数中调用了
当然,调用需要完成一系列的初始化,为了方便在主函数中一步调用,我又自己定义了调用的函数

//初始化配置所有超声波传感器
void Hcsr04_ALL_Init()
{
    Hcsr04_0_gpio_Init();
    Hcsr04_1_gpio_Init();
    Hcsr04_2_gpio_Init();
    Hcsr04_tim_Init();
    hcsr04_NVIC();
}

这样就可以只用这一个函数了,比较方便。
主函数如下:

#include "stm32f10x.h"
#include "usart.h"
#include "pwm.h"
#include "hcsr04.h"

float distance, distance1;
int main()
{
    SystemInit(); //初始化系统时钟
    systick_init(); //初始化系统滴答
    usart_init();
   while(1)
    {
    Hcsr04_ALL_Init();
    printf("HCSR04_ALL init successful\n");
    printf("USART init successful\n");
                
    

    distance = Hcsr04_1_GetLength();
    distance1 = Hcsr04_0_GetLength();
    if(distance > distance1)
    {
   	 TIM_SetCompare1(TIM3,900);
   	TIM_SetCompare2(TIM3,100);
     }
    	else
    	{
    		TIM_SetCompare3(TIM3,900);
    		TIM_SetCompare4(TIM3,100);
    	}
    }

但是在测距的时候我发现,超声波每次在等待的时候,就是

float  Hcsr04_0_GetLength(void)

这里面的

while(GPIO_ReadInputDataBit(HCSR04_0_PORT, HCSR04_0_ECHO) == 0);
//等待接收口高电平输出

这一句,如果在小车移动的过程中,出现什么问题,这句就会被卡死,所以这种方法我感觉单独的做超声波还可以,但是如果要用在移动的物体,例如小车定位上,是不靠谱的。

本次发现的问题:
1,超声波定位的小车,超声波速度太慢,反应迟钝
2,超声波采用while()等待高电平,容易出问题,接收不到,卡死,建议加看门狗或者采用输入捕获(如果用输入捕获,需要配置中断)
3,板子有的时候下载之后超声波需要手动按下复位键,才能连续的通过usart串口向电脑发送数据,也就是说,只有手动按下复位键之后,才能开始连续测距,不知道这是什么问题,正在找,也希望大神们指教。我选了下载后执行了啊,用的ST LINK V2, ST ultility下载的。

以上是本次学习笔记,希望各位大神赐教

你可能感兴趣的:(学习笔记)