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

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

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

    % H. J# F$ k# s  l1 o, X9 z( H
借助示波器方法的实例
    Delay_us函数使用STM32系统滴答定时器实现:
  1. <font face="Tahoma" color="#000000">2 ~$ A. [6 h( B5 d2 h% q8 t' Q
  2. #include "systick.h"
    2 a" F; F* m  x3 {3 e: R

  3.   b$ @/ z" F8 m' H
  4. /* SystemFrequency / 1000    1ms中断一次
    # i$ @7 X& o/ u4 C" G+ P3 P
  5. * SystemFrequency / 100000     10us中断一次
    ' j* q3 O8 ~' |( m% @. Q
  6. * SystemFrequency / 1000000 1us中断一次$ C9 r! c% j* T' l, [* B3 A
  7. */0 L& t1 a. B1 Q
  8. 4 `- W+ l$ x, l3 {& v& l5 E
  9. #define SYSTICKPERIOD                    0.000001& J# w. X1 o6 C  ^8 J5 Q
  10. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)6 i2 {0 T4 |2 A( |2 N5 G
  11. 2 J& X' u2 U2 r  S- `; s
  12. /**6 e* N/ P, B. P/ n! m
  13.   * @brief  读取SysTick的状态位COUNTFLAG; L6 S7 o& y, u5 M, k" f
  14.   * @param  无
    * j9 L2 ^3 X2 C% K* Q6 Q$ G" E% N
  15.   * @retval The new state of USART_FLAG (SET or RESET).  q, g' \2 ?* r1 C
  16.   */) i/ ^5 g5 \) [$ L
  17. static FlagStatus SysTick_GetFlagStatus(void)
    0 V. O8 I- y# }, X1 N' v
  18. {; G/ r7 e8 E$ A) v, I3 B  \
  19. if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk) 2 }  h3 a: c4 u1 A6 H9 X$ y
  20.     {
    & N' Z- z4 F$ M% L
  21. return SET;. M# @/ O. }& ^8 M. i0 Y: |5 d
  22.     }
    : n9 p% a; D$ H0 K) W1 t" S
  23. else3 E2 z& b) T) k; n
  24.     {7 p1 d  H! U6 I" G
  25. return RESET;$ H# |6 V) X* o0 h& j
  26.     }# V, r; o, v0 g* d6 [2 m  r3 w
  27. }
    - A# p( d& T* G' }! K+ U
  28. $ g% p9 ?5 ]# ~0 m/ I2 f
  29. /**2 g5 c0 q, e+ y
  30.   * @brief  配置系统滴答定时器 SysTick9 ]% x& t6 |( ~2 [' H2 @
  31.   * @param  无+ b* ^# ~$ A, O# b
  32.   * @retval 1 = failed, 0 = successful9 ?0 v. j! s. m/ K. ]
  33.   */
    : F/ ^- D$ x4 s# i
  34. uint32_t SysTick_Init(void)
    ' `8 D8 Y6 M4 L& _5 {
  35. {) D2 P- D0 C5 @; y3 A4 F
  36. /* 设置定时周期为1us  */
    & `+ H0 c/ O9 p& R8 I' y
  37. if (SysTick_Config(SystemCoreClock / SYSTICKFREQUENCY))
    9 k& B, J& \4 }
  38.     { 1 J% a, ^) W8 N% v3 z
  39. /* Capture error */
    4 J9 x- b6 r% \  |
  40. return (1);6 E+ f) r" P* b2 n1 \
  41.     }, P. E+ W4 |( h; {: V7 l: W0 m
  42. 7 a( |6 e- M, r! [% ~0 r
  43. /* 关闭滴答定时器且禁止中断  */# G( L- {; {5 u' S
  44.     SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);                                                  : j& B, F, [/ x
  45. return (0);/ @0 @! v* R& x
  46. }2 Z  i  _0 L' s) M! x2 S( K

  47. * U3 N" F1 w0 J8 d; I$ L. V
  48. /**
    2 n/ A1 S; q3 Q
  49.   * @brief   us延时程序,10us为一个单位
    ' ]# z+ I- z2 `( H* g9 z' I% P9 F: B
  50.   * @param
    . }  C2 U7 X3 m% l
  51.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us& r2 x5 x# ^) f$ I4 E; l$ \6 O
  52.   * @retval  无
    ) _# z+ U, _1 S, v  g; p
  53.   */' Q8 q* {; ?7 p( {$ _8 R
  54. void Delay_us(__IO uint32_t nTime)) h4 O2 D& u# Q8 [4 p
  55. {     % ]; u, O/ ^: J# P6 M
  56. /* 清零计数器并使能滴答定时器 */
    ; Q  k0 m! Y4 i# w' F" j7 d
  57.     SysTick->VAL   = 0;  
    , G5 t, D: e6 L: H& V
  58.     SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;     
    $ R% v/ u+ V9 F' n5 N( e; L( n3 D

  59. 0 C% S* C, Z( N; i/ A
  60. for( ; nTime > 0 ; nTime--)& ?- ^5 R5 o* T7 O8 ^; R0 M
  61.     {
    " b- D( Z( h$ H
  62. /* 等待一个延时单位的结束 */
    - _7 A$ j  ?* f) v
  63. while(SysTick_GetFlagStatus() != SET);0 y+ s$ d: l) M; p6 u
  64.     }1 d% v/ t0 `2 T! y2 T7 {5 Z
  65. + @& o) `0 B% x& @
  66. /* 关闭滴答定时器 */, {$ m5 @! x) |% q: |  i3 _
  67.     SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;( g& |. z/ W/ s& \$ L( l# q! F
  68. }</font>
复制代码
* {1 u2 f8 o% Y0 ]  `
    检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:
  1. <font face="Tahoma" color="#000000">#ifndef __GPIO_H
    ' n# C7 r5 P& ~* d7 y) a
  2. #define    __GPIO_H
      X4 R/ r4 a+ K* `! F

  3. * x; M5 O# B& H8 p9 F, o3 c& V
  4. #include "stm32f10x.h"
    # C4 s/ m: W/ a& I& n4 ]7 @5 `( m
  5. 1 f* N" l! K" _
  6. #define     LOW          0
    ; r, i% V3 ]  R% d3 h9 J# o
  7. #define     HIGH         1
    ! m5 R% K* \" {0 s- l  @

  8. ; O" i- t: j) P* C4 S5 T' {
  9. /* 带参宏,可以像内联函数一样使用 */, i  B) o$ E1 r5 F( t6 h
  10. #define TX(a)                if (a)    \
    2 Q7 \- o4 o* L, O
  11.                                             GPIO_SetBits(GPIOB,GPIO_Pin_0);\& c8 [( Z1 k- D4 @1 I
  12. else        \3 K( {( A  Q$ k
  13.                                             GPIO_ResetBits(GPIOB,GPIO_Pin_0): q/ l3 S' l4 ?: p. y/ B! t
  14. void GPIO_Config(void);
    0 D2 k  P7 N  _1 r! v/ @* _

  15. ! o+ y  }8 r: V+ o" B% X
  16. #endif
    4 Y) n$ J) n7 i: p# F: L

  17. # C  L( }8 I' B3 A3 r) T
  18. #include "gpio.h"9 i) K9 ^+ T6 S9 e. c; h2 v
  19. ( v5 r1 b* X$ y0 z- e
  20. /**
    + Z7 g$ l1 B, M3 a% {" x) T
  21.   * @brief  初始化GPIO& ?$ e, J! g/ s
  22.   * @param  无
    9 ?; \2 f% M1 N8 `; n, n
  23.   * @retval 无3 _( H; o7 i9 T. J+ @4 Q. z6 j2 B" H( z% f
  24.   */, i1 t1 ~  g2 h( c$ E' b( v, ?
  25. void GPIO_Config(void)# }5 O/ S2 i$ K- t4 t% p
  26. {        
    - ]( ]# R8 F3 L: J+ Q& s
  27. /*定义一个GPIO_InitTypeDef类型的结构体*/( J7 z- w, w7 G$ m2 ~3 U
  28.         GPIO_InitTypeDef GPIO_InitStructure;6 g# \% p) J3 b2 ~& Q

  29. 9 _# E/ V7 {$ U/ l& J: n4 P( v
  30. /*开启LED的外设时钟*/. f  K8 N+ b6 P2 b- q; d1 E
  31.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); 5 O/ {% m0 f& r2 b- I2 R; H

  32. 9 j" q- L' R' h! M3 r5 D+ y- p; L' u
  33.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;   
    ' g: E: P3 o- f3 L+ p- X! x
  34.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     0 {! {2 N4 P# Q2 `% T& G( |3 R
  35.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    ) X8 Y" S" b3 {9 F  ], V
  36.         GPIO_Init(GPIOB, &GPIO_InitStructure);   
    # _7 z* Z: R& I7 X4 j
  37. }</font>
复制代码
/ ]. z8 p  d+ {8 s
    在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。
    结论:此延时函数基本上还是可靠的。
使用定时器方法的实例4 g  k. c: ~2 e0 u9 a! H* u7 q
    Delay_us函数使用STM32定时器2实现:
  1. <font face="Tahoma" color="#000000">#include "timer.h"
    ( {; W/ W$ z/ M7 O& m- n
  2. % A4 R" I  m/ K6 e( R, B
  3. /* SystemFrequency / 1000            1ms中断一次. N& j8 i( w) v& J3 b. ?$ ?! q1 A9 B
  4. * SystemFrequency / 100000     10us中断一次
    ; r$ r# e9 C- F" K; N
  5. * SystemFrequency / 1000000         1us中断一次0 `9 F: @- x( a. K2 r  Z
  6. */2 ?- J1 J% ]0 N, d% C/ S3 l
  7. 9 Y: z! a( T: M  V- Y
  8. #define SYSTICKPERIOD                    0.000001
    3 o4 m" q; ^1 {* {
  9. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    4 C. r1 p3 j- W8 K
  10. - ?9 m7 F& D1 p2 z9 [
  11. /**2 \& v3 D1 c7 a
  12.   * @brief  定时器2的初始化,,定时周期1uS
    8 g5 E- n2 I# L  w9 x
  13.   * @param  无
    1 j  w$ n* u( c1 Y: {5 p/ o. F
  14.   * @retval 无: y% |& l2 B. q8 ~1 N% z2 o
  15.   */
    , k  w3 s7 q. b
  16. void TIM2_Init(void)2 ?& s! W3 L; {. e$ k# t6 U
  17. {
    4 t+ x' x" t. L* ^- ~
  18.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    ; `4 ~, z/ M: b: f7 u7 I- Y

  19. & u, I' [9 i" `; ~( u8 y$ u" i
  20. /*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */. C3 v! O/ v1 E' @
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);& Q/ F6 J. u- O, W

  22. % t/ L9 n/ {  P2 O( C2 e
  23. /* Time base configuration */6 @5 e8 w( _) Y: s5 b2 B1 ?
  24.     TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;2 D2 y$ q9 [  _7 I# ~# ^" q+ ~& u
  25.     TIM_TimeBaseStructure.TIM_Prescaler = 0;" A0 A( B; L* v* R! {7 S
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    % K$ ~9 h3 ], C% R
  27.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    : B; f% o: ^  b$ }1 S

  28. 7 D* K) r" \& D. w7 h
  29.     TIM_ARRPreloadConfig(TIM2, ENABLE);
    / i; o" }! x* S0 N* w
  30. # c: l# {& z9 }% K$ X
  31. /* 设置更新请求源只在计数器上溢或下溢时产生中断 */0 }2 v% ^% ?( o6 y7 E
  32.     TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global); 9 v) m' n  B. t8 v4 q. o# R
  33.     TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    6 G( B( |" S* g' o
  34. }
      q9 x9 v# K% T# F$ e
  35. 0 d/ H3 b+ q; W2 @. p
  36. /**2 e7 b2 E* m% T' I5 ^
  37.   * @brief   us延时程序,10us为一个单位! S. R" j5 V& [( s; \& U6 y
  38.   * @param  " f. ~6 o# l( l" O
  39.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us* X6 E7 u3 j, ?% V( U
  40.   * @retval  无7 `: W6 e3 L& h4 e+ @' h' i
  41.   */: F* M4 ~' m( A/ L* S
  42. void Delay_us(__IO uint32_t nTime)! a! y! R  j- J, x* L
  43. {     ( V1 P& e( L# X1 @/ {
  44. /* 清零计数器并使能滴答定时器 */# e" z( w& _7 q- J. y3 M
  45.     TIM2->CNT   = 0;  
    ' V9 Q8 F% _6 A. K( B! P3 p
  46.     TIM_Cmd(TIM2, ENABLE);     
    ; M/ c* A( E1 g2 a+ u8 }
  47. % |% p% s; c6 C3 ?' k
  48. for( ; nTime > 0 ; nTime--); X" U  g$ v% r" o! c& W; N
  49.     {. I; L- O1 M4 D# f5 V6 @
  50. /* 等待一个延时单位的结束 */! Q& f8 H6 J6 i2 n/ R
  51. while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);
    6 i$ V$ {. P# H8 K% L
  52.      TIM_ClearFlag(TIM2, TIM_FLAG_Update);5 D& }" l+ L) p# Q* C4 o
  53.     }+ F8 }4 S$ T& B  W2 ^

  54. ! Y! a/ W; a- T- p4 `. p
  55.     TIM_Cmd(TIM2, DISABLE);4 _& D. P1 v# _) r/ W4 R
  56. }</font>
复制代码
: e( _2 j3 o* j  X" a9 k
  在main函数中检验Delay_us的执行时间:
  1. <font face="Tahoma" color="#000000">#include "stm32f10x.h"
    6 o# ]  i' _3 v7 l' S
  2. #include "Timer_Drive.h"" ?0 _7 d) _; S9 \
  3. #include "gpio.h"' d: y  V. y. ?1 p4 u0 L& `/ ~) C
  4. #include "systick.h"
    & k9 N' {; E; B# e/ g

  5. . w9 [7 b4 I  e9 t  {+ G) L) J0 y
  6. TimingVarTypeDef Time;& X6 ?5 s9 H2 j& y( @+ h; `

  7. 8 W/ r5 k) k. _% V5 g5 \6 Z2 O9 [
  8. int main(void), U1 G5 \6 L$ p9 V) I; V$ z0 e
  9. {    * M+ A% [) p% p" c  l3 a
  10.     TIM2_Init();   
    - _$ I) _) g1 v& \; V5 ?
  11.     SysTick_Init();# m9 }; ?1 v! Y% O& A# p5 m
  12.     SysTick_Time_Init(&Time);; N9 J$ `+ q5 C3 i0 O5 h8 I
  13. 6 Z6 e8 T3 t' w& X2 e4 F; H
  14. for(;;)( J" v; ]9 `0 ~
  15.     {
    + }" B2 I2 s! U$ R; D5 o" F
  16.         SysTick_Time_Start(); - ^2 k) t' }3 m/ L: e  O! P
  17.         Delay_us(1000);
    ! @5 g8 c  @4 A7 T( |- Z$ b& A0 {1 _
  18.         SysTick_Time_Stop();
    6 e. \4 w' ]4 d
  19.     }     
    + t+ z; j( a" l# a8 @
  20. }</font>
复制代码
5 T/ a1 t' [0 T2 H% D; v& |
    怎么去看检测结果呢?用调试的办法,打开调试界面后,将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以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。
3 k. ^8 H4 ?7 H5 x! a7 m
收藏 评论0 发布时间:2021-11-7 15:39

举报

0个回答

所属标签

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