一、超声波测距基本原理
' K+ \! c, P# q' e超声波测距的原理非常简单,超声波发生器在某一时刻发出一个超声波信号,当这个超声波信号遇到被测物体后会反射回来,被超声波接收器接收到。这样只要计算出从发出超声波信号到接收到返回信号所用的时间,就可以计算出超声波发生器与反射物体的距离。" u5 ^7 S/ M) N* [* r
1 g) Z" _8 i2 x: c, K4 D: n距离的计算公::d=s/2=(c*t)/2, C& x8 H- c2 n$ O- b0 x
- |. X5 c) U, e
其中 d 为被测物与测距器的距离,s 为声波的来回路程,c 为声波,t 为声波来回所用的时间。
- w9 M& n: a- c3 |
3 \' {! @( x1 a8 x由于超声波也是一种声波,其声速 c 与温度有关,在不同温度下的超声波声速不同。在使用时,如果温度变化不大,则可认为声速是基本不变的。如果测距精度要求很高,则应通过温度补偿的方法加以校正。声速校正后,只要测得超声波往返的时间,即可求得距离。不同外温度下的超声波声速表如下7 {) o2 i; N1 ?: A! _4 K5 J0 ]
" T6 K8 g, I3 w4 [
, B: @ j+ N/ Z4 k" t& ]
不同温度下超声波声速表 . ^! Z. X+ Y+ q
. E8 H6 J D! A补充说明* N1 ?9 R( @* X; Q6 |: R
最近在问答区发现有小伙伴疑惑为什么在计算距离时是根据Echo输出的高电平持续时间来计算距离。这里简单解释一下,仅供参考
4 Y* w3 a: k( K( A1. 实际超声波测距的原理就是上面介绍的,记录的时间是从超声波发出到回波被接收的时间,然后这个时间除以2,再乘超声波的传播速度得到距离。超声波测距模块实际也是这么做的。模块内部会记录超声波发出的时刻,在接收到回波后会立刻以高电平的形式从Echo引脚输出高电平,高电平的持续时间就是超声波从发出到被接收到的时间间隔。这么做是模块为了方便单片机处理,快速得到超声波从发出到被接收到的时间间隔。
# v1 j1 d' V. ]% H: l
9 b h! ~0 P& G9 W0 j1 h2. 实际大家可以自己测试一下,初始化一个定时器,从给Trig引脚10us高电平结束时刻开启定时器,到Echo引脚接收到高电平的时刻停止计时,用这个时间来计算距离,实际和用高电平持续时间来计算距离得到的结果基本是相同的。* [; h& m; D) q1 u% l# W
; b. R1 R- [/ F9 l, }
3. 给Trig引脚10us高电平并不是说只发送10us的超声波。10us的高电平是为了触发模块工作。10us高电平结束后,模块会发送一个8个40KHz的方波。
8 f( F" F+ A3 \( h- A0 d, C- [# B: w% |, P0 {9 x
1 ]5 k& T0 h# [ J% M' D二、超声波传感器简介
- c5 o0 u# W! g6 B( M ~总体上讲,超声波发生器可以分为两大类:一类是用电气方式产生超声波类是用机械方式产生超声波。电气方式包括压电型、磁致伸缩型和电动型等:机械方式有加尔统笛、液哨和气流旋笛等。他们所产生的超声波的频率、功率和声波特性各不相同,因而用途也各不相同。目前较为常用的是压电式超声波发生。& L! o; q4 O' C2 @3 K: N
8 H# y7 l1 r$ J3 Y$ j3 {: y
压电式超声波发生器实际上是利用压电晶体的谐振来工作的。它有两个压电晶片和一个共振板。当它的两极外加脉冲信号,其频率等于压电晶片的固有振荡频率时,压电晶片将会发生共振,并带动共振板振动,便产生超声波。反之,如果两极间未外加电压,当共振板接收到超声波时,将压迫压电晶片作振动,将机械能转换为电信号,这时它就成为超声波接收器了。这里介绍的是一个超声波测距模块——HC-SR04。2 f& r8 H1 p! s8 z
3 E- X. F$ q' Q$ k% r/ c7 g! q* J6 x
HC-SR04
) a+ C8 H7 G$ O9 G" a
0 i! T/ N3 N: F d
o' V- t) f! e2 y三、HC-SR04测距实现思路% c( l/ v3 I0 B5 i3 _2 C
这里就不再针对HC-SR04模块的原理和电路做详细介绍了,直接介绍利用该模块实现测距的思路。该模块有四个引脚。
/ p6 ^0 v9 Q" ^• VCC —— 通常是5V供电+ f. S; k: H. }3 E4 w
• GND —— 地 h5 { Y2 @5 G4 W: e1 U; ?
• Trig —— 给该引脚大于10us的高电平,超声波发射头会发送一个超声波信号
& n2 ^. @2 ?! ~• Echo —— 该引脚在接收到返回的超声波信号后会变为高电平
8 i- @5 O3 d5 J5 {6 ]3 r" n7 R( n4 P7 V
Echo接收到的高电平持续时间即为超声波一个来回所用的时间,利用该时间除以2再乘上光速,即可得到测量距离。8 T* E) M. f0 ~8 R
& [7 r0 w' K9 k: Q3 ]7 r! E
9 ~% S, q5 H6 D5 Y. M* ]( U
四、超声波测距程序实现! `% |! N6 B( w6 g4 {* f; A
4.1 HC-SR04初始化程序0 x7 Z* k& T; }! G8 ?, e! n" b6 Q: U
HC-SR04初始化程序主要包括两部分,一部分是初始化HC-SR04的GPIO,Trig引脚设置为推挽式输出,Echo引脚设置为浮空输入。另一部分是初始化TIM2。初始化TIM2之后,没有立即使能,而是等到Echo接收到高电平的时刻,开启TIM2。; P) |& C0 q$ O7 d0 G( L9 ]+ P
- /*
% W2 {: ?/ C" s6 u6 v - *==============================================================================
* l( h: B4 m4 b- L - *函数名称:Drv_Hcsr04_Init
3 V. O" n3 x' a$ Q9 l3 f - *函数功能:初始化HC-SR04
/ \7 p7 i3 J2 k - *输入参数:无% X5 i. L0 B6 W9 h. C. Z
- *返回值:无
! D! ]9 ~: I! C - *备 注:初始化HC-SR04引脚的同时,初始化了TIM2,用来记录高电平持续时间
- u" i9 f: o" `6 n8 o! e% K" _ - 初始化完TIM2后,没有使能,当Echo收到高电平后使能
3 N4 D2 j T2 ^! V! a - *==============================================================================
' S5 _: S7 D1 n+ p1 Q& c - */- H+ d, n+ ^* Z
- void Drv_Hcsr04_Init (void) // Hc-sr04初始化
" m2 k! O# S0 ~ - {
& B f/ W9 s4 |+ }7 H) p- z - // 结构体定义2 B* f: F/ I4 s: A7 I! X5 i
- TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; // 生成用于定时器设置的结构体 , T% x2 B, H( N | J% V
- GPIO_InitTypeDef GPIO_InitStructure; // GPIO结构体
, x. V+ j4 E( P9 v) G) s1 E! [0 E$ ~ - NVIC_InitTypeDef NVIC_InitStructure; // NVIC结构体
4 r9 e1 p3 n& Y( M; Y H - RCC_APB2PeriphClockCmd(HCSR04_CLK, ENABLE); // 使能GPIO时钟5 U4 n1 W" e: L2 [7 x
' u/ o: c6 ]5 q z& x- // GPIO初始化
" v2 R! d+ {. h/ a, R - GPIO_InitStructure.GPIO_Pin =HCSR04_TRIG; // 发送电平引脚
* A9 T) L* ]8 Q S6 P8 U - GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; & k) ?" ]( l, w% P+ {+ H3 M
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; // 推挽式输出 * M6 i7 ~& H/ ^9 \& K2 v8 S
- GPIO_Init(HCSR04_PORT, &GPIO_InitStructure); 2 _- S1 Z/ n0 H) x! m0 N$ d) A
- GPIO_ResetBits(HCSR04_PORT,HCSR04_TRIG); ; k7 t5 ]) U P; h% Z
- 0 x$ S6 @5 ^/ j5 K3 H" W E, g: J
- GPIO_InitStructure.GPIO_Pin = HCSR04_ECHO; // 返回电平引脚 * i/ ?- [* ?$ f# h8 E' O! {% B4 a
- GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 浮空输入 5 }% t1 @$ f/ W5 L) h' g" n
- GPIO_Init(HCSR04_PORT, &GPIO_InitStructure); & |7 c+ B ?4 E3 H* M
- GPIO_ResetBits(HCSR04_PORT,HCSR04_ECHO); z9 `6 J' j- w! f& c7 @
- , X% e6 n3 O; w/ d0 V r; j" [' ~
- // 定时器初始化 使用基本定时器TIM2
1 y# M) a: F+ R7 w! L$ U - RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); // 使能对应RCC时钟
' X3 ~ |# n3 t8 W$ d) G8 M - // 配置定时器基础结构体
. ^9 J( `; i$ `4 E/ e. f& i - TIM_DeInit(TIM2); 8 \, l6 T! b h1 }8 G/ O7 O
- TIM_TimeBaseStructure.TIM_Period = (1000-1); // 设置在下一个更新事件装入活动的自动重装载寄存器周期的值(计数到1000为1ms )
, x- K' x* y( Q, X0 t! s - TIM_TimeBaseStructure.TIM_Prescaler =(72-1); // 设置用来作为TIMx时钟频率除数的预分频值 1M的计数频率 1US计数
: `7 \1 {- H4 O$ T d; ` - TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1; // 不分频 I* k, k" w* Q9 B
- TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; // TIM向上计数模式 % ^& A# Y6 P' U) Y* J6 l
- TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); // 根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位 * A+ `5 R$ r' D* l2 I
- ; Q2 k. V; ]0 x5 ~8 z$ T
- TIM_ClearFlag(TIM2, TIM_FLAG_Update); // 清除更新中断,免得一打开中断立即产生中断 : ]) ?/ o1 y/ D4 d# X% T/ U
- TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE); // 打开定时器更新中断 : i. _7 y' _+ Q
1 V% n+ `( h9 P; d- // NVIC配置 8 X+ z, z8 c+ ]0 r
- NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置中断优先级分组为组2:2位抢占优先级,2位响应优先级
8 U7 @( T! a# _9 b4 L - u; a% X9 P. ]# Z
- NVIC_InitStructure.NVIC_IRQChannel =TIM2_IRQn; // 选择定时器2中断
! s* S3 q# U L( P/ |/ L) ~0 m# H - NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; // 抢占式中断优先级设置为0 , U) K/ A1 S3 n3 d( Z# B
- NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; // 响应式中断优先级设置为0
4 q+ C+ N0 F8 q& Y% V. z - NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断 # `+ D7 F) j. N$ I. Z8 F; m
- NVIC_Init(&NVIC_InitStructure); ' ]; R0 b$ ]2 ]. b
- : P! [/ P) ~2 }% D+ D
- TIM_Cmd(TIM2,DISABLE);
- \* }: H4 ]& x, u - }
复制代码
9 \' z% r2 m/ u# Q* z( m4.2 TIM开关程序4 E* K; ~, Q* I( a
- /*
. }3 }( f/ y0 B5 @6 s - *==============================================================================3 [ K5 b7 I: N4 {+ d$ a3 M- \" N
- *函数名称:Drv_Hcsr04_OpenTimerForHc
! P1 a/ j( q+ u6 f - *函数功能:打开定时器
! T1 h. B# K! m3 j: b3 B - *输入参数:无
* a7 H# P6 g- k2 d" Z - *返回值:无
/ A+ z* W' e0 W* {( n$ q - *备 注:无
2 d! f0 ^& }( k- U - *==============================================================================/ U/ k; P% T& h0 h- j& N+ N
- */2 `7 f! Q0 |- M' g
- void Drv_Hcsr04_OpenTimerForHc (void) // 打开定时器
5 C/ W. s; z2 v1 E4 G - {
1 }) c% z7 C) c. T6 Q4 o. } - TIM_SetCounter(TIM2,0); // 清除计数
. t4 W3 M7 u# o8 Z - gMsHcCount = 0;
! I5 Y1 K3 Y) j! Z* v; q - TIM_Cmd(TIM2, ENABLE); // 使能TIMx外设 ! j9 G: O1 U6 e: e- c, u
- }: `! z! F) R- x9 f
- /*8 X3 i: x- Y0 _$ {
- *==============================================================================
8 A7 H; _; R/ q* u: x4 i9 d% v - *函数名称:Drv_Hcsr04_CloseTimerForHc* P9 P5 A3 u6 I( d& c: G$ L
- *函数功能:关闭定时器
) C4 F# ^4 `& S) P9 G$ o* d0 C - *输入参数:无
' B/ q/ r' J% O" f. C! w0 t - *返回值:无5 h* A, F) V* K+ b, q
- *备 注:无/ j9 V8 {8 Q C* F# L
- *==============================================================================5 J ?" T' e7 z1 W& J/ d
- *// h5 A5 T- f$ A$ a2 m i
- void Drv_Hcsr04_CloseTimerForHc (void) // 关闭定时器 + ^: E/ h, A3 \7 e% p. x! r; ~# |
- {
W; H6 }. `) ~$ h' F9 K! k& B - TIM_Cmd(TIM2, DISABLE); // 使能TIMx外设
, |! t8 y# l: A# H - }
复制代码
8 b* U) Q, ^! U5 g( i! i3 ~定时器中断服务函数如下
, P1 g2 `0 H9 c- /*# Z+ b7 t% u: A, ~- v: h
- *==============================================================================
/ ]* I4 `8 C0 ]+ y% R. V) ^ - *函数名称:TIM2_IRQHandler' O6 r, ]) [3 @
- *函数功能:定时器2中断服务程序 % J) _9 C' m+ y i5 T8 k$ I4 i
- *输入参数:无
5 j! J+ |% o! t/ d d1 }+ t - *返回值:无
4 S- D9 n; d' Z* P- E' h - *备 注:无' v' ~- x# u% B3 r0 I8 Q
- *==============================================================================
" i/ R/ T. N9 r0 P! N4 j8 k - */( w. |- E# m7 f! j+ |
- void TIM2_IRQHandler (void) // TIM2中断
5 E( l; `% v, h* G5 l) K - { % k9 g0 ]% Y1 y# X2 ~ S) V
- if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET) // 检查TIM2更新中断发生与否 : U# L t) j4 F0 @2 Z) k/ d
- { " h- e A+ A- u2 W5 ^ a U" m- h( E, o% M
- TIM_ClearITPendingBit(TIM2, TIM_IT_Update); // 清除TIMx更新中断标志
2 r- y! k7 l7 o$ ~# z7 N, k - gMsHcCount++;
- ^' l% ~8 N b+ |- j% k; L. J; X1 A - }
) h1 v0 d# j5 C. ~2 B6 W" | - }
复制代码
. I* _- D8 z/ R2 {3 u) x3 Q4.3 获取定时时间% _- _ t2 Z5 \6 c$ R) N! ]; E
- /*
2 m0 b- Q* |; [) z - *==============================================================================, j) ?* y4 T+ N
- *函数名称:Drv_Hcsr04_GetEchoTimer0 a5 o. S! R" ~' m! q' c% C( {
- *函数功能:获取定时器定时时间
7 Z9 y+ J! z, ^/ E# K9 c* e - *输入参数:无& r0 g/ d- [: j+ u* O
- *返回值:无& ?0 l& F8 a. ~: l$ E3 u" J X
- *备 注:无
% D' \, f9 j" k. |9 Q - *==============================================================================. y; m& z l: [4 Y
- */
8 L3 R; n8 Z5 ]0 }0 M% `5 K1 X6 C - u32 Drv_Hcsr04_GetEchoTimer (void)
5 o- n$ F, t; n) ~. J q: k3 d - { + h z. _! h. e$ r& g6 ?
- u32 t = 0; " P3 Q- `) \' a, p( C
- t = gMsHcCount * 1000; // 得到MS ; i. P& Z& U3 C9 z5 |
- t += TIM_GetCounter(TIM2); // 得到US - V! K# ?- P9 I6 d
- TIM2 -> CNT = 0; // 将TIM2计数寄存器的计数值清零 " x4 b0 ^! [, U$ Z
- delay_ms(50);
; c, U0 M# X9 q+ b% a; Q! c" z - return t;
! A$ a8 d2 ^* p. I7 R- Z - }
复制代码 ' U' E! u4 u* ~$ H
4.4 计算测量距离
: Y- w2 @+ n/ n$ e) z0 B- /*( u4 H5 `4 a7 J, c6 N2 J5 ^/ i
- *==============================================================================
* }: C: C4 k) ] - *函数名称:Med_Hcsr04_GetLength
7 W& N3 n8 m, B/ O4 I - *函数功能:获取测量距离
1 i9 T; l( ?0 ~0 j( P$ K8 n - *输入参数:无
. K/ V( \; Z! X6 `: ^ I - *返回值:无
5 f; R4 N/ u+ o' }- Z - *备 注:一次获取超声波测距数据 两次测距之间需要相隔一段时间,隔断回响信号
$ I; V8 g9 V4 T$ h+ v - 为了消除余震的影响,取五次数据的平均值进行加权滤波! G' D' X1 B: S( m/ a8 B: v
- *==============================================================================/ t' k( p# X( G( F
- */
% _4 g: [& N# d8 l7 E - float Med_Hcsr04_GetLength (void ) 4 M# v, f, B1 ?; |4 Y! W, g
- { 3 B: z' R4 {( a# c. B
- u32 t = 0;
# o6 x5 D& h- a( h W0 o7 L3 G - int i = 0;
9 }5 H5 I! w0 T p; m - float lengthTemp = 0;
, _' e. Q1 \; z9 i+ r - float sum = 0; , w$ p( j7 f8 |& V# e
- while(i!=5) + X5 \7 b" c, T6 S. k% N" Q
- { 3 B0 U! D% \- L! T+ V
- TRIG_Send = 1; // 发送口高电平输出
( C! X, h: ~ t7 | P1 B - delay_us(20);1 _) o* n8 X- a- a# n$ h
- TRIG_Send = 0;
! a" I; E. N5 h8 S! a - while(ECHO_Reci == 0); // 等待接收口高电平输出 6 b X9 s7 E: g6 ]' i3 j
- Drv_Hcsr04_OpenTimerForHc(); //打开定时器 b$ n G" H: B8 e
- i = i + 1; * ]6 {6 t3 a' y" G- E! o N, h
- while(ECHO_Reci == 1);
7 r- z |* l- F6 X: Q' H - Drv_Hcsr04_CloseTimerForHc(); // 关闭定时器
) ~& n2 T6 S6 {! g/ R) c+ j' Z - t = Drv_Hcsr04_GetEchoTimer(); // 获取时间,分辨率为1us
" B `, S( w& ?* a( j0 N4 f, `. z - lengthTemp = ((float)t/58.0); // cm , k& k6 v4 d6 k7 ~! s$ ?% j
- sum = lengthTemp + sum ;
* w$ H/ E' i* {' O9 J4 x' ^ - } 4 ]! a9 J# v* z2 A0 c# G+ l. l* {
- lengthTemp = sum/5.0; 7 ~0 N( R; s: d# ]2 p3 z
- return lengthTemp;
0 T& Q2 N$ ~$ @1 N" v. h - }
复制代码
9 g- U# J8 |9 [' {# E4.5 宏定义1 o7 S) C* o) u# c
- #define HCSR04_PORT GPIOB // 定义IO口
$ T$ G! I, L4 s6 C/ y6 D" m/ V - #define HCSR04_CLK RCC_APB2Periph_GPIOB // 开启GPIO时钟
) Q2 T. }9 d9 {/ J1 I% a - #define HCSR04_TRIG GPIO_Pin_8 // 定义Trig对应引脚
% S3 s* ], M# l% A, X/ H - #define HCSR04_ECHO GPIO_Pin_9 // 定义Echo对应引脚
' v. s; h6 v" B1 V6 S- E - ; f+ E* b8 ~/ h2 R4 o
- #define TRIG_Send PBout(8) // 将TRIG_Send映射到PB8
& r2 d; w0 p9 z" a! A+ N* F - #define ECHO_Reci PBin(9) // 将ECHO_Reci映射到PB9
. S* [1 I# z; g" s( E/ v$ ?
; U4 \( n8 B4 j1 } v- void Drv_Hcsr04_Init(void); // Hc-sr04初始化+ [# ^! K1 J6 b: V
- void Drv_Hcsr04_OpenTimerForHc (void); //打开定时器' B* q) B! O& @1 @$ ]3 X' M) G
- void Drv_Hcsr04_CloseTimerForHc (void); //关闭定时器+ }( N1 I$ B+ L1 l9 |4 w
- u32 Drv_Hcsr04_GetEchoTimer(void); // 获取定时器时间
复制代码
# Y7 R% y/ Z* k+ h* J
3 h( z+ u7 q2 C2 E# g4 T五、应用实例
' E; N+ b+ j* B/ h% e利用串口打印距离信息,main函数如下
. G* m$ Y9 e; b( M! j/ [# L- float gDistance = 0; //定义获取返回距离变量
3 N9 B: j) @+ c* w# t7 I- o$ ] - ) J# ]8 O I4 v4 m& j
- int main(void)- s% M# c1 I/ A2 o$ u3 C, M; c
- {
/ U `; d. r0 |) z3 Z+ ?8 q7 t/ ? - Med_Mcu_Iint(); // 系统初始化 u2 \3 `% F; s" O
-
) ^" u; I. m' w1 s' T - while(1)$ r+ A0 J6 I2 w: p( W) v
- {
. M6 T9 \* E; y" G1 }+ Y4 U - gDistance = Med_Hcsr04_GetLength(); //获取返回距离
& k3 b/ Z( u2 E7 o% o Z1 [& S8 a - printf ("距离为:%.3f cm\n",gDistance); //串口打印返回距离, i# i: a; X ?# p H/ g4 e) ?
-
: j$ P+ B& K& e- I7 B5 C - delay_ms(500); //延时500ms = 0.5s2 E- b9 j7 \& R, |+ g; {1 n8 T4 q
- }
3 W8 ~6 W5 o! A& K - }
复制代码 5 q, t! g" s- P7 F* ?2 M$ Y
六、拓展应用
7 D3 t9 Z$ o. _- U* |/ |超声波测距比较常用的,比如利用超声波测距模块实现智能车的自动避障,这个在后续实战项目系列中会有,在此就不再详细介绍了。主要思路就是根据HC-SR04测得的与障碍物的距离,来决定是否要停止或转弯,以及往哪边转弯。
2 [9 P0 X' G5 v1 L7 W0 M4 v# U* E$ i9 r# G8 Y* x }: A' m
转载自:二土电子
: F4 f' O6 y5 r' P0 j如有侵权请联系删除
: F2 B8 {, G" d/ w% V
, ?* G; c, Z: R3 I" U5 ~( p/ A
. t1 j8 \3 s! ^" Y d |