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

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

[复制链接]
STMCU小助手 发布时间:2021-11-7 15:39
前言
    测试代码的运行时间的两种方法:
  • 使用单片机内部定时器,在待测程序段的开始启动定时器,在待测程序段的结尾关闭定时器。为了测量的准确性,要进行多次测量,并进行平均取值。
  • 借助示波器的方法是:在待测程序段的开始阶段使单片机的一个GPIO输出高电平,在待测程序段的结尾阶段再令这个GPIO输出低电平。用示波器通过检查高电平的时间长度,就知道了这段代码的运行时间。显然,借助于示波器的方法更为简便。
    " T4 Y, j5 x' V5 |; f
    0 @  V: x" t1 a2 e1 R' d. _  o4 l9 E
借助示波器方法的实例
    Delay_us函数使用STM32系统滴答定时器实现:
  1. <font face="Tahoma" color="#000000">
    6 p5 q) D+ T' U$ }9 `1 V6 \2 l4 w% H
  2. #include "systick.h"/ L. ?$ {6 T/ E+ M6 P! O

  3. . ?& ]: |. Y  f0 z. B. M4 K* B
  4. /* SystemFrequency / 1000    1ms中断一次
    / N, @- m" s" Z$ `( a% R. Z
  5. * SystemFrequency / 100000     10us中断一次: ~( M! ^; V4 a
  6. * SystemFrequency / 1000000 1us中断一次
      B: A  K! T& X" Z& C+ c& A' F
  7. */
    0 x) q" W9 P& p. O- T

  8. 7 [# `0 ~; U1 u" K& Q
  9. #define SYSTICKPERIOD                    0.000001
    ; O" c4 m! w% U; l9 B. Z8 P) s
  10. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    / w7 o. x2 a& |1 Y' K0 W$ |
  11. 9 y0 e  `! _. Y
  12. /**. X5 [" d% a) n, E
  13.   * @brief  读取SysTick的状态位COUNTFLAG
    ' V, k5 [) {6 q; S
  14.   * @param  无8 W- B$ C2 o5 O/ O/ f, l
  15.   * @retval The new state of USART_FLAG (SET or RESET).: n0 z- ?0 P. [, y6 _0 J. u
  16.   */( ^+ D# L' l+ Z/ a0 _% A
  17. static FlagStatus SysTick_GetFlagStatus(void) 0 k3 R1 f" V' f1 L& n3 o  ^
  18. {- L+ y" g7 v9 o# A) w1 V
  19. if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk)
    9 M0 v/ e$ Z. O6 ~8 Q" b
  20.     {! @, o5 p. g1 C3 _
  21. return SET;3 V. A1 n" z  @3 |' I/ r# j, S. g
  22.     }$ M" z1 T6 ]( K$ p2 Y
  23. else* }& k7 ~! ^$ x; C* Y
  24.     {; w: X% Q! X5 ]0 F6 A% y4 D! Y
  25. return RESET;; g7 o% S4 h* U9 x1 q  f
  26.     }
    . K" p" S6 c( K, e
  27. }$ j" `) d) ^1 \0 V# f& W3 @  s$ @
  28. 6 S% E  }8 j' g$ V
  29. /**7 w( ?2 {* ?  V$ f3 I) @
  30.   * @brief  配置系统滴答定时器 SysTick
    6 c5 E- m3 R9 T- N8 A. `
  31.   * @param  无
    7 v" I( x* y0 ~
  32.   * @retval 1 = failed, 0 = successful7 \' e# ^6 [2 W' z- h, U
  33.   *// n" Z- Y2 o& \* z' e3 t; `: p
  34. uint32_t SysTick_Init(void)/ ]1 [, p& L, }# f# L+ |, F. e
  35. {0 b" Z# C0 _+ h: g) B
  36. /* 设置定时周期为1us  */
    2 M# H; e$ d- s- U( |  U
  37. if (SysTick_Config(SystemCoreClock / SYSTICKFREQUENCY)) 3 F. y" G! }. m
  38.     { / F% m3 e, J4 [9 j' b4 `
  39. /* Capture error */$ h+ T2 ^5 l/ p' @2 e; f% J' ~1 o
  40. return (1);
    7 y1 A) j; ]; p7 B
  41.     }
    3 x0 ?$ h5 ]& b+ E1 w, D
  42. " q, A+ t3 V0 s; s$ `% N9 R
  43. /* 关闭滴答定时器且禁止中断  */5 G1 a3 @& N1 p4 d3 ~2 e# q  D6 P
  44.     SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);                                                  : `1 ^. X+ F& `* O* B
  45. return (0);
    ( B! T! Y! ?% |$ X. T  k
  46. }" f! ^& W8 p4 z

  47. . x0 s7 G8 Q* {! x* L  k; Z: y) P$ K
  48. /**
    ' c: W+ f! @0 I) i2 \& ^& @- r: u
  49.   * @brief   us延时程序,10us为一个单位; z7 w' Q$ e4 b" k6 @
  50.   * @param, C" J; j2 l( I$ K' e
  51.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us7 d( R. g) n7 m; W$ o3 _4 R0 i
  52.   * @retval  无7 o+ D% b' U6 ?1 g, t! w
  53.   */
    % T) z2 [$ M/ b3 e1 j. ^4 C& J
  54. void Delay_us(__IO uint32_t nTime)- V% B$ W% p. O* X$ D
  55. {     + K$ N6 M# B8 S
  56. /* 清零计数器并使能滴答定时器 */
    4 `# d7 q3 \# N& l# O& P
  57.     SysTick->VAL   = 0;  1 _5 I5 e8 J( l2 N+ i
  58.     SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;     9 f8 S) u, N! f* H% R
  59. 7 H7 A4 X( _9 q2 G  p9 {
  60. for( ; nTime > 0 ; nTime--)# a- V$ f" h  D5 u2 V% K
  61.     {) k" M/ N0 n8 t  L4 |- `$ z
  62. /* 等待一个延时单位的结束 */6 e. N5 l4 Q4 a1 f3 y) m+ J
  63. while(SysTick_GetFlagStatus() != SET);
    3 y$ z% E% M0 }
  64.     }0 l, S$ _( F9 ?* \# Y* E

  65. 1 \, C' w2 r: s2 J
  66. /* 关闭滴答定时器 */: J( \+ D: J+ p0 v6 U
  67.     SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;2 A* c- f2 T! e2 ?0 ~9 \: Y
  68. }</font>
复制代码

3 R- J" i1 }- c
    检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:
  1. <font face="Tahoma" color="#000000">#ifndef __GPIO_H
    7 W! f. l' S/ m; w
  2. #define    __GPIO_H0 q# a  \% G1 m' Y2 _: y# C( K
  3. 4 C6 c  Q+ A+ g) `; K7 ]
  4. #include "stm32f10x.h"
    * ~1 L2 M- I' ]$ F

  5. 7 ~1 W& P0 p9 f
  6. #define     LOW          0- b5 A3 y: l  e
  7. #define     HIGH         1  R% H& l& m2 b, \
  8. % H5 @/ v$ }1 m( W4 G7 c
  9. /* 带参宏,可以像内联函数一样使用 */3 S" ?+ N1 ~* q; d1 G3 C, u
  10. #define TX(a)                if (a)    \
    7 Z8 Q0 ~% l2 F
  11.                                             GPIO_SetBits(GPIOB,GPIO_Pin_0);\
    / N3 _3 U$ s( g# i7 h
  12. else        \
    - z8 P3 I3 n" V
  13.                                             GPIO_ResetBits(GPIOB,GPIO_Pin_0)8 z* B5 W5 B5 p
  14. void GPIO_Config(void);
    3 F6 _1 G+ L* c4 ~& e' G

  15. 0 G  x6 a* b5 Y8 a5 P! k
  16. #endif
    9 |* G8 h# j8 U) p

  17. / p% H4 a3 G; R* n4 F
  18. #include "gpio.h"
    & q/ v/ Q$ h5 J0 o& g
  19. / M' |" J: Y  Q. y. j% I8 Z, H
  20. /**4 d/ c1 b4 K- l" _: o
  21.   * @brief  初始化GPIO2 y4 u# U" r( H+ d5 K5 u
  22.   * @param  无& T. v8 T$ M3 L8 @
  23.   * @retval 无9 g4 p9 I5 Y+ V; f( I5 _- Q
  24.   */& s8 g# V8 ?: d2 d0 |+ [
  25. void GPIO_Config(void)
    ; \! e$ I! E5 X, u4 I; [
  26. {        6 G0 W5 _4 ^/ J0 p
  27. /*定义一个GPIO_InitTypeDef类型的结构体*/" H* ?0 Z$ B# e& D* Q
  28.         GPIO_InitTypeDef GPIO_InitStructure;
    9 i2 k' ~" {$ r9 u3 ~$ z
  29. # H/ F3 X* c; n! a
  30. /*开启LED的外设时钟*/
    ' r* E/ Z9 G9 O6 i5 m0 k% j4 u
  31.         RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE); & [# i8 P' e$ v, I3 b2 V. V

  32. 2 |; B$ Q! E+ G" S: G
  33.         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;   
    ' Q3 D; i4 z- H4 h6 D0 c) R
  34.         GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;     1 [7 }5 c$ ]2 i& q& G. s
  35.         GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    / Z( \( T% b+ V
  36.         GPIO_Init(GPIOB, &GPIO_InitStructure);    # F! |+ L+ N. V- k& j1 X* E/ i
  37. }</font>
复制代码

5 O+ z, V" E5 L- s& `, ]8 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。
    结论:此延时函数基本上还是可靠的。
使用定时器方法的实例
/ t& W/ |* r$ |$ I7 U
    Delay_us函数使用STM32定时器2实现:
  1. <font face="Tahoma" color="#000000">#include "timer.h"
    6 V- i9 \/ C7 o5 m* W0 `( ^

  2. 8 _. @/ R9 \0 P8 s
  3. /* SystemFrequency / 1000            1ms中断一次. x" t- T9 ]# D( T0 D  s$ A
  4. * SystemFrequency / 100000     10us中断一次
    ' C% l) u1 J4 V* Q( X
  5. * SystemFrequency / 1000000         1us中断一次5 m6 ?; A$ X7 k# [, n
  6. */% o' X0 k: ]- ]' I/ Q9 a
  7. / V- g* X$ f2 _6 V0 w* B
  8. #define SYSTICKPERIOD                    0.000001/ y; \+ a6 f$ T+ x1 c
  9. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    4 ~- x7 X* [" l3 M7 l0 \

  10. - v# F9 m9 B, B  h, K4 G3 }4 N6 w
  11. /**# b' i8 a7 J. H& K: n: {
  12.   * @brief  定时器2的初始化,,定时周期1uS7 s( l  M/ i& A3 J+ t
  13.   * @param  无
    ' k3 Z# \# ~& l! \1 \
  14.   * @retval 无
    + \; S1 g9 q1 b3 C) {+ n  q
  15.   */
    2 g/ M# O& a, l/ k1 E- ^% M
  16. void TIM2_Init(void)
    5 h2 S0 G0 Y0 g) k
  17. {
    + B9 x: E! p9 j! G  `
  18.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;$ I- z  U) s8 i  i& F

  19. . ~& I* X$ F0 Z# T2 \5 F9 |, H
  20. /*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */  X8 L5 P9 a1 [' V/ x
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);5 j5 K  a9 _! z( S9 v

  22. ( j9 n4 Z" D2 x
  23. /* Time base configuration */+ ?: D9 N1 P( d# V6 `
  24.     TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;
    : z* n2 x7 w" R
  25.     TIM_TimeBaseStructure.TIM_Prescaler = 0;
    / h! ]" t( ?% a8 w
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    ; I& W7 T9 C! w4 Z+ |
  27.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    9 ?# ~  [2 c9 K9 [

  28. # s* ^# Y4 r7 ^' Y; z/ @5 r; N
  29.     TIM_ARRPreloadConfig(TIM2, ENABLE);
    " E' l- u9 q7 v* G. S# Y" z
  30. 4 D+ |4 s( x* K
  31. /* 设置更新请求源只在计数器上溢或下溢时产生中断 */
    % e4 j7 ^8 o6 ?! r) h
  32.     TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global); ) L. h  T1 O5 u* ?4 z" n2 W
  33.     TIM_ClearFlag(TIM2, TIM_FLAG_Update);  |$ D0 B% d/ j' j2 V; C
  34. }3 _2 R+ H- M  h) {  z
  35. & Z2 V2 T3 [) H0 L# l
  36. /**
    6 V3 Q, Q, V" [" ?3 Y
  37.   * @brief   us延时程序,10us为一个单位
    ( l' W; ~* a; r( T" S3 e/ i5 n
  38.   * @param  
    9 J, Y) _7 x, ]. k. R: K6 |
  39.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us
    " J/ }! X; v7 H
  40.   * @retval  无- E; ]3 ?: x  S: t9 g# B2 v6 Y1 Z$ Y
  41.   *// f6 _, d, k6 z' w
  42. void Delay_us(__IO uint32_t nTime)
      {9 X2 C8 G: E
  43. {     2 E4 s, f3 x! L
  44. /* 清零计数器并使能滴答定时器 */
    + E( {& ?" B/ J8 E
  45.     TIM2->CNT   = 0;  9 s% e, S* ~+ N) }* ?) ~
  46.     TIM_Cmd(TIM2, ENABLE);     
    * {  P) g) B$ Y
  47. ) P- S" Z( {; e; D1 R; I; A
  48. for( ; nTime > 0 ; nTime--)
    8 e5 l$ o% O$ k7 [
  49.     {
    8 {& y, v- r, E3 c' u
  50. /* 等待一个延时单位的结束 */
    # L1 \5 A. E: V
  51. while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);
    6 M$ i, ~4 }2 R) O4 V# E8 Z
  52.      TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    ; B6 ^) @/ ?2 `" I" l
  53.     }
    # M8 h; a2 ]- Q5 D
  54. / z7 I+ i+ [' N( n; C. B; @
  55.     TIM_Cmd(TIM2, DISABLE);
    ) H9 i, t* z) F+ F: c" y
  56. }</font>
复制代码
* k$ J4 y  j3 ~) N2 B, ], a4 U
  在main函数中检验Delay_us的执行时间:
  1. <font face="Tahoma" color="#000000">#include "stm32f10x.h"( _1 F* t9 p! T: K4 p
  2. #include "Timer_Drive.h"
    1 ]7 D2 j! R$ `) h
  3. #include "gpio.h"
    * w0 i4 q0 w* D6 U" n) N: ~
  4. #include "systick.h"
    0 p7 J. J: y7 ?2 q, @0 N
  5. , G% ?5 d# V& ^. s' e
  6. TimingVarTypeDef Time;
    9 H* v8 F9 J. A/ o/ d: T- b1 k

  7. " ^, h! K+ T; M3 U7 d
  8. int main(void)5 Q& j5 M+ _" m9 `3 g( e
  9. {    & A) Y: Y" e! g9 M4 j- ?# Z
  10.     TIM2_Init();   
      y, U- U' x, d7 W
  11.     SysTick_Init();9 ~2 u. L( U; X+ q6 o
  12.     SysTick_Time_Init(&Time);
    8 o  x& A) s8 A# b( A; h
  13. , i& ]. p* Q; q, G* c2 k
  14. for(;;)6 o4 f5 i1 u7 y8 h
  15.     {
    4 j- B; X* A6 x9 H( {
  16.         SysTick_Time_Start();
    " s2 X% z! F* ]8 O* f  c
  17.         Delay_us(1000);7 K/ g- d$ }, b0 x, V$ I# |9 a7 O
  18.         SysTick_Time_Stop();
    # D0 {: C' F0 p: ~1 X9 d
  19.     }     
    + d* S5 d' z, S8 m- P  T4 D
  20. }</font>
复制代码

' z2 S  g$ t- K! c/ [( q
    怎么去看检测结果呢?用调试的办法,打开调试界面后,将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以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。
' ^* L2 H6 j2 R, W% c( }/ z# b$ h
收藏 评论0 发布时间:2021-11-7 15:39

举报

0个回答

所属标签

关于
我们是谁
投资者关系
意法半导体可持续发展举措
创新与技术
意法半导体官网
联系我们
联系ST分支机构
寻找销售人员和分销渠道
社区
媒体中心
活动与培训
隐私策略
隐私策略
Cookies管理
行使您的权利
官方最新发布
STM32N6 AI生态系统
STM32MCU,MPU高性能GUI
ST ACEPACK电源模块
意法半导体生物传感器
STM32Cube扩展软件包
关注我们
st-img 微信公众号
st-img 手机版