一、超声波测距基本原理6 R2 N( Q; }+ x& k8 y
超声波测距的原理非常简单,超声波发生器在某一时刻发出一个超声波信号,当这个超声波信号遇到被测物体后会反射回来,被超声波接收器接收到。这样只要计算出从发出超声波信号到接收到返回信号所用的时间,就可以计算出超声波发生器与反射物体的距离。8 [; M" |3 @. n- B3 F
; V3 m- P4 ^& y% [
距离的计算公::d=s/2=(c*t)/2
; V# F/ ^2 |# G3 Q! z, s
0 y3 d, \9 s. \# _* j6 g其中 d 为被测物与测距器的距离,s 为声波的来回路程,c 为声波,t 为声波来回所用的时间。
8 ~& z; ~) K5 X0 ^; l1 U# R0 d6 I" J" ?
由于超声波也是一种声波,其声速 c 与温度有关,在不同温度下的超声波声速不同。在使用时,如果温度变化不大,则可认为声速是基本不变的。如果测距精度要求很高,则应通过温度补偿的方法加以校正。声速校正后,只要测得超声波往返的时间,即可求得距离。不同外温度下的超声波声速表如下
& U8 W/ B7 E. [/ e9 ^1 C; R* K3 C4 t, z4 e* j
9 o' j* x9 }2 A+ `: a不同温度下超声波声速表
l# P9 R6 `- O1 u M: l. a0 j: z1 x, b/ N- L! d
补充说明
/ ~0 M* }2 L6 C最近在问答区发现有小伙伴疑惑为什么在计算距离时是根据Echo输出的高电平持续时间来计算距离。这里简单解释一下,仅供参考
0 n) J: X; r) e" s9 Q1. 实际超声波测距的原理就是上面介绍的,记录的时间是从超声波发出到回波被接收的时间,然后这个时间除以2,再乘超声波的传播速度得到距离。超声波测距模块实际也是这么做的。模块内部会记录超声波发出的时刻,在接收到回波后会立刻以高电平的形式从Echo引脚输出高电平,高电平的持续时间就是超声波从发出到被接收到的时间间隔。这么做是模块为了方便单片机处理,快速得到超声波从发出到被接收到的时间间隔。5 O9 t7 i/ S9 |" z
/ E0 ]- W7 V- }6 [+ i1 c+ K2. 实际大家可以自己测试一下,初始化一个定时器,从给Trig引脚10us高电平结束时刻开启定时器,到Echo引脚接收到高电平的时刻停止计时,用这个时间来计算距离,实际和用高电平持续时间来计算距离得到的结果基本是相同的。
! j% J. W5 y$ o" G1 `& o; x1 l# q0 w3 C" w" R2 k, P" |
3. 给Trig引脚10us高电平并不是说只发送10us的超声波。10us的高电平是为了触发模块工作。10us高电平结束后,模块会发送一个8个40KHz的方波。0 J1 r6 N+ l, h1 a# L" _
+ a4 w3 ~) g) L; s. N9 a* B: O6 O5 F( Z; I* l1 u$ X
二、超声波传感器简介6 x6 O {0 j/ R: `0 p
总体上讲,超声波发生器可以分为两大类:一类是用电气方式产生超声波类是用机械方式产生超声波。电气方式包括压电型、磁致伸缩型和电动型等:机械方式有加尔统笛、液哨和气流旋笛等。他们所产生的超声波的频率、功率和声波特性各不相同,因而用途也各不相同。目前较为常用的是压电式超声波发生。
+ ~* Y( K. r% y' ]# o
6 w5 Y% l1 K2 C) z压电式超声波发生器实际上是利用压电晶体的谐振来工作的。它有两个压电晶片和一个共振板。当它的两极外加脉冲信号,其频率等于压电晶片的固有振荡频率时,压电晶片将会发生共振,并带动共振板振动,便产生超声波。反之,如果两极间未外加电压,当共振板接收到超声波时,将压迫压电晶片作振动,将机械能转换为电信号,这时它就成为超声波接收器了。这里介绍的是一个超声波测距模块——HC-SR04。
- D# N+ Y6 H( M6 ?6 a' h m
e# U' V8 }. S# s# r6 _ O2 n0 R1 V' F3 Z
HC-SR04 1 V; ?: _% X5 @1 {, r
; T! V+ t6 n6 F d1 O# R }, H: o
# b5 o$ |1 B3 W三、HC-SR04测距实现思路# q- a5 }/ ^/ u5 d8 O( ^
这里就不再针对HC-SR04模块的原理和电路做详细介绍了,直接介绍利用该模块实现测距的思路。该模块有四个引脚。1 y9 Y2 k( a- \
• VCC —— 通常是5V供电2 s/ p4 m8 {4 q1 }7 g0 R7 g
• GND —— 地& G" w+ y4 ^: ?' ]
• Trig —— 给该引脚大于10us的高电平,超声波发射头会发送一个超声波信号! ]' h. H0 w3 f: y. v% s, g
• Echo —— 该引脚在接收到返回的超声波信号后会变为高电平+ i8 @+ K" O$ C# C5 W" I# G
- N5 X( @5 `* b+ \# O0 e) n( [7 UEcho接收到的高电平持续时间即为超声波一个来回所用的时间,利用该时间除以2再乘上光速,即可得到测量距离。
6 n' s: O4 l f. N, p2 T; |6 e: x; j. K b0 X
3 C4 c6 D' F. _0 G) H4 D
四、超声波测距程序实现
/ w& B4 ]) m/ e. m4 f; B6 R- C4.1 HC-SR04初始化程序
+ V" L6 J! E1 ]1 @. S- zHC-SR04初始化程序主要包括两部分,一部分是初始化HC-SR04的GPIO,Trig引脚设置为推挽式输出,Echo引脚设置为浮空输入。另一部分是初始化TIM2。初始化TIM2之后,没有立即使能,而是等到Echo接收到高电平的时刻,开启TIM2。' z4 _6 Q: `# Y) E' d) [
- /*
" N+ f9 K* `& N( c: e( K0 ? - *==============================================================================
- R* F7 _& l/ Q: L - *函数名称:Drv_Hcsr04_Init
& I) }& V; K; l0 R9 Q* _. y+ r - *函数功能:初始化HC-SR04
1 x; w7 T" u( x# {* [: O - *输入参数:无; |! N$ {. }, N& }3 \' [
- *返回值:无7 c1 c6 _& T/ p, m% `8 L. M" T
- *备 注:初始化HC-SR04引脚的同时,初始化了TIM2,用来记录高电平持续时间, R' b+ S5 H* Z
- 初始化完TIM2后,没有使能,当Echo收到高电平后使能
, k' m' [* G1 I' K5 `, s6 u - *==============================================================================5 `+ ^& b0 r1 |' h7 U. G& U- M# E
- */
6 M e l/ ^" A4 j. f. n - void Drv_Hcsr04_Init (void) // Hc-sr04初始化' `% S4 q) D& E- X
- { 2 ]4 g! C/ F& I$ Q, L- [
- // 结构体定义
s7 q9 m) u n* T8 O g9 t - TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // 生成用于定时器设置的结构体 - I" u" q. o$ Z5 _! h; a
- GPIO_InitTypeDef GPIO_InitStructure; // GPIO结构体
! I9 ^% R2 l# n( S% S - NVIC_InitTypeDef NVIC_InitStructure; // NVIC结构体, g3 c5 q4 G' M, O, C
- RCC_APB2PeriphClockCmd(HCSR04_CLK, ENABLE); // 使能GPIO时钟: {1 ?7 r) Q6 |* B( r
& h$ `- X- w% z$ |- // GPIO初始化
: U* r {. Y# a9 K8 b C& J - GPIO_InitStructure.GPIO_Pin =HCSR04_TRIG; // 发送电平引脚 % h+ ^8 x5 q, F
- GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; ' b1 z& r# T% o$ Z8 {+ G& L
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽式输出 0 h& l& d& q, C2 ]/ C
- GPIO_Init(HCSR04_PORT, &GPIO_InitStructure); . i7 t4 n/ i) k, b+ ]/ J1 V0 b
- GPIO_ResetBits(HCSR04_PORT,HCSR04_TRIG); ) |- g. W- E) e! W( s+ ]9 i, K2 h
- c' W3 e% j# L& f; _8 v
- GPIO_InitStructure.GPIO_Pin = HCSR04_ECHO; // 返回电平引脚
/ ~3 u1 r9 h2 n ^# l - GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入
# p y) E* R% @5 ?/ z5 M6 [ - GPIO_Init(HCSR04_PORT, &GPIO_InitStructure); ! x; \, @# d0 P' W" L ~) f
- GPIO_ResetBits(HCSR04_PORT,HCSR04_ECHO); ) b$ Y; I i) D) L
-
- l) l3 @. t+ @) q+ Y - // 定时器初始化 使用基本定时器TIM2 ' T$ Y' d! a$ x& Z- N0 A
- RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能对应RCC时钟
% p! D$ ~5 V* Y8 z9 E' L( [ - // 配置定时器基础结构体 7 p3 E) U9 q4 K% l1 Y" z% z$ U
- TIM_DeInit(TIM2); 4 h# U* m. e. x; e
- TIM_TimeBaseStructure.TIM_Period = (1000-1); // 设置在下一个更新事件装入活动的自动重装载寄存器周期的值(计数到1000为1ms ) ( P7 y) ~; u% W& M( n% B
- TIM_TimeBaseStructure.TIM_Prescaler =(72-1); // 设置用来作为TIMx时钟频率除数的预分频值 1M的计数频率 1US计数
7 U: E. W1 V, R. a* o* J, T) o - TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; // 不分频 - l: T7 u: j% s# h6 l/ ~
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // TIM向上计数模式 y) ?/ q- j9 \$ n: [, [5 c4 i: V
- TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 + Z5 }2 O: }, z; L. J1 G+ f% Q) X) a* O) S
- 0 S7 `: y2 }1 V" n
- TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 清除更新中断,免得一打开中断立即产生中断
& G" B( b4 f; D6 R" ]$ R - TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); // 打开定时器更新中断 0 ~" P/ R. E5 c& g# u8 N
- * {( \$ B* S& @
- // NVIC配置 0 w" J: I! P1 `! E9 c" v1 t: {7 x
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
$ z( K- p7 `4 B/ d5 s -
# O; Q# G9 q: I - NVIC_InitStructure.NVIC_IRQChannel =TIM2_IRQn; // 选择定时器2中断 2 B/ X3 D' h5 m/ C: f$ d
- NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占式中断优先级设置为0
* W8 Q9 H% H# Z5 z6 M$ g* P - NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 响应式中断优先级设置为0
: H) k) m X$ s4 m* }2 j8 W0 @ - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断 % l( G0 D/ H m* W6 z
- NVIC_Init(&NVIC_InitStructure);
$ k$ y+ q$ [' I4 e -
4 Q- p: \7 A5 [7 D. j" f# ~ - TIM_Cmd(TIM2,DISABLE); 9 K' d1 N! |! v
- }
复制代码 ; h& R! c4 d8 P2 I: z6 K8 N' B
4.2 TIM开关程序
$ q1 u- k; g. K0 G$ }- /*3 q4 V; A% t8 F5 s1 J4 x( m _4 D
- *==============================================================================
6 L2 B" ^' C8 }% F$ f. F% y" [ - *函数名称:Drv_Hcsr04_OpenTimerForHc
" ]$ F9 O; l, X+ P: e) ^$ k - *函数功能:打开定时器
$ g7 }; |0 h4 F6 c$ Q9 }' N - *输入参数:无 v' S: _6 r" u6 A3 u9 g
- *返回值:无, Z @- `' J U+ Y4 r6 M
- *备 注:无, q! A! Y$ C1 i" j* s9 T3 `2 O! o
- *==============================================================================
2 m) u5 X6 R) y - *// j& }( ?9 J8 s. N3 Z8 l2 p
- void Drv_Hcsr04_OpenTimerForHc (void) // 打开定时器 : c! v' r: S( D% p
- { 7 m$ h7 B( r- t
- TIM_SetCounter(TIM2,0); // 清除计数
) R! {" K4 E9 a0 } - gMsHcCount = 0; 0 J+ p% w/ N0 C2 L
- TIM_Cmd(TIM2, ENABLE); // 使能TIMx外设
8 z& s) l, V/ u% ^2 d9 w - }
0 j4 u9 J4 Y. M0 i- s - /*
9 R: C1 I4 _) S2 z8 P - *==============================================================================
: q, g3 O9 n4 b5 M7 n6 z4 ` - *函数名称:Drv_Hcsr04_CloseTimerForHc
* j5 j9 d6 o- L) b: z% Q - *函数功能:关闭定时器# G D2 V0 A5 H3 q; _
- *输入参数:无/ c/ [9 O& B/ i5 M, u7 F
- *返回值:无
1 s2 z1 t' K* |( N5 O% V& C - *备 注:无
9 A* i; @2 @0 L- Y6 d7 D$ H - *==============================================================================
# y( C" Y$ k9 y' g - */
( d( C9 F/ s$ \, W - void Drv_Hcsr04_CloseTimerForHc (void) // 关闭定时器 2 E& O! m8 o3 d9 Y6 X
- {
& L4 Y- F2 H) ^# b# K) \/ A - TIM_Cmd(TIM2, DISABLE); // 使能TIMx外设 3 {4 F: R+ ]2 U' y2 s
- }
复制代码
& Z5 m* Y4 `& p& l' v( W, K: {定时器中断服务函数如下( E) v& X5 d+ W. Z3 P% d4 @, ]/ h( D
- /*
6 L6 m1 M, U5 V0 u& ~ - *==============================================================================3 |& ?% B& O7 b9 S0 f/ p
- *函数名称:TIM2_IRQHandler
9 G6 h) O9 [( r7 N. i5 S0 A - *函数功能:定时器2中断服务程序 ; Q- E6 s, f9 |5 ^& T6 t$ _
- *输入参数:无/ C, s9 |! [/ _6 K% [
- *返回值:无
$ B3 {3 l3 Q! E8 Y7 [ - *备 注:无; k) q* i- \& B9 N
- *==============================================================================; B9 k; s. N7 m1 W3 N
- */
) r: d- J5 c7 e% w1 \- D - void TIM2_IRQHandler (void) // TIM2中断 $ Q* k$ v! A# v# O2 j
- {
# X ]6 m* {- Z7 p1 ^$ A% y - if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) // 检查TIM2更新中断发生与否
( ~2 B0 d9 f7 D4 v! U* b1 G - { % U, w& y/ a: |0 a
- TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除TIMx更新中断标志 & ^0 R3 Y ^( }1 f2 h F
- gMsHcCount++; , ^: U% i5 m9 u. A, V9 x
- } ; p8 ?, k- c a) A. Z
- }
复制代码 & S" D# L/ W5 C4 F9 P
4.3 获取定时时间
0 }$ u- B- ?! _3 V+ j5 d- /*2 h5 l2 h: [3 y- v/ T+ u
- *==============================================================================* {: r( p: f* \0 @
- *函数名称:Drv_Hcsr04_GetEchoTimer
6 U# L# s7 `! L% D8 v9 { - *函数功能:获取定时器定时时间
( p3 p. g' {1 F% ^( m - *输入参数:无
" j1 V' I n, P9 K# |+ ^! Y - *返回值:无
. M8 V" O+ f' s N# X" ^, w0 H& Q, b - *备 注:无" l- ?( x' T$ o K; d; x. B" x
- *==============================================================================) J8 j' S) S: V( V3 g: w. N
- */+ t' }+ w8 T+ ~, f# W
- u32 Drv_Hcsr04_GetEchoTimer (void) 0 ?; k# f6 H' ^- Y& o% ]
- {
3 F- |' ]7 F# T - u32 t = 0; ; f; n% z2 L3 J% N
- t = gMsHcCount * 1000; // 得到MS
6 f" _- ~1 f: X' d4 B! {4 g5 d- B: T - t += TIM_GetCounter(TIM2); // 得到US
! _, m2 J; Y* A$ R8 k - TIM2 -> CNT = 0; // 将TIM2计数寄存器的计数值清零
4 e) X" i: Q& O8 Z5 S( B/ U8 B - delay_ms(50);3 _( Z8 r# d4 }$ r: a/ |
- return t;
$ ^- v K u$ I6 T: f- V+ J! ] - }
复制代码
8 T* r4 U% K- c) w% {; |& p& S' J4.4 计算测量距离# v- U6 C: P. s7 c
- /*
6 [" N6 [' ]# O$ y - *==============================================================================
! O% X5 G w) z7 Q* y4 M - *函数名称:Med_Hcsr04_GetLength3 ?. `4 v/ ~3 E5 |7 o
- *函数功能:获取测量距离2 a$ W$ K, A; \! {! ~
- *输入参数:无9 m) V1 N% P7 F0 [. i0 }
- *返回值:无" Y% E, y$ u$ V2 G" [$ d3 f
- *备 注:一次获取超声波测距数据 两次测距之间需要相隔一段时间,隔断回响信号
0 F: ~. T" k3 `2 t7 m: p - 为了消除余震的影响,取五次数据的平均值进行加权滤波
+ E; r: C" n2 w5 W/ Y, P5 O% ? - *==============================================================================
+ ?+ h& y1 x7 A# h" S0 u - */% J; ?7 a9 X7 O! h* V/ R3 g
- float Med_Hcsr04_GetLength (void ) & m- g& N: s! H# e: {; ]9 W
- {
, _1 t9 \0 t2 ?5 O9 f5 q2 E - u32 t = 0; ( ^7 _- `. A+ l
- int i = 0;
8 B: j) C% Q, [8 } - float lengthTemp = 0;
3 W1 Y8 T5 p9 g; Y+ B$ ?: a1 P! o - float sum = 0; 7 U2 [' H: T4 f! S
- while(i!=5)
9 |) ^, ]5 U: P' F# y - {
# ^1 i9 l5 C$ L - TRIG_Send = 1; // 发送口高电平输出 9 Q& v0 ^3 G: J: s6 D( g
- delay_us(20);! L5 E& }# m; D- Y
- TRIG_Send = 0;
0 D% T% o% G- _! y0 @ - while(ECHO_Reci == 0); // 等待接收口高电平输出 B+ r0 I/ G" d) j" a& k
- Drv_Hcsr04_OpenTimerForHc(); //打开定时器
8 O8 Z* K q* @5 n# g7 R - i = i + 1; ( g& A0 X( _0 N3 ]
- while(ECHO_Reci == 1);
' c% \$ `% ]& m+ k# Q: r - Drv_Hcsr04_CloseTimerForHc(); // 关闭定时器 * d2 V& E F" C4 u8 b
- t = Drv_Hcsr04_GetEchoTimer(); // 获取时间,分辨率为1us
. w. e; ~* x4 R+ Y9 ]% _ - lengthTemp = ((float)t/58.0); // cm : f! S) B. q7 z# a3 w/ C+ O
- sum = lengthTemp + sum ;
S" f2 {* j! S - } ! q# ~4 S$ s3 G; {+ V( V
- lengthTemp = sum/5.0; $ U5 b! `% v& L; F% d; Z
- return lengthTemp; - j/ f1 |* W; v! I; N4 ^
- }
复制代码 9 m. ?0 ~; P% C0 Z' Y" d, j
4.5 宏定义
/ J) s( U7 P+ [1 O- #define HCSR04_PORT GPIOB // 定义IO口8 r8 ~. Z: c/ z! _. K+ k9 t
- #define HCSR04_CLK RCC_APB2Periph_GPIOB // 开启GPIO时钟 5 i! n7 Q \9 \9 z3 Z- S' h% v+ h
- #define HCSR04_TRIG GPIO_Pin_8 // 定义Trig对应引脚
0 D- Z! n# \1 W- I7 I$ f3 W - #define HCSR04_ECHO GPIO_Pin_9 // 定义Echo对应引脚2 }" K/ u7 g3 Q+ ^
-
: {. P# _3 f! ~- J6 R9 h, I1 ] - #define TRIG_Send PBout(8) // 将TRIG_Send映射到PB8! K8 {7 {2 H$ A& F% r ^
- #define ECHO_Reci PBin(9) // 将ECHO_Reci映射到PB9' x$ o5 \7 E! |3 J: Z$ @0 F- |+ d
- & |! Q! V* b5 `
- void Drv_Hcsr04_Init(void); // Hc-sr04初始化
$ {. z& R, T ]4 [" E, [$ E - void Drv_Hcsr04_OpenTimerForHc (void); //打开定时器
1 [) [* P) z$ a s! ~# H - void Drv_Hcsr04_CloseTimerForHc (void); //关闭定时器
% W3 k/ h2 b( l: q2 f: N; n. z1 I - u32 Drv_Hcsr04_GetEchoTimer(void); // 获取定时器时间
复制代码 8 t( D* H/ Z, |6 P: u% p) u. q
; F: P4 \% j3 s) N3 I; t
五、应用实例9 z: A5 v9 }) Y' r
利用串口打印距离信息,main函数如下. m2 I+ t& }, v2 s4 u. j
- float gDistance = 0; //定义获取返回距离变量 `( \+ f/ {; k( i7 U
- ^ w$ f: s1 h H
- int main(void)
# b* B) Q* [* |1 t B9 ^ - {
) i2 S: Q! h' H3 r! y - Med_Mcu_Iint(); // 系统初始化
' y0 e- Q2 L% `5 o5 N7 x1 N0 i! ` - ' W. ^$ L3 z% t8 {5 A3 R# Y, P
- while(1)
; ^% A0 O1 B. {. A% ~- y - {% X, b* C; \4 R
- gDistance = Med_Hcsr04_GetLength(); //获取返回距离
: T6 q# n {: K' K - printf ("距离为:%.3f cm\n",gDistance); //串口打印返回距离/ c4 Q' |" |; ~ c- w
-
! I, |) T. ^- h - delay_ms(500); //延时500ms = 0.5s
( q* o/ Q! n0 u( \8 C - }
4 i/ k- D( j$ L) L, R' }0 e0 | - }
复制代码
2 v% |/ o6 N8 C六、拓展应用
- p* [2 k* Q9 `超声波测距比较常用的,比如利用超声波测距模块实现智能车的自动避障,这个在后续实战项目系列中会有,在此就不再详细介绍了。主要思路就是根据HC-SR04测得的与障碍物的距离,来决定是否要停止或转弯,以及往哪边转弯。
+ l& `/ t" H/ V
, p1 F! f9 A" g, d- X5 d* i3 ]- `转载自:二土电子1 c. q7 e1 ?8 h) X
如有侵权请联系删除
$ p7 k/ R- K" r8 I" H
+ Y6 W# A! }. r8 `; [5 @+ b- g* X7 v/ j' o- Q) x+ Y% X
|