
传感器名称:HC-SR04(超声波测距传感器),可以测量当前位置和障碍物之间的距离
实验:
用手掌放在传感器前方模拟障碍物,传感器测量的就是当前位置和手掌之间的距离,测量得到距离之后使用单片机上的串口和USB转TTL模块把测量结果发送到电脑上

1.HCSR-04模块的使用方法
超声波测距传感器基本原理:


向Trig引脚输入一个不小于10μs脉冲信号,传感器开始工作,
通过左边T眼睛向外发送超声波(8个周期40kHz大约为0.2毫秒),发送完超声波后Echo引脚立即输出一个高电压,超声波在空气中传播遇到障碍物反射回来形成一个回声,右边眼睛R接收回声,当全部回声(8个周期)接收完后,Echo引脚向外输出一个低电压,所以距离=脉宽(时间)*340m/s÷2。
脉宽可以通过定时器的输入捕获功能获得
2.搭建电路

Trig引脚启动测量,通过引脚输入一个不小于10μs信号,脉冲不需要十分精确所以选用普通引脚PA0
Echo引脚用于返回测量结果,返回形式也是脉冲,需要测量脉冲的脉宽用到定时器的输入捕获功能,所以引脚接到定时器1(TIM1)的通道1(CH1)PA8引脚
USB转TTL模块:选择单片机的串口引脚PA9、PA10

2.1初始化串口:
a.初始化引脚

b.初始USART化模块本身
开启USART模块本身时钟、设置参数、闭合总开关

#include "stm32f10x.h"
void App_USARTl_Init (void);
int main(void)
{
App_USARTl_Init ();
while(1)
{
}
}
void App_USARTl_Init (void)
{
//#1.初始化Io引脚
GPIO_InitTypeDef GPIO_InitStruct;
// PA9 AF PP Tx
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init (GPIOA, &GPIO_InitStruct);
//PA10 IPU Rx
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init (GPIOA, &GPIO_InitStruct);
//#2.初始化UsART1
//#2.1开启USART1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//#2.2初始化USART1的参数
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate = 115200;
USART_InitStruct.USART_HardwareFlowControl =USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init (USART1, &USART_InitStruct);
//#2.3闭合总开关
USART_Cmd(USART1,ENABLE);
}
测试代码:在串口调试助手打印字符串'Hello word ' (使用到Mylib库里面usart.h文件)
#include "stm32f10x.h"
#include "usart.h"
void App_USARTl_Init (void);
int main(void)
{
App_USARTl_Init ();
My_USART_SendString(USART1, "Hello world. \r\n");
while(1)
{
}
}
void App_USARTl_Init (void)
{
//#1.初始化Io引脚
GPIO_InitTypeDef GPIO_InitStruct;
// PA9 AF PP Tx
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init (GPIOA, &GPIO_InitStruct);
//PA10 IPU Rx
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init (GPIOA, &GPIO_InitStruct);
//#2.初始化UsART1
//#2.1开启USART1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//#2.2初始化USART1的参数
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate = 115200;
USART_InitStruct.USART_HardwareFlowControl =USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init (USART1, &USART_InitStruct);
//#2.3闭合总开关
USART_Cmd(USART1,ENABLE);
}
3.梳理思路

HC-SR04传感器是超声波测距传感器,超声波测距传感器返回测量结果的方式,根据Echo引脚返回一个脉冲信号,需要测量脉冲信号的脉宽,只有测量出模块才得出于障碍物的距离
测量脉宽需要用到定时器的输入捕获功能
用定时器其中一个通道去捕获输入信号的上升沿,用另外一个信号去捕获输入信号的下降沿,再用两个捕获的结果作差,作差结果就是脉宽。(CCR1-CCR2)*分辨率

4.初始化时基单元

定时器分别率:图中台阶宽度为分别率,台阶越小测量结果越精细,分辨率不可以无限小,HC-SR04精度最高为3mm,3mm转换为时间是8微秒(3/340m/s=8微秒左右),台阶宽度小于8微秒,
定时器周期:图中溢出一次的时间为周期,周期越长越好,但是时基单元内的寄存器都是16位寄存器,范围为000000000000到111111111111也就是0到65535,所以ARR取最大值,ARR=65535周期为最大值
分别率为1微秒
周期为65535

定时器为TIM1,TIM1挂在APB2上,PCLK2当前频率为72Hz,APB2的分频系数为1分屏,所以TIM1的倍屏系数为1倍屏,TIM1频率=PCLK2*1=72*1=72Hz
定时器的分辨率为1us,PSC=71Hz
ARR=65535
RCR=0
void App_HCSR04_Init (void);//初始化传感器函数
开启定时器时钟
配置时基单元参数
void App_HCSR04_Init (void)
{
//#1.初始化时基单元
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 65535;
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit (TIM1, &TIM_TimeBaseInitStruct);
}
5.初始化输入捕获



这里用到的是输入捕获功能,手册建议GPIO配置为浮空输入,Echo引脚空闲时两边都是低电压,最好GPIO配置为输入下拉,当设置为输入下拉时,当Echo引脚意外断开时输入下拉模式可以提供一个默认的低电压,低电压可以模拟脉冲信号空闲状态,所以最好设置为输入下拉模式
初始化输入捕获:
初始化IO引脚、初始化输入捕获本身(初始化通道)

//#2.初始化输入捕获
//#2.1.初始化Io引脚PA8 IPD
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOA, &GPIO_InitStruct);
//#2.2.初始化输入捕获的通道1
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
TIM_ICInitStruct.TIM_ICFilter = 0;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit (TIM1, &TIM_ICInitStruct);
//#2.3.初始化输入捕获的通道2
TIM_ICInitStruct.TIM_Channel = TIM_Channel_2;
TIM_ICInitStruct.TIM_ICFilter = 0;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Falling;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_IndirectTI;
TIM_ICInit (TIM1, &TIM_ICInitStruct);
6.编程

6.1、向CNT写0
![]()
6.2清除CC1和CC2标志位
![]()
while(1)
{
//#1.向cNT写0
TIM_SetCounter (TIM1,0);
//#2.清除cc1和cc2标志位
TIM_ClearFlag (TIM1, TIM_FLAG_CC1);
TIM_ClearFlag (TIM1, TIM_FLAG_CC2);
}
6.3开启定时器TIM_Cmd
//3.开启定时器
TIM_Cmd (TIM1, ENABLE)
6.4初始化Echo引脚
//.初始化rig引脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
6.5发送脉冲
先向引脚写1变成高电压,中间延迟10微秒,再向引脚写0变成低电压
延迟函数:delay函数最短延迟时间为1毫秒,1毫秒=1000微秒,但是不太合适,
微秒级延迟函数void DelayUs (uint32_t us);// 微秒级延迟
//#4.向RIG引脚发送10us的脉冲
GPIO_WriteBit(GPIOA, GPIO_Pin_0,Bit_SET);
DelayUs (10);
GPIO_WriteBit(GPIOA, GPIO_Pin_0,Bit_RESET);
6.6不断查询cc1和cc2标志位
![]()
//#5.等待测量完成
while (TIM_GetFlagStatus (TIM1, TIM_FLAG_CC1) ==RESET);
while (TIM_GetFlagStatus (TIM1, TIM_FLAG_CC2)== RESET);
6.7关闭定时器
6.8计算测量结果

uint16_t ccr1 = TIM_GetCapture1 (TIM1);
uint16_t ccr2 = TIM_GetCapture2 (TIM1);
float distance =(ccr2-ccr1) * 1.0e-6f * 340.0f / 2;
My_USART_Printf(USART1, "distance = %.4f\r\n" ,distance);
Delay (100);
总代码
#include "stm32f10x.h"
#include "usart.h"
#include "delay.h"
void App_USARTl_Init (void);
void App_HCSR04_Init (void);
int main(void)
{
App_USARTl_Init ();
App_USARTl_Init ();
My_USART_SendString(USART1, "Hello world. \r\n");
while(1)
{
//#1.向cNT写0
TIM_SetCounter (TIM1,0);
//#2.清除cc1和cc2标志位
TIM_ClearFlag (TIM1, TIM_FLAG_CC1);
TIM_ClearFlag (TIM1, TIM_FLAG_CC2);
//3.开启定时器
TIM_Cmd (TIM1, ENABLE);
//#4.向RIG引脚发送10us的脉冲
GPIO_WriteBit(GPIOA, GPIO_Pin_0,Bit_SET);
DelayUs (10);
GPIO_WriteBit(GPIOA, GPIO_Pin_0,Bit_RESET);
//#5.等待测量完成
while (TIM_GetFlagStatus (TIM1, TIM_FLAG_CC1) ==RESET);
while (TIM_GetFlagStatus (TIM1, TIM_FLAG_CC2)== RESET);
//#6.关闭定时器
TIM_Cmd(TIM1,DISABLE);
uint16_t ccr1 = TIM_GetCapture1 (TIM1);
uint16_t ccr2 = TIM_GetCapture2 (TIM1);
float distance =(ccr2-ccr1) * 1.0e-6f * 340.0f / 2;
My_USART_Printf(USART1, "distance = %.4f\r\n" ,distance);
Delay (100);
}
}
void App_USARTl_Init (void)
{
//#1.初始化Io引脚
GPIO_InitTypeDef GPIO_InitStruct;
// PA9 AF PP Tx
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_9;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_AF_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_10MHz;
GPIO_Init (GPIOA, &GPIO_InitStruct);
//PA10 IPU Rx
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_10;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPU;
GPIO_Init (GPIOA, &GPIO_InitStruct);
//#2.初始化UsART1
//#2.1开启USART1的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
//#2.2初始化USART1的参数
USART_InitTypeDef USART_InitStruct;
USART_InitStruct.USART_BaudRate = 115200;
USART_InitStruct.USART_HardwareFlowControl =USART_HardwareFlowControl_None;
USART_InitStruct.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
USART_InitStruct.USART_Parity = USART_Parity_No;
USART_InitStruct.USART_StopBits = USART_StopBits_1;
USART_InitStruct.USART_WordLength = USART_WordLength_8b;
USART_Init (USART1, &USART_InitStruct);
//#2.3闭合总开关
USART_Cmd(USART1,ENABLE);
}
void App_HCSR04_Init (void)
{
//#1.初始化时基单元
RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStruct.TIM_Period = 65535;
TIM_TimeBaseInitStruct.TIM_Prescaler = 71;
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit (TIM1, &TIM_TimeBaseInitStruct);
//#2.初始化输入捕获
//#2.1.初始化Io引脚PA8 IPD
RCC_APB2PeriphClockCmd (RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitTypeDef GPIO_InitStruct;
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_8;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;
GPIO_Init(GPIOA, &GPIO_InitStruct);
//#2.2.初始化输入捕获的通道1
TIM_ICInitTypeDef TIM_ICInitStruct;
TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;
TIM_ICInitStruct.TIM_ICFilter = 0;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI;
TIM_ICInit (TIM1, &TIM_ICInitStruct);
//#2.3.初始化输入捕获的通道2
TIM_ICInitStruct.TIM_Channel = TIM_Channel_2;
TIM_ICInitStruct.TIM_ICFilter = 0;
TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Falling;
TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1;
TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_IndirectTI;
TIM_ICInit (TIM1, &TIM_ICInitStruct);
//.初始化rig引脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
GPIO_InitStruct.GPIO_Speed = GPIO_Speed_2MHz;
GPIO_Init(GPIOA,&GPIO_InitStruct);
}

.



1004

被折叠的 条评论
为什么被折叠?



