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

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

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

    . _/ ?3 c3 {6 Y0 g3 u* r
    6 J$ w& c) t& y* e0 M4 Z
借助示波器方法的实例
    Delay_us函数使用STM32系统滴答定时器实现:
  1. <font face="Tahoma" color="#000000">) M* S* G) c3 O/ X
  2. #include "systick.h"
    ' B" ~' A8 z; f+ j
  3. 2 ^% u; R+ e* O, V8 H/ G
  4. /* SystemFrequency / 1000    1ms中断一次
    . q- P. z3 I- G# j* _. [0 N
  5. * SystemFrequency / 100000     10us中断一次
    : H3 @! D* {/ D1 \+ e
  6. * SystemFrequency / 1000000 1us中断一次8 m9 V" h. M3 a8 ^1 S1 `
  7. */' G! y/ r* _' |4 X2 u6 N
  8. ; q8 H0 \; Y# I( Y
  9. #define SYSTICKPERIOD                    0.000001, Q( p3 ~& X5 Q% [3 Y
  10. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)  X4 a) Z# c0 q+ h3 e3 O% G8 S$ c
  11. ) S. z* x; `, \
  12. /**4 {7 S9 q) X6 {5 y; y6 H
  13.   * @brief  读取SysTick的状态位COUNTFLAG
    * v( l- o7 f' J% \) v; ]6 h7 s! Q
  14.   * @param  无
    6 Q  p- g) R  j7 y( x+ k
  15.   * @retval The new state of USART_FLAG (SET or RESET).( ]! y) b8 m& Y# D
  16.   */4 P& Y' Y- ?; {( q
  17. static FlagStatus SysTick_GetFlagStatus(void) & U% q9 j" d& k9 F+ e* ^; ?
  18. {9 S  ]2 m( K) x0 ?2 O
  19. if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk) ! z8 J+ Q2 B: h2 ?# g
  20.     {7 N* [6 ]; Y, O
  21. return SET;: b/ v4 f" x  a% y+ A7 s3 M
  22.     }
    / F- z8 e( H7 p1 q4 l: y% P0 c
  23. else
      M' j; M3 ]7 l. x. @. e% n& j: H
  24.     {
    & y0 @' j  w$ o6 i/ i9 w. M
  25. return RESET;
    3 y! V$ b3 O; d2 t. r' E0 Q) u
  26.     }0 s4 P  O8 ^. R; ^2 c( j6 P+ n, H" D
  27. }
    ! M$ l- X- A$ I! E! I
  28. : |( [" G* N7 U4 {& b
  29. /**
    " H" N6 ?3 p$ J4 B: R0 j
  30.   * @brief  配置系统滴答定时器 SysTick8 b1 O: `4 V4 x5 S& G2 {
  31.   * @param  无
    + X3 u  B& b9 ?! V' m; W
  32.   * @retval 1 = failed, 0 = successful; B% N. {2 n& n% J, A" q  F0 D  A3 V
  33.   */* |/ T( R: i$ \1 G4 g& x2 h: a
  34. uint32_t SysTick_Init(void)2 c( h1 M2 c4 v7 @3 d2 B
  35. {* A" n8 p# j: ~3 x
  36. /* 设置定时周期为1us  */7 V" d7 s; x2 l6 i2 j2 A, D
  37. if (SysTick_Config(SystemCoreClock / SYSTICKFREQUENCY)) 1 K# g$ t5 }. H- u
  38.     { ) L  [, b; R& E' w( b* ^1 R
  39. /* Capture error */' c& v- S, b& B" F/ W$ O; L' w2 D
  40. return (1);
    / b$ [* w! T8 b, R' f) E
  41.     }8 o+ I1 w8 P& f7 Y; Y( l
  42. 9 r* ^/ j# z& R1 E. U
  43. /* 关闭滴答定时器且禁止中断  */
    / Q+ s4 L- c1 S- _
  44.     SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);                                                  
    8 i! o2 S1 h3 I" [& b" M5 l$ y. e
  45. return (0);
    ( m2 D0 @8 H' N5 {2 o# L% q
  46. }! U- \) |+ C( z% C
  47. # f- k2 }' [. V, d
  48. /**
    ! Q9 x# ]  ^! {5 T+ c* c9 c
  49.   * @brief   us延时程序,10us为一个单位
    % P1 ]& o8 J' v: \4 d3 t
  50.   * @param
    8 H& S; o. I: \* c5 l
  51.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us
    8 ~; H- ?! B8 G1 E$ y
  52.   * @retval  无
    0 Q6 R, W, d' w+ l& m# P* n' ~! q
  53.   */% i; R. A* k1 ^. s
  54. void Delay_us(__IO uint32_t nTime)( O+ {3 ?0 e7 q* Z! ~4 h. I2 b
  55. {     
    ; u6 f6 K0 N) `# b- C7 ^4 P
  56. /* 清零计数器并使能滴答定时器 */0 A- @, `2 q2 K9 N: w
  57.     SysTick->VAL   = 0;  
    * E/ o9 M% e- c( T' c
  58.     SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;     
    % V0 }6 u, j% P# v

  59. % x: J8 m% X2 O6 W; Z( s3 v+ R
  60. for( ; nTime > 0 ; nTime--)# U  O6 j/ E, W9 ?
  61.     {0 M3 a/ P$ F2 f2 G2 b
  62. /* 等待一个延时单位的结束 */* v4 }2 D1 T3 v
  63. while(SysTick_GetFlagStatus() != SET);
    . o, R( O( G) L! J; p3 Q$ z  n
  64.     }
    # C7 w6 N" |' i7 _

  65. / Z6 F) J* Q- @  A; w* y
  66. /* 关闭滴答定时器 */
    2 N. o* D6 ^" t' U9 E
  67.     SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
    ; U2 V5 }6 H2 O+ ~. r* G1 n
  68. }</font>
复制代码

8 y# r* f2 r8 C& _
    检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:
  1. <font face="Tahoma" color="#000000">#ifndef __GPIO_H+ c1 U3 N9 @: d6 T  W2 s9 X
  2. #define    __GPIO_H) U! W7 n2 j6 }( ]$ m2 s; V  a1 x( t3 t

  3. ) d' t' H; ~, L2 y
  4. #include "stm32f10x.h"' {' O, `3 A# P- u7 I9 \& X) n

  5. & z$ t) S$ S$ g" t4 H2 ^' B
  6. #define     LOW          0
    % V0 c" \. K" p
  7. #define     HIGH         1
    3 {0 y  i( t- r9 Z4 B' O# [6 ]1 i, A  q
  8. " i( A! w% [0 D5 ^7 g' G
  9. /* 带参宏,可以像内联函数一样使用 */4 M0 M- Z6 N8 ~: k/ Y3 C% H1 f
  10. #define TX(a)                if (a)    \
    9 w, \& U- [5 m3 M" h
  11.                                             GPIO_SetBits(GPIOB,GPIO_Pin_0);\
    * A8 u8 B' {- M/ [) i
  12. else        \
    % F7 r5 A$ C# M, u
  13.                                             GPIO_ResetBits(GPIOB,GPIO_Pin_0)
    , a1 Q: p, p! v/ B# ]& t% V
  14. void GPIO_Config(void);$ C4 I  X' p5 O8 y
  15. ) u4 {( a5 r$ W! p( w
  16. #endif
    : Y" [6 z/ ]0 k: X$ A0 g9 L6 M

  17. ; a) d0 j% @* y) W  @
  18. #include "gpio.h"3 O. I' p7 C6 V# T+ y

  19. 9 v0 x* [1 v/ H! u* P
  20. /**8 w' D! L; ?: b0 w1 q: m
  21.   * @brief  初始化GPIO1 V# ~' E" L# i* Q/ z
  22.   * @param  无
    5 I1 I3 f3 t$ y# D# J- O8 H
  23.   * @retval 无
    " ~* e# }$ M) A! n+ b. Z
  24.   */+ a: g7 l* k8 W: {3 }
  25. void GPIO_Config(void)) Z7 V5 o5 ~, y# i) b
  26. {        % |4 V, X. F; F. N
  27. /*定义一个GPIO_InitTypeDef类型的结构体*// E1 |6 P% ?- V
  28.         GPIO_InitTypeDef GPIO_InitStructure;! r0 v0 x8 D; [7 m4 Y1 H6 ^1 C
  29. 1 n4 I& n/ }8 H# x& J. y4 H
  30. /*开启LED的外设时钟*/
    " F  ]6 {+ R3 ^, U  F
  31.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); * u0 g7 v, s$ X6 A5 Z

  32. 0 P8 O" J8 Y7 j% m
  33.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;    : t& g+ ~/ h& S; z9 T
  34.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     + R* A1 T& ]0 @& O, P% |
  35.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; * ?% X+ v: l& i# c$ ~
  36.         GPIO_Init(GPIOB, &GPIO_InitStructure);    3 C$ z* T1 ]% ^
  37. }</font>
复制代码
& \. s% D, }* e! y
    在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。
    结论:此延时函数基本上还是可靠的。
使用定时器方法的实例3 z' q8 Z& g7 i( q7 E9 s
    Delay_us函数使用STM32定时器2实现:
  1. <font face="Tahoma" color="#000000">#include "timer.h"3 k' n5 i% F) L: M4 _

  2. / O2 ^* ]. I4 k2 S! C$ V  p
  3. /* SystemFrequency / 1000            1ms中断一次
    & S7 E( r( e  T8 e
  4. * SystemFrequency / 100000     10us中断一次
    5 r) f/ Y) w/ _( z0 X, H, Q/ Y
  5. * SystemFrequency / 1000000         1us中断一次
    " S: \1 R% k( c6 N
  6. */
    & \) m6 S0 I4 @" `# Y
  7. 2 s- f1 N5 |- \, Q, l
  8. #define SYSTICKPERIOD                    0.000001
    ( ~3 q( h1 l' U
  9. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)" E; G5 `0 Y9 s
  10. + h. D$ V1 w8 h5 o, ?* R1 @
  11. /**7 }7 R# H- E$ H6 q
  12.   * @brief  定时器2的初始化,,定时周期1uS
      i$ _0 _: T+ ~2 P- p5 [
  13.   * @param  无
    - M* \5 Q  G6 E* O0 P1 U4 N7 ?
  14.   * @retval 无
    ) l* X' `5 o2 K9 y, h
  15.   */
    1 B+ i1 F+ G; G: h6 |: `
  16. void TIM2_Init(void)0 B& [4 K, d" `1 o. ~  T: h* @$ ]
  17. {/ b! F0 g- V/ B: _5 A% S  i
  18.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;1 W2 j" E8 Z) V

  19. " _8 J+ F( K4 M- U
  20. /*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */  Y, l2 m6 D' q2 f% S6 J
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);& N" Y( \( w* d# [+ T% j
  22. 7 _( t2 o; p% m
  23. /* Time base configuration */
    * ^) H  r3 O: i; O1 [! A
  24.     TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;
    6 J" m6 ]$ s5 g( o+ }# X7 h4 s" j
  25.     TIM_TimeBaseStructure.TIM_Prescaler = 0;. V4 j5 ~( p" N! c: x# D
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    9 k( J, @0 \& E0 e( q( B
  27.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);* Q7 d0 t7 [, O; |+ @; J# q
  28. + J& v4 E; U: [9 Q9 K
  29.     TIM_ARRPreloadConfig(TIM2, ENABLE);
      n* y& N. M7 R6 \% V- u- X
  30. - l3 Z, z5 y. H1 Z( N2 i) R
  31. /* 设置更新请求源只在计数器上溢或下溢时产生中断 */3 y1 H4 o" w) ~3 h
  32.     TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global); , |) b. D/ N2 n- u
  33.     TIM_ClearFlag(TIM2, TIM_FLAG_Update);1 g6 g9 {# o- K+ X
  34. }
    $ r5 h* O1 Q# z& x. j

  35. " N! _7 Y4 H! y( S0 h8 i% o
  36. /*** t2 Q. ?4 M7 [6 A  z7 _2 B& J
  37.   * @brief   us延时程序,10us为一个单位, |7 c  X$ ^. `
  38.   * @param  $ U( b# H* ^* M) X0 e* q& K' c& j' [
  39.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us% D  _3 y6 f8 C; M0 H& f6 H
  40.   * @retval  无$ k. b# S; l! x! `* {* J; o8 U
  41.   */' e( m5 x* c/ i1 x
  42. void Delay_us(__IO uint32_t nTime)$ @4 q- ]* ^; W
  43. {     7 c, K3 N( F. F
  44. /* 清零计数器并使能滴答定时器 */7 @- r9 T; t2 U2 _/ ~9 C
  45.     TIM2->CNT   = 0;  
    / x- T! j2 d4 e
  46.     TIM_Cmd(TIM2, ENABLE);     % n- u. z& K$ N$ U  Y$ U
  47. - T) k2 p6 m$ `" C" M
  48. for( ; nTime > 0 ; nTime--)& W. B0 A. o0 y+ h$ S$ f; H, L* J! m: H
  49.     {
    2 ]/ h( L1 S/ B8 {3 Y& D$ d1 Y
  50. /* 等待一个延时单位的结束 */
    ( Z0 Q/ h& Y3 z- m1 a9 j
  51. while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);
    ; n  a: _/ |0 F& ~' M$ \
  52.      TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    ' N1 @8 l" s: g4 A% F: v4 b' e
  53.     }
    ; \6 j2 A$ C8 x# I
  54. / t3 h1 r  w$ T/ \! t/ D
  55.     TIM_Cmd(TIM2, DISABLE);* R! C) y! d7 M7 T
  56. }</font>
复制代码

; Q' ^6 i5 I% z$ U& a1 ]! }
  在main函数中检验Delay_us的执行时间:
  1. <font face="Tahoma" color="#000000">#include "stm32f10x.h"
    / U9 N  H% |( e5 P
  2. #include "Timer_Drive.h"2 V) \" W0 R9 M& A6 z
  3. #include "gpio.h"
    & v1 W& P0 l( T& h
  4. #include "systick.h"# ~$ G% r6 P9 T  V
  5. - _. c# a7 v$ M5 ~+ j: E8 f% c0 p  b! S
  6. TimingVarTypeDef Time;' E" C5 s) V" q$ m. A6 q

  7. 0 R5 n  m& Z# b9 @+ [: u7 Y" x" k% s
  8. int main(void)' `! {& ]/ k& |2 P% q
  9. {    8 }; g  ^2 p$ `" @
  10.     TIM2_Init();   
    9 p. A4 C1 D9 H+ E' ~
  11.     SysTick_Init();$ d1 j3 R" |/ [7 S, m
  12.     SysTick_Time_Init(&Time);0 Q2 f* ?6 `) N1 Y' G' V& y, W

  13. , W; b0 E- S- F! L$ M
  14. for(;;)
    . }1 o' i4 o9 f; A+ z
  15.     {
    5 W* V% ~0 \. G9 d9 B, U. h+ Q
  16.         SysTick_Time_Start();
    ; Q- i- T* ?5 i4 \6 D' B/ K! R* k
  17.         Delay_us(1000);7 @. D! q, x  `/ u& H' u
  18.         SysTick_Time_Stop();6 x! G$ C% J3 D, c0 _. G6 {/ @
  19.     }     
    ( w4 T/ ?8 H. M' O8 w, a
  20. }</font>
复制代码
7 ~# c$ g4 H: p( \$ X# e/ O- U! 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以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。
: N1 L4 @( z* e9 a2 {" V
收藏 评论0 发布时间:2021-11-7 15:39

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
人形机器人运动控制、感知与智能配电
半导体创新技术与应用方向
EE架构与软件定义汽车
12V/48V 汽车智能配电(SPD)
区域控制单元(ZCU)与分区架构
关注我们
st-img 微信公众号
st-img 手机版