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

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

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

    ) z( h3 _4 R. S: `' J0 a3 j
    4 F  {( @, d, g; w2 S
借助示波器方法的实例
    Delay_us函数使用STM32系统滴答定时器实现:
  1. <font face="Tahoma" color="#000000">$ ~' y" a" D5 C2 d" e
  2. #include "systick.h"
    ' x3 Q9 m) G9 W! ^* C* X

  3. - Q6 t, p+ W; ^+ w5 e
  4. /* SystemFrequency / 1000    1ms中断一次
    - Z) Z( m) }. E* p
  5. * SystemFrequency / 100000     10us中断一次
    5 }) \6 X' K  B% D) X
  6. * SystemFrequency / 1000000 1us中断一次
    % W. y% s/ V6 F! m8 [+ |
  7. */
    8 M. x3 g2 @2 H) C

  8. ) \5 ^& ?, U' W* N! }
  9. #define SYSTICKPERIOD                    0.0000011 Z! p/ a: G$ o, q3 B0 e3 O/ w
  10. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    " @1 K/ P6 W, |; d, A3 `
  11. 5 R* ~. N0 y% I8 ~6 d  [
  12. /**
    7 P& e; Q# h, E. @) k! `
  13.   * @brief  读取SysTick的状态位COUNTFLAG- k, c+ O' H7 ?6 O8 U" r9 t
  14.   * @param  无% j) B- K- u/ E, d- `
  15.   * @retval The new state of USART_FLAG (SET or RESET).3 h) G  I  ~+ Q" x) |
  16.   */1 X+ o4 \1 ^' ~0 D
  17. static FlagStatus SysTick_GetFlagStatus(void) , v3 J: A# O5 h  f! X: c
  18. {$ f5 O; U; C& Q! C# c3 k& r* k
  19. if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk) + O" d3 |% W. ~2 k
  20.     {* @: t6 f# \8 S; a
  21. return SET;
    7 n6 q( e7 F% ?% l+ A9 O
  22.     }& A  M# s9 j0 v: F- ]( |
  23. else
    , q% |4 X5 V8 l: j5 V+ r& E4 u! V
  24.     {" q7 x7 ~+ T) ~* I
  25. return RESET;
    2 U  ^. W- W) ^) j) t7 W+ P
  26.     }, |$ ~5 @0 e# G/ [( c
  27. }: `" S6 a2 C" l# g( d$ _1 E: N- @
  28. 8 |0 X0 [& V" O, z8 q
  29. /**- @) p: [& @( a+ M
  30.   * @brief  配置系统滴答定时器 SysTick5 ?4 n  p+ d! r5 R( Z( _4 z5 g
  31.   * @param  无" S- N( _4 T1 M% D& ~7 i  B
  32.   * @retval 1 = failed, 0 = successful
    3 ~  N  U6 ^! w
  33.   */
    3 f& o: k0 `4 ~; G0 s9 S, u1 |- r
  34. uint32_t SysTick_Init(void)
    " q" Z) @$ y/ x+ {; w/ i
  35. {* }5 U, P7 }" G& }
  36. /* 设置定时周期为1us  */4 E" b2 \  d, S6 z! F; \4 q* Z
  37. if (SysTick_Config(SystemCoreClock / SYSTICKFREQUENCY))
    . w7 }$ H' Q7 N# U
  38.     {
    . d, |* _2 W; I! l
  39. /* Capture error */" X- Y: `1 r4 r# ~
  40. return (1);( {3 C+ @; b/ S. `& ?" R* D
  41.     }
    / p" j7 e0 M* v( H, {* n

  42. " n$ y. }7 Z5 Y5 N7 B
  43. /* 关闭滴答定时器且禁止中断  */
    ! _% w/ y4 N2 T5 ~" l& B
  44.     SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);                                                  5 A: o( g2 [; j/ B9 F  g! B+ K
  45. return (0);
    ! d( t2 d) _1 C; w2 j1 U+ e4 j
  46. }
    ! b! s; W4 i: r5 h' X& H) C& Y* n

  47. " A2 s- g/ u' \5 Z
  48. /**2 K4 w! p3 a, _* t6 l
  49.   * @brief   us延时程序,10us为一个单位
    0 a7 k+ G0 Z/ R2 n/ d
  50.   * @param
    7 k+ d; [. D5 z8 F- @$ m
  51.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us
    - e6 z* J! S9 G' v6 P  R2 I9 u
  52.   * @retval  无6 A' ^) f3 w+ _! ]2 \
  53.   */% ^$ f7 H; @9 l: x1 ~
  54. void Delay_us(__IO uint32_t nTime)# q' }) n4 F6 T9 o# e1 G
  55. {     
    ' x- o( w+ Y  q' a
  56. /* 清零计数器并使能滴答定时器 */7 `' W6 D2 M( E# ?
  57.     SysTick->VAL   = 0;  
    2 E, Z: j0 J8 b: E3 v" v
  58.     SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;     / X1 G/ _$ Z! ~7 A

  59. $ B4 ~) o3 E7 R: W* n; E) S; B
  60. for( ; nTime > 0 ; nTime--)$ E$ F% k' J  y# [
  61.     {2 L6 Z/ T9 P- g$ N8 L2 h+ h, @4 c
  62. /* 等待一个延时单位的结束 */& i7 x# q6 B0 p/ g( x
  63. while(SysTick_GetFlagStatus() != SET);
    2 l$ n) {7 G+ ?, O% j
  64.     }2 E, w9 @# Q: A
  65. 4 s$ C; {( ^9 p3 F. [; u" Q* O
  66. /* 关闭滴答定时器 */) {% I, v) \5 U& V" c
  67.     SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;4 N2 [. w! r6 f1 |2 r
  68. }</font>
复制代码

/ Q8 g# V6 ^% i! x( S
    检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:
  1. <font face="Tahoma" color="#000000">#ifndef __GPIO_H
    3 p$ }6 w* }% b# s
  2. #define    __GPIO_H  M' m* E* T: y& M" Y4 a% c

  3. 0 Y' x: ?3 f) n
  4. #include "stm32f10x.h"
    $ U: o& Q2 o/ Z( v- b, Q$ s

  5. 0 d8 I5 J: J/ A1 v. q
  6. #define     LOW          0
    " \) B( q7 t7 E8 T" Z2 X
  7. #define     HIGH         1
    # M* W# f! ^# t4 k7 R3 v
  8. 5 ]: R' N+ v$ m. v0 _0 z" P! d
  9. /* 带参宏,可以像内联函数一样使用 */
    ) v/ L9 p- e# S8 }4 h: O* @0 ]
  10. #define TX(a)                if (a)    \  d1 B: P: |$ y  J% \9 |6 i
  11.                                             GPIO_SetBits(GPIOB,GPIO_Pin_0);\
    6 l+ h  k0 Z9 a% q) Y
  12. else        \5 G- e4 Y& H2 \2 ?- l
  13.                                             GPIO_ResetBits(GPIOB,GPIO_Pin_0)
    1 S4 d, q5 ]- u9 Z6 X
  14. void GPIO_Config(void);
    5 d1 l. l+ H/ b5 g
  15. * O4 }" J6 O9 i1 ?2 N1 y
  16. #endif
      n' t; m+ G- w7 s1 L

  17. - M/ s0 P0 S, h! I) E, U
  18. #include "gpio.h") y. U& D' Z5 o, Y$ {# T; P2 K

  19. " H; e" l( K1 ^- S  y
  20. /**
    6 Y  z, E8 }3 y$ n: R
  21.   * @brief  初始化GPIO
    9 n) U0 k- B" h* M2 F0 Z3 p8 O
  22.   * @param  无0 ?7 |) n; g3 \% @/ D! }
  23.   * @retval 无
    , j( p: q: q7 t) c6 Q# r
  24.   */) ?. |' J1 A( F+ v. Z' s0 I
  25. void GPIO_Config(void)
    # \& ]7 x' u3 o( [- W& e
  26. {        
    ' O3 N5 J( ]- m+ I* N
  27. /*定义一个GPIO_InitTypeDef类型的结构体*/
      T' c- T, F; S( \
  28.         GPIO_InitTypeDef GPIO_InitStructure;. i  t2 c+ _; L, _
  29. / v3 c) W3 @9 Y
  30. /*开启LED的外设时钟*/
    % s1 M7 k/ r' M, x, Q3 c( E
  31.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
    , v( l4 |* g7 @! O) {) \0 t2 k
  32. " z3 u' p" F5 V1 F, h: W0 \. P
  33.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;    & M+ d6 W: K8 @- E' v
  34.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     , m1 V/ K) ]2 S4 K8 _. \
  35.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    % a- ~3 d' G3 x" [3 d6 z
  36.         GPIO_Init(GPIOB, &GPIO_InitStructure);   
    " G9 N8 \2 g: E1 ~4 N- |6 ?
  37. }</font>
复制代码
' M7 l! _3 t2 T; W7 P% r
    在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。
    结论:此延时函数基本上还是可靠的。
使用定时器方法的实例# g4 _0 ^% I: E) |7 F
    Delay_us函数使用STM32定时器2实现:
  1. <font face="Tahoma" color="#000000">#include "timer.h"7 `  V3 I/ q5 I9 c9 d+ J

  2. $ N( m9 E; {/ X
  3. /* SystemFrequency / 1000            1ms中断一次
    ( C$ s! B6 k( v* |( [6 J6 H& \9 c
  4. * SystemFrequency / 100000     10us中断一次
    ( F- S' I+ V  ], o
  5. * SystemFrequency / 1000000         1us中断一次
    / `+ o) Q$ O4 c, |4 a
  6. */# J8 n3 Z$ s3 }) m  g' ~: r
  7. . Y( y' b$ a4 ~& H' s
  8. #define SYSTICKPERIOD                    0.000001
    % P  s: d- N  [8 \/ W
  9. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)3 b& [, G/ Y) r$ F

  10. 4 A  l, B8 z, [
  11. /**
    2 L" b" l8 N. ^" v% C5 w
  12.   * @brief  定时器2的初始化,,定时周期1uS
    , K7 m* d4 F4 S& i5 G
  13.   * @param  无
    ( _" \) \$ d4 i+ O3 ~, K
  14.   * @retval 无
    $ c+ [4 g0 [  z3 t, J
  15.   */' l6 C/ d- d8 M5 x5 j( P
  16. void TIM2_Init(void)
    + I( i3 x0 v# m
  17. {
    5 w) e/ g! z+ o5 ?5 l! t( M
  18.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    3 k: X2 m+ _' N9 R, L& k
  19. ' Y. C/ C- _4 j" q! O) L1 Y
  20. /*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */- r& v) }0 I" y0 ~$ ]3 ]/ o/ D; r
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);$ o4 Z7 E) X3 c0 E: w  j4 j
  22. 2 ~' H8 ?  h% T
  23. /* Time base configuration */* l5 I/ {. }, H" R$ t# j- m& b
  24.     TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;- Y6 O3 \& K$ c: L" E
  25.     TIM_TimeBaseStructure.TIM_Prescaler = 0;
    ' e& ^& l. H3 Z
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;$ J' v5 G! t& J; K6 S% ?
  27.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);8 ~" q/ [, w  J1 ]

  28. 4 e7 h8 o+ _- a/ L# Z
  29.     TIM_ARRPreloadConfig(TIM2, ENABLE);, k# Z7 Q  f5 h" x" r
  30. ) `3 g/ w0 W- k) z' W* y
  31. /* 设置更新请求源只在计数器上溢或下溢时产生中断 */' K, r; r9 Z! i( M5 T: `& M
  32.     TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global);
    , O, B4 ~8 M2 Y/ X2 h* j
  33.     TIM_ClearFlag(TIM2, TIM_FLAG_Update);6 |0 ]5 G0 ^1 F1 w
  34. }1 i- _6 `% d2 L, W3 b9 |/ V
  35. 9 e: J/ Z, H3 `
  36. /**
    ( v/ o9 m% K" G4 f0 L
  37.   * @brief   us延时程序,10us为一个单位
    " J1 t( u3 B9 M
  38.   * @param  6 L7 J% W7 p) ~9 J% m$ q$ w) C
  39.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us
    % w0 D2 Z" v* K( R% }, P9 k
  40.   * @retval  无( {1 y2 V. a, I
  41.   */% e( Q: c$ d1 U( E* t
  42. void Delay_us(__IO uint32_t nTime)
    7 f: N7 r7 H8 o" G
  43. {     
    ' `( a: N! Z2 C6 ^# F& Q( x  u- z5 Y
  44. /* 清零计数器并使能滴答定时器 */
    , F4 U0 j! Q2 R) v1 F
  45.     TIM2->CNT   = 0;  ) o2 v# {/ S. l
  46.     TIM_Cmd(TIM2, ENABLE);     
    8 L# i8 l0 H7 A$ o  f) z$ J# c
  47. ! Y3 b$ c: w+ @6 }8 E% S& p- @
  48. for( ; nTime > 0 ; nTime--)
    * I% B. k6 T; K1 p
  49.     {5 ]: g/ A4 @+ x: x
  50. /* 等待一个延时单位的结束 */& d, Z" L* o& E  k& N
  51. while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);
    3 @& x) W6 Q7 ]8 c
  52.      TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    4 _2 f. y1 Z' ~7 V8 U+ ?/ h
  53.     }
    3 r& E& L9 w, g& {) v5 W

  54. / Z4 `# `# n0 H- M# k
  55.     TIM_Cmd(TIM2, DISABLE);0 l/ Q6 [2 o/ q
  56. }</font>
复制代码

' G- n& R" G7 t8 `1 V
  在main函数中检验Delay_us的执行时间:
  1. <font face="Tahoma" color="#000000">#include "stm32f10x.h"3 R# K- t8 v/ z# P
  2. #include "Timer_Drive.h"
    3 Y5 E6 k' B5 N6 J4 O, m% r
  3. #include "gpio.h"& A- `4 P- q+ @6 W' t+ u% ~& W
  4. #include "systick.h"2 m; {( l8 r1 O3 {+ O
  5. # ]8 j8 i# H* [' G$ @1 p
  6. TimingVarTypeDef Time;
    8 s& v' ~" k+ k& b8 n
  7. . I* @6 |. Z3 \/ w
  8. int main(void)( b! S  Y6 H& `, G8 l4 }
  9. {    + H# {/ n7 N7 y$ ?9 U* F+ Q
  10.     TIM2_Init();   
    ( n8 x! W8 K1 Q( d1 }
  11.     SysTick_Init();
    7 e" O" t- k! G' Z' _1 P. L
  12.     SysTick_Time_Init(&Time);
    ( e3 g$ @' ]( K

  13. ! D* F$ x  }0 g& k! r- p" L
  14. for(;;)
    / m/ a, Z* o4 c( g
  15.     {
    3 o- c; ^! N/ N6 D( K
  16.         SysTick_Time_Start(); 1 B/ A+ k3 F/ g+ ^/ N- o' J2 O1 O" s
  17.         Delay_us(1000);
    ) ?1 V8 t- g8 N, ?' p, P4 j
  18.         SysTick_Time_Stop();
    # u" j" Z% G4 K& {$ I: t, i
  19.     }     9 l* d% q8 a! j. j$ S
  20. }</font>
复制代码
: L5 v8 ]7 o" |5 k* Z( l% e
    怎么去看检测结果呢?用调试的办法,打开调试界面后,将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以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。

6 F+ y1 V* P% E1 J8 w  i
收藏 评论0 发布时间:2021-11-7 15:39

举报

0个回答

所属标签

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