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

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

[复制链接]
STMCU小助手 发布时间:2021-11-7 15:39
前言
    测试代码的运行时间的两种方法:
  • 使用单片机内部定时器,在待测程序段的开始启动定时器,在待测程序段的结尾关闭定时器。为了测量的准确性,要进行多次测量,并进行平均取值。
  • 借助示波器的方法是:在待测程序段的开始阶段使单片机的一个GPIO输出高电平,在待测程序段的结尾阶段再令这个GPIO输出低电平。用示波器通过检查高电平的时间长度,就知道了这段代码的运行时间。显然,借助于示波器的方法更为简便。
    8 p+ X* G$ z# G* e7 V( c/ K% F
    5 b+ R: H. D2 E2 L7 L/ W& L
借助示波器方法的实例
    Delay_us函数使用STM32系统滴答定时器实现:
  1. <font face="Tahoma" color="#000000">9 p6 e" [( [2 l' k  b3 d4 ]
  2. #include "systick.h"
    / B5 D( ^, W: B# A  O( U7 d, U

  3. / s4 L8 u! r' [2 Q% \) v
  4. /* SystemFrequency / 1000    1ms中断一次; p+ e9 ~4 @4 v; i
  5. * SystemFrequency / 100000     10us中断一次# M4 F$ s( O- Z. D, j* L* ?
  6. * SystemFrequency / 1000000 1us中断一次9 [, m! g, j' C' J! x! |5 C
  7. */7 s8 i. L" _& I8 ]
  8.   c! f4 ]: _) `4 ~( B& Q
  9. #define SYSTICKPERIOD                    0.000001. K( N8 B4 N! b4 S8 k6 a0 _: h4 S
  10. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)- S) x+ k3 w1 _$ ~0 _8 H& g

  11. " Q$ s. M6 V2 P: H0 v4 R0 M
  12. /**
      t8 B- }7 p$ B. e
  13.   * @brief  读取SysTick的状态位COUNTFLAG
    ! S2 a' d- W/ h* K  w' y
  14.   * @param  无
    , r4 I% a4 u9 E4 b6 q
  15.   * @retval The new state of USART_FLAG (SET or RESET).
    : `$ t) o: C, f& m  P
  16.   */; r$ W0 U9 A* b9 N  J
  17. static FlagStatus SysTick_GetFlagStatus(void)
    , c; K0 G8 |/ }. V( V& k! B
  18. {  y; d$ [1 z  J& k& E7 X
  19. if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk)   Y. F! i; P! R3 ~" o  c5 T3 f* o
  20.     {
    - h+ l0 l2 P" w# _, h+ x
  21. return SET;
    & [0 [9 B! ?& ~$ k, C6 A+ r; c
  22.     }/ n3 H$ R- y9 l+ c; G- P
  23. else
    + s- @+ d! S* S: _, a
  24.     {
    # U, }% d3 f1 i- Q7 Q
  25. return RESET;
    : F+ g' V5 H& T$ L
  26.     }/ L$ r) p) ^7 `: p
  27. }
    ( y9 n4 S. v% t+ j5 `( w" G& T. N
  28. 1 i9 M3 k; X2 V
  29. /**' V  x+ B0 D% ^- R
  30.   * @brief  配置系统滴答定时器 SysTick# X' q$ x4 u! Y
  31.   * @param  无2 j* h) f. @4 N! K
  32.   * @retval 1 = failed, 0 = successful& x  n0 E; O/ P: S+ n. F# N" I: _
  33.   */
    ' P; f6 Y- y6 Z+ d
  34. uint32_t SysTick_Init(void); L! d, x# }# k: Y8 z7 K" }
  35. {
    1 ], o1 _% {4 C, U6 r
  36. /* 设置定时周期为1us  */( a4 R. X6 s3 N0 B; C* c; f
  37. if (SysTick_Config(SystemCoreClock / SYSTICKFREQUENCY))
    7 _8 _' j* i  c
  38.     {
    % d. C' m0 N, A
  39. /* Capture error */  i0 d# H$ \3 k: F4 }3 c
  40. return (1);
    6 D# i: K. m' |" r5 d' n' m
  41.     }" G2 V5 [3 W0 A$ T# @' _) w4 w2 s
  42. ; d0 @2 J: k6 v9 Y  O
  43. /* 关闭滴答定时器且禁止中断  */' o% f( F6 ~+ N
  44.     SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);                                                  
    5 i% s# W7 N, U( ]: A3 k
  45. return (0);
    5 s# L7 ^$ _1 r" ^
  46. }. ?, X( ~9 b4 h: u

  47. $ u% t7 J# E( o" [4 J
  48. /**( U) T; M+ N! @# {( ~
  49.   * @brief   us延时程序,10us为一个单位
    # T5 m. [, l! z5 g
  50.   * @param
    ) R: Q1 _! j: l: t
  51.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us# N" C' s7 U8 w( h5 w. H, y
  52.   * @retval  无* ~$ g0 Y. F; [. ?; S2 G; K* P" Z
  53.   */
    8 c- \7 V1 D' y8 `& v
  54. void Delay_us(__IO uint32_t nTime)
    / C" g" F$ r$ p3 m8 s
  55. {     
    $ v: B* N% D6 @% G
  56. /* 清零计数器并使能滴答定时器 */
    . O# `$ Z) [0 _: n1 u: Q, k. ^! N
  57.     SysTick->VAL   = 0;  
    . o/ H. q" ^  W4 h) w
  58.     SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;     
    3 M; b& ?8 R9 u! `8 s" o6 V

  59. 2 Q3 \2 ^  S4 q6 c1 G
  60. for( ; nTime > 0 ; nTime--)
    % _9 v& t$ c6 ^- N2 i" y% P
  61.     {
    : ]6 {, g; N8 p4 O( A8 P
  62. /* 等待一个延时单位的结束 */
    9 L9 `0 k) L& ?) A! b
  63. while(SysTick_GetFlagStatus() != SET);) l7 J4 _' D& {" C3 M2 L3 ]
  64.     }
    & e" I. }" W/ c4 l- A3 _3 ^
  65. ; {/ _+ ^+ Y3 S: ]8 f/ j( `1 S
  66. /* 关闭滴答定时器 */5 k6 `- u( a! q: H, @! ]
  67.     SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
    ! a7 G& {4 n+ H- G- O
  68. }</font>
复制代码
6 G/ z7 @3 P6 C: ^5 P- ^& E5 [* F
    检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:
  1. <font face="Tahoma" color="#000000">#ifndef __GPIO_H
    , {2 j3 L0 a& v! _3 C7 |/ k5 X
  2. #define    __GPIO_H
    ; ^" Z' p4 @7 J2 o% k, |* i

  3. ( s6 F, f' K$ Z' |% U" y7 Y
  4. #include "stm32f10x.h"" b( ]% ]( o3 {& Z
  5. 8 g; L6 i2 o8 L" x2 h( u
  6. #define     LOW          0+ p1 n: L1 I+ i
  7. #define     HIGH         1+ S4 X& {5 y1 v, u6 H

  8. ) v2 w  V+ T4 b2 w# t2 U
  9. /* 带参宏,可以像内联函数一样使用 */+ F6 `" G( D9 V) K  ^* i5 U, ]5 g: D
  10. #define TX(a)                if (a)    \, |9 w# m$ h# R. w  d' X
  11.                                             GPIO_SetBits(GPIOB,GPIO_Pin_0);\" B6 ^( @2 m" L, ?' _: T) u
  12. else        \
    9 E! x5 ]% H& B3 _* q  b
  13.                                             GPIO_ResetBits(GPIOB,GPIO_Pin_0)8 r2 }2 K1 b1 u' T6 b' Z
  14. void GPIO_Config(void);
    # T0 ?3 V/ P( u$ T7 [
  15. " U1 \+ H; Z/ _# n. a, B: H! u
  16. #endif
    , }' ^" d  C" q: s
  17. $ j' M2 {) l% Z1 s
  18. #include "gpio.h"
    - r0 w1 z% P4 s& o) j# l

  19. 9 E; ], I9 h6 ]* ]; ?
  20. /**
    9 ^8 k/ x1 K& S8 T* Q5 y) m
  21.   * @brief  初始化GPIO. K5 v# \" K* z
  22.   * @param  无
    / T8 B1 S; z( |& x$ ]7 j
  23.   * @retval 无0 k2 t9 n, s& Y' i6 c- S5 j
  24.   */+ ?& J( K/ E: K3 N! R
  25. void GPIO_Config(void)
    9 r+ v9 ~0 d5 [) A4 Z+ _$ s
  26. {        . o8 ]' F" B# m/ j! {- C8 i
  27. /*定义一个GPIO_InitTypeDef类型的结构体*/8 @4 M2 B& [1 U- v  X# s/ K
  28.         GPIO_InitTypeDef GPIO_InitStructure;# ?5 G2 y% ?' c
  29. ' k7 \$ Q, r. G1 H& [; m
  30. /*开启LED的外设时钟*/
    9 `  y/ l9 [/ T  J2 k" t4 N8 E
  31.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
    & r2 g1 a) Q' S8 b$ o6 I
  32. 5 V& }3 O- d3 M$ }! a
  33.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;    . M0 H% ~+ h; v$ N' V  b* Z4 m
  34.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     + p4 ~8 `& U# i  u1 d* \  Z
  35.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; ; e7 c3 a' P5 U9 m+ U! n
  36.         GPIO_Init(GPIOB, &GPIO_InitStructure);      y% J0 C6 i; S+ _
  37. }</font>
复制代码
% ~: u* f1 N: l4 |3 a- o  ^! N7 W
    在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。
    结论:此延时函数基本上还是可靠的。
使用定时器方法的实例, [5 z$ g9 l  s% S! T8 Q
    Delay_us函数使用STM32定时器2实现:
  1. <font face="Tahoma" color="#000000">#include "timer.h"
    ' X/ Z; Z) {3 l- T$ h3 A% ^
  2.   P, t7 \7 ^2 n1 D! f4 \
  3. /* SystemFrequency / 1000            1ms中断一次. Y- T% `' ?, ~- i
  4. * SystemFrequency / 100000     10us中断一次
    , u2 D. S( \( \, z* Q
  5. * SystemFrequency / 1000000         1us中断一次& R) q- }3 c0 p& v; l4 o
  6. */2 }- v4 _% W8 t7 y9 c
  7. & G& W) j/ V9 i4 U: k* f; ]
  8. #define SYSTICKPERIOD                    0.000001
    + J8 p  k7 T# t8 G8 ^* W' [! b; x" C
  9. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    / T2 B& e0 V* g, N* f* H) E, ^" Y  L

  10. % h% o# [& ~7 d$ Y
  11. /**
    1 y; n$ q5 s3 b0 W
  12.   * @brief  定时器2的初始化,,定时周期1uS/ l( E+ F$ M) r$ v7 E/ Q/ Q* N$ S) A
  13.   * @param  无( m( B) L% X: i6 g
  14.   * @retval 无: z* b+ R( o$ K& e* B+ E" N
  15.   */$ [; r, T, x- n( T( n
  16. void TIM2_Init(void)3 ]6 ]- V+ {9 ]; q* u' F9 x2 Q
  17. {% ^; ?+ Z7 w3 x8 T3 B" h6 t
  18.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    , ?4 V2 H! B6 g; b9 k

  19. 0 g' t, r' e3 P/ D- G+ e, s
  20. /*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */
    * ]1 o/ S# ?, |
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);9 b. h+ t$ B" Y' H; b) Y( P( F4 P

  22. 9 I  g1 j2 J  t4 F# L
  23. /* Time base configuration */, j4 c/ [$ `. {2 @
  24.     TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;' w" W  l4 r& h- X+ ^7 _' ~9 A
  25.     TIM_TimeBaseStructure.TIM_Prescaler = 0;
    ; [- D; N+ j" ~' C, r7 w! T
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    ' X6 S5 D" T. h! R
  27.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    7 g# M7 U5 q6 ?
  28. 8 E0 ~) c. H9 D/ E
  29.     TIM_ARRPreloadConfig(TIM2, ENABLE);9 Q( n0 d2 S9 b# ^& t

  30. % n% {9 M) u* y  Q- ^7 h# D# j! B
  31. /* 设置更新请求源只在计数器上溢或下溢时产生中断 */
    6 O# L) f' N+ J: w/ U
  32.     TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global); 5 M! v- E3 x0 v0 f
  33.     TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    & w# h7 A) D* W: X0 l) [& N3 g
  34. }
    * \2 X0 O, p3 X* h

  35. / P6 A, W& j$ `2 T/ }  g+ `7 [$ G
  36. /*** o0 @1 _7 p7 M1 o% M
  37.   * @brief   us延时程序,10us为一个单位1 M& i. X, p" o& u( F+ I
  38.   * @param  8 M. ?2 m+ C% L
  39.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us
    ! d2 y1 y. ?9 |2 i, l; W
  40.   * @retval  无5 g# N+ m6 g  b" Q7 }6 Q( F
  41.   */" j; J) U  d( p, s
  42. void Delay_us(__IO uint32_t nTime)
    6 l2 D0 ?% k4 k# ]9 N6 E5 t
  43. {     : @! f" j1 U1 S
  44. /* 清零计数器并使能滴答定时器 */
    / _* S) {$ S/ f
  45.     TIM2->CNT   = 0;  " ^: z3 y! ?  @' p% b
  46.     TIM_Cmd(TIM2, ENABLE);     
    , W1 W# ]* b) Y# o

  47. ! d, @! i( x3 _5 W/ K) L5 K  W$ K; H
  48. for( ; nTime > 0 ; nTime--)2 _, R8 M6 G, [* Y" @
  49.     {: P6 A$ z1 U+ a; O8 i1 p8 @2 X
  50. /* 等待一个延时单位的结束 */6 S. C. b1 V! s& Z% U- y  \
  51. while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);3 [6 @! |; Y$ `' l4 o7 U
  52.      TIM_ClearFlag(TIM2, TIM_FLAG_Update);
      @: y6 j5 A9 O, \8 Q
  53.     }' Z% f" q2 ~) j( Y( Y  A
  54. ; {$ X& P" G5 ]) b6 s( H7 P
  55.     TIM_Cmd(TIM2, DISABLE);1 D: ~1 M  r/ }+ a
  56. }</font>
复制代码
( G4 e. V! z- |! M3 g: B; X$ Z3 h
  在main函数中检验Delay_us的执行时间:
  1. <font face="Tahoma" color="#000000">#include "stm32f10x.h"% a  G. E( c/ S/ V- y
  2. #include "Timer_Drive.h"
    3 A0 C) a+ ^  L. I5 ^" ?% k% J, m
  3. #include "gpio.h"; P& }. q6 K; W. r5 Y( \: J, v% @
  4. #include "systick.h"! a& W9 P# x4 k" t; A( i3 n

  5.   m; W7 _$ v+ b+ l
  6. TimingVarTypeDef Time;
      i0 m5 u2 r/ x& ~! x1 Z

  7. + x% b. C6 d  R  x
  8. int main(void). G1 t# v  A9 |1 ?: U- B! h
  9. {    - I! q3 J2 M$ S
  10.     TIM2_Init();    ' {6 A7 a: `9 w" ~& r( G; O
  11.     SysTick_Init();
    ; A7 `) @, p8 I# N# f3 W4 z
  12.     SysTick_Time_Init(&Time);
    / N5 i. u% S9 c6 E% a+ w
  13. ! d* n7 {$ e5 z$ b% I" N
  14. for(;;)3 G+ J, |$ v' G1 Q! q
  15.     {8 y$ J7 i6 n, k7 r
  16.         SysTick_Time_Start(); 6 j2 P) ?. Q9 X- T3 H$ }
  17.         Delay_us(1000);
    % R8 Z: ~2 s: T, \8 e% \1 U5 @" [4 i! E
  18.         SysTick_Time_Stop();
    : l. _8 K4 D1 k7 k. u
  19.     }     $ `0 c- w$ w+ x8 r* Y) B
  20. }</font>
复制代码

4 h0 A# i* k% _  `9 M3 z' M* Z
    怎么去看检测结果呢?用调试的办法,打开调试界面后,将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以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。

4 X+ n, o! }5 }2 c; I
收藏 评论0 发布时间:2021-11-7 15:39

举报

0个回答

所属标签

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