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

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

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

      g6 I5 W# }' {: P! V$ F5 j4 G! N
    # M9 k0 T/ ?: Q- E
借助示波器方法的实例
    Delay_us函数使用STM32系统滴答定时器实现:
  1. <font face="Tahoma" color="#000000">
    : v$ d# q, d- G/ ]. Y
  2. #include "systick.h"( |0 f' X: Q3 y$ G& C$ v3 r

  3. 0 {% l; v2 K! U/ Y$ l4 P
  4. /* SystemFrequency / 1000    1ms中断一次, r1 z) n6 }; J1 _$ \1 k
  5. * SystemFrequency / 100000     10us中断一次
    + h- k/ m. X* ]1 }# i
  6. * SystemFrequency / 1000000 1us中断一次% [1 ~6 T% ~0 q6 o& ?; V1 e
  7. */
    1 I- W- W) P0 h5 |
  8. ! |$ \/ q$ n% e! y
  9. #define SYSTICKPERIOD                    0.000001
    % r& I! D6 S$ l5 W- N. Z
  10. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    ! U; L5 N8 l- J( F3 k1 p
  11. " l8 n3 k+ @" C2 R* p* B' ]9 g
  12. /**9 K3 E" Q( ^1 P' U0 z: z( c1 P
  13.   * @brief  读取SysTick的状态位COUNTFLAG
    ; s( }7 s! L; I# [$ i
  14.   * @param  无
    : B2 j2 F7 i3 o/ V4 [
  15.   * @retval The new state of USART_FLAG (SET or RESET).
    2 a0 S6 E2 z/ Z8 Q
  16.   */4 J* {) }8 i* {$ p2 t, N' H6 g
  17. static FlagStatus SysTick_GetFlagStatus(void)
      ]4 G. G" d- u7 d8 `/ X7 }2 `$ h  t
  18. {
    6 u  K- _; f, S4 q! W4 ]) F
  19. if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk)
    , W7 X( L$ m6 k0 @7 u
  20.     {! r, I! G! H4 I! f6 Y
  21. return SET;
    ) D$ P4 n1 _6 G: a9 N( S$ \& p
  22.     }
    ( W# L- m; @7 G: g, i8 |) f
  23. else
    4 `1 r, A1 n" i- w
  24.     {
    0 n+ Z% Z% q0 N) H. j
  25. return RESET;: C) o7 [7 q& y% }( b$ G$ a
  26.     }
    " F7 X3 }, Z& j: o* q& L
  27. }& m& J3 N1 l) E0 a  R4 K
  28. 3 o+ i6 b* C$ ^! r; \% a3 s
  29. /**
    . M" {% ]8 p! _
  30.   * @brief  配置系统滴答定时器 SysTick+ i6 P! {$ a$ r! b' v# c  s
  31.   * @param  无* O+ L+ F6 S! b1 @/ x
  32.   * @retval 1 = failed, 0 = successful0 ?% g! B! T% ?. E, Q7 L( ]
  33.   */% q) ?2 H% A; C3 h
  34. uint32_t SysTick_Init(void)! |" B2 ?4 M, r6 P% B, u
  35. {. W/ _: p/ n; G$ G* t
  36. /* 设置定时周期为1us  */; m( c4 X" l) h* i
  37. if (SysTick_Config(SystemCoreClock / SYSTICKFREQUENCY)) & \" p% I" G- m
  38.     {
    + d! k! o8 M" Y* `7 v
  39. /* Capture error */
    , P2 _) Z5 W2 n& F1 l' a
  40. return (1);
    ' k# C1 \: C* P: _+ J( N
  41.     }
    . N5 T+ Z. @) e" E" P9 e; `: v

  42. . k3 r7 B8 O  h5 J
  43. /* 关闭滴答定时器且禁止中断  */, ^6 V# {2 V: A" u
  44.     SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);                                                  
    3 v2 B2 u, T# |8 @" ]
  45. return (0);! u, f4 I+ A4 J3 y- L& N' F* K
  46. }
      |, I  }: k/ N

  47. - K1 K% I8 w  L; |4 e8 i5 k
  48. /**
    ) y- C$ F3 e4 Y1 t6 i
  49.   * @brief   us延时程序,10us为一个单位
    ) D9 x/ T7 X% x6 T5 h
  50.   * @param
    3 V) x/ J3 f  X8 n
  51.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us
    ) A1 |% ^+ f$ A; r, Z4 w5 a( A4 L; X) v
  52.   * @retval  无
    $ f) E! |- Y/ z
  53.   */# T" V2 U& H9 j7 @) x# T% ?
  54. void Delay_us(__IO uint32_t nTime)4 n7 |# Y$ o8 i) D
  55. {     
    ( k. W* ~6 w' z2 z- e" ]
  56. /* 清零计数器并使能滴答定时器 */  L% T. P; `* E9 u+ V
  57.     SysTick->VAL   = 0;  
    7 [/ {/ Z- n4 \
  58.     SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;     
    . f/ P# q. I9 o+ J
  59. - l) i) G9 A$ D* V, s9 ?+ M
  60. for( ; nTime > 0 ; nTime--)
    , _- \' @' ~7 h" P
  61.     {
    : X2 v! {' E1 h: u9 U  c
  62. /* 等待一个延时单位的结束 */
    - R7 @# g4 e5 q9 b$ u3 u( t% e
  63. while(SysTick_GetFlagStatus() != SET);) E+ F# H* Y8 }. n2 U' y
  64.     }
    ! |+ g5 E9 A7 }8 e6 \" f3 r6 ^7 Y

  65. # }2 x# h- g) u- v
  66. /* 关闭滴答定时器 */
      v. v4 Q# ]- S  }, N* u+ b
  67.     SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
    ) M2 g5 C7 d, N7 G* q6 `' o$ b
  68. }</font>
复制代码
) X! T) v1 j8 K7 Q; S2 B4 S  j* p
    检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:
  1. <font face="Tahoma" color="#000000">#ifndef __GPIO_H
    8 g) z& _; p* P
  2. #define    __GPIO_H
    6 I9 ~% B; H" R& b; _
  3. + w$ r( I; {+ E& P/ Z) c
  4. #include "stm32f10x.h"
    8 G  d- b# f' }& z9 H+ t# Z0 r; A
  5. $ g8 I' Z3 Q2 B. s6 G2 r' W3 A' u
  6. #define     LOW          04 U; P6 X" l9 f) d
  7. #define     HIGH         1
    $ T5 c" l$ [3 }% |* B

  8. 2 t# Z/ c" i) E) r
  9. /* 带参宏,可以像内联函数一样使用 */
    # T1 p/ y4 R) F' H. y, A# a1 G3 Q
  10. #define TX(a)                if (a)    \- w& V3 w( t. P% I& l$ g4 F
  11.                                             GPIO_SetBits(GPIOB,GPIO_Pin_0);\6 F6 l3 z% |8 `' Y/ I
  12. else        \
    5 g: i2 l7 ?2 T4 Q( @
  13.                                             GPIO_ResetBits(GPIOB,GPIO_Pin_0), R! f5 D. z9 R, B8 w" x$ f
  14. void GPIO_Config(void);3 c( O% L0 }: p3 i+ W9 H% u

  15. " L8 t- p9 T/ O* V
  16. #endif
    ' E8 Y+ m: u# ]  u
  17. ; y4 m3 ?: O! |6 b9 {0 |8 N8 Q' E
  18. #include "gpio.h"/ X/ z2 `* m1 e, M: {6 F
  19. ' ?6 q4 Z+ b* J6 M, c, ~0 Y' r
  20. /**
    ) T( P* G. q5 P
  21.   * @brief  初始化GPIO0 ]5 L9 V" e! f* G  P
  22.   * @param  无
    2 ]2 y9 k5 v; t7 h" u# X% m& c
  23.   * @retval 无
    : T7 g" L9 R7 {& E, g
  24.   */, w# l, w+ e% d( x! A
  25. void GPIO_Config(void)
    6 H( m' Y/ S4 f- w
  26. {        % r4 q/ H/ D# S' \
  27. /*定义一个GPIO_InitTypeDef类型的结构体*/, G. @' h2 d% Z& t  G
  28.         GPIO_InitTypeDef GPIO_InitStructure;6 d5 ~, f. d6 a; E5 t

  29. - l+ I' h, B7 m3 r% G- C5 S
  30. /*开启LED的外设时钟*/
    ! i, d2 H0 P2 n1 z! M1 V) j( S
  31.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
    8 V1 c9 n+ d4 R5 d3 L% L

  32. + _" b) h4 b( Q0 L
  33.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;   
    ( D/ R3 ~, A1 E4 [5 Q
  34.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     4 L- e8 q7 W3 ?9 J5 A
  35.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    * k0 ^8 |0 g, U. S
  36.         GPIO_Init(GPIOB, &GPIO_InitStructure);   
    0 j( z8 G1 U  K3 U9 f* j
  37. }</font>
复制代码
7 D4 v  ^' o# `
    在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。
    结论:此延时函数基本上还是可靠的。
使用定时器方法的实例2 K( J9 c" w- O
    Delay_us函数使用STM32定时器2实现:
  1. <font face="Tahoma" color="#000000">#include "timer.h"1 R* m! B# w( A6 c8 ?3 J6 J, b$ L

  2. , c& T+ [" R: N: }
  3. /* SystemFrequency / 1000            1ms中断一次
    # n, _4 g# H" I  H. B; `) ^) b
  4. * SystemFrequency / 100000     10us中断一次3 o% h( O5 u. N8 L, w, N: l
  5. * SystemFrequency / 1000000         1us中断一次
      J; T0 y$ E  F5 ~, c
  6. */
    ) c* ?* ^1 v- H, V

  7. / g6 W3 G! U+ P3 s  X0 g
  8. #define SYSTICKPERIOD                    0.000001
    9 Y0 M: [, a' {2 w- u
  9. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    # x/ |. \  h+ Y1 a$ o

  10. ( }* _7 f. v% j% ^5 R
  11. /**& C1 H2 T- d$ [9 h2 b
  12.   * @brief  定时器2的初始化,,定时周期1uS
    , L; H1 `1 r/ O, q; x
  13.   * @param  无1 D6 [' v* a3 D2 Q
  14.   * @retval 无& q6 _8 o, ]& ~
  15.   */; x7 b% i" b9 a3 E3 F
  16. void TIM2_Init(void)( q$ |& w8 }; O
  17. {0 e3 |, ~, C0 N5 M0 E: Q& z
  18.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    , \: C/ Z5 C7 p: V
  19. " W/ P% _% X- x" c5 v/ L
  20. /*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */+ t& I7 |" [( V& }7 H4 j
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    ! \1 Z9 a0 T' O, c, R9 t
  22. . X# h, D9 v- A) z
  23. /* Time base configuration */
    : w- T, s" P* |0 u) h  s* p
  24.     TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;; ~' g9 H( k/ p1 v/ e: P3 K2 g
  25.     TIM_TimeBaseStructure.TIM_Prescaler = 0;
    $ M2 q4 f4 x' W1 J4 E
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    & |, {  U* h- x/ _/ z
  27.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
      J4 \& D! l" P6 U# M

  28. ; H7 {2 B1 R/ F8 C8 D9 T
  29.     TIM_ARRPreloadConfig(TIM2, ENABLE);
    3 q; ?; ~( |# `, ?. N% L1 U) w
  30. ) o) w3 m2 j/ s' k
  31. /* 设置更新请求源只在计数器上溢或下溢时产生中断 */
    , f- J! _* S' Q4 B; Q
  32.     TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global); 1 |% g; Y* T, }7 M8 {! T
  33.     TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    4 [7 N! N' Y, r  W8 ?
  34. }
    " }$ A6 D- m) w" h

  35. % V7 U/ J' s# b1 B
  36. /**# x9 [% S! U8 B4 r
  37.   * @brief   us延时程序,10us为一个单位
    , W) [1 l& P/ \) g2 Q
  38.   * @param  , E* }; h. {5 a8 l- X4 D7 r
  39.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us( s7 v1 z" W" q% Y! `; U
  40.   * @retval  无
    ( v& s% o& s; v" q, t
  41.   */
    ' G" a* i: S' `/ {8 }9 v  f
  42. void Delay_us(__IO uint32_t nTime)
    ' a1 H# ?4 ], w* j5 f
  43. {     
    8 d. a- B' m5 D3 w& |
  44. /* 清零计数器并使能滴答定时器 */
    5 z# t% ^4 t+ G, a2 m- }9 R
  45.     TIM2->CNT   = 0;  % X/ U% v0 }9 N* j! d
  46.     TIM_Cmd(TIM2, ENABLE);     
    ) D" H3 N6 Y( L" h# n
  47. 9 G& x  F( |8 ~( f$ T" b
  48. for( ; nTime > 0 ; nTime--)
    7 \8 @! S1 a8 L7 V
  49.     {# k$ a' W& f. |1 @; |: H2 j
  50. /* 等待一个延时单位的结束 */
    6 k* F2 t! I. y+ T; ?7 M
  51. while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);* D  M( \6 W$ T& h5 A2 v- [
  52.      TIM_ClearFlag(TIM2, TIM_FLAG_Update);: E) C5 N; ^6 d' I# N5 p% Y
  53.     }
    ! U! [0 ?' m2 j. c+ R- N! g7 I

  54. 8 c& s2 }/ ^8 Q2 R7 |1 G; N
  55.     TIM_Cmd(TIM2, DISABLE);
    4 H6 \& }7 y1 K% a
  56. }</font>
复制代码

5 B$ r2 K. e7 S" k* d6 F: x
  在main函数中检验Delay_us的执行时间:
  1. <font face="Tahoma" color="#000000">#include "stm32f10x.h"2 N) S, a* {) q2 O2 [
  2. #include "Timer_Drive.h"/ E( H  K: ~/ o! @
  3. #include "gpio.h"
    / d4 _' J( r5 I" L* X% X3 g5 K+ X8 t
  4. #include "systick.h"
    3 D- M* Y7 C: Z0 g# `  V$ u0 q" W
  5. ) ]1 G" @% y8 g
  6. TimingVarTypeDef Time;
    8 }( v7 ~' ~9 p4 \6 t5 T5 i
  7. ; C" G2 A8 A% P' p  T# z
  8. int main(void)7 O, v( x/ ^2 M; g4 O7 }
  9. {   
    ' r8 w3 L( F' \- {
  10.     TIM2_Init();   
    + T: P- [: ~2 Y: w' ~
  11.     SysTick_Init();
    ! V$ e, H4 o  |+ I+ N
  12.     SysTick_Time_Init(&Time);
    : w: R: J; Z5 G3 e( x- I6 Z% l
  13. ) o9 B, D6 z7 [
  14. for(;;)5 f: T+ Y0 {0 X7 R5 K* _- V
  15.     {  w; s) q* e% O* ~/ ]+ V9 R
  16.         SysTick_Time_Start(); ; P- d% `' @7 z' e6 E! B% ~( t
  17.         Delay_us(1000);; q$ L3 i! J# M8 s
  18.         SysTick_Time_Stop();
    * ^9 v/ w* ~$ _6 O
  19.     }     8 P! L' X5 j3 I- e" A
  20. }</font>
复制代码

. L0 c" p- H& I. M
    怎么去看检测结果呢?用调试的办法,打开调试界面后,将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以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。
8 n( O- ?' m' S& T/ x9 e# ]. ~+ G
收藏 评论0 发布时间:2021-11-7 15:39

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32Cube扩展软件包
意法半导体边缘AI套件
ST - 理想汽车豪华SUV案例
ST意法半导体智能家居案例
STM32 ARM Cortex 32位微控制器
关注我们
st-img 微信公众号
st-img 手机版