你的浏览器版本过低,可能导致网站不能正常访问!
为了你能正常使用网站功能,请使用这些浏览器。

基于STM32的驱动HC-SR04超声波测距模块的经验分享

[复制链接]
攻城狮Melo 发布时间:2023-3-18 14:38
超声波测距原理
+ E( L% e( d2 C, ~* a& l利用HC-SR04超声波测距模块可以实现比较精确的直线测距,其测距原理图如下:
* `, v) ]1 h7 R! P- \( \0 b" E& s) Y! Y1 R
20191123190005785.png ( C1 @5 y  Y* t8 i0 Z# s
$ G" h$ x2 Q, j* w7 Q
HC-SR04的一端发出超声波,接触到反射物后反射,被另一个端口接收到,所以只要知道发射和接收的时间差,就可以根据声波传播的速率算出HC-SR04和反射物直接的距离。2 f' n8 b$ K+ b
所以实现超声波测距就需要俩个条件:* ?  H% W" v; z* s" u8 w+ E2 u$ r% [
发射和接收的时间差
/ F; y3 N5 r1 r2 ^% I超声波传输的速率' _6 U" D% E) Z5 M6 n0 Z) _8 M
HC-SR04工作原理
3 r+ Y9 S2 a0 K, ~" eHC-SR04模块的电气参数如示:
4 c" K7 C0 n' ^: t0 O
. d5 C; D: D9 }0 C* C% L! N1 d. q
2019112319045710.png - ?& R" j- `! B7 w5 m
" ]" b: t( I6 b2 Y+ v/ T
HC-SR04模块的实物图如示:* u* [+ L6 k, V) [& R3 o  h

, n/ V2 k& |3 i1 O. Q# c9 F$ l) ?
20191123190743986.png 4 ^2 J- C- J1 b. M+ T7 E& y4 k6 S
2 p7 }: ?. _$ S3 f# {
有四个引脚:
, C4 ?$ F- _0 n7 }& f0 CVcc:+5V电源供电
+ }, c2 E3 K6 X9 z0 Z7 ETrig:输入触发信号(可以触发测距)
. ^" ^6 Z" h" B" s: k& E2 W% AEcho:传出信号回响(可以传回时间差)
# [4 e/ D9 g& y4 ^4 wGnd:接地
* m; E2 [- p& x/ |: F) A: w6 k# y' \5 R0 x7 _8 g
用Trig和Echo引脚实现测距的流程:2 \& k( o1 y, {6 j+ t
1.通过Trig输出一段至少10us的高电平(脉冲),触发一次测距,超声波在传输的过程中Echo一直输出高电平。" e2 J3 w, ?; s8 C4 m1 y
2.在Trig脉冲输出后,立即检测Echo引脚的电平,测出Echo高电平持续的时间t,t就是超声波在所测距离一个来回所需时间。  ?. _5 D2 t" `& ~8 l* {

8 t# }/ x' U0 Y' K
测距时序图如示:
; V% w( u  N7 m7 q; b% |; L+ U8 A0 v5 a6 h- G
20191123191907919.png ! `/ V& E# ^4 U
- h1 y/ t; @; B% V
STM32实现驱动: L# d9 k: ]% l7 _2 X
利用STM32驱动HC-SR04需要做好几个关键点:
6 a$ \1 Z1 B1 m! d7 j2 G引脚的配置
1 D0 B; x9 J) z6 v  `时序的控制
: I, R) ~& e" j1 W* K时间差的测量
$ M0 c3 O1 T( ]% {
) j4 u" t, v8 a/ ~  |' T1 y
下面来分开实现几个关键点
) N3 _" J7 w2 ^- ^& R5 R. O3 S# T6 N3 Q" k
1.引脚的配置
7 J: x  g0 G5 h& F2 w" Z4 T2 Q! j, e$ qHC-SR04四个引脚,Vcc和Gnd直接接在开发板的电源上即可,主要是Trig和Echo引脚的配置,我选择了PB1连接Trig引脚、PB2连接Echo引脚。% h1 C/ J4 x. e( W& X# P
因为要控制Trig输出电平,所以PB1引脚模式是推挽输出GPIO_Mode_Out_PP
4 z7 p# H  Y, m3 t' b3 TEcho要检测高电平持续的时间,所以PB2引脚模式是浮空输入GPIO_Mode_IN_FLOATING
6 h9 b; f: Q3 e# ?; x" I/ K0 O相关的配置代码如下:* C% ?0 O5 R! D) f; F
  1. void SR04_GPIO_Init( void )
    3 u" L. m0 D! n0 a& z+ }% Z: z
  2. {+ b3 Q8 ]7 o. p$ e$ i
  3.         GPIO_InitTypeDef GPIO_InitStruct;
    6 U7 ^3 e# t0 |2 P' {0 o9 x* n; j
  4.         RCC_APB2PeriphClockCmd( Trig_Clock  |Echo_Clock , ENABLE );
    + y: N$ e/ w! T
  5.         " i1 \  t$ ~' r( S
  6.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;) `0 b1 y1 U/ l" _  ?
  7.         GPIO_InitStruct.GPIO_Pin = Trig_PIN;. H" n6 ?5 h; [, \- u% ^
  8.         GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;& \% G& b3 {2 U, Y% G" T/ x
  9.         GPIO_Init(Trig_PORT, &GPIO_InitStruct);
    4 A  s6 ~  K  U; E' {5 y
  10.        
    1 Z; E1 ]: C. ]3 A) Z$ |- J0 k
  11.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    2 j! f% j$ [+ r4 O6 P% i
  12.         GPIO_InitStruct.GPIO_Pin = Echo_PIN;
    6 R: s" d4 M/ r* k
  13.         GPIO_Init(Echo_PORT, &GPIO_InitStruct);5 g, S6 H- h: _
  14. }  b3 c, y8 `3 r- r% U/ w) E4 z: _
复制代码
* K. Y& B3 J; L! R) ~# A
2.时序控制
3 }) ]' U" |* C- q# aHC-SR04的时序是:先来一段10us的Trig高电平,接着接收一段Echo的高电平,伪代码如下:" e& x9 \7 y7 F, o- n# W2 L
  1. #define Trig_H  GPIO_SetBits(GPIOB, GPIO_Pin_1)
    . f' E1 ?( V- b) }8 f
  2. #define Trig_L  GPIO_ResetBits(GPIOB, GPIO_Pin_1). x  |7 ~7 H- i+ q' R: \

  3. 4 i9 }9 U% `8 B/ h+ _
  4. /* Trig给一个至少10us的高电平,超声波进行一次测距 */# J9 e4 u1 E- ?
  5.         Trig_H;1 w& }0 @2 g% D- ^+ W, ?  @0 l
  6.         Delay_us( 10 );. O) L, @! l4 B) j" k+ I  h1 z, {
  7.         Trig_L;
    7 i  T5 P0 K! I- ?% V8 J% `7 [
  8. /* 等待Echo高电平 */
    - C3 g" ~' A" F% A0 F  E+ L+ ?
复制代码

: S' y- y% ]: U( X. x3.时间差测量/ _7 }4 i9 v/ O* Q& O# B1 g
这个是最重要的一步,要测量Echo高电平持续的时间,因为光传播的速率是340m/s,而测距的范围大多是cm级别,所以相应Echo高电平持续的时间也就是us级别的。
2 C3 S4 H% @6 N2 f: R所以,测量时间差的条件就比较苛刻,我是利用SysTick(系统计数器)的原理实现计时的。SysTick计数器原理是对通过SysTick_Config()函数配置每俩次中断之间的节拍数,也就是俩次中断之间的机器周期,我大概算出了,测出0.1cm距离的Echo高电平时间约为6um,而系统时钟的频率是72MHz,所以配置每俩次中断之间的节拍为432的时候,进入一次中断就代表0.1cm的距离,所以只需要记录进入中断的次数就可以算出距离。通过一个全局变量在中断函数中自增来记录中断次数。SysTick_Config函数源代码如下:9 X+ G! s+ N- v  }/ E
  1. static __INLINE uint32_t SysTick_Config(uint32_t ticks)
    . O0 e# R$ p1 `$ i$ J6 C/ V
  2. { 3 i1 y+ S& ^1 `7 @( S
  3.         /* 判断ticks 是否超出装填值和重装值的最大值 */* J  Y7 k/ P0 N2 _
  4.   if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            + g% J/ Q7 X4 v- j: c" v
  5.   
    " c+ n  x' ]7 {' E0 x
  6.   /* 配置 装载寄存器 */        $ ]) L8 ?$ G- }1 b$ c/ A( R) C
  7.   SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;     
    ' k) @% B4 K+ x: d$ n
  8.         /* 配置 内核中断的优先级,也是在NVIC中 */
    + L3 H4 D7 e  x
  9.   NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);
    ) k* r- P8 M9 H2 L( H* O
  10.         /* 加载计数器的值 */
      s) y6 {/ i1 L$ Y; w: x$ n
  11.         /* SysTick->VAL是当前数值寄存器的值 */
    - i! @2 k# B# }; M: G; r7 H
  12.   SysTick->VAL   = 0;      
    4 _. z2 t0 T3 ^. d4 ~: v6 y
  13. + U- U8 N2 E* s  H( N: \5 L
  14.         /* CTRL是SysTick控制及状态寄存器:
    ) E9 [) B% e' Y( s1 d" x. K
  15.                 CLKSOURCE:位段2 时钟源选择,0=APB/8;1=APB  APB即72MHz( D# R. x  g4 x* R/ j8 l
  16.           TICKINT:   位段1 当置为1时,计数器递减到0时会产生中断请求;当置为0时无动作( B/ h8 W  S. n! `7 ]3 ^0 t
  17.           ENABLE:   位段0 使能位,可以启动SysTick定时器*/7 I3 T& \3 I3 S3 X! D
  18.         SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | 1 V! W2 h& ^& M& u  J8 ]( d7 Z1 D
  19.                    SysTick_CTRL_TICKINT_Msk   |
    0 P. B/ Z' s/ Z/ u
  20.                    SysTick_CTRL_ENABLE_Msk;                  0 }4 W2 s' g1 Z6 h: e
  21.   return (0);                                             
    ) V6 ~' X: H, C
  22. }1 {8 b: a/ q0 F- J; j; t
  23. # t+ w9 q& {) S& n" j
复制代码

9 J  \- T% S" @SysTick的具体原理可以参考一下我之前的博客:SysTick原理
% {5 b' k, Q/ S' Z: {8 Q0 U
6 W( g+ C1 U8 |7 d8 ]
注意:SysTick_Config()函数执行完就开启了中断,所以必须在Echo为低电平后及时关闭中断,并且将记录中断的变量清零。& N7 }6 }$ A) G# o$ j8 s8 |

$ `* L% S# O+ G' [) L8 h
中断函数如示:" J2 Q9 ~7 m* `# ^) s3 h9 l
  1. /* 用extern和volatile关键字修饰的 全局变量n */1 v- _6 O1 o8 Y% f0 w  n
  2. extern volatile uint32_t n;4 ^0 V+ f4 `( J: V
  3. 4 L. f9 i3 h2 l6 p4 i3 f; i# p( e
  4. void SysTick_Handler(void)
    6 D  x" U2 O+ I. _% ?2 x0 Z
  5. {
    % U7 d( R5 R, Z8 A& m2 B
  6.         n++;
    4 L* }+ a( ?% M2 G  S: }! N
  7. }
    ) n' t) X  y$ |6 x" t0 \3 f; p/ R
复制代码

0 ]4 H( l7 c) |! ?关闭中断及清零n的代码如下:2 v- D% w3 Z. j6 U
  1. /* 本来的使能位取反 */1 s; u# M7 m( C+ `
  2. SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
    ( a2 q) w% H" T; M  x
复制代码

7 `, g# `: U4 P; p  USysTick->CTRL寄存器的0位控制着中断的使能,具体情况在之前SysTick的博客中已做详细说明。& C' r9 S8 z, g3 [& s" |& ?
& b$ Z( _: t1 p) c1 e5 I
4.如何将距离测出来
8 x+ z9 K& W$ o& s" t& b0 l我在main函数中实现了距离的测量,并且通过串口打印函数将距离传到上位机,具体代码如示:' C; m5 T; b* O( f  j# ^5 C
  1. int main(void)
    1 w1 ~; v" c! K1 g
  2. {
    3 _) G! s  t- N. W# ]
  3. 3 \6 c; Y3 W$ j! \) B9 O
  4.         int i=1,q;
    ) J& G- b& G" L* I2 x# |
  5.         float p;
    ) `1 `4 S; D  L2 |
  6.         /* HC-SR04模块引脚初始化 */
    # R/ O. u+ c3 L/ Q8 m5 t" O
  7.         SR04_GPIO_Init();' H  f; w8 r( [% X/ E
  8.         /* 串口相关配置 */! c! ~# ?3 O& b; M+ m8 U1 a6 z
  9.         GQ_UART_Config();
    7 Y% \' c! z" S! V
  10.         /* 打印调试信息 */
    6 i; A8 z) H( k8 W" @
  11.         printf("慢漫的测距实验\n ");
    5 _0 c; i8 K0 T. }. `
  12.         ( [6 C0 U  w& Z) A
  13.         2 h. j3 B. \/ f: _0 B
  14.         while( 1 )6 L& L7 _- Y) C+ r1 W4 z( s8 e' P
  15.         {4 {0 R. w: j; j
  16.                 /* 每0.5s测一次距离 */: Y* N; [8 ]+ [0 _
  17.                 Delay_ms( 500 );
    8 T1 Z: G5 }  ^4 w
  18.        
    ( F1 w) @' ], b/ s
  19.                 /* Trig给一个至少10us的高电平,超声波进行一次测距 */& h, y( i' f5 Y) _0 W$ O9 d' S- Q
  20.                 Trig_H;
    * v3 ^' H% e2 b' p# l& s$ k, ?
  21.                 Delay_us( 10 );+ c7 g. A: p( f  g' V6 u2 f
  22.                 Trig_L;
    2 g$ S$ @  e  f  F0 f, l+ ]6 s
  23.                 /* 等待Echo高电平 */$ q7 D9 y( W1 P, y
  24.                 while( Echo_Value != 1 );) a/ m; z2 W# Q! u9 {
  25.                 /* 打开中断,对Echo高电平时间计时 */4 g1 x7 v3 E7 Y+ s$ e
  26.                 /* 配置计数器的装载值是72*6=432,即一次中断6um,正好是超声波的0.1cm,所以中断次数n对应着n*0.1cm */
    ) X; _' M4 P: e4 m7 \7 D. P8 C
  27.                 /* SysTick_Config()中已经使能计数器了,所以无需再开启        */$ `* U! [, ]9 `( u: O
  28.                 SysTick_Config( 432 );
    & A: c$ B; x1 J3 s5 _
  29.                 /* 等待直到Echo为低电平 */5 v9 q( M0 }4 ?$ s6 h
  30.                 while(Echo_Value == 1);1 C. p' K, M2 R- g, G
  31.                 /* 关闭中断,通过参数n来取得距离参数 */
    1 w9 b: |/ ^+ N7 A
  32.                 /* 本来的使能位取反 */5 L9 k; ~0 N% Z0 Q/ }/ q/ G
  33.                 SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
    : x$ g) j7 _( T- j% h. s
  34.                 /* p、q分别是距离的整数部分和小数部分 */( d! Q! U% T: B2 r* G/ p$ j: A
  35.                 p=n/10;& C5 H( z( V1 G. T+ x+ O
  36.                 q=n%10;
    / S; C2 T" U8 s7 y# L" f! |
  37.                 /* 打印距离信息 */
    # V8 ?9 A: }% q1 ~' X! e  V
  38.                 /* p-50时经过调试的,因为测量的距离和诸多因素有关,这个操作减小了误差 */6 N( B& h( u6 k/ {1 D3 N" |$ I
  39.                 printf("第%d次测量为:%.0f.%dcm\n",i,p-50,q);# x3 H! m% ]' H
  40.                 i++;
    ( @9 I- m- L& W" K+ B
  41.                 /* 清零中断记录变量n */) V2 C# i2 }) ~7 e
  42.                 n=0;! V7 E  c7 H( a9 ?. o- _2 n* S# U2 ~
  43.         }- X/ L$ a8 }- @( u6 X- k
  44.        
    . D, `7 Q! _5 z/ a. o8 f
  45. }
    $ F/ K! D) p# F% E. E% a; M! \
复制代码

" ~$ c. t$ i5 s3 B4 V————————————————
3 I' J9 z; }5 Y6 i( c, e$ Y1 `版权声明:Aspirant-GQ
- f7 H! x; U5 t  ]& }  j: E5 o0 m$ \如有侵权请联系删除
8 u* e6 {+ {$ J$ d3 X% F
& H. ~( e$ c! n& v' v
赞 收藏 评论0 发布时间:2023-3-18 14:38

举报

0个回答

所属标签

相似技术帖

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版