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

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

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

    2 e: t+ {, ?' a- p
    : x, E3 ]( ]2 g; ^, q! F
借助示波器方法的实例
    Delay_us函数使用STM32系统滴答定时器实现:
  1. <font face="Tahoma" color="#000000">% p) V/ R5 @8 }; J# V7 p6 R
  2. #include "systick.h"
    * i5 {  m, m  e- G/ @* G' }) b5 `* S

  3. # |% t, {8 R4 b$ E# @
  4. /* SystemFrequency / 1000    1ms中断一次
    6 C7 z- W. m* q$ R8 C, a0 c
  5. * SystemFrequency / 100000     10us中断一次
    ) s; o$ k9 [* p. Y# ^4 [
  6. * SystemFrequency / 1000000 1us中断一次
    , ?) x8 I6 S+ N! ^% h$ Q( e
  7. */
    , b" J6 h0 y  b. m  |$ L4 c& V
  8.   n4 r6 j0 {  S0 ^( G$ U
  9. #define SYSTICKPERIOD                    0.000001
      Q  S: V+ Y; v
  10. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    ; P7 \2 {" ~" C/ B' A' K

  11. ; s5 s: ^2 |. o0 [$ A- ?
  12. /**
    2 i0 W9 N1 K6 _" z
  13.   * @brief  读取SysTick的状态位COUNTFLAG
    $ N! T$ V' x) P5 `4 ?% q' {
  14.   * @param  无
    - x- |3 n9 f5 H! [/ ?- R; q
  15.   * @retval The new state of USART_FLAG (SET or RESET).
    9 S; j# g8 o% c; X' C4 `, c* k
  16.   *// }& ^) |) x7 |, B& n% Z
  17. static FlagStatus SysTick_GetFlagStatus(void)
    % G0 _% C+ ^% ~/ C( T4 e3 n& g
  18. {. P( x* M3 G- O3 Q4 b( K( z
  19. if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk)
    $ S3 T/ n/ m! U9 Z5 M2 l$ ?; @
  20.     {
    + g, P& _; K* h% N! V
  21. return SET;8 b4 d8 l! K. }: L3 c$ V# {
  22.     }
    ( y; ~& G2 N, {  g% L$ [
  23. else0 |4 J' Z4 Q$ r) D3 Z" I6 D; M
  24.     {. o8 G; T) r/ `# i/ m+ h/ T/ I
  25. return RESET;3 f: K8 f& Y4 q' H" D
  26.     }
    * M/ T) ]9 I! a. k) G, m
  27. }
    ! y: {, e' h; w: p$ t

  28. $ F( X2 @& p) ~& T; j
  29. /**
    * W8 [4 E, ]- O- r5 u
  30.   * @brief  配置系统滴答定时器 SysTick) O+ i* K4 ]+ Z' e; @* \6 ?
  31.   * @param  无- b9 P6 {* Z2 W0 c! r, E& E
  32.   * @retval 1 = failed, 0 = successful
    1 O% n: v5 e2 T0 T$ T. S- X
  33.   */
    : I/ u' A. @7 M' G, Y* G
  34. uint32_t SysTick_Init(void)2 S' j* F4 H: G% n
  35. {
    & [) K* z; x9 J7 t% e' C
  36. /* 设置定时周期为1us  */
    6 I  y% }1 V) ]9 L, ^" h
  37. if (SysTick_Config(SystemCoreClock / SYSTICKFREQUENCY)) 0 z( D( s6 b/ z5 m" I4 c0 s
  38.     { + N) U! e- v; U( j
  39. /* Capture error */0 g( X7 z: Q2 ?: e: _; r% Z+ \
  40. return (1);+ v: X; r: }4 r5 i- Q
  41.     }
    & T, T7 P1 V" `) `

  42. & t$ ~4 E; R7 H7 s  p# k
  43. /* 关闭滴答定时器且禁止中断  */
    ' R/ a* o1 H3 D% o  D
  44.     SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);                                                  , c9 `/ M6 u/ u
  45. return (0);
    1 a  `/ ^% f" q* V
  46. }" H, ?2 _, r0 o% r  j
  47. . c- u- U: x3 u) B3 O5 O7 D- S
  48. /**4 Q. m* a* j7 t$ O+ b% B
  49.   * @brief   us延时程序,10us为一个单位
    ! ?; h' ]4 l! G  b
  50.   * @param
    & d$ E9 z8 w% E  ^. L7 i
  51.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us4 m% d8 p4 |; V( A; K( K9 L
  52.   * @retval  无
    ! H, @, M' `* K
  53.   */% ~, `1 R% v3 `, S
  54. void Delay_us(__IO uint32_t nTime)
    $ R- M( }( M! Y, `1 C4 {+ V
  55. {     * I3 X0 M0 V- q$ p3 [2 `, w, E6 P
  56. /* 清零计数器并使能滴答定时器 */
    ) h' k; p8 A9 y, ^& P$ z# I
  57.     SysTick->VAL   = 0;  
    . |& {! k  ~, E0 z! E6 ^+ M
  58.     SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;     
    ( j, {' L' Q, j& r9 }0 l

  59. * B" |" V9 a2 ~0 N3 L; x$ _( C
  60. for( ; nTime > 0 ; nTime--)
    3 ~# @; ~  L/ x, i; d/ P8 p
  61.     {
    : k7 ?3 W9 x( I0 _4 u6 \: k5 ]5 |
  62. /* 等待一个延时单位的结束 */1 O  x- @6 s: _: |' J
  63. while(SysTick_GetFlagStatus() != SET);( c: [( \, x& a7 T7 ^3 ^/ F
  64.     }7 O4 d) V/ R8 i2 _  F

  65. 2 n& K; n! i! {+ f8 E) d% F  D
  66. /* 关闭滴答定时器 */
    3 W6 X: x1 U6 ?* A. T2 d( p
  67.     SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;; X; j( S: U- _& y8 L
  68. }</font>
复制代码

4 g8 v8 K3 U9 ~( W1 b5 n: l; j9 v0 E
    检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:
  1. <font face="Tahoma" color="#000000">#ifndef __GPIO_H
    $ I" O8 h# y& Y1 E% i0 @* d- \
  2. #define    __GPIO_H7 w8 }) [( ?- ^3 y
  3. ) A3 z2 G& v3 R" A1 s
  4. #include "stm32f10x.h"2 i. C4 N3 g5 x2 o2 k

  5. % E) {3 b: u$ m9 D2 R; Z3 {# n
  6. #define     LOW          0
    , G+ `6 G" x- U; |3 L; H8 c
  7. #define     HIGH         1
    * d8 m, n) s; q  t

  8. . g6 C% p8 h# ^! G- x- c8 h
  9. /* 带参宏,可以像内联函数一样使用 */
    - E$ t' r' m5 A- g; w
  10. #define TX(a)                if (a)    \5 ?- i+ K0 W. O8 Z7 c0 s
  11.                                             GPIO_SetBits(GPIOB,GPIO_Pin_0);\' k0 N* k% O7 C
  12. else        \1 L* W4 {+ F. e( P' I* C
  13.                                             GPIO_ResetBits(GPIOB,GPIO_Pin_0), M: O7 N) F* `- X  z$ S9 c; f
  14. void GPIO_Config(void);7 \, B9 {' M  r: X  G; U
  15. ) Q! _/ a$ T' f
  16. #endif
    0 y1 z1 `' x1 p8 N3 A
  17. # x& K+ W. f' s/ E! h6 z
  18. #include "gpio.h"- ]; P' }3 P5 ?$ X5 S

  19. " e) h6 Q& F% ]6 F) [
  20. /**; d% t& W$ ^5 Z
  21.   * @brief  初始化GPIO
    0 v6 y# ]2 G! L5 O2 B
  22.   * @param  无
    / x; Q% H, q; I
  23.   * @retval 无
    ' j9 Q+ m8 H0 j9 h6 h+ p
  24.   */
    2 X! f# Y* P; X- ?5 W+ \
  25. void GPIO_Config(void). K; V5 G& M( i
  26. {        3 X) ]8 c8 `7 \$ |3 |+ t/ z
  27. /*定义一个GPIO_InitTypeDef类型的结构体*/
    " U6 y, c) H( V
  28.         GPIO_InitTypeDef GPIO_InitStructure;
    ; J( m+ ?- O. m/ D  W4 ]

  29. 7 H: e) I0 @  O& G# n" o
  30. /*开启LED的外设时钟*/
    + }/ I3 W0 G! A9 Y
  31.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
    # K& G5 T* v4 P8 t8 r5 [
  32. ; L: [" h* I# c( n8 [3 q% h
  33.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;   
    3 ]" D: m; t" ~( ?9 V* @0 D9 W, Q
  34.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     % `9 v; M0 Q8 a8 x
  35.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; * e/ I" E/ w$ A8 H
  36.         GPIO_Init(GPIOB, &GPIO_InitStructure);   
    , q# U  D5 c; P$ S* V
  37. }</font>
复制代码

0 S7 R, X0 ^6 M5 U1 k4 b# D
    在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。
    结论:此延时函数基本上还是可靠的。
使用定时器方法的实例
* S2 M6 W; ^, N. o
    Delay_us函数使用STM32定时器2实现:
  1. <font face="Tahoma" color="#000000">#include "timer.h"
    " L( `5 z  `- {8 K; P
  2. 0 a) ]; W' S+ Z/ Z
  3. /* SystemFrequency / 1000            1ms中断一次
    2 p0 }6 D# _& E
  4. * SystemFrequency / 100000     10us中断一次
    5 k2 i2 X9 I; n& v3 a: [8 M
  5. * SystemFrequency / 1000000         1us中断一次
    8 [$ ^# Q& ~* @' N0 Z7 A. s
  6. */
    3 c. _# t1 T" [8 F
  7. 0 D' G0 u0 w; h
  8. #define SYSTICKPERIOD                    0.000001, O6 ^' h: o) K2 K7 ~
  9. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)0 e8 S4 v: u6 d) D- Y- [- {" x/ s
  10. 5 q: U5 j  t6 |) D+ F
  11. /**
    5 h; U: }6 J3 x
  12.   * @brief  定时器2的初始化,,定时周期1uS
    2 ?3 ~, Y, H2 \. i: n
  13.   * @param  无
    ) \+ ^/ P& Z! j4 S( i, _4 Z
  14.   * @retval 无; N: q$ m7 b7 Z3 ?$ O: m
  15.   */& L/ n! m; b- }- @; U' d/ X& }7 B
  16. void TIM2_Init(void)
    * x1 N7 ]  W2 p3 l4 |: w8 `  {
  17. {
    7 w0 I. x' `5 n* m; \& L
  18.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;9 a* n: A& s5 X
  19. 5 L" q8 Y9 W$ G# w" j0 X( M
  20. /*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */& M# D. C& G0 B9 x$ {
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    3 l9 g# s7 L" e$ x
  22. 8 ]" D. ^4 X5 Y& l& `/ o
  23. /* Time base configuration */6 ^2 Y: D/ {1 |: C0 M
  24.     TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;$ c+ `3 ]. _+ F- N% \, h( P0 x+ A
  25.     TIM_TimeBaseStructure.TIM_Prescaler = 0;+ w! q- [4 C) P: Y7 [* u6 ], b) }
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    , p3 ~; [0 }$ W0 [9 }" n* z
  27.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    $ N, D8 j) Z) y, ]

  28. 8 ~# l( f& r8 \+ z0 N
  29.     TIM_ARRPreloadConfig(TIM2, ENABLE);/ h* ^! G4 K) v* F4 X

  30. $ |( B: h' x1 g
  31. /* 设置更新请求源只在计数器上溢或下溢时产生中断 */
    ( c8 B8 F2 C* ?+ _
  32.     TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global);
    6 ^. f" V( {2 Y& j* G
  33.     TIM_ClearFlag(TIM2, TIM_FLAG_Update);/ g3 t% `  {) a6 ^6 b& S7 w5 Z
  34. }% m  e+ z4 ~& _) U

  35. 6 o$ ]4 ~9 O5 s1 H
  36. /**& q9 r# G# J, e& }
  37.   * @brief   us延时程序,10us为一个单位7 u- }+ F3 O* W  o, k' Z
  38.   * @param  
    0 [9 v2 `0 H$ F5 p2 j9 ^
  39.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us5 d$ v  J( g$ m: Y. h6 ]! f
  40.   * @retval  无0 v& m; L: l% C& Z2 t+ _; P
  41.   */  E+ Y/ O2 n# f
  42. void Delay_us(__IO uint32_t nTime)* G- \- Y* a# b$ F  U
  43. {     ' }7 ~  ]3 Y% Y# t
  44. /* 清零计数器并使能滴答定时器 */5 L0 \3 ?1 ~0 v
  45.     TIM2->CNT   = 0;  
    0 g4 w& D! a/ i/ \; i
  46.     TIM_Cmd(TIM2, ENABLE);     
    ) d$ y& l9 l! U  I5 E" l
  47. 5 N% `/ E3 h9 `8 y6 w6 p
  48. for( ; nTime > 0 ; nTime--)
    * B/ t8 U+ z7 e9 ~: `& e
  49.     {* |  H. E0 \2 J+ z( t" X
  50. /* 等待一个延时单位的结束 */
    ! S/ @. r6 p6 Q  x9 Z; f( o3 ^
  51. while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);7 v- D6 l* ]! v( c, f
  52.      TIM_ClearFlag(TIM2, TIM_FLAG_Update);" J* }! W! K' `, k  D
  53.     }
    5 O+ P9 p/ L$ r* H& y

  54. - C& t( C% ]% M, ?9 D
  55.     TIM_Cmd(TIM2, DISABLE);
    3 w& E8 K  ]3 y$ H9 E% G, ^! N( \
  56. }</font>
复制代码
1 u: a6 n4 A, Y. J  G% f2 z
  在main函数中检验Delay_us的执行时间:
  1. <font face="Tahoma" color="#000000">#include "stm32f10x.h"$ G+ j* ^/ Z: L$ x7 V: [
  2. #include "Timer_Drive.h"6 B4 `( o& b( b! M9 |- }% s
  3. #include "gpio.h"
    , {4 \( _( K, ?
  4. #include "systick.h"
    % r: Z9 E) T' k  g$ f1 _
  5. / Q. j+ Y6 P7 ?- O/ _3 i
  6. TimingVarTypeDef Time;3 O  t7 m4 C2 e; s3 \4 p
  7. - s. I7 u! Y4 ]% P& @
  8. int main(void)! o; D* L* }! ^: r! I& n3 c" Q
  9. {    $ X8 f# z/ K9 q2 N$ l9 Y: r4 A# I
  10.     TIM2_Init();    * d+ Z  y6 R6 s, Z$ m. R8 o
  11.     SysTick_Init();
    : Y2 }- C. \& e* V
  12.     SysTick_Time_Init(&Time);
    - X4 K1 i2 {) n2 y( ?! x0 x4 z- v
  13. : r: l5 [5 h) g5 x" s0 \; R4 Y
  14. for(;;)
    6 Q# o) C0 U* X; _( l  e$ O) T
  15.     {
    ' n7 H$ R9 K* W' ?! j9 E+ s
  16.         SysTick_Time_Start();
    * s1 ~5 p% k  Q( c, I
  17.         Delay_us(1000);
    ) e5 ?9 c3 N6 p6 \; s$ \1 y
  18.         SysTick_Time_Stop();$ V  q, ^1 T7 k7 l
  19.     }     & ~; }. v  w' B9 R) P. X- g0 {
  20. }</font>
复制代码

& T) s' s& q8 J, I# U# G
    怎么去看检测结果呢?用调试的办法,打开调试界面后,将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以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。
& b. t4 c+ y2 \/ \2 W8 d5 X
收藏 评论0 发布时间:2021-11-7 15:39

举报

0个回答

所属标签

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