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

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

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

    / H: n  `3 m! H( q; w
    / C0 ~3 I6 w+ `
借助示波器方法的实例
    Delay_us函数使用STM32系统滴答定时器实现:
  1. <font face="Tahoma" color="#000000">, x$ _, T8 E7 V
  2. #include "systick.h"- Y& j6 N# e! s2 L$ Z
  3. ( O" v0 S- c4 v) Q
  4. /* SystemFrequency / 1000    1ms中断一次  o6 {; `; g& J" y3 \$ X3 C
  5. * SystemFrequency / 100000     10us中断一次% u6 ^2 N9 I1 i$ z
  6. * SystemFrequency / 1000000 1us中断一次
    6 m3 s' ^* k" B/ s& H* ?
  7. */) O$ {$ x& p$ d# {
  8. " h3 G1 K$ `8 g# H
  9. #define SYSTICKPERIOD                    0.000001
    , P8 _* ?* n* ?$ W0 g* r
  10. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    1 s, ^& |1 R4 l/ g

  11. ( _+ F7 a; k3 W; f! N. f+ _
  12. /**
    - u; l0 ^& [* M" {
  13.   * @brief  读取SysTick的状态位COUNTFLAG: Y3 V! f& |* w$ |6 _+ N6 C
  14.   * @param  无
    8 j- }' k/ I, Z; ?& Q! I# {1 u
  15.   * @retval The new state of USART_FLAG (SET or RESET).7 I2 n+ ~% N* x
  16.   */
    " o. `9 A6 H) S8 b4 f
  17. static FlagStatus SysTick_GetFlagStatus(void)
    . T+ I' u, q1 B8 j6 w
  18. {
    # Z- F+ `, Q7 Z1 c  t. {
  19. if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk)
    ' o9 c* I" O" d. M
  20.     {# E+ @  G" C$ f8 [" w
  21. return SET;
      e3 ]9 f8 v# K# k' e' l
  22.     }. R) g. G6 H2 N8 G7 r9 S
  23. else
    ) I4 g3 Y; c7 Y5 K
  24.     {, X! T' P2 q4 d7 F7 k/ n/ r( M4 W
  25. return RESET;$ A5 J  g1 N1 N( \
  26.     }1 ^6 {' K" c+ U1 u- A0 ]* q
  27. }
    ; C; \" _- o. i

  28. + d9 t6 r4 \' {  f) Z
  29. /**; B. B( H* D. o# k# B
  30.   * @brief  配置系统滴答定时器 SysTick5 f  }: o5 [/ v3 g
  31.   * @param  无
    * \1 a6 L6 U! n4 |
  32.   * @retval 1 = failed, 0 = successful
    3 B2 Y; N+ N; D! I5 A3 N* h$ V, n
  33.   */
    2 m) U, Q: Y$ d  B( ]" V/ y9 a
  34. uint32_t SysTick_Init(void)" O5 `  |& Y. w  V4 w. }
  35. {
    # W1 F$ s8 t- i2 ~6 r
  36. /* 设置定时周期为1us  */
    : F/ n3 Y' A! P- n7 Z, J
  37. if (SysTick_Config(SystemCoreClock / SYSTICKFREQUENCY))
    # T% Q" T$ n8 m" C1 r4 p
  38.     {
    ' @- ?* T  y% P: s- b! K0 E
  39. /* Capture error */
    5 o$ ?- e- @- P' b" m9 ~1 p
  40. return (1);
    5 `! \3 k& [0 g! x
  41.     }
    % n) \. L8 I3 F  \  Z( V5 H! W" \
  42. & {( Q5 A# U$ u1 d- P) r  t" d
  43. /* 关闭滴答定时器且禁止中断  */
    $ X( ]6 E( D5 o1 Q
  44.     SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);                                                  
    # w4 z1 X2 }- e; f( Z- o
  45. return (0);7 T( o) ?' m4 ]. O; S( B
  46. }7 b! j8 ?# P. X  V. Y9 J4 p

  47. + Q, ]' m1 ?3 N. _: [
  48. /**
    % l) n& c7 q3 e/ w9 {9 }4 v
  49.   * @brief   us延时程序,10us为一个单位
    ( I7 B0 i" a+ m8 q+ V6 }
  50.   * @param' [# i: i8 k8 x
  51.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us5 U3 l7 T  Y- b4 o2 ^3 \
  52.   * @retval  无
    ' f2 {# q" ]# Z: c6 X8 @
  53.   */
    * \9 {8 n% C& b% W
  54. void Delay_us(__IO uint32_t nTime)+ Y* \7 ^/ ^' Z  z4 ?; J+ s
  55. {     , a8 G. N# ~6 ?# m& d9 {
  56. /* 清零计数器并使能滴答定时器 */
    $ C: F3 ^  z4 w& \5 R. y
  57.     SysTick->VAL   = 0;  ! e7 z. }$ P4 L% d+ p+ V; E. H" N
  58.     SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;     
      V1 ~0 f" @& ]9 D! d. @
  59. ; L7 u. p  |3 C
  60. for( ; nTime > 0 ; nTime--)/ r' K, C' o6 A6 l) J, a
  61.     {
    + H( `& s4 C9 B$ Q, `, [& ?
  62. /* 等待一个延时单位的结束 */, u5 f* K4 O7 P. x. [
  63. while(SysTick_GetFlagStatus() != SET);
    & \: Z5 {* t- J, r( [1 O; w) s
  64.     }( u+ h6 g" x7 L8 e8 ]3 ]
  65. $ v% \' q% l6 w
  66. /* 关闭滴答定时器 */8 E4 S% w, U3 U$ k: Y8 a
  67.     SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
    4 |5 d( S3 K0 \1 ]
  68. }</font>
复制代码

/ z8 H# K* r5 |
    检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:
  1. <font face="Tahoma" color="#000000">#ifndef __GPIO_H
    . l2 F) J% H1 y3 U# v. y/ T
  2. #define    __GPIO_H
    ! S8 T! W$ j. I' l

  3. " k" A* h$ G# {
  4. #include "stm32f10x.h"7 G: w& Y) j3 k' P8 F

  5. ( D* s; x9 d& \. U7 @6 @) V
  6. #define     LOW          0
    $ _3 J. `0 H$ y
  7. #define     HIGH         1
    4 D1 [. E& `+ m
  8. 3 k7 v1 e! D+ b- f
  9. /* 带参宏,可以像内联函数一样使用 */
      O0 G. V* d7 A  M
  10. #define TX(a)                if (a)    \
    ! I9 ~& q  A2 L3 d. p/ ?
  11.                                             GPIO_SetBits(GPIOB,GPIO_Pin_0);\
    ( `' Y8 e! d! b% W+ p+ ~
  12. else        \3 z$ k( Y- L) J  x1 b1 B2 T
  13.                                             GPIO_ResetBits(GPIOB,GPIO_Pin_0)
    & n$ Z% m# y6 {  Y/ P8 b9 K
  14. void GPIO_Config(void);
    8 J: A: J/ x! d  X8 B

  15. - x3 b# q) T& o2 f! R+ b1 s
  16. #endif
    - Q3 u, ?; b/ Q  Q8 ?1 q
  17. $ }; ]7 x9 i. D" ]0 _# d% a
  18. #include "gpio.h"4 `3 n3 Y  P) @0 d

  19. 8 L4 c1 j9 ?4 l" l+ R2 P2 }
  20. /**% V& R" j; C3 ~
  21.   * @brief  初始化GPIO$ V. W: ^4 x* j. A& \
  22.   * @param  无
    # i0 s: ?, G/ S. \1 P! x! }! s
  23.   * @retval 无
    6 N0 _. X' y, h
  24.   */
    & e0 {1 c4 i5 u; I/ H: `- G
  25. void GPIO_Config(void)
    $ s& z% A: n' Y2 T4 S
  26. {        
    , q  v) x* R/ k
  27. /*定义一个GPIO_InitTypeDef类型的结构体*/
    6 T) s. {) W* ]$ d- m2 H
  28.         GPIO_InitTypeDef GPIO_InitStructure;
    7 o8 E$ h/ T* f1 Q4 k$ F

  29. - `5 R! a* B" O4 O, @& w
  30. /*开启LED的外设时钟*/+ a6 n7 {) d. Z5 Y% |: f! s
  31.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
    9 ?; ~% i" b# V. m
  32. , ~: r- [* y3 J: S
  33.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;   
    . F. a4 H/ K/ z
  34.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     # |  t/ D8 j( f3 r
  35.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    - M3 u) }/ e- d2 n0 B( C4 }0 x
  36.         GPIO_Init(GPIOB, &GPIO_InitStructure);   
    4 s* m( @% O) H5 [: P8 [( W
  37. }</font>
复制代码

2 n: H6 \8 W3 l2 }$ g
    在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。
    结论:此延时函数基本上还是可靠的。
使用定时器方法的实例1 D6 L7 u6 n! f; [
    Delay_us函数使用STM32定时器2实现:
  1. <font face="Tahoma" color="#000000">#include "timer.h"" R9 Y5 l0 h7 l$ R
  2. & r; m) w3 z# s5 V$ ?9 B, M
  3. /* SystemFrequency / 1000            1ms中断一次
    . \* X9 n8 m+ h$ N/ ]2 R
  4. * SystemFrequency / 100000     10us中断一次
    % B! K: Q- X* G/ l5 i
  5. * SystemFrequency / 1000000         1us中断一次( D: W( V  E& Y- M2 q! t
  6. */
    / B5 W9 H1 R) ~1 ]+ ~0 z* \

  7. 2 H3 b7 u: T7 U  e
  8. #define SYSTICKPERIOD                    0.000001. z6 O7 i1 v% J# D
  9. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    & q5 w# r3 {" Z. j$ e0 P$ ?
  10. 6 n# n1 `  t: e* b- a
  11. /**
    % g2 y& g, Q" |2 n, P
  12.   * @brief  定时器2的初始化,,定时周期1uS& M( Z) C5 l$ S: p
  13.   * @param  无! [' i* {+ d# _: ]% B" @& t0 F+ u
  14.   * @retval 无
    3 i, Z) ~1 ^1 J3 W8 L
  15.   */
    6 ]$ L3 L3 M1 L
  16. void TIM2_Init(void)
    ! \! z4 K6 R& O2 H" ~
  17. {0 Z# Y# w8 ]9 E. r" o
  18.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    ( s/ X1 m+ f4 |7 l& f+ `5 f

  19. 3 i2 W0 R( f1 r) ^
  20. /*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */
    , y* \( N0 i5 R* F% h
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);0 I3 t7 R- k4 C- O

  22. , [4 s7 _9 X+ m- t+ j+ Z
  23. /* Time base configuration */4 V% i. W7 v( S$ d
  24.     TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;9 Q7 S3 q5 Q$ m) e" k
  25.     TIM_TimeBaseStructure.TIM_Prescaler = 0;9 q& M+ T" l: T2 U# j/ d' |- b
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    5 z5 P7 c$ ^0 L; L* ~9 B2 A
  27.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);; e( M3 ?( @$ {' K5 q6 u
  28. 6 z. U( M$ Q4 J% B- g6 C
  29.     TIM_ARRPreloadConfig(TIM2, ENABLE);
    , N# ?) X% W; d9 |  z5 ?% K) i% a& R
  30. $ L+ m# M' B9 g/ k# O
  31. /* 设置更新请求源只在计数器上溢或下溢时产生中断 */
    ! T6 e' {$ w) U, j6 X
  32.     TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global); 9 O* G4 v5 K6 u0 P, r
  33.     TIM_ClearFlag(TIM2, TIM_FLAG_Update);- ?# R/ R/ q6 X; D
  34. }
    ) z+ F, E* q1 v& a: P

  35. " a( X1 O5 c/ F* P- h" _
  36. /**8 e  t; d7 p) T. a* Z" k) E, L3 w& a
  37.   * @brief   us延时程序,10us为一个单位
    ( s; n/ P! ~; F" L0 F
  38.   * @param  
    8 {- w! `* }# V) D; ]' M  L8 Z
  39.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us
    + R# N; w. f$ q. x4 F+ ~. o% }
  40.   * @retval  无
    5 d0 q! \; ~0 H4 Q5 N9 H; L
  41.   */
    * I+ Y. r7 r0 \' t  Q4 I
  42. void Delay_us(__IO uint32_t nTime)
    2 r9 @: R. C. M' m  Q4 G9 {
  43. {     
    " Y6 H5 Q( o+ R+ C. R6 `2 x
  44. /* 清零计数器并使能滴答定时器 */
    # G, ~# ^$ |+ R; ^8 o" B
  45.     TIM2->CNT   = 0;  
    $ ^& Y$ A! v3 ]  b! _1 A
  46.     TIM_Cmd(TIM2, ENABLE);     
    " a; E+ F  a! P8 z4 f
  47. . R$ z7 B' y8 O# w0 m8 j
  48. for( ; nTime > 0 ; nTime--)
    9 {8 J7 b  T2 w, ]. H
  49.     {, ]  T. Q7 i; K& t( j. c( C
  50. /* 等待一个延时单位的结束 */
    8 Z: v1 J$ P) [0 [6 E  W
  51. while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);0 _* Z1 O8 R' _! X/ P2 i$ X/ \
  52.      TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    + u! t* z2 R$ F) @! y
  53.     }+ J5 d9 u0 S* r7 \6 {

  54. , q3 @, t1 p" u" ~% p) b
  55.     TIM_Cmd(TIM2, DISABLE);, [+ F, j* ]" h' {4 F( {
  56. }</font>
复制代码
8 \" F4 ?) U2 j' y! P
  在main函数中检验Delay_us的执行时间:
  1. <font face="Tahoma" color="#000000">#include "stm32f10x.h"
    0 o* I' b# P* P6 }: |* {
  2. #include "Timer_Drive.h"
    % _+ ]% {. `0 O" @
  3. #include "gpio.h"
    ' f2 s1 G0 G/ U; |3 ]) o  U4 ?$ A
  4. #include "systick.h"
    6 A3 I0 S. I: a# c4 n$ K$ J* Z
  5. 2 `$ q( A- q0 Q8 m& e5 p0 b* e. g
  6. TimingVarTypeDef Time;. N8 a5 a7 s4 w$ B: w! W
  7. 5 A, U! |+ o* ?
  8. int main(void)) k- n8 n7 K% N  ?- |# d
  9. {    & I; Y7 ]8 m2 K6 h+ f& E
  10.     TIM2_Init();   
    7 _  U5 |' K" P: S- `9 Y
  11.     SysTick_Init();; l" F& X; [: T/ f# {3 u
  12.     SysTick_Time_Init(&Time);: n* c/ Q# p# _% E. c% Q( ~: V7 y
  13. 7 `* q0 k- q6 l% s( F, a
  14. for(;;)4 S# w  \* [% U
  15.     {# X3 K/ ~7 g, I
  16.         SysTick_Time_Start();
    : ~/ i8 ]5 g$ |/ W* ~
  17.         Delay_us(1000);
    , E/ _3 h$ _. I; \* G+ W5 p
  18.         SysTick_Time_Stop();
    + c; @- t' A6 j- r! J
  19.     }     
    $ J2 C6 l, m" }
  20. }</font>
复制代码
$ t. B* X6 p5 `
    怎么去看检测结果呢?用调试的办法,打开调试界面后,将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 c( s0 s5 e* s4 S; d5 q
收藏 评论0 发布时间:2021-11-7 15:39

举报

0个回答

所属标签

相似分享

关于意法半导体
我们是谁
投资者关系
意法半导体可持续发展举措
创新和工艺
招聘信息
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
关注我们
st-img 微信公众号
st-img 手机版