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

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

[复制链接]
STMCU小助手 发布时间:2022-6-9 23:34
前言
    测试代码的运行时间的两种方法:
  • 使用单片机内部定时器,在待测程序段的开始启动定时器,在待测程序段的结尾关闭定时器。为了测量的准确性,要进行多次测量,并进行平均取值。
  • 借助示波器的方法是:在待测程序段的开始阶段使单片机的一个GPIO输出高电平,在待测程序段的结尾阶段再令这个GPIO输出低电平。用示波器通过检查高电平的时间长度,就知道了这段代码的运行时间。显然,借助于示波器的方法更为简便。
    , t" R0 Q5 w+ p: E
借助示波器方法的实例
    Delay_us函数使用STM32系统滴答定时器实现:
  1. #include "systick.h"
    * }: q5 X. B  `# u4 |7 m

  2. & `5 n4 `0 _0 I9 |) v
  3. /* SystemFrequency / 1000    1ms中断一次
    2 d1 y) s2 g. |& Z
  4. * SystemFrequency / 100000     10us中断一次
    - q% C! r+ w) l( H2 e% @, Z
  5. * SystemFrequency / 1000000 1us中断一次8 v5 w7 c4 i) z3 R
  6. */! D3 Y4 b7 _& k$ V

  7. . [7 i. H7 r+ n! P! F  t9 s
  8. #define SYSTICKPERIOD                    0.000001
    ( M4 V& G0 |2 R3 _& B) `
  9. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)2 E' \6 B' m' `) o

  10. # p" D2 f. I7 M
  11. /**
    - y5 x& M4 ]0 k3 Y) P5 c) E
  12.   * @brief  读取SysTick的状态位COUNTFLAG
    # e9 f( u- T2 P' K& j
  13.   * @param  无
    4 \5 W& n5 D. o0 V% j
  14.   * @retval The new state of USART_FLAG (SET or RESET).7 W8 `6 F4 m5 v! g
  15.   */- [8 |" y! q0 f' R0 `( }! D
  16. static FlagStatus SysTick_GetFlagStatus(void)
    ) @: s5 g7 x0 O% y
  17. {# W" `4 h9 y* A! N
  18.     if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk) $ f# @  {- T: N; i; x
  19.     {
    % N& s& Q7 w; U, m3 p7 j( N( B
  20.         return SET;
    6 y2 n% P. m5 ^3 i4 G
  21.     }1 H1 U+ R- g! R( O5 r/ m, @2 A7 _8 N
  22.     else$ O& v" T4 \( [) I! L3 f
  23.     {. s* G8 C7 D& z3 }
  24.         return RESET;
    ( U' R( v4 B7 F: @1 V3 d
  25.     }+ `' n( D" j' Q4 X. v6 ]( V, ^7 ]1 Z
  26. }) x) t0 t4 z) X! g% A
  27. + l6 l2 C; ?) g- V
  28. /**
    " I5 u) N1 n, u4 N( U2 m2 i. E
  29.   * @brief  配置系统滴答定时器 SysTick
    : x7 R: a+ |  [% _( L
  30.   * @param  无3 W! I" a( n) s. E4 A& z: Q. }
  31.   * @retval 1 = failed, 0 = successful
    " B' l: r* r; K
  32.   */
    9 S% R- L. }8 x6 q2 x
  33. uint32_t SysTick_Init(void)1 D# ~2 o4 w! G) `' d+ ^0 L
  34. {0 ~- f. @9 a  i/ ~1 ?  V
  35.        /* 设置定时周期为1us  */
    , Y+ R7 a5 `, G" w5 t" J
  36.     if (SysTick_Config(SystemCoreClock / SYSTICKFREQUENCY)) % H% \3 m0 }  s+ v
  37.     {
    + ^7 w, C6 C+ b2 m& x/ B$ U2 Q
  38.         /* Capture error */ + Q# M) _9 T6 A1 n, q% f6 V6 v
  39.         return (1);* Q. p1 V; G# j' H" t0 ]7 e3 d
  40.     }2 ]* W: G- f, Y; P! Z9 ?4 G; Y- e9 ^

  41. 7 A' ^, p" ~$ @" L
  42.     /* 关闭滴答定时器且禁止中断  */
    & S- i' `7 Y0 ^7 W3 a3 O
  43.     SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);                                                  / g2 d2 ^, [( s- @
  44.     return (0);
    ( k& T0 O% b5 A4 D, Y
  45. }4 n: r, @9 D: V+ ]3 F$ a8 @, i
  46. & l4 p8 F4 D( A: r# E
  47. /**
    6 ^* U, C) s% k& F9 {
  48.   * @brief   us延时程序,10us为一个单位9 ^2 {' S0 ]/ b. x$ a; M$ y" ]7 K
  49.   * @param  * \! [$ p$ Q/ c9 z4 g5 x# n4 _: `
  50.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us
    # l/ q1 a2 T4 b, Q# T3 |9 B7 D
  51.   * @retval  无
    / K/ k9 K1 @* ^/ [, d& s
  52.   */
    $ N+ _! @# I% S" |
  53. void Delay_us(__IO uint32_t nTime)6 x  o2 c6 @9 g. o: v& K# n8 b
  54. {     ; J' O) z6 e; I0 w
  55.     /* 清零计数器并使能滴答定时器 */  ' |' F  [* K# l8 ]. w
  56.     SysTick->VAL   = 0;  
    8 W/ w4 B% N% U/ K
  57.     SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;     
    3 r7 O7 X+ e7 N8 t' E/ B  f9 G/ _

  58. ' o; k0 j, C+ ]( H- K
  59.     for( ; nTime > 0 ; nTime--)
    ( N+ i- W9 |6 {% r
  60.     {. X- T$ `* B: F3 P
  61.      /* 等待一个延时单位的结束 */5 @. ]+ _7 I( y) W( q& u! h
  62.      while(SysTick_GetFlagStatus() != SET);) d: p$ A6 C3 N+ K' e! \  O3 T
  63.     }. q+ i( _- M+ I" M  t

  64. . o9 M9 \6 ]7 g# [* Q5 B! S3 |
  65.     /* 关闭滴答定时器 */6 L- q7 m0 \5 f" k% M* w+ E3 [
  66.     SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
    5 m9 K9 B" O6 y) i7 e
  67. }
复制代码
  j8 V$ x$ t( }, P- a$ F
    检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:
  1. #ifndef __GPIO_H
    . i8 S" n4 [$ r" u- _1 j
  2. #define    __GPIO_H
    0 a9 ?0 N! M; D- f
  3. 3 ~! w* q! S# g
  4. #include "stm32f10x.h"
    9 b1 `# \# m/ B0 g- {. C
  5. 1 X' D0 O) r" l. i4 G% r, E1 C
  6. #define     LOW          0
    7 X! z* i) r  q4 ~
  7. #define     HIGH         1$ t3 @( `% X7 c* l2 W& Z+ j

  8. 9 {$ r7 Q6 c% Z1 M, u7 J
  9. /* 带参宏,可以像内联函数一样使用 */
    ( P2 d2 F% E+ W, V% {* o
  10. #define TX(a)                if (a)    \0 N  b9 o' J0 S$ X+ k
  11.                                             GPIO_SetBits(GPIOB,GPIO_Pin_0);\
    7 [. n' k: |# j" [% N2 n9 X: y/ \
  12.                                         else        \, I1 Z/ n3 H" X+ B
  13.                                             GPIO_ResetBits(GPIOB,GPIO_Pin_0)9 y! k+ z2 q+ T. p
  14. void GPIO_Config(void);
    4 F- @( k5 l& r' I, ~( z+ V! B
  15. ' q, d: B9 Z$ H6 X( `# ?' y
  16. #endif6 j- b, t4 Y; Q" }  C" Y
  17. / V/ X+ G* G8 O- K4 l
  18. #include "gpio.h"   
    3 _. X# f& y4 Q  ~% E5 Z; d
  19. 5 P, u# S0 }5 ~. I
  20. /**# U" Z. j! F" k, i* @$ y
  21.   * @brief  初始化GPIO( K3 `) K7 X6 G4 U$ K5 d
  22.   * @param  无
    ) E1 ]/ Z- ~% ~- f9 Q
  23.   * @retval 无3 v/ o" o1 Z/ n3 L2 o: J1 H+ F( b
  24.   */) ^5 g0 o" t1 D4 P
  25. void GPIO_Config(void)
    $ l$ [0 Z" U* O6 n
  26. {          W" O2 t- n" N) r+ z
  27.         /*定义一个GPIO_InitTypeDef类型的结构体*/4 W* X& M9 _4 k: ?
  28.         GPIO_InitTypeDef GPIO_InitStructure;: O% R# }1 G! t( G

  29. * e* h+ P+ U$ \# x9 s& c% `- f
  30.         /*开启LED的外设时钟*/
    . Q3 t7 u) l$ w
  31.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
    & G; e. ^& n+ }0 q' M
  32.                                                            - K2 C* K, l5 `' M
  33.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;    1 P) \$ u$ U2 I- q7 E2 {( r
  34.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     
    6 f  c* I' N" a7 G$ W( v
  35.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    / z4 s1 [0 i( v: ^3 y
  36.         GPIO_Init(GPIOB, &GPIO_InitStructure);   
    : L3 R& J( B: v( \
  37. }
复制代码
; U4 D6 N) D; \' L" R
    在main函数中检验Delay_us的执行时间:
  1. #include "systick.h"/ }3 z3 V' s2 m9 L% p: f! G
  2. #include "gpio.h"8 }- j* K8 w8 e# A. `" s4 D
  3. $ f0 b+ ^+ D) [0 {
  4. /**
    ) E" h6 h% R7 x
  5.   * @brief  主函数; X6 D* |9 @5 g
  6.   * @param  无  
    % X9 \% F) T0 g
  7.   * @retval 无
    ( Y5 P; P/ L* c" H2 D) V
  8.   */
    / `; k  T$ b+ L' \+ [( L7 D$ j( }
  9. int main(void)
    1 X4 v( b: z6 v' q* U7 s0 T+ h2 w+ W) Q
  10. {   
    3 l4 j) u* P% d! e7 C( Z
  11.     GPIO_Config();( a8 U0 H  B) c" z$ c5 {

  12. ! S- |2 H" z, f" B/ s- S2 @7 a" _8 h& h
  13.     /* 配置SysTick定时周期为1us */, R% q/ l' o- T2 i
  14.     SysTick_Init();# \: z0 L& v: o+ b3 ]
  15. 8 i4 `( Q, y0 f
  16.     for(;;)
      V4 ]& i( ]6 r/ u
  17.     {
    / m; M# A- |; H7 K( \. L) g( }
  18.         TX(HIGH);
    . p, L7 Q$ m! o$ L& _& V
  19.         Delay_us(1);
    # Z0 x6 W2 d; V/ P0 @$ R, p
  20.         TX(LOW);
    ( k$ [: u2 q6 I* R% h
  21.         Delay_us(100);! A$ j3 q! [9 A- d$ t  Y( p
  22.     }     
    $ g! N9 t; C2 G& H/ w4 ]6 d
  23. }
复制代码
2 w9 W+ Y, W9 _# w/ A5 j$ ^4 \) F$ Y0 X+ @
    示波器的观察结果:
! k$ N+ a9 G$ u  k
" z8 y5 o4 v& K6 e5 e7 g
    可见Delay_us(100),执行了大概102us,而Delay_us(1)执行了2.2us。
    更改一下main函数的延时参数:
  1. int main(void)
    ' h$ F4 p8 q! L# h9 f
  2. {    4 _8 J7 L) R8 ~9 E6 Q3 y
  3.     /* LED 端口初始化 */. k7 V& n3 x# p+ `7 a
  4.     GPIO_Config();7 v8 V1 Q. [' {, y# w7 K

  5. ' L% p* g( q4 ?8 Y8 e7 S; p, ^
  6.     /* 配置SysTick定时周期为1us */
    # M! s+ g' D) U: ?9 ]# ]
  7.     SysTick_Init();
    ( W  ~9 n. R* s+ o

  8. & s  d5 }% J. G+ d8 l
  9.     for(;;)
    + G, i; z: e" B8 N6 f( i0 c# Y
  10.     {
    . q' }/ a% i$ b& x: h/ T
  11.         TX(HIGH);
    ' X  R- M( a: ~$ G( b! L$ o8 q
  12.         Delay_us(10);
    5 D& L3 ]$ I, ~, K* O' ]
  13.         TX(LOW);8 C9 l* Q9 ~- l9 `4 v- v( l
  14.         Delay_us(100);
    ) H/ A% \# i2 P" ^- `& f
  15.     }     
    3 B# f. \3 {' b  w/ P
  16. }
复制代码
- @1 {& k; _9 L4 ?4 O% B$ L
    示波器的观察结果:
) u! H' _3 R4 S# a- n

2 e: l2 `/ V% r% O( F+ w- n
    可见Delay_us(100),执行了大概101us,而Delay_us(10)执行了11.4us。
    结论:此延时函数基本上还是可靠的。
0 \) _% E. P7 q( Q
使用定时器方法的实例
    Delay_us函数使用STM32定时器2实现:
  1. #include "timer.h"
    6 N- }! i. R" Q
  2. : ]9 l# H! ~1 J7 q" J! r
  3. /* SystemFrequency / 1000            1ms中断一次3 N- L$ {/ v+ V' M' q
  4. * SystemFrequency / 100000     10us中断一次) f! z/ q6 o% H3 a
  5. * SystemFrequency / 1000000         1us中断一次
    6 ~4 [; h0 ~8 i, K4 r* T
  6. */0 ]* p8 V* B0 P

  7. / X$ |. R$ V' d4 w) x" Z; l
  8. #define SYSTICKPERIOD                    0.0000013 j* r1 W1 n( ~* ^) g7 a
  9. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    ( W# O0 H% n1 N  }* e8 `

  10. % {: S8 q5 m/ k: A8 ?
  11. /**
    6 c: Y) |" i! V5 y; f
  12.   * @brief  定时器2的初始化,,定时周期1uS  k' y$ H+ Y+ u4 b' J5 q) n7 A4 r9 v- V
  13.   * @param  无0 v- _$ u: A) C: k( u* C- m
  14.   * @retval 无. O' l% D( H; ?7 d0 ^+ R# H
  15.   */
    / Q' U: `9 w  V4 p; E( i
  16. void TIM2_Init(void)' }/ K* v: h' Q' V
  17. {2 k9 Q7 o: }( Z1 _' u# L0 f
  18.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;, ^1 f  b( ]5 |- `: q9 S

  19. 0 J- r$ `0 x5 D0 H, X* r% ]' O
  20.     /*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */: P8 T6 a. b+ T8 F7 `# L- u, t4 m5 b
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
      I  l& s8 I6 h0 K7 R& R" U
  22.       ]& @" S( K5 R' ~% K* x& M5 ^( d
  23.     /* Time base configuration */         
    9 R/ ]  ?: ^; E4 Z$ Q
  24.     TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;
    2 t8 F$ r1 l8 `6 b( i: K9 {2 |
  25.     TIM_TimeBaseStructure.TIM_Prescaler = 0;, n! R6 o- F8 `! S* g1 \' }* ]% W
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    $ P& D, G( v. h1 p
  27.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);$ i9 ]$ d) Q" v: v
  28.    
    . m% o, n: i) K& @" l
  29.     TIM_ARRPreloadConfig(TIM2, ENABLE);8 w* g$ S# {) I6 a6 O
  30.    
    ! j) b6 E. Y! H+ R  G5 ], j" k
  31.     /* 设置更新请求源只在计数器上溢或下溢时产生中断 */
    & y3 R$ i' L5 w4 @  m* Y7 m3 i1 G
  32.     TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global);
    , u/ U* s# k- G5 u" a( l
  33.     TIM_ClearFlag(TIM2, TIM_FLAG_Update);5 S+ F4 o# k" z2 x6 X3 }( R
  34. }
    ) g% x- u$ G1 S% s8 H4 ?

  35. 8 m# O6 U1 l6 I1 b6 N7 z
  36. /**
    % r2 }+ F1 A+ B
  37.   * @brief   us延时程序,10us为一个单位1 u( O# F: [( v5 o) |  f/ k
  38.   * @param  
    % [9 _9 |! G/ W" g$ W9 T6 I, V* ]
  39.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us' s$ \: o$ m& X: Z4 u/ x9 o
  40.   * @retval  无" k5 d) t  B% N7 g( e) _
  41.   */9 I: O1 G3 p6 c! l+ c" u
  42. void Delay_us(__IO uint32_t nTime)2 X- j4 z% v9 |! h
  43. {     & l! O9 l2 h, J% P* S$ b1 k
  44.     /* 清零计数器并使能滴答定时器 */  7 {+ m# _/ h; n+ k9 Q8 Q
  45.     TIM2->CNT   = 0;    }) b7 H+ R! }3 A( m
  46.     TIM_Cmd(TIM2, ENABLE);     5 P0 D8 q1 B2 \1 a. g7 }+ u

  47. # A: \( D& o6 g3 ~- x
  48.     for( ; nTime > 0 ; nTime--)
    ( P# W2 _5 ~: U5 p5 \, F- W
  49.     {# T- U6 l" `, R* \( W
  50.      /* 等待一个延时单位的结束 */
    & _# A/ W6 ^1 o; Y
  51.      while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);& `3 w! X4 N% a# [  E) B* @! a
  52.      TIM_ClearFlag(TIM2, TIM_FLAG_Update);/ L  @, L0 i0 P1 ?& c$ J$ C% D
  53.     }
    ' M3 A) P& x7 n/ l+ h  E& r

  54. 3 V! T4 B- T5 a$ O' q2 b; o. _' i1 I
  55.     TIM_Cmd(TIM2, DISABLE);) z! e( z1 O5 J/ ]- b; Y: X. `
  56. }
复制代码

+ U- i: Z2 Y$ p
    在main函数中检验Delay_us的执行时间:
  1. #include "stm32f10x.h"
    $ e' X6 T* Z* Y: O- G( l5 u
  2. #include "Timer_Drive.h"( Q1 S! s9 J* ], g8 @$ Q* p
  3. #include "gpio.h"
    $ Y. R0 Z: D! U0 o6 K" d$ B
  4. #include "systick.h"* O, K6 s! P2 r$ L. S/ N
  5. , i; E4 J2 o+ l+ {# k0 Z
  6. TimingVarTypeDef Time;
    % u8 {& s% }4 {; z* w' {
  7. ! w2 n% J! h& q6 a/ h) ^
  8. int main(void)0 V( k( W5 `1 @9 v( Z
  9. {    % J% |, W4 }8 D6 b; v, {
  10.     TIM2_Init();   
    ( ~" N4 [1 X) v* O6 c9 j
  11.     SysTick_Init();/ G0 q. q0 N3 Y. D$ o1 N. d
  12.     SysTick_Time_Init(&Time);
    " y, d0 |7 G) u- a
  13.    
    0 ?# r3 v8 k) N5 `+ w
  14.     for(;;)" `) l6 D' W. z1 [9 [
  15.     {
    " m1 [- ?' r* d
  16.         SysTick_Time_Start();   F; ~  u5 R8 a5 n
  17.         Delay_us(1000);) a/ Z% X; S" a- s& v* [" u
  18.         SysTick_Time_Stop();& o0 Z9 k' X4 N2 Q# K
  19.     }     4 M; h4 {  m' x% S
  20. }
复制代码
8 j' K! H- [+ @/ r! u1 S
    怎么去看检测结果呢?用调试的办法,打开调试界面后,将Time变量添加到Watch一栏中。然后全速运行程序,既可以看到Time中保存变量的变化情况,其中TimeWidthAvrage就是最终的结果。
5 S5 T4 n; |- X4 a; @; {# q( l" s/ p) @

1 Q. d* _5 {6 T+ t) @( H/ _, @6 f8 w
    可以看到TimeWidthAvrage的值等于0x119B8,十进制数对应72120,滴答定时器的一个滴答为1/72M(s),所以Delay_us(1000)的执行时间就是72120*1/72M (s) = 0.001001s,也就是1ms。验证成功。
    备注:定时器方法输出检测结果有待改善,你可以把得到的TimeWidthAvrage转换成时间(以us、ms、s)为单位,然后通过串口打印出来,不过这部分工作对于经常使用调试的人员来说也可有可无。
6 }8 j0 o! N5 v8 ?
两种方法对比
软件测试方法
    操作起来复杂,由于在原代码基础上增加了测试代码,可能会影响到原代码的工作,测试可靠性相对较低。由于使用32位的变量保存systick的计数次数,计时的最大长度可以达到2^32/72M = 59.65 s。
6 ]- ]! N2 Y" s" `4 J* t4 A
示波器方法
    操作简单,在原代码基础上几乎没有增加代码,测试可靠性很高。由于示波器的显示能力有限,超过1s以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。
- y  G$ X! s' }) l
赞 收藏 评论0 发布时间:2022-6-9 23:34

举报

0个回答

所属标签

相似技术帖

官网相关资源

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