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

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

[复制链接]
攻城狮Melo 发布时间:2023-3-18 14:38
超声波测距原理+ I3 p; t# v- q& y( r0 _6 O
利用HC-SR04超声波测距模块可以实现比较精确的直线测距,其测距原理图如下:! n+ g1 H8 C+ J( D

/ g5 T( l: p% o$ R
20191123190005785.png : \% |. y! F6 h

8 H5 K( I9 w4 QHC-SR04的一端发出超声波,接触到反射物后反射,被另一个端口接收到,所以只要知道发射和接收的时间差,就可以根据声波传播的速率算出HC-SR04和反射物直接的距离。
# G* Z6 Y( b; m) R所以实现超声波测距就需要俩个条件:
5 A5 ]) E5 c+ J# o2 k* u发射和接收的时间差
- i! w  C1 ~1 [- D( g超声波传输的速率7 ~* Z# y+ B1 d0 G$ |: j
HC-SR04工作原理; h* b3 [5 P0 b
HC-SR04模块的电气参数如示:
  k, x3 R3 C9 L. D
; T  F. \  n. |2 x% M4 j
2019112319045710.png / P- f* @( x# y% O8 `  O+ Z
0 A9 U& d* B* ?+ i, R" h
HC-SR04模块的实物图如示:0 d8 `2 u9 b/ e' c1 N" p! Q: y/ I
0 y' Q7 y! n* A$ ]2 S# P
20191123190743986.png
4 x8 M, D- _) W) ]6 O

6 O6 x( T* Q2 ^; b6 g有四个引脚:
& a3 b% M2 T+ R; W/ T* [Vcc:+5V电源供电
/ B' M  b2 Q+ D+ cTrig:输入触发信号(可以触发测距)
0 G+ Q3 x, x, o! TEcho:传出信号回响(可以传回时间差)/ j1 d3 g8 i$ n. l/ A9 g3 j! q, a
Gnd:接地/ U* O9 `0 o. P, _6 E& `

& ]" K1 N! p9 h2 q: e+ v# N
用Trig和Echo引脚实现测距的流程:4 K! u: h+ J9 I! p! P- B
1.通过Trig输出一段至少10us的高电平(脉冲),触发一次测距,超声波在传输的过程中Echo一直输出高电平。
8 A0 [) ^2 c* T. M2.在Trig脉冲输出后,立即检测Echo引脚的电平,测出Echo高电平持续的时间t,t就是超声波在所测距离一个来回所需时间。
9 I1 g0 I2 B4 ?% q; Y. K' ]# L* O
* _: Y( v& e5 i  h
测距时序图如示:, j! z- w1 o, w/ d* n( O9 K
3 y. J, s; I, p& h) J
20191123191907919.png 5 m" t% {! }' P/ o+ \; t

9 j7 z2 j4 Z2 `1 }0 B" a7 mSTM32实现驱动
& w# m7 ?4 n* V# Y( h  i利用STM32驱动HC-SR04需要做好几个关键点:- ^7 j0 ^7 S; P( g/ _7 E
引脚的配置
' i. Q$ h4 v; E  d7 t2 }$ U! A! h时序的控制  _  i; ~: L2 _  }$ I- r. q
时间差的测量: V$ `  H; Q+ F0 M+ \. E

" g8 R- a' O/ e% v
下面来分开实现几个关键点/ Y- h9 K  h4 j+ E6 k# U& y0 u8 |

$ {8 l' u) h8 E" Z' Z1.引脚的配置
% V! N2 p  S& R$ L6 s- R! U+ O! @HC-SR04四个引脚,Vcc和Gnd直接接在开发板的电源上即可,主要是Trig和Echo引脚的配置,我选择了PB1连接Trig引脚、PB2连接Echo引脚。
9 r  `. ~  v6 v7 L5 b) T因为要控制Trig输出电平,所以PB1引脚模式是推挽输出GPIO_Mode_Out_PP8 @  w5 V' w5 [9 b
Echo要检测高电平持续的时间,所以PB2引脚模式是浮空输入GPIO_Mode_IN_FLOATING( O7 c! O" V1 r2 q
相关的配置代码如下:) y/ u% Z6 X) r0 M  |& F% E
  1. void SR04_GPIO_Init( void )
    " M: E6 d3 h- h8 g" o1 m0 q
  2. {
    / {4 v$ l" h. i" Y" t, h* I- m
  3.         GPIO_InitTypeDef GPIO_InitStruct;6 c& t/ @' o5 k
  4.         RCC_APB2PeriphClockCmd( Trig_Clock  |Echo_Clock , ENABLE );
    " f, s$ L$ _% G1 l
  5.        
    - [- Q$ |: L( G) k) }# q$ O9 M5 |
  6.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;6 n" P- M8 s: }' w* `
  7.         GPIO_InitStruct.GPIO_Pin = Trig_PIN;
    , ?3 @2 k; l( V: L: C$ j6 c7 `
  8.         GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
    ; r3 t4 B' F$ V1 S( m! R  t* u
  9.         GPIO_Init(Trig_PORT, &GPIO_InitStruct);
    2 s; G% Y5 L( M( k! y" E9 E2 c% D
  10.        
    ) i1 `+ |# z0 n: j( i
  11.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    # t% H5 C  \. F3 a2 M- k* U5 W
  12.         GPIO_InitStruct.GPIO_Pin = Echo_PIN;
    ' V, B1 a8 q7 x6 h4 d+ F; m$ t
  13.         GPIO_Init(Echo_PORT, &GPIO_InitStruct);
    $ E* R* l( X$ L, a- O5 B1 o) z' _
  14. }
    . Y" w* R% t9 a
复制代码
& t7 w: r6 ?+ F3 r
2.时序控制
7 [- E- M4 V1 i3 j+ _. @; xHC-SR04的时序是:先来一段10us的Trig高电平,接着接收一段Echo的高电平,伪代码如下:( C' f; i& `' h8 S. n6 u
  1. #define Trig_H  GPIO_SetBits(GPIOB, GPIO_Pin_1)+ s) i; I8 j9 h. K9 e  w
  2. #define Trig_L  GPIO_ResetBits(GPIOB, GPIO_Pin_1)' q" M* ]2 u* m' a0 {

  3. 7 ~9 J8 U7 T. ^( b/ L1 @5 E
  4. /* Trig给一个至少10us的高电平,超声波进行一次测距 */7 t  Y. O- P7 V6 ~; {
  5.         Trig_H;
    : f% P0 E% N/ m0 {
  6.         Delay_us( 10 );
    % I: C8 p' h  m6 d
  7.         Trig_L;
    1 _. F; T' U+ I' {
  8. /* 等待Echo高电平 */
    % O0 h2 J; q9 t" i
复制代码

* \  x& w% D& ?" Y3.时间差测量
8 W8 \7 n5 \) D  f这个是最重要的一步,要测量Echo高电平持续的时间,因为光传播的速率是340m/s,而测距的范围大多是cm级别,所以相应Echo高电平持续的时间也就是us级别的。
: e- p, }1 V' n2 h4 R( t4 X所以,测量时间差的条件就比较苛刻,我是利用SysTick(系统计数器)的原理实现计时的。SysTick计数器原理是对通过SysTick_Config()函数配置每俩次中断之间的节拍数,也就是俩次中断之间的机器周期,我大概算出了,测出0.1cm距离的Echo高电平时间约为6um,而系统时钟的频率是72MHz,所以配置每俩次中断之间的节拍为432的时候,进入一次中断就代表0.1cm的距离,所以只需要记录进入中断的次数就可以算出距离。通过一个全局变量在中断函数中自增来记录中断次数。SysTick_Config函数源代码如下:
' v7 V; b% ^' B3 d* }
  1. static __INLINE uint32_t SysTick_Config(uint32_t ticks)+ L; t6 W, W! F5 J5 n  R* w
  2. { 7 ^/ c1 v. e4 J" \( J
  3.         /* 判断ticks 是否超出装填值和重装值的最大值 */; V* D0 g+ B( y2 d* q1 b1 x
  4.   if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            
    5 |* S* w9 f( ]+ {  d
  5.   2 W4 j5 x$ \. A# b! |
  6.   /* 配置 装载寄存器 */        0 o3 O$ ^) L; m( ~6 ?: c9 O; A/ R9 |6 `
  7.   SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;     
    ) }3 |) E3 C8 s; h" K
  8.         /* 配置 内核中断的优先级,也是在NVIC中 */
      d& A& Q2 ~+ d% `: S" s4 l2 v6 t
  9.   NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1); " ^/ X! e2 T/ B/ j  i2 I) d9 B
  10.         /* 加载计数器的值 */
    5 G3 D: ~$ [$ i; C% \2 O$ h
  11.         /* SysTick->VAL是当前数值寄存器的值 */. o- \, I7 X0 L+ m% J) |: w# h
  12.   SysTick->VAL   = 0;      
    2 }5 S( k4 }: p5 ?, [8 O7 X
  13. 4 }+ u/ B9 ]3 R. |2 n8 o2 P
  14.         /* CTRL是SysTick控制及状态寄存器:
    - ]/ z: X( m2 Y, z
  15.                 CLKSOURCE:位段2 时钟源选择,0=APB/8;1=APB  APB即72MHz  ]0 u, O  y- S3 y2 C
  16.           TICKINT:   位段1 当置为1时,计数器递减到0时会产生中断请求;当置为0时无动作9 s, B* F8 g4 s0 j
  17.           ENABLE:   位段0 使能位,可以启动SysTick定时器*/. n  Z0 j1 y5 L
  18.         SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk |
      |0 O8 b- P- N$ t
  19.                    SysTick_CTRL_TICKINT_Msk   | # P% m4 M, ^3 c1 \2 v  |
  20.                    SysTick_CTRL_ENABLE_Msk;                  . J9 d' b+ _! b* U  d! @9 y  x
  21.   return (0);                                             
    1 J) G6 q; d) o5 a2 I2 f* m6 f' _
  22. }3 ^% @: c0 o5 I6 Q8 r* k  T3 k

  23.   D9 P2 `- w$ S# R, {' U
复制代码

1 M( F* l; V9 C2 A1 ySysTick的具体原理可以参考一下我之前的博客:SysTick原理& [. U- ~* ^2 D. E
8 G+ s( Q! Q+ a2 e, L
注意:SysTick_Config()函数执行完就开启了中断,所以必须在Echo为低电平后及时关闭中断,并且将记录中断的变量清零。
. B$ U: K1 l( ^( `6 s# B$ L! T# l
  f- k% N; L7 K2 n) i
中断函数如示:" r* i1 D( l' k5 |, k- P
  1. /* 用extern和volatile关键字修饰的 全局变量n */" l, h( S" t- _$ A. j: p( v  s
  2. extern volatile uint32_t n;
    ; H7 m' j. Z/ k  ]
  3. 7 [$ B8 n3 {* q/ |- S* R$ f$ R
  4. void SysTick_Handler(void): Q3 f) E2 \9 n4 c/ @7 Y
  5. {) o/ o7 h. K# N0 S! v  ^" I
  6.         n++;( V& ~" W' j% L4 a4 ~
  7. }) e+ D8 b0 h1 B, {1 f1 Z
复制代码

. z+ _6 T& w0 [; ~+ X) l/ s关闭中断及清零n的代码如下:
) Z# \( a' r0 R2 [& Z
  1. /* 本来的使能位取反 */
    5 U' \+ I6 z1 z( p
  2. SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;6 f0 {- Y! U3 [# S% `! D9 X! S
复制代码
+ M" K4 i1 H4 ]; u& T) e) ^1 I, E
SysTick->CTRL寄存器的0位控制着中断的使能,具体情况在之前SysTick的博客中已做详细说明。
( E6 S. M) k: N3 y
% G' t4 A' z7 ~6 ?; X7 R) o' i  i  U4.如何将距离测出来( W3 q4 x9 m, g' Y; U+ i# ]: X
我在main函数中实现了距离的测量,并且通过串口打印函数将距离传到上位机,具体代码如示:! g1 t. Y. R: j  D5 _
  1. int main(void)9 [3 w" W& v# a
  2. {
    5 H" _% }, I2 ]" e

  3. . Y# J7 N) ^8 S- h) n0 x% T; P) d
  4.         int i=1,q;3 d+ J% o" t* p( p& J
  5.         float p;
    2 h7 e9 g2 Y0 |( y5 }2 z1 E
  6.         /* HC-SR04模块引脚初始化 */5 W  q1 P9 @! g
  7.         SR04_GPIO_Init();
    ' L8 s1 K4 ^6 o7 D1 C
  8.         /* 串口相关配置 */: Z- O. a4 N# S
  9.         GQ_UART_Config();
    3 r2 ?" y; @* b/ l, ^& a
  10.         /* 打印调试信息 */' b% }0 [  N0 V% C' P
  11.         printf("慢漫的测距实验\n ");
    / {* \' u' f' A/ @: r) [$ E
  12.         3 t! v0 k( x2 t3 N; [5 V
  13.         ' R' J. e% L9 }+ l. e& F) ~
  14.         while( 1 )$ ^7 P( C8 d- I* i! \1 W9 A* j0 g
  15.         {
    7 s. g8 E) p8 r6 @8 h9 o& g
  16.                 /* 每0.5s测一次距离 */
    ! ]0 B% |4 |- t5 V
  17.                 Delay_ms( 500 );
    6 J' i; o( _$ W; o
  18.        
    # \, h6 s/ ^) m) Y
  19.                 /* Trig给一个至少10us的高电平,超声波进行一次测距 */
    , r' Z/ [( y8 `
  20.                 Trig_H;. J9 Z3 T5 G2 l, r
  21.                 Delay_us( 10 );; [  p  F1 F  G+ @" V' T
  22.                 Trig_L;5 ~/ u7 J1 D+ B3 W: a6 Z1 o: a9 C
  23.                 /* 等待Echo高电平 */* t2 l6 W5 {/ ]5 ^) k, c. S) I/ g: ]
  24.                 while( Echo_Value != 1 );
    * r+ K7 M( \/ T% F
  25.                 /* 打开中断,对Echo高电平时间计时 */. t' D+ x6 v4 L6 X
  26.                 /* 配置计数器的装载值是72*6=432,即一次中断6um,正好是超声波的0.1cm,所以中断次数n对应着n*0.1cm */
    0 z( m" J! B* {6 \% T0 Y
  27.                 /* SysTick_Config()中已经使能计数器了,所以无需再开启        */
    # I* B1 O7 U6 ]6 L+ I
  28.                 SysTick_Config( 432 );
    6 g0 L5 E, R& s' [1 `, {3 B$ _1 H1 C
  29.                 /* 等待直到Echo为低电平 */  g# t) t2 @- |9 I# u! i, W
  30.                 while(Echo_Value == 1);+ z3 s- r- w) [7 L- C, c6 v
  31.                 /* 关闭中断,通过参数n来取得距离参数 */! }9 \+ Q% V5 F" a; |
  32.                 /* 本来的使能位取反 */4 [2 g2 x: c% p, O& e3 M4 q
  33.                 SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;3 @  C/ }5 z9 a) v! p3 b
  34.                 /* p、q分别是距离的整数部分和小数部分 */) u' P( Z# j6 j; a3 g, K
  35.                 p=n/10;; }5 F  _! O. r
  36.                 q=n%10;
    , F/ s6 C4 ^6 r
  37.                 /* 打印距离信息 */
    ' z; T! N! O0 q4 |2 T7 p
  38.                 /* p-50时经过调试的,因为测量的距离和诸多因素有关,这个操作减小了误差 */
    * {$ q1 g% ^4 g7 V0 S# i- y+ Q
  39.                 printf("第%d次测量为:%.0f.%dcm\n",i,p-50,q);  G7 @5 M# v) n9 p
  40.                 i++;6 A5 D3 U( s( L3 @. l( P* M
  41.                 /* 清零中断记录变量n */7 y8 \* s2 {: i& p2 l. R
  42.                 n=0;+ y+ ~. B: ^( ~8 `
  43.         }+ C5 s2 }% s" C! W
  44.         9 h; V9 @8 d# Q6 h3 |& D$ ]
  45. }& S' d% m5 v- e  p
复制代码

2 T# `; S- B% R/ R% a————————————————- ]1 q6 y8 v! w$ s/ ~3 i
版权声明:Aspirant-GQ
7 M' s) x* B+ z# R0 O1 b. f% e如有侵权请联系删除; F# N6 ]5 ^. e" x! u/ Q

5 v* f3 i1 }: ]/ a$ _; A9 O
收藏 评论0 发布时间:2023-3-18 14:38

举报

0个回答

所属标签

相似分享

官网相关资源

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