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

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

[复制链接]
STMCU小助手 发布时间:2021-11-7 15:39
前言
    测试代码的运行时间的两种方法:
  • 使用单片机内部定时器,在待测程序段的开始启动定时器,在待测程序段的结尾关闭定时器。为了测量的准确性,要进行多次测量,并进行平均取值。
  • 借助示波器的方法是:在待测程序段的开始阶段使单片机的一个GPIO输出高电平,在待测程序段的结尾阶段再令这个GPIO输出低电平。用示波器通过检查高电平的时间长度,就知道了这段代码的运行时间。显然,借助于示波器的方法更为简便。
      b& O3 K1 J( {9 R

    2 u; R5 h4 A1 O7 {9 B$ m6 \4 l( e
借助示波器方法的实例
    Delay_us函数使用STM32系统滴答定时器实现:
  1. <font face="Tahoma" color="#000000">: F" k3 _3 H2 Z" u
  2. #include "systick.h"
    2 _% B/ I* z3 s. U6 V% |
  3. 2 O# M1 M3 ^4 k: _" J
  4. /* SystemFrequency / 1000    1ms中断一次
    1 q% C% `3 V. C& e# ~
  5. * SystemFrequency / 100000     10us中断一次& d. w- C/ O! D" P+ O. P  R5 Z
  6. * SystemFrequency / 1000000 1us中断一次- v- d$ a* ~9 e+ f5 N1 Y
  7. */5 v0 d3 D9 }3 N; |9 W& V! q0 g: l

  8. 4 ^) ?: m' }6 D( t2 W3 P
  9. #define SYSTICKPERIOD                    0.000001
    9 L$ @& |. ]8 H' z/ s6 E; Y
  10. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    " @* |( L! r' `* s; @

  11. ( H" m) l+ ^0 _/ Q
  12. /**
    % z# J' Q: X* {5 |4 g
  13.   * @brief  读取SysTick的状态位COUNTFLAG! i5 b( X2 J4 f& h6 g. h% C
  14.   * @param  无* k9 W7 U1 A) r, `0 S4 @% v8 A
  15.   * @retval The new state of USART_FLAG (SET or RESET).
    + g- w, y9 S) Y- ]% V
  16.   */, M. o/ V4 n6 M9 l( m% F: o
  17. static FlagStatus SysTick_GetFlagStatus(void)
    * \6 g! P! L8 j
  18. {
    5 K! X* L: d4 {$ `5 v- |
  19. if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk) $ R" H) O& Z2 F  K+ r
  20.     {
    : M; y4 w3 n& M
  21. return SET;5 v0 y/ q$ P) v" a6 l# W% ~: D
  22.     }
    " f2 D8 |6 u/ k( r0 m3 B1 d
  23. else
    4 b1 T' }$ e" Q
  24.     {
    - A) t) p( P8 p: d. @( W* I+ y! f
  25. return RESET;# A5 `: q0 r8 k6 w' E
  26.     }
    ! y! v3 l5 D: h+ Z* i: A% E4 m
  27. }5 s$ i& G, Y3 Y: s+ w& ?3 R
  28. 5 S' H& Z6 d8 _) H% V
  29. /**
    & S; a% r; g8 m3 d. a* }8 Z2 {* D
  30.   * @brief  配置系统滴答定时器 SysTick/ `4 R% u7 o2 D3 w3 h: G+ d
  31.   * @param  无
    . W& V% r0 M5 X7 E) |: F' \8 f
  32.   * @retval 1 = failed, 0 = successful
    * p; R( h1 L. q3 Y9 Z& \
  33.   */
    ' q  G0 F3 E* L% _' u, I9 ?9 M! b/ h
  34. uint32_t SysTick_Init(void)
    2 T/ j5 B' t) w! i- h) ]* q5 t! `
  35. {! n# o4 o1 O# g6 n
  36. /* 设置定时周期为1us  */. Z9 }+ K% x$ d4 [" G+ u
  37. if (SysTick_Config(SystemCoreClock / SYSTICKFREQUENCY)) ! S5 [" y0 q: j, I) L. \, ]( w; A
  38.     { 9 t' ]: \# C$ y" h
  39. /* Capture error */
    5 c) ^7 _" q# O* A1 V8 [/ D
  40. return (1);) N0 i9 B7 M1 {& r, h
  41.     }
    ( U& Z% X& {6 C( E) H4 I

  42.   W' R' g, y% q9 Z- o
  43. /* 关闭滴答定时器且禁止中断  *// G" ?) m% x! S$ \8 T, Y" d" t) F
  44.     SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);                                                  % s) R1 J- C0 i# I
  45. return (0);( g* m. g( _, t, T# f$ W2 T  ]/ S
  46. }+ T6 c8 a- N) \" G# B* h5 N% |+ E

  47. $ h) ^, T" |: V5 [1 P7 c/ Y6 c
  48. /**# m0 h* A4 l: i7 |
  49.   * @brief   us延时程序,10us为一个单位
    1 N! F( s9 R2 c/ O& [) l
  50.   * @param4 A8 g: h: ?6 q- l7 {. D) Q- P
  51.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us
    6 F  r7 C  a- n2 E: `: r4 D
  52.   * @retval  无' X# y9 R6 {) H2 A
  53.   */
    2 g- _; S1 H. i# ~
  54. void Delay_us(__IO uint32_t nTime)
    + f- [- a3 a( K7 u; U
  55. {     4 r. J' I$ t* _5 \* _; g
  56. /* 清零计数器并使能滴答定时器 */$ {5 ^- x. h. X) P; h2 O1 a6 k
  57.     SysTick->VAL   = 0;  
    5 [! r0 x/ x1 K% l3 P' o: C! e7 Q$ p  y
  58.     SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;     
    9 [8 z8 k' Y: p3 Y
  59. . ^" U. w, O  B" l5 X7 O+ l$ s+ S$ ?* a
  60. for( ; nTime > 0 ; nTime--)& p8 N: ~$ D$ r, y- y7 c& M: Q
  61.     {
    3 V; x# u0 E) u; Y" M  i6 A
  62. /* 等待一个延时单位的结束 */9 |7 V; F. [8 q. x) e/ Z& m' m# `
  63. while(SysTick_GetFlagStatus() != SET);
    : q$ [" D1 j4 t
  64.     }. w, }4 T& N9 f) ?% C6 _0 x$ P- S7 ]! s

  65. 0 D7 X3 i) [3 _0 J  X
  66. /* 关闭滴答定时器 */2 s. Y! u$ P8 f, j$ x- S; i' s
  67.     SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;8 k- [+ C$ T/ q* ?- H+ M6 N, w2 }
  68. }</font>
复制代码

5 X: |- v% P) i; w. M
    检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:
  1. <font face="Tahoma" color="#000000">#ifndef __GPIO_H
    1 P) H: N8 R& ?' j- B. ]
  2. #define    __GPIO_H: g) z4 j9 U8 ?) U- f

  3. - M" Z; V1 ^4 a1 G
  4. #include "stm32f10x.h"
    2 a& q4 g; G: \
  5. 4 c9 Y% s/ i- ]0 `/ N: u
  6. #define     LOW          0
    9 m. q! X1 m9 O3 \
  7. #define     HIGH         1, {4 {8 h6 K7 n- W; A3 t  Z' y3 z

  8. $ _3 @8 v) {+ |$ \0 D/ F
  9. /* 带参宏,可以像内联函数一样使用 */- r" E( R( H6 g8 ]1 d4 t; }
  10. #define TX(a)                if (a)    \
    5 W! f% m! I/ A' s6 E) l# i
  11.                                             GPIO_SetBits(GPIOB,GPIO_Pin_0);\/ P5 v  z5 N2 O$ F  ^2 Z7 `
  12. else        \; c) f4 O0 k9 Z9 V# c0 W6 x: j! l& N
  13.                                             GPIO_ResetBits(GPIOB,GPIO_Pin_0)
    + L+ p% D# g" U3 N: N* w- k7 q* T
  14. void GPIO_Config(void);
    % L+ b) g7 v$ U. X
  15. - X0 A& O; e8 X0 n
  16. #endif9 J) V7 |! ^' `$ W
  17. ! {. A% x( k1 U9 y3 w1 H
  18. #include "gpio.h"
    3 V9 `& R9 f# m

  19. # N9 o  V" F0 {2 ]) |& \
  20. /**
    ' k4 |# C6 n1 g8 v8 f& e( |
  21.   * @brief  初始化GPIO% ]* C8 u6 ?. e  V. }6 `0 t' O
  22.   * @param  无( |- I8 d5 V8 O4 \3 z
  23.   * @retval 无: N" r+ A: u3 `4 O, Q, D# G
  24.   */% m: M& Y/ \! Y9 Q: p
  25. void GPIO_Config(void)
    * j. L' X& L/ Y) r4 w% @+ S0 G+ K
  26. {        , B: d2 q+ \" A' z
  27. /*定义一个GPIO_InitTypeDef类型的结构体*/
    5 p1 ?- m. V5 x. D! s( \
  28.         GPIO_InitTypeDef GPIO_InitStructure;. R% I4 f$ m3 C( s6 S

  29. 8 r% X3 a; C, c5 C9 m
  30. /*开启LED的外设时钟*/# J/ b: D1 }* C' O+ K6 J* ?
  31.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
    - I; I- d2 M: k) k& U/ _
  32. ! o0 f! t/ U/ b: @
  33.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;    - s* C) a9 \: G( ~8 w0 x/ X
  34.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     4 [" u& ^% r- \: i6 A* Y9 b
  35.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + o4 |5 V4 F) e1 Y: B5 V8 z6 a( u
  36.         GPIO_Init(GPIOB, &GPIO_InitStructure);    * p5 h1 C0 N+ Q
  37. }</font>
复制代码

2 P  {" U  }* o# l( B
    在main函数中检验Delay_us的执行时间:
3H]P{7Z537([92MUCNH7CT9.png
    示波器的观察结果:
{$J7IITJ`4F5~C)UD_(99}T.png
    可见Delay_us(100),执行了大概102us,而Delay_us(1)执行了2.2us。
    更改一下main函数的延时参数:
X2HEQI]HZ_B{W1EI9AGFR7F.png
    示波器的观察结果:
{@EM)6PJ@Y7WDJ3`Q8D}J@X.png
    可见Delay_us(100),执行了大概101us,而Delay_us(10)执行了11.4us。
    结论:此延时函数基本上还是可靠的。
使用定时器方法的实例
9 K2 y+ s$ Q, @8 ]9 B# j1 \- {8 X
    Delay_us函数使用STM32定时器2实现:
  1. <font face="Tahoma" color="#000000">#include "timer.h"
    $ ^+ ~" U2 i0 A: Z( }4 N7 n

  2.   d/ X) r- Q' i; V4 p! E( f" Y$ O
  3. /* SystemFrequency / 1000            1ms中断一次' `5 e- W( J# P. P) B1 {
  4. * SystemFrequency / 100000     10us中断一次. G( h/ P% q" a" }$ @2 o2 t# R
  5. * SystemFrequency / 1000000         1us中断一次3 c3 i4 g/ j6 a0 E# s) v7 }8 m
  6. */4 L% n  a8 ~1 ]' N% |4 s
  7. 6 b- q" o8 r( Q! a5 `, r3 M- P
  8. #define SYSTICKPERIOD                    0.000001. z2 [# n: X5 [/ ^  N/ p7 W
  9. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    ; L& J) _9 g' r/ x% f

  10. ; g% J: F4 p& C* L4 ]
  11. /**
    6 a; j2 j# z$ i3 ]
  12.   * @brief  定时器2的初始化,,定时周期1uS3 c2 `; V/ f" V) x5 A4 f/ x
  13.   * @param  无1 r) \! _: P# z* d/ J
  14.   * @retval 无
    + A# t) ?* t" m9 l/ t* D/ }( m
  15.   */
    2 E3 h+ f: O; M+ t+ }; p
  16. void TIM2_Init(void)/ v7 s6 n9 @3 W6 M2 z0 v2 A
  17. {, a; @- b: X4 D& O
  18.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;6 x6 D" w# K' {5 a7 D1 L
  19. 5 i% J2 R( ?) u9 r$ h. P3 M7 K' C
  20. /*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */" b8 ^3 ~5 J8 s5 V5 Q3 ^% |4 K& q  H, p
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    : k9 U& d) V" ^, i+ e9 j$ X
  22. 9 c: G' ^/ g6 X- s- f; }+ @
  23. /* Time base configuration */
    # `5 u( |; l/ b5 T# X
  24.     TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;7 {% f% ]7 R8 C% I
  25.     TIM_TimeBaseStructure.TIM_Prescaler = 0;3 q3 y9 P% K9 N' q; V9 b: N! N1 g
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    , i% B$ \5 H) [, a$ [
  27.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);: s5 n8 V; ?: ]* z( ?

  28. 3 O. ?- A0 P& D" Q6 p: C/ @
  29.     TIM_ARRPreloadConfig(TIM2, ENABLE);
    ' a. m8 y. u9 `) p

  30. + t/ ^% N' E+ S- C1 N7 A, i# k
  31. /* 设置更新请求源只在计数器上溢或下溢时产生中断 */
    ( g7 Z) R8 W) s  O* v
  32.     TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global);
    ; Z! q4 N- U$ Z. T! J( U! \$ p$ d
  33.     TIM_ClearFlag(TIM2, TIM_FLAG_Update);& h# a8 m( X# ?
  34. }
    + f( }: b9 L" x/ ]

  35. * ~2 W, o' C$ |% C1 e7 l
  36. /**( d! u7 i8 `( v( l$ E+ i* j
  37.   * @brief   us延时程序,10us为一个单位4 g& d" n9 S7 T/ v
  38.   * @param  - s: X4 Q1 t! r) E
  39.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us
    8 n' _$ f( R6 Z' i1 g1 {& t
  40.   * @retval  无
    3 d$ U% q1 H2 R
  41.   */
    0 O1 _; P& X( b6 u, @; R8 J
  42. void Delay_us(__IO uint32_t nTime)
    " }- l3 a+ ]/ V: B0 Y/ [* c# O
  43. {     2 C: |4 B) ~  v# _6 s% c! l& C
  44. /* 清零计数器并使能滴答定时器 */& ?& J1 c1 C7 @/ A& e! F7 g
  45.     TIM2->CNT   = 0;  4 I7 P# r- S& e7 @. D8 j
  46.     TIM_Cmd(TIM2, ENABLE);     ' e" i' u$ }  {& \# A& [( k% W
  47. . z. N) P  s3 o6 x
  48. for( ; nTime > 0 ; nTime--)
    6 B9 C7 r" C6 }, h9 N& h% H
  49.     {" W; \& K$ h  }( s5 U
  50. /* 等待一个延时单位的结束 */
    - ]7 k- R' |6 p) b. O
  51. while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);7 _2 x& l7 i2 I
  52.      TIM_ClearFlag(TIM2, TIM_FLAG_Update);) p3 h. u. g, a4 Y
  53.     }9 N2 p  |" Z$ ~; {
  54. 0 E" \8 C# i7 Z; G  }
  55.     TIM_Cmd(TIM2, DISABLE);4 ^0 ?/ o9 B3 D6 U1 _1 G
  56. }</font>
复制代码
6 t+ V* t3 L( r  f
  在main函数中检验Delay_us的执行时间:
  1. <font face="Tahoma" color="#000000">#include "stm32f10x.h"
    ' W) |& E. V; Q  F1 i
  2. #include "Timer_Drive.h"
    8 j, s& q. }7 }1 p
  3. #include "gpio.h"& h) w8 \' _- e+ [6 J/ r
  4. #include "systick.h"
    8 n3 P( x$ i( B' u0 V

  5. * ^5 y+ R& a; b5 i
  6. TimingVarTypeDef Time;) ]/ [( \0 \1 l2 `" i- v0 ~' T  S

  7. % L  v" G+ G/ y& |5 Q/ s& H
  8. int main(void)
    ' n7 P6 B- q2 ?; G% ~5 J! u& l: L0 R$ e
  9. {    0 E4 R8 Y$ x& q+ x$ l
  10.     TIM2_Init();   
    ! j1 U, q" w% ]1 C
  11.     SysTick_Init();
    " G  e5 k: k7 R7 _1 d( I: q
  12.     SysTick_Time_Init(&Time);5 e. f5 x# [  l# M% T- M
  13. $ f* M- f; U' ]
  14. for(;;)
      W4 A  R! w' d
  15.     {- F, j! ?0 U& o0 ]$ Y& [: C
  16.         SysTick_Time_Start();
    1 Y! ]; b/ u6 M/ H+ [& I
  17.         Delay_us(1000);. g: T+ c. A: B5 o
  18.         SysTick_Time_Stop();
    9 f! k# R2 F; H$ ]1 c$ q0 Z
  19.     }     
    # H; ]4 p* Z* m6 z. |6 g
  20. }</font>
复制代码

. m" e/ O# b/ n
    怎么去看检测结果呢?用调试的办法,打开调试界面后,将Time变量添加到Watch一栏中。然后全速运行程序,既可以看到Time中保存变量的变化情况,其中TimeWidthAvrage就是最终的结果。
GPFQEL[VE53~YBL~2F7G2PV.png
    可以看到TimeWidthAvrage的值等于0x119B8,十进制数对应72120,滴答定时器的一个滴答为1/72M(s),所以Delay_us(1000)的执行时间就是72120*1/72M (s) = 0.001001s,也就是1ms。验证成功。
    备注:定时器方法输出检测结果有待改善,你可以把得到的TimeWidthAvrage转换成时间(以us、ms、s)为单位,然后通过串口打印出来,不过这部分工作对于经常使用调试的人员来说也可有可无。
两种方法对比
软件测试方法
    操作起来复杂,由于在原代码基础上增加了测试代码,可能会影响到原代码的工作,测试可靠性相对较低。由于使用32位的变量保存systick的计数次数,计时的最大长度可以达到2^32/72M = 59.65 s。
示波器方法
    操作简单,在原代码基础上几乎没有增加代码,测试可靠性很高。由于示波器的显示能力有限,超过1s以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。
1 a1 C2 Q( m( m# ?9 y
收藏 评论0 发布时间:2021-11-7 15:39

举报

0个回答

所属标签

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