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

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

[复制链接]
STMCU小助手 发布时间:2021-11-7 15:39
前言
    测试代码的运行时间的两种方法:
  • 使用单片机内部定时器,在待测程序段的开始启动定时器,在待测程序段的结尾关闭定时器。为了测量的准确性,要进行多次测量,并进行平均取值。
  • 借助示波器的方法是:在待测程序段的开始阶段使单片机的一个GPIO输出高电平,在待测程序段的结尾阶段再令这个GPIO输出低电平。用示波器通过检查高电平的时间长度,就知道了这段代码的运行时间。显然,借助于示波器的方法更为简便。
    + c, ]. r1 u  ]3 h4 m  t
    - n0 t2 q+ Z, l. j# v
借助示波器方法的实例
    Delay_us函数使用STM32系统滴答定时器实现:
  1. <font face="Tahoma" color="#000000">
    9 }/ ^7 v, T/ e
  2. #include "systick.h"
    4 k* J) w7 b7 q" F8 N
  3. & ?4 a* N) k" H
  4. /* SystemFrequency / 1000    1ms中断一次6 T& f. c3 n: L8 e3 Z: V4 {. c
  5. * SystemFrequency / 100000     10us中断一次
    % k9 f: [# b+ ]5 q( d4 d
  6. * SystemFrequency / 1000000 1us中断一次4 a7 [! `* O0 a; e4 J- O# `' ^4 L
  7. */
    ( A" m/ a9 y, {, ~, C7 t1 g* R

  8. ' x6 b7 A* P/ k9 }) r, x
  9. #define SYSTICKPERIOD                    0.0000012 M6 n8 s! Q6 Q; F
  10. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)7 b: {2 D0 @- Q. ]9 q

  11. 8 _$ q; M. \+ x6 l
  12. /**# F, ]6 q, |) k0 {; G8 E2 Z
  13.   * @brief  读取SysTick的状态位COUNTFLAG
    $ }# C% y# m1 E- ?
  14.   * @param  无
    ) T! n# a* F6 o0 Q; h* \
  15.   * @retval The new state of USART_FLAG (SET or RESET).0 M0 E  p( i7 k
  16.   */  `8 @3 y" s4 n7 Z# D! Y
  17. static FlagStatus SysTick_GetFlagStatus(void)
      K0 H+ j6 N3 H9 {" Q, h
  18. {
    8 g/ O* h# I' r: |4 r. a  V0 R
  19. if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk) # e/ x! E: _0 O5 v6 [
  20.     {
    7 J8 n" e. D7 q. y8 Q& H
  21. return SET;5 n/ s! y' b" P' f. `6 n
  22.     }
    $ A( Q7 d% m) @" W" s
  23. else
    ! @; b7 P1 d: \
  24.     {
    6 P: U% n# {: c& ^
  25. return RESET;, E& R  G/ E5 i; B" D  s9 \! H
  26.     }0 Y: I; Z2 Y. V  h0 u$ @- D3 v/ r
  27. }7 q% w; D) `5 p
  28.   ]" m3 J; }) ~) o
  29. /**& T% [& n1 d2 t9 [
  30.   * @brief  配置系统滴答定时器 SysTick$ R  ~3 l) H+ C: _
  31.   * @param  无7 H2 _1 n, h& R: p# M
  32.   * @retval 1 = failed, 0 = successful
    9 ]+ B+ d" g4 X
  33.   */$ h8 T. j& }, ?( M; i' u# I
  34. uint32_t SysTick_Init(void)$ u% `+ a& C4 h4 z+ g5 S
  35. {
    ! S! S$ r3 S8 x& X
  36. /* 设置定时周期为1us  */
    2 A; A- o. z1 L
  37. if (SysTick_Config(SystemCoreClock / SYSTICKFREQUENCY)) 3 K4 G' C" U; N2 B; w
  38.     { ) G* r$ `6 A3 D: c+ t; M8 a# C% s
  39. /* Capture error */
    & A: W3 y  d! O' _( E6 b2 L9 U( F
  40. return (1);2 C7 a1 P& a  _8 d
  41.     }: h, b; ]- p6 e

  42. 4 {+ V/ u! H4 F+ x& P& ~! p( {
  43. /* 关闭滴答定时器且禁止中断  */% U9 b1 q. Z) V4 K: [
  44.     SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);                                                  
    & p# o* W6 K( D
  45. return (0);2 s0 y- ^( h; d9 L* o* G/ B
  46. }( A# ^$ @0 q: j0 B
  47. : `0 M. ^8 l+ X4 R0 I
  48. /**
    2 _* B" P( G% |1 y
  49.   * @brief   us延时程序,10us为一个单位# f7 p0 C2 V8 M4 f0 K
  50.   * @param; n( D5 o; f: e/ K0 L
  51.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us
    8 h- D% l# z% ?& ?% K2 \$ D
  52.   * @retval  无0 \4 D1 _! t$ ^& r
  53.   */
    " Z/ W* q2 r/ _2 s, h+ m
  54. void Delay_us(__IO uint32_t nTime)
    + G& X! G# t# R$ [
  55. {     
    4 ~% m0 d' a& x0 A( r1 ?" W
  56. /* 清零计数器并使能滴答定时器 */
    3 f& h! q- T5 B0 Y* k/ q
  57.     SysTick->VAL   = 0;  - X) s, f' R4 f) X1 n/ Z& f
  58.     SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;     4 B( m) v* Y! Q% b7 c2 }& \1 |9 F

  59. % W+ n6 m5 W" e8 `1 D" O
  60. for( ; nTime > 0 ; nTime--)  X- z: L  t5 R3 @. i
  61.     {
    8 r  R% V4 P6 i8 `& H. k1 X7 f
  62. /* 等待一个延时单位的结束 *// u, x& v- V# q
  63. while(SysTick_GetFlagStatus() != SET);
    - |3 g, R: N. b! [, \
  64.     }
    ( e, Z) {7 X% w/ m8 E( Z

  65. % M6 |( g: \! H* z! _
  66. /* 关闭滴答定时器 */# l, c: B( t! P) j- a* D" F: p
  67.     SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;; x6 K  u9 Q; ]  i1 B6 Y& n
  68. }</font>
复制代码

6 l. o* U, h) Z0 ~9 P
    检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:
  1. <font face="Tahoma" color="#000000">#ifndef __GPIO_H
    - |6 S, E8 d0 x* |* G
  2. #define    __GPIO_H! z5 @; k& F  J7 X# d
  3. ! D- ?% \, x5 ?$ x3 R. n9 [
  4. #include "stm32f10x.h"% N( \1 \3 H: b

  5. % |  D$ I' N2 h6 _1 g9 h3 g
  6. #define     LOW          0; Q  `# a8 E9 O) w2 _
  7. #define     HIGH         1) |9 U' G. K& ?
  8. / _* q' x; z9 K2 E3 N
  9. /* 带参宏,可以像内联函数一样使用 */8 S, |) R  n8 i0 }# X
  10. #define TX(a)                if (a)    \/ G" l8 ^5 D- B
  11.                                             GPIO_SetBits(GPIOB,GPIO_Pin_0);\
    ; Z3 B( ]2 n; h
  12. else        \
    7 O& F) e% c6 P8 u
  13.                                             GPIO_ResetBits(GPIOB,GPIO_Pin_0)8 f6 }) `% T. c) Q  e# l
  14. void GPIO_Config(void);$ N! g& c' n+ S" v2 R" ], ~+ t

  15. " p! h) f& \6 J5 y9 q
  16. #endif9 \& k2 F9 S+ Q+ f- X8 c% j
  17. 5 h) N/ f. J) G/ L. @' N
  18. #include "gpio.h"* |" V% s7 [- V8 ^
  19. 4 L: K, w" |: c- X+ y( x& r
  20. /**7 r0 g! ]: n; Y5 `
  21.   * @brief  初始化GPIO) I/ y* Z: f1 Y5 u4 v" ^$ L+ x1 j. |
  22.   * @param  无
    & @  w5 P9 @2 ~, ]7 ^  ]' T5 `  {) U4 f
  23.   * @retval 无0 q, e+ z( S& z" G, d* ]
  24.   */0 N" U6 h+ E& }$ L
  25. void GPIO_Config(void)+ g+ w+ T# v+ A3 i) I3 l
  26. {        
    ; ]" @: T3 [" N
  27. /*定义一个GPIO_InitTypeDef类型的结构体*/
    9 i# T1 G' Y; ?% i
  28.         GPIO_InitTypeDef GPIO_InitStructure;
    " J- |  e* T! K/ C5 Z6 W4 v) {; Q9 x

  29. ) ?/ p/ v) }# w3 E
  30. /*开启LED的外设时钟*/- w$ g- B5 F# V" w0 [" |/ T
  31.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); 9 ^; e' o0 z" u& m4 Z) y' E

  32. % v" o' `+ v, c5 z! T2 d) x' n( v
  33.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;   
    & c3 y7 W& \4 O- v0 @6 |
  34.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     
    , e# p# a8 i' J' {" h1 X+ a- K
  35.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    1 z# s- @$ C: ]( v4 e
  36.         GPIO_Init(GPIOB, &GPIO_InitStructure);   
    3 Q1 h2 o; e/ f1 l8 \" J
  37. }</font>
复制代码
' Z' U) R' g' _7 z" |
    在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 u; f: M; ?& i: I
    Delay_us函数使用STM32定时器2实现:
  1. <font face="Tahoma" color="#000000">#include "timer.h"
    - U  \: V& n3 ]# K) A! D
  2. 3 A) L8 {  f( t- e9 L6 b! M
  3. /* SystemFrequency / 1000            1ms中断一次7 t# t0 B+ L$ L) f! r0 U
  4. * SystemFrequency / 100000     10us中断一次
    $ ~) N1 n+ `. [: y
  5. * SystemFrequency / 1000000         1us中断一次7 Q3 |! g5 D; [
  6. */; g4 G! h* T. D. d0 P! ?" W# {3 [

  7. + J; i, I1 W! h7 l% c
  8. #define SYSTICKPERIOD                    0.000001
    , H# R3 ~. o1 ?' k
  9. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    2 {; N5 O4 ^$ E0 _4 H" \
  10. $ n7 Y+ F4 H7 L/ k
  11. /**6 j+ J; \0 Q) o+ q* H* A3 t
  12.   * @brief  定时器2的初始化,,定时周期1uS" [/ g+ t1 C( a7 S
  13.   * @param  无
    * E5 a) w5 V1 R1 j' U
  14.   * @retval 无; K( M& c6 V: k0 ]$ S0 L
  15.   */! E; }: W$ u& Z5 n" g. O3 v
  16. void TIM2_Init(void)( }) p$ C  Z. r8 g6 S8 |
  17. {
    " s! z) t5 K/ G$ {1 B
  18.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;/ R5 s$ O. W  w) \! w2 R

  19. ' l# c) d& b# q3 K% }8 g
  20. /*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */
    0 ^, ^; a8 V: X) ^& M& ~& G
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    3 o: ^) w/ P% t3 s7 W

  22. : _8 {" _* f, G, ?8 D2 `) A8 F
  23. /* Time base configuration */
    7 J. q0 T! j1 |: H$ q7 g9 i" I
  24.     TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;
    + X( @8 F+ a* i! ^" i
  25.     TIM_TimeBaseStructure.TIM_Prescaler = 0;$ p- i- g5 i5 s- p# w' |
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;) Y% L3 z. l3 h; L& f/ L+ ]% T: f
  27.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    % ~) S! k: U: P: x7 d$ w# l* Z
  28. 5 X- j' j2 ~. e* V1 K
  29.     TIM_ARRPreloadConfig(TIM2, ENABLE);
    * B/ {9 v9 s6 S( n( O1 ?
  30. ( m6 u4 `1 k. u* P; o# |2 t% M
  31. /* 设置更新请求源只在计数器上溢或下溢时产生中断 */; B# q- w. y# Q6 D2 J
  32.     TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global); & l. h; k8 m+ K# f: |4 d' a4 ?# }
  33.     TIM_ClearFlag(TIM2, TIM_FLAG_Update);: P% \' L% E8 v+ A! |' r( c( v! d8 x
  34. }& x" \5 n: o5 D; w- `4 {1 u

  35. ; ]9 Q7 A' V2 ~
  36. /**  Y: j- x: n4 E1 S8 w( l5 A; c6 S8 a3 M
  37.   * @brief   us延时程序,10us为一个单位
    $ n+ w% a) q4 n( s  Y5 O9 h6 O* K
  38.   * @param  $ c- L! `* |$ J2 z: i
  39.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us: y' j5 t. @0 d9 k8 H  Z
  40.   * @retval  无
    : c8 B1 d$ t/ I- L7 B/ T1 [4 B& {1 C
  41.   */1 Q- T$ J& n5 v1 Q
  42. void Delay_us(__IO uint32_t nTime)% x: |6 b  o& d5 z  U8 G9 z
  43. {     8 _" I: E* z7 X! K, @( e( w
  44. /* 清零计数器并使能滴答定时器 */
    ( M# G6 [! L( @
  45.     TIM2->CNT   = 0;  
    . s- j3 ^  ^8 |; ?
  46.     TIM_Cmd(TIM2, ENABLE);     # O% @# C8 S7 j% t  ~

  47. 6 x7 X3 D  b! `1 G
  48. for( ; nTime > 0 ; nTime--)7 P- g* r7 E( n1 ^3 m% t
  49.     {
    # F- q/ h6 t( a+ R! k; p
  50. /* 等待一个延时单位的结束 */
    1 s# e9 {6 K& i6 x. Q4 @& v! `+ j
  51. while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);
    / B' q) U! z6 y+ o
  52.      TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    4 {2 e0 q9 P0 `4 l9 {0 n
  53.     }: s' Y. l# W1 H; i5 w
  54. ( ^$ s& {% g! q# s! g+ e
  55.     TIM_Cmd(TIM2, DISABLE);) _$ e. N' O% ~; |, R5 x9 s
  56. }</font>
复制代码

0 u0 X- X" d& g; K) y2 i
  在main函数中检验Delay_us的执行时间:
  1. <font face="Tahoma" color="#000000">#include "stm32f10x.h"5 _( m; G% }, B& Q: r
  2. #include "Timer_Drive.h"2 i" j" T" {! @. {* b0 M
  3. #include "gpio.h"  {0 y& G0 m, V0 y' |
  4. #include "systick.h"( B/ a7 Q: m- f/ V, |8 X, J

  5. 1 ?7 {: g9 D3 ?
  6. TimingVarTypeDef Time;
    & D. s- h2 U6 G) R
  7. # N. E  O: a* x- D8 ^6 D' C
  8. int main(void)
    , }  n# P  P; K
  9. {    & _6 S0 N+ a2 N3 {7 Z+ ?
  10.     TIM2_Init();   
    * ^7 f8 S; Q0 q8 Z- H: R# u
  11.     SysTick_Init();
    9 E; g$ g) ]& [9 S; O8 B
  12.     SysTick_Time_Init(&Time);
    6 o0 c1 p8 g2 W9 _4 p

  13. ; A% a/ u9 y. n/ |9 {
  14. for(;;)1 y9 h1 \" o1 O* h1 Y1 I1 M. U+ v
  15.     {+ t, k0 u/ l7 s  G/ {5 O5 ^
  16.         SysTick_Time_Start(); ' Q5 s0 B4 A) T0 m4 _& G
  17.         Delay_us(1000);* `7 A) M7 Y" b+ K: U
  18.         SysTick_Time_Stop();% u1 k- V! T4 O2 v) B
  19.     }     : V! b. O( n5 K# S
  20. }</font>
复制代码
+ N+ I. K1 m5 o- \0 p) M- E! n# }; \; j
    怎么去看检测结果呢?用调试的办法,打开调试界面后,将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以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。

! e7 H; s% e/ Q0 [& }
收藏 评论0 发布时间:2021-11-7 15:39

举报

0个回答

所属标签

相似分享

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