STM32f407系列之超声波模块

目录

1、模块说明

2、时序图

3、注意事项

4、完整代码


1、模块说明

VCC引脚:5V电源

TRIG引脚:触发信号引脚,单片机给超声波模块一个信号,超声波模块就会工作。

ECHO引脚:回响信号引脚,当超声波模块已经测量距离成功后,通过该引脚告诉单片机当前超声波传输的时间。

GND:信号地。

2、时序图

看时序图的技巧,从上到下,从左到右,因为一般的时序图是隐含了时间轴。如何区分t1、t2、t3、t4,就是要找出它们的变化点。

 

 

 

超声波模块实物硬件连接如下:

  • 参考角度1

  • 参考角度2

3、注意事项

1)不同的温度,声音的传播速度是不一样的

2)超声波的测量误差为3mm,可根据3mm的测距求出测量时间,求算如下:

 3)可编写简单代码如下

int32_t sr04_get_distance(void)
{
    uint32_t t=0;
    uint32_t d=0;
    
    //1.PB6引脚输出至少10us以上的高电平
    PBout(6)=1;
    
    delay_us(20);
    
    PBout(6)=0;    

    //2.等待超声波模块测距完毕,标志性的动作就是回响信号引脚从输出低电平变为输出高电平
    //在等待过程当中,最好添加超时处理
    
    t=0;
    while(PEin(6)==0)
    {
    
        delay_us(1);
        
        t++;
        
        //超时处理
        if(t >= 1000000)
            return -1;
    }
    
    
    //3.测量PE6引脚的高电平时间,该高电平的持续时间就是超声波模块的传输时间
    
    t=0;
    while(PEin(6))
    {
        
        delay_us(9); //9us == 3mm
        t++;
        
        //超时处理
        if(t >= 1000000)
            return -2;    
    }

    //4.将时间转换为距离
    d =  t*3/2;
    
    return  d;
}

加超时处理(属于容错处理中的一部分)有以下原因:

1)硬件问题:引脚连接有问题、超声波模块的损坏

2)软件问题:延时不准确

4、完整代码

沿用串口的代码模板:

#include "stm32f4xx.h"
#include <stdio.h>
static GPIO_InitTypeDef GPIO_InitStruct;//不加*
static USART_InitTypeDef USART_InitStructure;
static NVIC_InitTypeDef NVIC_InitStructure;

#define PEout(x) *(volatile uint32_t *)(0x42000000+(GPIOE_BASE+0x14-0x40000000)*32+(x)*4)
#define PFout(x) *(volatile uint32_t *)(0x42000000+(GPIOF_BASE+0x14-0x40000000)*32+(x)*4)
#define PAin(x) *(volatile uint32_t *)(0x42000000+(GPIOA_BASE+0x10-0x40000000)*32+(x)*4)
#define PEin(x) *(volatile uint32_t *)(0x42000000+(GPIOE_BASE+0x10-0x40000000)*32+(x)*4)
#define PBout(x) *(volatile uint32_t *)(0x42000000+(GPIOB_BASE+0x14-0x40000000)*32+(x)*4)
#define PBin(x) *(volatile uint32_t *)(0x42000000+(GPIOB_BASE+0x10-0x40000000)*32+(x)*4)


struct __FILE { int handle; /* Add whatever you need here */ };
FILE __stdout;
FILE __stdin;

int fputc(int ch, FILE *f) {
    USART_SendData(USART1,ch);
    while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
    return ch;
}

void delay_us(uint32_t n)
{
    //使用系统始终的8分频作为系统定时器的时钟源
    SysTick->CTRL = 0; // Disable SysTick
    SysTick->LOAD = n*21-1; // Count from 255 to 0 (256 cycles)
    SysTick->VAL = 0; // Clear current value as well as count flag
    SysTick->CTRL = 1; // Enable SysTick timer with processor clock
    while ((SysTick->CTRL & 0x00010000)==0);// Wait until count flag is set
    SysTick->CTRL = 0; // Disable SysTick
}


void delay_ms(uint32_t n)
{

    while(n--)
    {
        SysTick->CTRL = 0; // Disable SysTick,关闭系统定时器后才能取配置
        SysTick->LOAD = 21000-1;     // 填写计数值,就是我们的延时时间
        SysTick->VAL = 0;             // 清空标志位
        SysTick->CTRL = 1;             // 选中21MHz的时钟源,并开始让系统定时器工作
        while ((SysTick->CTRL & 0x10000)==0);//等待计数完毕
        
    }
    SysTick->CTRL = 0; // 不再使用就关闭系统定时器
}

void usart1_init(uint32_t baud)
{
    //打开PA硬件时钟
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);
    //打开串口1硬件时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
    //PA9,PA10配置为复用功能模式
    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9|GPIO_Pin_10;
    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_AF;
    GPIO_InitStruct.GPIO_Speed=GPIO_High_Speed;
    GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA,&GPIO_InitStruct);
    //将PA9和PA10引脚连接到串口1
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource9, GPIO_AF_USART1);
    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_USART1);

    //配置串口1相关参数:波特率、无校验位、8位数据位、1个停止位
    USART_InitStructure.USART_BaudRate = baud;            //波特率
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;//8位数据位
    USART_InitStructure.USART_StopBits = USART_StopBits_1;//1个停止位
    /* When using Parity the word length must be configured to 9 bits */
    USART_InitStructure.USART_Parity = USART_Parity_No;//无奇偶校验
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;//无硬件流控制
    USART_InitStructure.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;//允许收发数据
    USART_Init(USART1, &USART_InitStructure);

    //配置串口1的中断触发方法:接收一个字节触发中断 
    USART_ITConfig(USART1, USART_IT_RXNE, ENABLE);

    //配置串口1的中断优先级
    
    NVIC_InitStructure.NVIC_IRQChannel = USART1_IRQn;
    NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0;
    NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
    NVIC_Init(&NVIC_InitStructure);

    //使能串口1工作
    USART_Cmd(USART1, ENABLE);
}

void sr04_init(void)
{
    //端口B硬件时钟使能
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOB, ENABLE);

    
    //端口E硬件时钟使能
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOE, ENABLE);

    
    //配置PB6为输出模式
    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;
    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_Speed=GPIO_High_Speed;
    GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOB,&GPIO_InitStruct);
    
    //配置PE6为输入模式
    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_6;
    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_IN;
    GPIO_InitStruct.GPIO_Speed=GPIO_High_Speed;
    GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOE,&GPIO_InitStruct);
    
    //PB6初始状态为低电平
    PBout(6)=0;
}

int32_t sr04_get_distance(void)
{
    uint32_t t=0;
    int32_t d=0;
    //PB6高电平
    PBout(6)=1;
    //持续10us以上
    delay_us(20);
    //PB6低电平
    PBout(6)=0;
    //等待回响信号(等待PE6出现高电平)
    while(PEin(6)==0)
    {
        t++;
        delay_us(1);
        if(t>=1000000)
            return -1;
    }
        
    //测量高电平的时间
    t=0;
    while(PEin(6))
    {
        t++;
        delay_us(9);  //9us=3mm
        if(t>=1000000)
            return -2;
    }
    //得到距离
    t=t/2;   //时间是超声波从发射到返回的时间
    d=3*t;
    return d;
}

int main()
{
    int32_t d=0;
    //打开端口F的硬件时钟,就是为端口F供电
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOF, ENABLE);
    
    //配置引脚
    GPIO_InitStruct.GPIO_Pin=GPIO_Pin_9;
    GPIO_InitStruct.GPIO_Mode=GPIO_Mode_OUT;
    GPIO_InitStruct.GPIO_Speed=GPIO_High_Speed;
    GPIO_InitStruct.GPIO_OType=GPIO_OType_PP;
    GPIO_InitStruct.GPIO_PuPd=GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOF,&GPIO_InitStruct);//为何传地址
    usart1_init(115200);//串口1波特率115200bps
    PFout(9)=1;
    sr04_init();
    
    while(1)
    {
        d=sr04_get_distance();
        if(d>0)
        {
            if(d>=20 && d<=4000)
            {
                printf("distance=%dmm\r\n",d);
            }
        }
        delay_ms(1000);
    }
    //为何用此循环
}

void USART1_IRQHandler(void)
{
    uint32_t d;
    //检测标志位
    if(USART_GetFlagStatus(USART1,USART_FLAG_RXNE) == SET)
    {
        //接收数据
        d=USART_ReceiveData(USART1);
        //将接收到的数据返发给PC
        USART_SendData(USART1,d);
        while(USART_GetFlagStatus(USART1,USART_FLAG_TXE)==RESET);
        if(d=='1') PFout(9)=1;
        if(d=='0') PFout(9)=0;
        //清空标志位
        USART_ClearITPendingBit(USART1,USART_FLAG_RXNE);
    }
}

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值