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

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

[复制链接]
攻城狮Melo 发布时间:2023-3-18 14:38
超声波测距原理  }, `/ A9 t* T; C# t3 k5 H  u& o
利用HC-SR04超声波测距模块可以实现比较精确的直线测距,其测距原理图如下:
7 J- Y7 T4 @5 h$ `, Y0 r5 I! ?% G9 r. ]$ U( Z
20191123190005785.png " P" [# M/ u, x3 N3 d0 M; d9 x! z7 y1 K
9 R3 A1 m0 g5 u5 {1 t) A& \9 X* g1 J, k
HC-SR04的一端发出超声波,接触到反射物后反射,被另一个端口接收到,所以只要知道发射和接收的时间差,就可以根据声波传播的速率算出HC-SR04和反射物直接的距离。
' F1 n# Z, `0 h; r) O: o" b所以实现超声波测距就需要俩个条件:
7 C0 u$ d9 K: h, h发射和接收的时间差( E$ M/ }6 V5 b& V0 Q% m
超声波传输的速率% J" h( m1 h' N0 z7 X$ T1 F( N
HC-SR04工作原理  d! X. g: s- E( u2 U7 [
HC-SR04模块的电气参数如示:
0 G9 [& M2 r3 E/ @. A9 N% I9 W2 K$ U$ d' K: |/ d7 M) C$ x. j; ?- N+ @
2019112319045710.png
/ u. A5 l4 T+ C3 D2 e" g/ i+ l3 C

8 L6 I8 O) G9 c$ hHC-SR04模块的实物图如示:6 ]; q0 Y/ C# Z

0 E- m- f& V; b6 ~& {; H+ z
20191123190743986.png ! o3 Q5 a4 Q  a, g8 |

$ u; }# G8 @4 z/ F# t. Q% i有四个引脚:
( ], I) b& a8 [% e0 f8 xVcc:+5V电源供电" V$ x* l: \9 z
Trig:输入触发信号(可以触发测距)
5 L; e, ?; @/ }5 M" x( X; {Echo:传出信号回响(可以传回时间差)
: r/ F" g, E' z' w+ MGnd:接地% H( b- j& O9 f3 A5 V$ [
, \4 r8 E4 S+ v
用Trig和Echo引脚实现测距的流程:7 A/ {2 O9 A" @: V5 L
1.通过Trig输出一段至少10us的高电平(脉冲),触发一次测距,超声波在传输的过程中Echo一直输出高电平。- N1 {4 L  U( q  g  k; {6 A
2.在Trig脉冲输出后,立即检测Echo引脚的电平,测出Echo高电平持续的时间t,t就是超声波在所测距离一个来回所需时间。
* [" S5 K+ p8 h5 |( r; B/ _  y! |& C( a* g3 U' q( G
测距时序图如示:
  u# F' X& q! A% m- {
6 z5 l2 H4 u" {% {0 y+ h
20191123191907919.png 2 E8 g0 Z4 `5 a. O+ k- D' z
" }  K1 p, T: K# z+ B8 _6 ^
STM32实现驱动
8 O# b; [5 e' C2 x9 s4 c' I利用STM32驱动HC-SR04需要做好几个关键点:
2 {3 }6 R+ O9 h引脚的配置3 E8 V4 ~" ]2 W& H: d& n9 W0 Z
时序的控制
1 Y+ ]3 i; H$ l7 N  B( V4 F0 l6 c$ ?时间差的测量# y7 e9 N$ \+ x- J. [& Z
& p( m5 h5 u# M' U1 }
下面来分开实现几个关键点; T4 k! F# n7 Z6 q% x$ w7 l8 }
; U" w4 n+ Z% i" _7 D; c
1.引脚的配置9 v% L4 x$ O) M4 S; |
HC-SR04四个引脚,Vcc和Gnd直接接在开发板的电源上即可,主要是Trig和Echo引脚的配置,我选择了PB1连接Trig引脚、PB2连接Echo引脚。) ?; Y# m, v# ~0 \8 X3 n. B6 n
因为要控制Trig输出电平,所以PB1引脚模式是推挽输出GPIO_Mode_Out_PP6 t8 y: o6 Y: J& G  f
Echo要检测高电平持续的时间,所以PB2引脚模式是浮空输入GPIO_Mode_IN_FLOATING7 B0 `# d  r# l: X
相关的配置代码如下:) g, }; i. u6 u. L# {
  1. void SR04_GPIO_Init( void ). g) h8 g6 ?- A# j* j3 ~
  2. {2 u. r9 d. x! t. k: {" m4 X
  3.         GPIO_InitTypeDef GPIO_InitStruct;
    $ O4 M9 r& e8 h1 d, c
  4.         RCC_APB2PeriphClockCmd( Trig_Clock  |Echo_Clock , ENABLE );
    1 H' O+ U& t5 J. `0 v
  5.        
    + T. g* ?! t. A
  6.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    7 b' @  M% V/ _$ H5 a1 U" @0 D
  7.         GPIO_InitStruct.GPIO_Pin = Trig_PIN;
    ( g+ W7 U) [, x! c1 z
  8.         GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;/ P5 Q, }) T" i  y( N
  9.         GPIO_Init(Trig_PORT, &GPIO_InitStruct);
    2 \; |$ |% x; a
  10.         " Z1 j$ q" x( v! V3 I. d! T
  11.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;5 J+ Q" G* S9 `# z" q! @
  12.         GPIO_InitStruct.GPIO_Pin = Echo_PIN;8 U! L( g) D9 |
  13.         GPIO_Init(Echo_PORT, &GPIO_InitStruct);: s/ d6 ~# I# z, N; r# @! A7 T& F
  14. }
    9 e8 {- p9 u; y' m: E
复制代码
/ x% q# d* I9 n/ a9 S7 c% m1 |
2.时序控制" ~: _6 ^2 ^  O# W$ A( d7 Y
HC-SR04的时序是:先来一段10us的Trig高电平,接着接收一段Echo的高电平,伪代码如下:
2 H* B4 j: _9 m2 }) X, W
  1. #define Trig_H  GPIO_SetBits(GPIOB, GPIO_Pin_1)
    ; ?* [( w& j( m! j
  2. #define Trig_L  GPIO_ResetBits(GPIOB, GPIO_Pin_1)- a: Y" o3 A: j: b8 ^  Q& A
  3. , X/ q, \4 Z3 C7 V0 [; A# l
  4. /* Trig给一个至少10us的高电平,超声波进行一次测距 */5 J/ V4 S. J* V, P8 D' u8 f
  5.         Trig_H;
    8 }3 _8 K  j, Q6 _, k
  6.         Delay_us( 10 );
    0 U0 f2 d+ X. Y, {# m1 m) _
  7.         Trig_L;% t  g* F: r, {, q( q
  8. /* 等待Echo高电平 */
    6 ^7 S+ N* A% y' t
复制代码
' \" M) q/ w# U) K6 M: [' P
3.时间差测量
- F( l4 Y( x" h! u2 @这个是最重要的一步,要测量Echo高电平持续的时间,因为光传播的速率是340m/s,而测距的范围大多是cm级别,所以相应Echo高电平持续的时间也就是us级别的。
& ?# E, n( n/ o) B/ f所以,测量时间差的条件就比较苛刻,我是利用SysTick(系统计数器)的原理实现计时的。SysTick计数器原理是对通过SysTick_Config()函数配置每俩次中断之间的节拍数,也就是俩次中断之间的机器周期,我大概算出了,测出0.1cm距离的Echo高电平时间约为6um,而系统时钟的频率是72MHz,所以配置每俩次中断之间的节拍为432的时候,进入一次中断就代表0.1cm的距离,所以只需要记录进入中断的次数就可以算出距离。通过一个全局变量在中断函数中自增来记录中断次数。SysTick_Config函数源代码如下:0 Z3 x+ @; U+ V& Y: F0 F( F9 a& T
  1. static __INLINE uint32_t SysTick_Config(uint32_t ticks)
    & G5 `. D& R6 A+ Y
  2. { 4 y" f$ d# @; `5 ?! f, ^
  3.         /* 判断ticks 是否超出装填值和重装值的最大值 */+ w8 \& s" E! A9 ~+ Y. l1 W+ k- W
  4.   if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            . I+ E+ ?" R% ]
  5.   4 F+ g% q3 n% B+ R0 D& T' h/ x1 E
  6.   /* 配置 装载寄存器 */       
    1 \% y2 C2 g/ \' O2 y  d  j# Q" L2 k/ \
  7.   SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;     
    * }3 ^+ s  u4 n: @$ T- K
  8.         /* 配置 内核中断的优先级,也是在NVIC中 */( X( ~5 @; @6 Z" g, w" {
  9.   NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); , v  ?/ }; @8 w) G/ Y2 J, D4 h
  10.         /* 加载计数器的值 */
    0 T: w; e) E8 B& ~2 P
  11.         /* SysTick->VAL是当前数值寄存器的值 */
    9 H5 n- E: s/ C! A# Y4 @$ V
  12.   SysTick->VAL   = 0;      
    / G  _" z/ A- q) d9 X

  13. - @6 a7 K# [. \0 o% z: N! y
  14.         /* CTRL是SysTick控制及状态寄存器:
    6 Q$ _: b* f+ h& C
  15.                 CLKSOURCE:位段2 时钟源选择,0=APB/8;1=APB  APB即72MHz* L. ?5 `5 e! w5 _5 W
  16.           TICKINT:   位段1 当置为1时,计数器递减到0时会产生中断请求;当置为0时无动作
    3 z5 |% T, Z2 J' \& Z; o
  17.           ENABLE:   位段0 使能位,可以启动SysTick定时器*/3 Z. w! e: ~6 a2 w. r+ p& |+ c& i. t. N
  18.         SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | " A, h- R+ i& }7 K# N9 o9 t& e
  19.                    SysTick_CTRL_TICKINT_Msk   |
    2 Z/ ]/ e; g+ g. {( ?
  20.                    SysTick_CTRL_ENABLE_Msk;                  1 L. x8 T2 G1 j% x+ j1 ]
  21.   return (0);                                              " W7 z6 @+ c  f+ \  T/ f
  22. }
    ! j: w4 s+ V' _# c; w6 e& g6 k
  23. / z; n$ W- k1 C. k
复制代码
( T1 E* i0 @2 T! }: }! L! \; K3 m
SysTick的具体原理可以参考一下我之前的博客:SysTick原理4 A: \4 g3 e) n( L  N

) o8 ^/ z6 a7 _0 E  U注意:SysTick_Config()函数执行完就开启了中断,所以必须在Echo为低电平后及时关闭中断,并且将记录中断的变量清零。
% q( M+ a  C+ s; S. Q+ V) |
* J6 @0 Q) L! v7 H
中断函数如示:
1 S3 Q* e- D) {9 u
  1. /* 用extern和volatile关键字修饰的 全局变量n */. Y& b: _4 e! V" ]5 F
  2. extern volatile uint32_t n;$ o2 `4 i7 z2 B9 g

  3. 4 `( t# Q$ W. H6 b
  4. void SysTick_Handler(void)
    4 B# ?3 F7 y; U. o2 M; f2 `, H
  5. {
    : ~/ \* h  b$ C; A
  6.         n++;
    / [) C/ a5 A: C/ b$ k' [8 _
  7. }
    * L! U0 g1 N. r, Y7 z, U
复制代码
: c0 p4 c9 P3 E  s( D0 |; c
关闭中断及清零n的代码如下:; B* j6 _. g5 S% q) _2 u
  1. /* 本来的使能位取反 */
      N' M7 d0 @! l3 V, B6 S0 [# V, H6 V
  2. SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;5 [: k) O" D) _8 ^$ j1 \3 R
复制代码
* k' z$ `# [* ]. s6 q/ t  L# D& p% @
SysTick->CTRL寄存器的0位控制着中断的使能,具体情况在之前SysTick的博客中已做详细说明。; H* S4 m; J7 ]! `
/ m5 f+ Z- N& v# L
4.如何将距离测出来& o* F/ ]" s; B6 s9 z
我在main函数中实现了距离的测量,并且通过串口打印函数将距离传到上位机,具体代码如示:
. [. W5 R* |( m5 I' `# y* h6 h" ~
  1. int main(void)
    / j8 [% W! U, v8 e4 d7 C' F
  2. {; e- X! |$ b4 U1 n% p
  3. 1 f/ U& O- \1 V
  4.         int i=1,q;
    6 \# {: Z! A7 q! U+ r
  5.         float p;
    . f8 i3 ~/ K% D# K5 `" }# J
  6.         /* HC-SR04模块引脚初始化 */) W1 q* l$ p/ F/ b5 z8 q& S, Q
  7.         SR04_GPIO_Init();( [) H6 C* G) p1 y2 g: X2 U7 g
  8.         /* 串口相关配置 */4 G8 e- d6 M/ S' q3 p
  9.         GQ_UART_Config();
    4 ^0 Q; w- X2 k- M' N8 j
  10.         /* 打印调试信息 */7 L- U7 J1 P4 ]  |! L
  11.         printf("慢漫的测距实验\n ");5 A6 Y5 Y# }7 y: \
  12.         / J3 L/ k# Q  W/ R3 b, g9 u# F
  13.        
    0 Q' R/ N8 a, q. _% Q# C
  14.         while( 1 )
    ) k6 {1 [* Y5 \7 L2 O1 @- d
  15.         {' S$ Z( E3 i1 _, x0 |' b  ]2 z
  16.                 /* 每0.5s测一次距离 */
    " r) `" O( \4 r$ _4 D4 _: U
  17.                 Delay_ms( 500 );5 `7 T. Q. Q* x$ ?
  18.         . M2 y3 i* O5 A: c5 g* H
  19.                 /* Trig给一个至少10us的高电平,超声波进行一次测距 */# k7 p/ d0 `2 C* a$ Z+ K7 A  T/ v
  20.                 Trig_H;
    - Y0 ]) {7 g  a# I: {, ~
  21.                 Delay_us( 10 );8 s$ \# j5 C' g
  22.                 Trig_L;
    ; W0 b* Q4 ?% Q: c4 a- S
  23.                 /* 等待Echo高电平 */
    ) i3 u, K; O. N5 x. j- t: I/ F' w
  24.                 while( Echo_Value != 1 );- z: v2 F4 B) j# l& {: n( g, I
  25.                 /* 打开中断,对Echo高电平时间计时 */4 r6 ?5 B* V; u& X4 O1 h
  26.                 /* 配置计数器的装载值是72*6=432,即一次中断6um,正好是超声波的0.1cm,所以中断次数n对应着n*0.1cm */- ^/ y6 y& q, T* C
  27.                 /* SysTick_Config()中已经使能计数器了,所以无需再开启        */
    : n4 l2 N5 z3 j/ B! ^
  28.                 SysTick_Config( 432 );
    ( X' D5 N! A  [  C" I2 e
  29.                 /* 等待直到Echo为低电平 */1 ^5 C# c) K* E* b" i% K
  30.                 while(Echo_Value == 1);) K  M' S) K) ^9 Z1 E) r3 n0 ?
  31.                 /* 关闭中断,通过参数n来取得距离参数 */6 Q' z5 T! S4 m
  32.                 /* 本来的使能位取反 */6 E; n! B0 D2 J; n8 D$ H) e* [$ `
  33.                 SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
    / q- M. h  C9 |0 n% i9 B; A
  34.                 /* p、q分别是距离的整数部分和小数部分 */
    % B9 m* t1 T  G/ T
  35.                 p=n/10;" t5 K' E* v% i, f5 b0 h2 I- E( X
  36.                 q=n%10;+ r+ j8 D$ e  @' P
  37.                 /* 打印距离信息 */
    ' t; R/ v* w8 k3 a! Y
  38.                 /* p-50时经过调试的,因为测量的距离和诸多因素有关,这个操作减小了误差 */
      H; Q. O. u( o' G- {. r
  39.                 printf("第%d次测量为:%.0f.%dcm\n",i,p-50,q);+ |" _; @" |0 w- n
  40.                 i++;
    / g+ v& t1 B2 {' B: u6 `8 {
  41.                 /* 清零中断记录变量n */% t) U% K0 Q6 q& f- w8 U
  42.                 n=0;
    " ~8 y& C1 k4 C
  43.         }& Y/ t. ]- U6 Y6 F
  44.         ! h8 S# ?' N* Q+ N4 T
  45. }
    5 T1 A* a  ]8 `* a' d) Y1 Z/ B8 D
复制代码

% Z, o% O* S% W3 |0 G————————————————
0 G2 P! }9 u: Q: |# S版权声明:Aspirant-GQ
8 C/ T- u8 M( {/ N4 T如有侵权请联系删除; Z. C4 A9 W9 H1 x6 T4 R) ^0 ^5 j% m

, o/ p, b7 F/ k
收藏 评论0 发布时间:2023-3-18 14:38

举报

0个回答

所属标签

相似分享

官网相关资源

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版