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

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

[复制链接]
STMCU小助手 发布时间:2021-11-7 15:39
前言
    测试代码的运行时间的两种方法:
  • 使用单片机内部定时器,在待测程序段的开始启动定时器,在待测程序段的结尾关闭定时器。为了测量的准确性,要进行多次测量,并进行平均取值。
  • 借助示波器的方法是:在待测程序段的开始阶段使单片机的一个GPIO输出高电平,在待测程序段的结尾阶段再令这个GPIO输出低电平。用示波器通过检查高电平的时间长度,就知道了这段代码的运行时间。显然,借助于示波器的方法更为简便。
    / h! T8 q- P$ }' t/ l/ N$ d* t" K
    , w! F* I# C$ Q. r. p& P7 O! l, b. _
借助示波器方法的实例
    Delay_us函数使用STM32系统滴答定时器实现:
  1. <font face="Tahoma" color="#000000">
    ( t; Q- \! ?$ c
  2. #include "systick.h"7 P4 q/ {. q0 s% U' N. U5 u. J

  3. 3 b6 b5 l# `8 @. f
  4. /* SystemFrequency / 1000    1ms中断一次& d  }: I# M9 \5 r( q5 a' t- b) b
  5. * SystemFrequency / 100000     10us中断一次1 F" q% ^1 P  A- ~
  6. * SystemFrequency / 1000000 1us中断一次* x4 F' D2 a& }& e
  7. */: c( r5 t1 t9 {: O" ?# t9 r

  8. % q, G' k7 z2 {( A4 ^; F: f2 B2 n
  9. #define SYSTICKPERIOD                    0.000001, t( B& |) ?& k$ {- v' x
  10. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)  i( ?* |% B) K9 Q/ h; T+ J

  11. & E! T# j1 @; c# }* b* w* S
  12. /**
    * C6 \- K4 T" H: Z
  13.   * @brief  读取SysTick的状态位COUNTFLAG
    * X$ h* M$ d$ z; |; W3 L; L) A
  14.   * @param  无. }% a: ]) N; ^3 q0 E
  15.   * @retval The new state of USART_FLAG (SET or RESET).
    2 e" v4 B& w3 p/ T5 O" I
  16.   */
    - v2 S7 S2 Y5 S
  17. static FlagStatus SysTick_GetFlagStatus(void) + @' l3 n- h& b+ Z. ~
  18. {
      N8 t$ [$ X- t" G) r
  19. if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk)   u" Q- `  q: p: @: T
  20.     {, A" N. Y& r3 I/ I
  21. return SET;
    4 L5 A; \. W) H. w: |
  22.     }
      L( Z. ^! `! W, |3 s9 e% S" i
  23. else. u' a' |, Y" f7 S* v" n5 T
  24.     {
    1 U& v5 i1 g) t
  25. return RESET;" S* I& m0 r+ [% ]) G, r
  26.     }; T9 j# j: g" E# A6 z0 J
  27. }3 Y7 ?. H1 |0 f+ I9 H

  28. $ G/ F& t8 |; V/ f# q+ c- ~
  29. /**% u, A, D0 m4 J, F) E0 L+ N  |
  30.   * @brief  配置系统滴答定时器 SysTick
    . W5 X8 p5 b# g/ k/ c
  31.   * @param  无: a( M/ F: m3 w- d* l( u
  32.   * @retval 1 = failed, 0 = successful3 j2 g9 g5 }* `# e* a- m
  33.   */
    " U" s, I; X) B  d: p; }
  34. uint32_t SysTick_Init(void)6 N; J) a8 B- h. `
  35. {
    0 P$ Z+ T( ?" ^
  36. /* 设置定时周期为1us  */4 Q4 P$ I! b9 ~- F$ X$ h  `1 {4 c
  37. if (SysTick_Config(SystemCoreClock / SYSTICKFREQUENCY))
    0 Y1 P, O  H( Y
  38.     { : {- W! p9 d, I' y: ~3 _
  39. /* Capture error */
    , ^7 L2 C2 N) t+ P; a1 s$ w3 x
  40. return (1);
    ( {7 j3 }% U! H% z: y6 E
  41.     }
    # Z+ b4 D' G( \: Z0 s* W
  42. 6 X% d: m3 ^  v5 z- k3 M
  43. /* 关闭滴答定时器且禁止中断  */
    . }4 d7 K  u! X( w
  44.     SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);                                                  
    8 `/ Z) P+ p2 }! G& m  W  _
  45. return (0);4 u3 c+ x2 t! ?: l/ a, ?  O7 a# K3 ~
  46. }
    & v4 J' v+ T/ V1 Y
  47. # R' t& {  F4 ^
  48. /**
    4 c( r6 l' O& q* O" ]
  49.   * @brief   us延时程序,10us为一个单位$ o, _! B6 ]% `) e
  50.   * @param, [1 o0 L/ R9 w# O5 R( |+ v1 x
  51.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us, \2 x0 e4 D6 n# [+ s" v' E
  52.   * @retval  无
    ( X% n. I7 g0 m" r9 G
  53.   *// l; @. L, X/ }. D. D. w
  54. void Delay_us(__IO uint32_t nTime)# v( i! U8 Y. {3 |5 @
  55. {     ( a7 t: Y% X( _* [2 B+ ^
  56. /* 清零计数器并使能滴答定时器 */# p& l; ?& W& C
  57.     SysTick->VAL   = 0;  - G7 L: t5 r5 t# s
  58.     SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;       g* }( R" `$ B% p6 x
  59. / O( o/ B( a+ `6 G' x. T2 i" j4 n
  60. for( ; nTime > 0 ; nTime--)+ o/ r2 Z5 e8 Y
  61.     {& @( T5 ?- a7 `2 e1 S0 c7 C9 g$ f
  62. /* 等待一个延时单位的结束 */
    $ h! m0 C: _9 l* ?2 B& b
  63. while(SysTick_GetFlagStatus() != SET);
    - P- n4 P) t( Y
  64.     }
      D) \, x6 ~' l. F4 l  {* d1 [7 z# H

  65. ( r+ G1 a" P: y8 ~9 \  i
  66. /* 关闭滴答定时器 */# V8 W: N5 t: a$ Y" g8 f: w
  67.     SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;
    2 F# G# D1 S/ l5 [9 C* s: E/ @4 T
  68. }</font>
复制代码

: y! i* G0 v" G
    检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:
  1. <font face="Tahoma" color="#000000">#ifndef __GPIO_H
    ( ?& d3 p& O% r& H7 T) |
  2. #define    __GPIO_H
    - r* s) J% G( Q* J! G) U

  3. % T4 d6 T3 U3 ?1 k1 f
  4. #include "stm32f10x.h". k0 d: c& }# G

  5. % h/ [% _/ _" i$ O! g3 e
  6. #define     LOW          08 n( J. d( [. @: q
  7. #define     HIGH         14 t0 ^9 L! ]' {) n. z2 m5 m
  8. + A6 m( @, D; k* e
  9. /* 带参宏,可以像内联函数一样使用 */
    - i  z  _7 z6 S+ s
  10. #define TX(a)                if (a)    \2 w: z/ p0 o& U$ a
  11.                                             GPIO_SetBits(GPIOB,GPIO_Pin_0);\
    : y# M: I9 H9 D
  12. else        \
    2 Q$ M9 z+ @$ S  P* }
  13.                                             GPIO_ResetBits(GPIOB,GPIO_Pin_0)
    * G% z5 A3 c9 K
  14. void GPIO_Config(void);
    / @) }3 K, R/ l( r( z  q0 }
  15. - M/ l: l5 t0 [' Z8 C
  16. #endif
    ; ~8 e6 M' ]+ K7 n. j4 j

  17. * G' F; A9 s. Q5 I- M* L4 N
  18. #include "gpio.h"
    8 u' J6 s- y. \6 k+ x1 ]- u% r

  19. + U1 X! N/ i1 S3 H* t" H
  20. /**
    $ F% u% q/ O1 W! l- S0 ^
  21.   * @brief  初始化GPIO. W* E4 P. ?8 ~; H3 B2 S' X
  22.   * @param  无) E0 V& d( R/ Z' }" L
  23.   * @retval 无: F4 J3 e' n5 a" G
  24.   */
    1 _# d8 u* q! d, W
  25. void GPIO_Config(void), ~$ S5 {& P6 R8 T0 L3 x* M
  26. {        
    2 O# v" R# S  P3 h0 {) E! S% R+ w
  27. /*定义一个GPIO_InitTypeDef类型的结构体*/3 y) U- u: U3 w6 L* k7 {8 O$ {8 D
  28.         GPIO_InitTypeDef GPIO_InitStructure;+ E0 E* _2 u" v6 q& k9 y/ U$ B
  29. + W! R/ R- g' c& L, p1 z
  30. /*开启LED的外设时钟*/3 [  V$ U" R5 |! s/ m
  31.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
    ' |: ?5 c3 v8 b  f8 U$ D- C. t
  32. . `/ e% l& g# t3 \1 z- Z
  33.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;    & E0 j1 v) ]) \6 ~! a, U* d& q
  34.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     
    , \; t7 R4 T8 R1 U1 k
  35.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; $ `& Y/ I2 `  Q! L9 `$ E
  36.         GPIO_Init(GPIOB, &GPIO_InitStructure);   
    ; l  P( x( k9 u' }2 Z* D; z
  37. }</font>
复制代码
& r6 r0 x: z, E& @$ s  C
    在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。
    结论:此延时函数基本上还是可靠的。
使用定时器方法的实例, v; B; V5 Y; }( W9 N# j8 I/ L* r( k
    Delay_us函数使用STM32定时器2实现:
  1. <font face="Tahoma" color="#000000">#include "timer.h"
    ( R; r$ c. P* f( o

  2. . Y3 i8 M/ N( @! h$ ~6 X6 _) D/ U
  3. /* SystemFrequency / 1000            1ms中断一次
    2 f1 e+ |' o  S
  4. * SystemFrequency / 100000     10us中断一次+ ?" D8 Y( |" _, |  e8 ~
  5. * SystemFrequency / 1000000         1us中断一次8 q* t& {* i7 m1 c
  6. */
    ( |# Z: J, K" v3 f- Y
  7. * v0 A% H2 m% X/ a$ N9 c. Z4 x
  8. #define SYSTICKPERIOD                    0.000001
    ) X1 b7 |# S' s2 g+ E& y
  9. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)  `% t- J. K4 g/ q1 V9 ~
  10. 3 s! ~- f+ w$ x" Z: B
  11. /**& w7 ^" o) Q* @" [; H
  12.   * @brief  定时器2的初始化,,定时周期1uS1 Z6 z2 K7 b7 t: Q! G
  13.   * @param  无
    : H! _% \6 e) O9 U+ l
  14.   * @retval 无5 O$ U4 v- o# [6 Y/ i! Y
  15.   */
    0 j. g0 s+ Z; N; S! ]
  16. void TIM2_Init(void)8 M+ ~* f3 e1 O3 }
  17. {
    " k' n" l4 S% n4 G
  18.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;% n) p- l9 j8 e2 y- S0 r7 P
  19.   }  k, R5 W9 ?2 e( a. a  ^: W$ m
  20. /*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */
    0 N4 j* P5 n9 x0 u4 Y$ y
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    ) F' \1 g' u& p" B& g" h- T, t
  22. 0 Y% C3 ~  V+ [8 x' p
  23. /* Time base configuration */* K( H2 g6 a9 |- n! [
  24.     TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;
      V! i, N  _- I! H) {1 j! S
  25.     TIM_TimeBaseStructure.TIM_Prescaler = 0;
    " a& o- T2 c3 N0 j
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;% ^0 q8 @* i# Q$ m* U/ m) \* }+ _, N
  27.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);9 W( O  h; D' J- K" k  V8 a) d; t
  28. - Y6 E# Q# H$ z$ Y# }6 \. k
  29.     TIM_ARRPreloadConfig(TIM2, ENABLE);
    ; |, \: y/ X- B' j# l
  30.   G( ~) O9 K9 W; f8 P: F7 Z
  31. /* 设置更新请求源只在计数器上溢或下溢时产生中断 */
    % k4 p2 J8 B- Z8 ?# x' b  P% p
  32.     TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global); 3 @  L6 }. _; }3 b+ D* e
  33.     TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    ; @: p; `, D( U% n! P4 |
  34. }5 S/ ]6 D) x; t8 S- W" h+ Z
  35. 6 J# W$ F6 F0 ^  R" Z
  36. /**
    1 }* w$ _1 R. i" b, m7 g0 e
  37.   * @brief   us延时程序,10us为一个单位
    - _& r/ M( I8 U) p3 T  H- t, P, Y
  38.   * @param  
    $ ~  h8 o; Z% c# i' w
  39.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us
    . d9 {4 U. [# }% v1 K
  40.   * @retval  无$ c2 B' U- B& ]3 e
  41.   */5 O7 R: K$ {. i1 B5 x5 R
  42. void Delay_us(__IO uint32_t nTime)
    2 D; s. _1 r$ I  o2 ]1 b
  43. {     & `% i. P! B+ B
  44. /* 清零计数器并使能滴答定时器 */4 _5 M1 B5 V' U! g9 n3 v" s
  45.     TIM2->CNT   = 0;  / W1 E9 `6 t0 J5 s% D
  46.     TIM_Cmd(TIM2, ENABLE);     ; Q9 v2 V% W# P7 j. B8 E. r
  47.   y5 f0 ?! E) ]8 p5 P3 E
  48. for( ; nTime > 0 ; nTime--). l: r7 Z0 ?) I- {5 X2 s
  49.     {
    - t" W' y9 l. p$ l, Q$ A3 Z
  50. /* 等待一个延时单位的结束 */
    7 |) `5 c+ G7 f! p
  51. while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);
    ' I0 j. e' v6 j- K* x, c
  52.      TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    ; I$ b. B) k$ R6 p; V
  53.     }
    & d' H5 s) G# i" u
  54. / e7 K% S( K) ]$ l) f9 f
  55.     TIM_Cmd(TIM2, DISABLE);
    7 F3 q& q- X/ Z7 F0 S
  56. }</font>
复制代码

+ R7 o/ t' @! n" p
  在main函数中检验Delay_us的执行时间:
  1. <font face="Tahoma" color="#000000">#include "stm32f10x.h"1 [) c* M0 x; j& B  d
  2. #include "Timer_Drive.h"
    " o+ O( b1 H0 ^' x% D6 B! E, _
  3. #include "gpio.h"
    8 o: L; _$ T3 p  R' w! n
  4. #include "systick.h"
    , Q7 g- a4 U, G! i
  5. 8 q. c% x; ?) E  U4 |- f+ u/ v
  6. TimingVarTypeDef Time;
    5 Z- F& y& d: u. H3 R  t7 T

  7.   f6 ]4 L1 f7 N0 E* L
  8. int main(void)- t8 L) B* N8 {
  9. {   
    * }" z3 k: F: u: z& v
  10.     TIM2_Init();    . j( h" j3 u4 q6 B7 `" D8 u
  11.     SysTick_Init();
    7 E: h9 m6 W* g5 T
  12.     SysTick_Time_Init(&Time);& T; J5 g4 @6 l) d& Q* z

  13. ' c  ^: |; t5 P/ R
  14. for(;;)2 \  [- ]6 h# Y( f, f% T
  15.     {
    $ H3 h1 p! r/ ~: H" e" e: W" H
  16.         SysTick_Time_Start();
    ; l6 s9 ^  Z( u; W3 |; l
  17.         Delay_us(1000);" p0 D/ u: l) |$ C' I* F
  18.         SysTick_Time_Stop();
    2 S& H8 I+ d7 \: R6 k
  19.     }     # D. Z0 n' z4 ?1 Y+ T2 \/ w8 |
  20. }</font>
复制代码
% O4 h: l% D- M) b( W
    怎么去看检测结果呢?用调试的办法,打开调试界面后,将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以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。
" R9 ~% E2 b" _
赞 收藏 评论0 发布时间:2021-11-7 15:39

举报

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