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

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

[复制链接]
攻城狮Melo 发布时间:2023-3-18 14:38
超声波测距原理' r3 R, ^! J& T0 H5 F
利用HC-SR04超声波测距模块可以实现比较精确的直线测距,其测距原理图如下:* ~! u. Y' j) R, D1 Z9 H% R# S% k" Q: q
. j6 V! Q( `! d' z
20191123190005785.png " G% G$ z" P7 Q# A5 w' s

. \% I+ ~) v4 G! i  w: Z( pHC-SR04的一端发出超声波,接触到反射物后反射,被另一个端口接收到,所以只要知道发射和接收的时间差,就可以根据声波传播的速率算出HC-SR04和反射物直接的距离。
( l9 y* `4 Q4 X2 d+ R所以实现超声波测距就需要俩个条件:
! v3 h8 u' \2 u6 z9 i. q2 Y& Z- S% B发射和接收的时间差, Y$ K3 F, q. z  G
超声波传输的速率
# M# B7 Z% Y. e
HC-SR04工作原理
) Z4 o& U% Y$ hHC-SR04模块的电气参数如示:
6 k8 H, r5 C+ v$ E( k: z( u3 h  c, d. b( @. S. h
2019112319045710.png
7 Y1 U% f3 G( w& T+ N/ a
- _1 L3 `9 A! m/ e& n) K
HC-SR04模块的实物图如示:
1 v# S9 A3 C3 h8 ~. p
: Q8 J# a# @; |7 ^1 `4 v
20191123190743986.png
. N* b' ~( T% {2 r. v$ ?
& e/ D: P+ Z8 w: |  `$ M
有四个引脚:
( u% T& N/ q& P2 T$ N$ y! C9 K7 G: }; pVcc:+5V电源供电
* T2 A3 ?+ M6 H+ T. Y  U+ \% RTrig:输入触发信号(可以触发测距)
  U. E6 t% Y9 }$ h1 y5 |Echo:传出信号回响(可以传回时间差)
, u+ n% G1 U2 k0 _Gnd:接地
, T8 u% T0 \$ W  J  I1 {
7 A, V, e+ c8 C% U+ Q, k1 X
用Trig和Echo引脚实现测距的流程:
: M& Y- z0 V. B) ~: S1.通过Trig输出一段至少10us的高电平(脉冲),触发一次测距,超声波在传输的过程中Echo一直输出高电平。+ I, n8 r; L$ E7 |. i! H7 M
2.在Trig脉冲输出后,立即检测Echo引脚的电平,测出Echo高电平持续的时间t,t就是超声波在所测距离一个来回所需时间。  q' f; ~0 Q- Z0 _- m8 v4 C
: X4 w0 h2 C* ?
测距时序图如示:) M% y7 @- k4 C" K# }& p0 c
. r# n& {+ Y% n5 j
20191123191907919.png 9 i7 ~3 [' @% v  g/ ?, L' [2 N

  G2 i: a: [- V& dSTM32实现驱动2 _  [( ?$ k) s* g& ~) X' `6 P  V
利用STM32驱动HC-SR04需要做好几个关键点:
% C- x4 h0 h7 T+ i7 ^引脚的配置- a% m$ S4 q: ]6 i- Z
时序的控制
7 H* O* s% l. d( H时间差的测量3 _4 A7 \& h8 C2 S

3 k4 ]. e5 X; F7 V
下面来分开实现几个关键点
' k! |: e' Q% k/ u9 W7 S0 ?5 ]- e1 ]* t# Z# S( M. R
1.引脚的配置
3 o4 X# Z5 A  s* V+ A8 VHC-SR04四个引脚,Vcc和Gnd直接接在开发板的电源上即可,主要是Trig和Echo引脚的配置,我选择了PB1连接Trig引脚、PB2连接Echo引脚。0 U" L/ _. K" T! c4 m& _$ m5 U
因为要控制Trig输出电平,所以PB1引脚模式是推挽输出GPIO_Mode_Out_PP
. i' v% t, A! r0 l& q3 h: ]6 ]1 iEcho要检测高电平持续的时间,所以PB2引脚模式是浮空输入GPIO_Mode_IN_FLOATING( B4 A  t, v2 ]! {( B
相关的配置代码如下:
; [5 ^% r+ @0 L
  1. void SR04_GPIO_Init( void )
    , u( o, F* F4 q! f8 z4 e. O
  2. {
    # O# e( T) H0 V  M
  3.         GPIO_InitTypeDef GPIO_InitStruct;* Z! z. V( h% c. T
  4.         RCC_APB2PeriphClockCmd( Trig_Clock  |Echo_Clock , ENABLE );" }' q* n0 q4 O. @2 _# i9 h
  5.        
    + W2 s% {3 L( [6 _& u8 K! {
  6.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_Out_PP;
    8 m1 e9 h3 S/ I) ]% r, [
  7.         GPIO_InitStruct.GPIO_Pin = Trig_PIN;- B6 D( j+ m( D/ k' g+ J& l3 T
  8.         GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;9 f2 |2 v9 E4 _/ r; \) U
  9.         GPIO_Init(Trig_PORT, &GPIO_InitStruct);
    * k0 [( F% G" w! [. V1 I4 x
  10.         0 Q1 n. _: P6 k% O8 h
  11.         GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    $ C0 p4 z  \9 |, g0 W2 M
  12.         GPIO_InitStruct.GPIO_Pin = Echo_PIN;8 r) e0 L9 h2 j9 |
  13.         GPIO_Init(Echo_PORT, &GPIO_InitStruct);" F5 Q! Q# U/ G! j) F" `  I$ l
  14. }6 F" c/ l; J6 l# D' E
复制代码
; l3 X7 o/ Z1 s2 o) A# u
2.时序控制  P3 M( c9 O* n3 M& q$ _" ]& x
HC-SR04的时序是:先来一段10us的Trig高电平,接着接收一段Echo的高电平,伪代码如下:+ G9 N: M: T1 s. ~
  1. #define Trig_H  GPIO_SetBits(GPIOB, GPIO_Pin_1)
    # ]7 m% e  N# B, d! [; E
  2. #define Trig_L  GPIO_ResetBits(GPIOB, GPIO_Pin_1)
    $ N: K* E+ _+ }% ]
  3. & a3 c$ X3 {$ k: q
  4. /* Trig给一个至少10us的高电平,超声波进行一次测距 */' D% ?4 a; e- ?. |/ F3 S
  5.         Trig_H;
    2 b+ o2 ~2 m8 c# T) N# J9 b
  6.         Delay_us( 10 );
    3 f. v6 ?1 l$ u+ l' ]
  7.         Trig_L;' J" P6 r" d* W  V. h! a
  8. /* 等待Echo高电平 */5 ]! h0 p- C: m- v  b6 ?) Z9 A
复制代码

* F2 N2 k* P) e7 P3.时间差测量
+ z0 w' s- F, l这个是最重要的一步,要测量Echo高电平持续的时间,因为光传播的速率是340m/s,而测距的范围大多是cm级别,所以相应Echo高电平持续的时间也就是us级别的。
! v1 y7 I, Y7 P6 T所以,测量时间差的条件就比较苛刻,我是利用SysTick(系统计数器)的原理实现计时的。SysTick计数器原理是对通过SysTick_Config()函数配置每俩次中断之间的节拍数,也就是俩次中断之间的机器周期,我大概算出了,测出0.1cm距离的Echo高电平时间约为6um,而系统时钟的频率是72MHz,所以配置每俩次中断之间的节拍为432的时候,进入一次中断就代表0.1cm的距离,所以只需要记录进入中断的次数就可以算出距离。通过一个全局变量在中断函数中自增来记录中断次数。SysTick_Config函数源代码如下:* f0 H4 Q* h( D/ ^) b; ^: s' d
  1. static __INLINE uint32_t SysTick_Config(uint32_t ticks)
    9 j' X0 p4 N9 J% R9 Z) W
  2. { / P1 R0 @1 ]+ a7 J- }, T7 L  C. \
  3.         /* 判断ticks 是否超出装填值和重装值的最大值 */
    9 ^5 t5 a  Z/ I
  4.   if (ticks > SysTick_LOAD_RELOAD_Msk)  return (1);            % c6 P1 k; P1 P/ t- Y
  5.   9 g0 N4 r7 @* t- [( o
  6.   /* 配置 装载寄存器 */        3 s+ q8 p/ }3 T
  7.   SysTick->LOAD  = (ticks & SysTick_LOAD_RELOAD_Msk) - 1;     ; U5 H$ R$ u* d# x& D
  8.         /* 配置 内核中断的优先级,也是在NVIC中 */  Z' h. x1 }  ?4 s
  9.   NVIC_SetPriority (SysTick_IRQn, (1<<__NVIC_PRIO_BITS) - 1);
    % F0 {$ B5 O0 ]7 ~1 Z, y
  10.         /* 加载计数器的值 */9 A4 e2 \" s9 W, f
  11.         /* SysTick->VAL是当前数值寄存器的值 */
    % v1 Q# t% k. I: E
  12.   SysTick->VAL   = 0;      
    ( {$ J2 j" {1 s

  13. 1 g/ Y, _& J/ m- s
  14.         /* CTRL是SysTick控制及状态寄存器:
    ! s. I8 a6 r1 i- [8 I: [
  15.                 CLKSOURCE:位段2 时钟源选择,0=APB/8;1=APB  APB即72MHz
    ) C' A% Q% B/ L+ N
  16.           TICKINT:   位段1 当置为1时,计数器递减到0时会产生中断请求;当置为0时无动作7 Z- E1 `! p1 q, }5 p
  17.           ENABLE:   位段0 使能位,可以启动SysTick定时器*/  J. o# b2 y! Z& Y' F" Q5 H
  18.         SysTick->CTRL  = SysTick_CTRL_CLKSOURCE_Msk | ; v# v6 u7 b  I  w4 V, S' I
  19.                    SysTick_CTRL_TICKINT_Msk   |
    / r; S; ^. S! Q
  20.                    SysTick_CTRL_ENABLE_Msk;                  
    - p8 }# g$ G2 F3 u& H: f4 O
  21.   return (0);                                              - S6 g1 @" k* T
  22. }
    , z+ v, ~! G! W- }% ^* U$ f
  23. 6 B6 @& k; Z' n3 X5 c' V
复制代码
, h) {+ h: w: K8 E+ {- Q4 n
SysTick的具体原理可以参考一下我之前的博客:SysTick原理
9 N: T+ y' |: F" d

/ v) T* D; N$ Z. ?注意:SysTick_Config()函数执行完就开启了中断,所以必须在Echo为低电平后及时关闭中断,并且将记录中断的变量清零。2 N& x- u& D' L$ r, y/ S) l: K

. m/ @4 l4 E- U; i; \
中断函数如示:
5 k) c* y$ ]0 `( N
  1. /* 用extern和volatile关键字修饰的 全局变量n */5 y% r2 N' `$ N, h
  2. extern volatile uint32_t n;, |8 U+ Y6 G/ r" ^# Y5 b

  3. 5 ~* ^' y, G- @* v% |, z1 }: F2 H
  4. void SysTick_Handler(void)
    5 j3 I% x5 {  Q
  5. {7 S: k. n& H2 g9 @; _
  6.         n++;3 ]8 |  b$ K, p/ U) g" V5 n* |4 A. d
  7. }
    ! w2 [* Z5 B. I! S. ]2 E5 B! Y, ^
复制代码
; s9 b  K& o8 h* F8 H9 D! v7 O
关闭中断及清零n的代码如下:: M9 W' W% M3 ^8 I$ Z% A
  1. /* 本来的使能位取反 */! t  }+ U3 M8 A- A: l5 Z7 `
  2. SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;5 E4 M, H& s& }. u, g
复制代码
2 p! f) ~3 U8 o- E$ f
SysTick->CTRL寄存器的0位控制着中断的使能,具体情况在之前SysTick的博客中已做详细说明。
' K& i  T3 `$ ^, y) M/ l
  M6 P2 A( }) ~- P$ A2 A4.如何将距离测出来0 E! G! A7 A; }
我在main函数中实现了距离的测量,并且通过串口打印函数将距离传到上位机,具体代码如示:
+ p- X( F" Y, z0 z, P. i* J) z9 D
  1. int main(void)$ @! Z; Q9 {; q) K
  2. {. _7 s9 R* |9 l' P- G! V

  3. 0 ^4 D/ W: Z% `% z1 b# b+ B
  4.         int i=1,q;% ~4 }1 S& e7 H% L. m4 P9 N, y
  5.         float p;
    ) H* Q0 Y2 I* @" z  `
  6.         /* HC-SR04模块引脚初始化 */
    4 Z: B' C& ~# l* H7 n7 F; {
  7.         SR04_GPIO_Init();" O. }: x  }. L5 X
  8.         /* 串口相关配置 */
    - u0 ]( Y9 }0 s6 w: Q
  9.         GQ_UART_Config();/ ]7 D) ~4 e! J" w
  10.         /* 打印调试信息 */3 L9 n% D" x# e6 T+ Y
  11.         printf("慢漫的测距实验\n ");
    7 B. x: z' T; x
  12.         ( Z# U/ H8 T1 [4 N0 @; N1 r- O
  13.        
    6 e# c9 q+ ]3 a: x; a& u) R3 J4 J
  14.         while( 1 )
    " Z. e6 {8 Z8 i, Q
  15.         {+ E/ r: A8 X: i
  16.                 /* 每0.5s测一次距离 */
    8 ^( l+ n& w# X! ?2 r# e% a. x  M
  17.                 Delay_ms( 500 );" v; q* W& F0 Y& M9 w, S4 ~
  18.         # P- {0 U! d$ F* b$ y. s
  19.                 /* Trig给一个至少10us的高电平,超声波进行一次测距 */8 J" N5 b/ z+ ^9 E. t: U
  20.                 Trig_H;- A; b0 D/ W: v
  21.                 Delay_us( 10 );
    3 G/ x7 q9 j$ y# v: ]+ p7 D
  22.                 Trig_L;2 D$ ~3 u  K1 Z+ o" a& @- z
  23.                 /* 等待Echo高电平 */
    ! N& F9 z" n! X7 n) S* R' G
  24.                 while( Echo_Value != 1 );
    6 M7 E; r8 i' g
  25.                 /* 打开中断,对Echo高电平时间计时 */
    3 H9 o+ s* `- ?  U: k) s
  26.                 /* 配置计数器的装载值是72*6=432,即一次中断6um,正好是超声波的0.1cm,所以中断次数n对应着n*0.1cm *// V8 U2 L3 p7 X+ o2 _( j1 u; [$ ?
  27.                 /* SysTick_Config()中已经使能计数器了,所以无需再开启        */
    ' j" u, @" q) I$ S3 B) m! E( n9 R8 S
  28.                 SysTick_Config( 432 );. m; m. U) l$ W6 P# T* [
  29.                 /* 等待直到Echo为低电平 */
    % y+ R0 B6 O0 c; w
  30.                 while(Echo_Value == 1);
    1 ]# T  b) A% _3 q7 E
  31.                 /* 关闭中断,通过参数n来取得距离参数 */4 V' @, P5 L5 j
  32.                 /* 本来的使能位取反 */
    0 e- Z5 x9 _+ D1 _
  33.                 SysTick->CTRL &= ~SysTick_CTRL_ENABLE_Msk;
    % I+ x; m( i0 {; u/ u
  34.                 /* p、q分别是距离的整数部分和小数部分 */3 g+ a9 D3 z9 C
  35.                 p=n/10;
    9 t( A4 Q- r4 K2 [9 v; i. {2 X
  36.                 q=n%10;
    6 E8 l3 O9 u) Q3 u; m
  37.                 /* 打印距离信息 */% z3 K+ u+ n2 I1 P4 P5 `" D0 e3 i
  38.                 /* p-50时经过调试的,因为测量的距离和诸多因素有关,这个操作减小了误差 */
    8 b7 K$ o: p% ~! ^4 Y
  39.                 printf("第%d次测量为:%.0f.%dcm\n",i,p-50,q);" k8 s$ W. h- y* E3 e: K
  40.                 i++;! b& m- _$ _0 v# w3 i- B4 S
  41.                 /* 清零中断记录变量n */
    9 `$ F; s' B+ x. S; X- B
  42.                 n=0;
    3 |1 o% A: \' [# K7 p, t* y7 n' o! n
  43.         }
    4 p0 O+ H6 Q2 `- t3 u3 W6 P4 n
  44.        
    ) _+ X& m: ^. X0 G: ?
  45. }
    2 e) @: p, u, j8 m+ y
复制代码

+ M8 c0 a0 n! l& `5 g4 d————————————————
3 B7 v" u, x7 e) |版权声明:Aspirant-GQ1 d2 v& W3 R. u5 U7 s
如有侵权请联系删除
) R" W8 k* l" U# g) l' H# W! {( `1 i; s' A
收藏 评论0 发布时间:2023-3-18 14:38

举报

0个回答

所属标签

相似分享

官网相关资源

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