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

【经验分享】获取STM32代码运行时间的技巧

[复制链接]
STMCU小助手 发布时间:2022-1-5 21:00
前言1 f  I* u  M& Y8 w3 o" E
    测试代码的运行时间的两种方法:
  • 使用单片机内部定时器,在待测程序段的开始启动定时器,在待测程序段的结尾关闭定时器。为了测量的准确性,要进行多次测量,并进行平均取值。
  • 借助示波器的方法是:在待测程序段的开始阶段使单片机的一个GPIO输出高电平,在待测程序段的结尾阶段再令这个GPIO输出低电平。用示波器通过检查高电平的时间长度,就知道了这段代码的运行时间。显然,借助于示波器的方法更为简便。. H9 X4 ~, N7 j' |
借助示波器方法的实例
3 R( @5 v7 Z& S% |Delay_us函数使用STM32系统滴答定时器实现:
/ \( e, g* S2 I& {( y( Q% Y
  1. #include "systick.h"4 b6 O% B' H6 h, D  }6 N2 E$ K/ s8 K9 o

  2. - h0 R2 P; L2 ]
  3. /* SystemFrequency / 1000    1ms中断一次
    : K  k; o3 X8 R3 F1 |: q( @
  4. * SystemFrequency / 100000     10us中断一次, _- |7 D0 W7 ~/ @2 P
  5. * SystemFrequency / 1000000 1us中断一次- n) a: S8 S" u! k) d/ o$ z9 ^. W
  6. */- t8 K8 |- t) j$ i! I3 h

  7. ! W+ L( q" I0 _7 R1 j! ~" f
  8. #define SYSTICKPERIOD                    0.000001
    ' v4 t/ M3 x0 h+ h
  9. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)% ~3 g$ A+ H: ~
  10. 0 U, g7 K* n# C% L
  11. /**
    * d/ j, {5 b0 E& K) N) h7 @& e
  12.   * @brief  读取SysTick的状态位COUNTFLAG
    % z( g. m1 n% V4 B" C5 `
  13.   * @param  无
    " y! |* d7 m9 G3 Y7 r
  14.   * @retval The new state of USART_FLAG (SET or RESET).6 W. I( h9 {& @! `* Z0 r
  15.   */- G1 U) g3 U& A3 ^+ F- ]
  16. static FlagStatus SysTick_GetFlagStatus(void)
    $ G# c- f" B; q5 d- _# F1 I0 w6 d$ D
  17. {, |7 ^; H# r7 T
  18.     if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk) 1 A3 v# {: R0 y* S3 L5 z
  19.     {
    " h( ~9 p8 ^2 j1 V* L( i
  20.         return SET;
    1 s  s% n( n& Y8 h* ~6 q% c
  21.     }
    : l; C0 `7 l! Q& W1 t. m% |$ j, x
  22.     else3 Y; z' ?$ R9 k+ p
  23.     {" S; r0 w* k" E& O: d( V
  24.         return RESET;
    0 R% B# |+ @$ [
  25.     }. X: j1 ~9 p' A8 \% }2 ~3 I. x* r
  26. }  o' E, M; W2 V. G

  27. % N& [0 }" G% g
  28. /**
    ; a! C4 F: k2 m! M7 X3 ?# }
  29.   * @brief  配置系统滴答定时器 SysTick0 V3 T5 `$ t+ l7 X! G
  30.   * @param  无5 x% Z2 C7 X2 ]
  31.   * @retval 1 = failed, 0 = successful
    6 a; m( V/ x* \0 B! A& c
  32.   */2 R0 I2 I# J/ c
  33. uint32_t SysTick_Init(void)7 F. j% {: R8 v8 f7 q5 P) O
  34. {* I. o6 r+ y9 d' {# z3 \6 h# ]- A
  35.        /* 设置定时周期为1us  */
    ; M8 m  U& v  V- \8 h5 ~
  36.     if (SysTick_Config(SystemCoreClock / SYSTICKFREQUENCY))
    ; {" z+ F& M" Y
  37.     {
    . @- h/ |9 d$ T! ?* F1 q0 {; q
  38.         /* Capture error */ 3 O8 {( f. s9 C; n/ ?
  39.         return (1);
    * ?9 v8 G  _  f# e( h4 R
  40.     }3 I1 x% W8 o# P& H

  41. 3 @& i" B& ~$ T# ?( ^" K" Z
  42.     /* 关闭滴答定时器且禁止中断  */
    9 i* k4 C+ h' v2 N0 X- _! W3 J
  43.     SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);                                                  
    6 I3 W* g" i7 i" h) R
  44.     return (0);/ ?; M0 O& w0 B* U; d) e% S/ ?
  45. }
    & ?8 y, a! q* Z- c+ `

  46. 2 a9 z# z/ u+ F/ s% p8 X
  47. /**
    / c( X' R  W6 R/ G# S
  48.   * @brief   us延时程序,10us为一个单位; R. C( Y4 i7 x4 S! C5 o
  49.   * @param  
    8 n7 l8 b% P" y" [6 }
  50.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us& R2 b5 ^/ n# p" ]) `8 b
  51.   * @retval  无
    ; x& ^; j9 w" Z) G
  52.   */, s9 y0 o0 n# {  K/ L
  53. void Delay_us(__IO uint32_t nTime)
    ) s6 l% w4 y% u* o
  54. {     / N$ s# t7 Y: x' {6 L
  55.     /* 清零计数器并使能滴答定时器 */  
    2 V  ^, ?. ~1 y+ n7 ]9 m
  56.     SysTick->VAL   = 0;  
    # X  d1 |( M) a
  57.     SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;     7 _! r& U% Q, K$ }2 i. r

  58. - F6 V% a* J/ C) B6 b
  59.     for( ; nTime > 0 ; nTime--)
    $ r, I8 n, \; f$ v% `' D( F/ e- U7 A
  60.     {$ G# m% b. Y  D5 D
  61.      /* 等待一个延时单位的结束 */
    & d8 ~9 r9 W) r  @  i
  62.      while(SysTick_GetFlagStatus() != SET);3 E- a- D5 |- E3 B
  63.     }
    ( m, P3 t. v* }* S3 ?
  64. 2 R( T  |* s. E1 y8 k8 n; |+ G
  65.     /* 关闭滴答定时器 */4 G, a# q5 U& T( m
  66.     SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
    " i; l& d+ Z) s
  67. }
复制代码
3 d5 N2 ~3 j8 {! [
检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:
  1. #ifndef __GPIO_H
    * r1 b( v: q& X6 P* R1 C  G, e  S
  2. #define    __GPIO_H. x" \" f) U8 C9 [1 Q0 o
  3. 3 z: Y% X& `4 U9 C+ j& g/ n
  4. #include "stm32f10x.h"0 D  d# h' x+ |$ u! O( k: r( W$ q' G
  5. + t5 D8 K1 m, a* t/ z4 d
  6. #define     LOW          0
    7 ^& P) o" \& K; R9 k9 _) U
  7. #define     HIGH         1
    , O' G. L% d0 ^" i$ O. [

  8.   j, ]! }4 R- z' q4 g
  9. /* 带参宏,可以像内联函数一样使用 */
    ' J, p4 s' U/ p
  10. #define TX(a)                if (a)    \
    / [+ X/ n9 N8 `" c$ x+ K5 W" ~6 J# p3 M
  11.                                             GPIO_SetBits(GPIOB,GPIO_Pin_0);\, d# ^) h, L# h1 H: g/ l( W
  12.                                         else        \
    6 ~' G  x7 o- _  [
  13.                                             GPIO_ResetBits(GPIOB,GPIO_Pin_0)- D1 c$ |( M, b* Y, p. Z
  14. void GPIO_Config(void);* V& U8 x( [) Z5 V& k" U

  15. 7 `) T% g; t0 C$ Y: W+ j  p. w7 i9 e
  16. #endif3 F' T  o! _) l$ X3 |+ C7 o: `
  17. / q0 u* ^* c% I7 h
  18. #include "gpio.h"   
    * k  [4 v  Y" b1 e9 M! {- a7 J3 d
  19. " ]5 J3 a7 H2 f* g, [" }
  20. /**
    1 _& }6 c" v  T( h
  21.   * @brief  初始化GPIO
    ! Z+ S- ]' {. T+ v& r+ i; r% A
  22.   * @param  无
    $ a* u2 L" h; u5 d# h
  23.   * @retval 无1 Z; e) V- R8 ?0 G- k/ y; I  H
  24.   */
    9 k. Z7 I! m2 @. [! E; }( \
  25. void GPIO_Config(void)
    # x% c4 o2 W9 |- J( L5 s
  26. {        
    - S- W+ N- N7 i1 m; i
  27.         /*定义一个GPIO_InitTypeDef类型的结构体*/
    & h+ M; r: n( ]- o) B
  28.         GPIO_InitTypeDef GPIO_InitStructure;
    : e  K; [2 [% V- ~% p0 Q  W8 [9 M# C

  29. 2 {0 K: z+ n' t) Z1 a8 K  q
  30.         /*开启LED的外设时钟*/+ g8 a: N( A, F' o, ~
  31.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
    . w8 w3 I& c& V7 b) R. d8 T
  32.                                                            # U% C0 `3 W# s/ Q4 ^* W
  33.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;    1 v3 ^1 w+ d5 o" t
  34.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     4 G2 e* l$ ~! l4 _% T
  35.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; ; `" _$ J/ k0 {, s' [0 \
  36.         GPIO_Init(GPIOB, &GPIO_InitStructure);    2 r' _0 J9 A% @
  37. }
复制代码
$ {' }1 q0 u2 j, ^2 J
在main函数中检验Delay_us的执行时间:
  1. #include "systick.h"8 c7 B( {* W6 b6 y
  2. #include "gpio.h"
    8 X6 g5 }( p! g# O# ~
  3. 5 K1 D0 ?; D! m: u& Z! L+ t
  4. /**6 D1 F! e* e) E. s- p
  5.   * @brief  主函数
    * T1 V9 w/ E# o; I* ~4 j4 T
  6.   * @param  无  
    ' U# U. p* i* t, V2 {4 R! r" `% H
  7.   * @retval 无
      V) c9 C, W" F- Z+ c
  8.   */
    ' h# a9 o  \' ^; t
  9. int main(void)
    $ l0 {# X5 b+ n1 Y3 b4 D
  10. {    ; g0 E; @3 B! E# t0 u! I5 Y7 `5 l
  11.     GPIO_Config();7 `( _7 S1 S5 P5 ~

  12. 1 t1 J2 }% _5 @
  13.     /* 配置SysTick定时周期为1us */4 T& e6 j) B" p5 g& o/ X2 @
  14.     SysTick_Init();
    + r& B8 g% U- [" x

  15. / S0 h5 D: m7 D9 f9 F* |% g0 A- X
  16.     for(;;)
    9 q% i+ b; s0 Z  _
  17.     {
    6 Z& x0 D1 ^* I8 X; \
  18.         TX(HIGH);
      [& t0 Q3 j5 `+ }! v
  19.         Delay_us(1);
    7 z  d- w5 E) R, A; c$ }2 p
  20.         TX(LOW);5 o% e* w, |& [/ [' C, \/ q" z9 Y
  21.         Delay_us(100);5 [1 B& Z. A- z1 A) L' O* |
  22.     }     ) i! v' e; |  T' s  J4 v
  23. }
复制代码

% |/ r8 ]# M. Z9 q
: Y4 y* ?! u' s- U
示波器的观察结果:
2 N6 b; F' T5 g- X" z
LS25D[ONEFV6G}JK{`$@{}8.png
4 @' @  B+ _/ r7 j7 V8 ]$ H& H
可见Delay_us(100),执行了大概102us,而Delay_us(1)执行了2.2us。
( @& W+ }2 L# J- O- S0 i0 x' A更改一下main函数的延时参数:
$ |/ }( ?& t7 `+ K) p8 O/ O" K7 Q
  1. int main(void)
    % _& o, a6 ]% d0 Q5 ~
  2. {    ! F* w1 T& x/ F3 J  k
  3.     /* LED 端口初始化 */
    + i' V' ?) D& T8 Z% g/ I
  4.     GPIO_Config();
    7 G/ H5 K  @7 n+ ^' W
  5. . M/ C, |8 k& J- X/ Z
  6.     /* 配置SysTick定时周期为1us */6 z0 @( l5 O4 m  @! E
  7.     SysTick_Init();
    7 E4 o( j1 K( A$ ?+ N% j

  8. 6 S/ o* E9 C& c6 l' m  |! k% p) |
  9.     for(;;)/ y; M4 b% H, ]* D" o
  10.     {7 ]2 V5 y: E" ~
  11.         TX(HIGH);
    " i. t5 x& c* Z% z3 }7 R
  12.         Delay_us(10);$ V3 f; Z: q0 B) s0 r* ?# g
  13.         TX(LOW);
    4 H0 @+ t, f+ Q" |, @* L
  14.         Delay_us(100);
    ! S. c3 h* y4 O
  15.     }     2 ~. ?% Y' ^1 V6 c
  16. }
复制代码
) D- \) e: Q; C$ ~' E& o# d
示波器的观察结果:
1 S2 m( x& q) X7 X5 G' P& @
12LV@6Q)OUGINPJB586V$N2.png
* X" h% D& D0 t- @- @
% t2 M( X5 b, F- V; y7 B
& T+ O# N/ S0 {
可见Delay_us(100),执行了大概101us,而Delay_us(10)执行了11.4us。
9 Z: ~% u, s0 i+ J3 \& O3 t! q+ u结论:此延时函数基本上还是可靠的。
  V+ H3 f: }' L# t( [7 ]$ q- o" u/ V0 ~8 S. p' l; V
使用定时器方法的实例
- T& N6 i$ V/ q0 l; eDelay_us函数使用STM32定时器2实现:
7 {/ {3 e0 A$ K9 [
  1. #include "timer.h"
    - s& A/ w4 F/ `
  2. 9 A( e8 u7 ]7 c; @6 \3 _) ]
  3. /* SystemFrequency / 1000            1ms中断一次7 E0 `! Z* Y: Q' n
  4. * SystemFrequency / 100000     10us中断一次
    - U5 ~  a7 M8 c+ |; f4 Y$ g
  5. * SystemFrequency / 1000000         1us中断一次
    0 g. |; n) f: j3 H3 O; W
  6. */; F3 a& a6 ]1 `8 g

  7. : J* N# ~3 N  m( s/ h
  8. #define SYSTICKPERIOD                    0.000001
    $ p+ [; l% {: \+ ]
  9. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    $ j# y+ C8 d1 L

  10. / ]* b7 s. D+ q- L8 Q$ c' Y
  11. /**) V. l1 d+ u: \. `
  12.   * @brief  定时器2的初始化,,定时周期1uS4 d9 S* g6 i. F3 S) ]3 W
  13.   * @param  无
    " A, G& c0 C" H/ S3 K' b( j" k3 i2 P% A
  14.   * @retval 无! a: ?1 r! f# i0 G5 p, p) G
  15.   */
    0 @$ A6 ?! W' X$ X0 y
  16. void TIM2_Init(void)
    3 E5 A5 T6 q% L$ T% @" l
  17. {
    ) |" G0 c9 K  g# g/ ]3 N9 b4 f0 j
  18.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    6 R' p3 {" R( \# H: V/ V, S
  19. 8 X( i- R( t7 H. H) g& B
  20.     /*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */
    ' I' D) Z' L+ R+ D
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);7 W% N% D" q1 ~% v* f) u( a
  22.     $ J0 @2 {% g6 Y8 D) S
  23.     /* Time base configuration */         
    2 ~- ~: L; }( N
  24.     TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;! r/ P. ?) N; I# Q
  25.     TIM_TimeBaseStructure.TIM_Prescaler = 0;
    & Z0 }+ K6 i+ p
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;* u. s- r2 ]9 u6 ^
  27.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);3 Q/ m* e  \$ c( U0 [, u
  28.    
    , O( S9 ^' U, k5 e( s" l
  29.     TIM_ARRPreloadConfig(TIM2, ENABLE);
    5 D+ d5 k$ l) y5 O1 D
  30.     ! w( b* Q& \* V' M1 K
  31.     /* 设置更新请求源只在计数器上溢或下溢时产生中断 */8 \  d3 U6 @1 t7 Z: }. I
  32.     TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global);
    # f& G9 o. B5 r6 d- L* _1 A& I
  33.     TIM_ClearFlag(TIM2, TIM_FLAG_Update);5 D) U* \1 k) G- k0 K# Z" K% K) m
  34. }
    ' T) C0 u( ?9 n. N" D( C! \

  35. 0 ?0 t8 w. H1 x2 p* r
  36. /**5 T6 F* S% J3 ?+ x9 B/ {8 K7 |& ~& w
  37.   * @brief   us延时程序,10us为一个单位
    1 s/ H  m) G1 b
  38.   * @param  
    : i9 i% k: s1 p& v: G
  39.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us, V' i/ Y) I) X
  40.   * @retval  无  [$ I" p) n: V
  41.   */
    , G$ t3 V+ L7 y6 H. @
  42. void Delay_us(__IO uint32_t nTime): m) u% h9 s* ~* w; Z8 J1 n
  43. {     2 ^1 W& }$ s  X) p
  44.     /* 清零计数器并使能滴答定时器 */  
    ) B& i7 ^0 d8 D! }. w- m$ V
  45.     TIM2->CNT   = 0;  2 a5 q) N0 Z$ n) ?4 m
  46.     TIM_Cmd(TIM2, ENABLE);     ' q0 o) e- J2 Q7 t; ]) `! @
  47. ) I6 {; t( ]- @. R; _
  48.     for( ; nTime > 0 ; nTime--)
      V' @+ k# T- a7 z3 f
  49.     {8 Y' g& f9 M+ H+ I, a" E8 \
  50.      /* 等待一个延时单位的结束 */0 [/ i; U& ^" Y1 a" Q
  51.      while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);
    % n* T! N% N6 A" H6 ]3 G; P% M) x1 m- X) L
  52.      TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    - d0 Y* f  X# T
  53.     }5 t5 M! S. m' s: K3 C+ r

  54. ' X' o+ i1 P$ {% K( D/ W/ l3 F
  55.     TIM_Cmd(TIM2, DISABLE);0 z' b% I1 o7 v/ q+ G% f' l& i" W2 J3 i
  56. }
复制代码
" C( Q7 ?8 m0 \
在main函数中检验Delay_us的执行时间:0 V) ~  z  b' `6 c: z
  1. % l3 L/ p3 q+ S) w4 V* {! d, K( G
  2. #include "stm32f10x.h"( H0 \  a7 M) s% Z
  3. #include "Timer_Drive.h"3 |, L6 F8 q; n
  4. #include "gpio.h"
    ' s" `$ X' E; O* b; h1 B
  5. #include "systick.h"
    0 R: X2 \' ~! H
  6. * v# \( o4 J. ]! t" u, @; n
  7. TimingVarTypeDef Time;
    & z( N3 f- v/ e2 t8 A1 W
  8. 1 F# \8 Q" `( F7 |
  9. int main(void)0 I3 y, N" _+ Y' C& ^- M8 c
  10. {    ' H" x' B; p+ f
  11.     TIM2_Init();    ( ]: M6 D& g: X! G- u
  12.     SysTick_Init();
    ( @" c+ |( g. K. J7 x
  13.     SysTick_Time_Init(&Time);9 d# P* x2 w, C$ b
  14.     ; C5 t& V; ?# i* P1 X" _7 W
  15.     for(;;)
    ' A' `+ E+ o3 g5 Y% I
  16.     {7 m: y9 P1 a" s; @
  17.         SysTick_Time_Start();
    - M& I2 l% h: ~1 @2 }
  18.         Delay_us(1000);* L5 O/ ]) R8 T8 a* J
  19.         SysTick_Time_Stop();3 ]+ K1 w9 n( P6 C: |# e
  20.     }     - C3 D1 J5 Z8 S# r0 k9 M/ K" |
  21. }
    ' t( o+ g( H* f: f1 Q7 Z3 H$ q
复制代码
( M2 D4 j1 R- J2 G7 j+ m# M
怎么去看检测结果呢?用调试的办法,打开调试界面后,将Time变量添加到Watch一栏中。然后全速运行程序,既可以看到Time中保存变量的变化情况,其中TimeWidthAvrage就是最终的结果。
, I) w/ U" I1 P! }" Z" Q; H
_W(`%MH`LRLQIQ33[H24~ZA.png
- C7 c( m# n5 [, q+ @" P% a
可以看到TimeWidthAvrage的值等于0x119B8,十进制数对应72120,滴答定时器的一个滴答为1/72M(s),所以Delay_us(1000)的执行时间就是72120*1/72M (s) = 0.001001s,也就是1ms。验证成功。0 R! R# K! b# y8 E6 j- W+ {5 z; S
备注:定时器方法输出检测结果有待改善,你可以把得到的TimeWidthAvrage转换成时间(以us、ms、s)为单位,然后通过串口打印出来,不过这部分工作对于经常使用调试的人员来说也可有可无。$ F8 I+ ]' u3 o8 O6 P
两种方法对比
3 {! E; f8 e2 b1 w2 p8 J* a软件测试方法
  T& }) [/ D' |3 l操作起来复杂,由于在原代码基础上增加了测试代码,可能会影响到原代码的工作,测试可靠性相对较低。由于使用32位的变量保存systick的计数次数,计时的最大长度可以达到2^32/72M = 59.65 s。* N0 V6 J' r- v/ n4 G: o4 B
示波器方法' P8 |7 P3 [4 t# `5 U
操作简单,在原代码基础上几乎没有增加代码,测试可靠性很高。由于示波器的显示能力有限,超过1s以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。2 m5 h1 o. ]0 q7 L; z
% C4 n, p  N$ s' i7 [# [8 @
赞 收藏 评论0 发布时间:2022-1-5 21:00

举报

0个回答

所属标签

相似技术帖

官网相关资源

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