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

有哪些技巧可以获取STM32代码运行时间?

[复制链接]
攻城狮Melo 发布时间:2024-5-24 18:15
前言    ' ?) j% B  x! E, v. v" V
测试代码的运行时间的两种方法:
  • 使用单片机内部定时器,在待测程序段的开始启动定时器,在待测程序段的结尾关闭定时器。为了测量的准确性,要进行多次测量,并进行平均取值。
  • 借助示波器的方法是:在待测程序段的开始阶段使单片机的一个GPIO输出高电平,在待测程序段的结尾阶段再令这个GPIO输出低电平。用示波器通过检查高电平的时间长度,就知道了这段代码的运行时间。显然,借助于示波器的方法更为简便。
    * X1 Y  E- t9 O: c7 g4 I
  f2 a: b7 h! h3 a& y4 l% j9 j0 ^

( {4 ~; v7 |4 O* i

: J" m" L6 Q4 H9 R借助示波器方法的实例    ( Z+ r+ C: b6 l$ N/ |2 q

( Q5 w1 l3 p) s0 K' e9 ?) x4 m* K) s

' q- k* c8 _' e) r4 q# BDelay_us函数使用STM32系统滴答定时器实现:
" S6 {, h" @$ a; v
  1. #include "systick.h"/* SystemFrequency / 1000    1ms中断一次 * SystemFrequency / 100000     10us中断一次 * SystemFrequency / 1000000 1us中断一次 */#define SYSTICKPERIOD                    0.000001#define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)/**  * @brief  读取SysTick的状态位COUNTFLAG  * @param  无  * @retval The new state of USART_FLAG (SET or RESET).  */static FlagStatus SysTick_GetFlagStatus(void) {if(SysTick->CTRL&SysTick_CTRL_COUNTFLAG_Msk)     {return SET;    }else    {return RESET;    }}/**  * @brief  配置系统滴答定时器 SysTick  * @param  无  * @retval 1 = failed, 0 = successful  */uint32_t SysTick_Init(void){/* 设置定时周期为1us  */if (SysTick_Config(SystemCoreClock / SYSTICKFREQUENCY))     { /* Capture error */return (1);    }/* 关闭滴答定时器且禁止中断  */    SysTick->CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);                                                  return (0);}/**  * @brief   us延时程序,10us为一个单位  * @param  *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us  * @retval  无  */void Delay_us(__IO uint32_t nTime){     /* 清零计数器并使能滴答定时器 */    SysTick->VAL   = 0;      SysTick->CTRL |=  SysTick_CTRL_ENABLE_Msk;     for( ; nTime > 0 ; nTime--)    {/* 等待一个延时单位的结束 */while(SysTick_GetFlagStatus() != SET);    }/* 关闭滴答定时器 */    SysTick->CTRL &= ~ SysTick_CTRL_ENABLE_Msk;}
复制代码

- p& h; W" K& u0 S$ x
* F% W! i- I" l5 ?6 Q
+ u& y' _  @) g3 D检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:
9 z* B% ]" @7 Z0 T
  1. #ifndef __GPIO_H#define    __GPIO_H#include "stm32f10x.h"#define     LOW          0#define     HIGH         1/* 带参宏,可以像内联函数一样使用 */#define TX(a)                if (a)    \                                            GPIO_SetBits(GPIOB,GPIO_Pin_0);\else        \                                            GPIO_ResetBits(GPIOB,GPIO_Pin_0)void GPIO_Config(void);#endif#include "gpio.h"/**  * @brief  初始化GPIO  * @param  无  * @retval 无  */void GPIO_Config(void){        /*定义一个GPIO_InitTypeDef类型的结构体*/        GPIO_InitTypeDef GPIO_InitStructure;/*开启LED的外设时钟*/        RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);         GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;            GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;             GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;         GPIO_Init(GPIOB, &GPIO_InitStructure);    }
复制代码

6 k0 d3 C5 Z) W% f; R/ z2 G7 Y. R0 F4 d
在main函数中检验Delay_us的执行时间:2 m) _. ?" J. E4 M
$ ?9 r% n  U$ G

- k3 l( o' ^1 X$ a 微信图片_20240524181424.png : O8 R0 `4 H1 `4 E. R
2 Q  f) l$ @! T' X* `% u+ A: M( L, E

+ |! k9 M" k5 K3 s9 s
9 j2 _$ H$ @7 w( n. m1 }" ?
    示波器的观察结果:: @: I  O6 A' z! {3 B2 @& k9 y
微信图片_20240524181439.png 5 m9 @6 f( V' m3 }8 z6 Z
/ T4 q7 E' g- M% B+ @& T& O
微信图片_20240524181443.png 6 L2 n, n' ]2 D
9 j- m4 k* h$ @' p- E: e/ ]
    可见Delay_us(100),执行了大概102us,而Delay_us(1)执行了2.2us。    # W* y5 b( |+ K9 `6 C/ c" \
5 \3 B% s, S/ i* ^' ]" p9 b

0 o/ [) j- I' i, |0 {更改一下main函数的延时参数:
0 p: ~$ T2 t6 l+ r. g% t: ?, H' M4 H* a$ X

4 B+ Y9 X# T! U5 X! w/ ^ 微信图片_20240524181448.png
6 U' \: `0 Q$ e

1 l2 @8 u* ]( {4 w
# f, |, E) s8 f; W5 D1 B$ O6 D

  }$ v/ v, v% d$ w/ {    示波器的观察结果:
. r0 [1 F" B3 o8 f; X; h9 s- ^
微信图片_20240524181451.png
7 Y/ l, ]0 J" @( n3 s6 @7 E
微信图片_20240524181454.png
# o/ F, g0 Z# F/ i) v! z" s+ d- [0 P$ b

% L3 z6 r2 S( }1 _& Q$ N0 P' Z. R5 s    可见Delay_us(100),执行了大概101us,而Delay_us(10)执行了11.4us。   
: |9 T/ u" T1 q  ~4 P6 _: a- J+ E* l9 V, I2 d9 e

: Y* I5 `2 L& j* f- t2 Y4 i( X/ ~结论:此延时函数基本上还是可靠的。
6 }/ [7 n/ |9 \. a# M' s
& d' U* O  e$ L$ ]4 V5 H
( Q0 q+ T. A- [3 y: D
使用定时器方法的实例   
5 O& _; j8 B* w, ^! s, u. F9 r- g5 N9 g  M$ _  d" t4 j% z# V

' z1 |  U- ?9 KDelay_us函数使用STM32定时器2实现:6 @: X! S. u# P0 R8 D0 [3 _$ q. p( I
  1. #include "timer.h"/ S) o4 D+ B# z  ]
  2. , v, u9 X; j  V  V+ o
  3. /* SystemFrequency / 1000            1ms中断一次
      p9 [. M- q$ f6 K7 D/ ^; y1 x0 Z/ q" P
  4. * SystemFrequency / 100000     10us中断一次' f8 l3 Q5 s0 Z" n- e/ f5 i) f
  5. * SystemFrequency / 1000000         1us中断一次0 Q+ L' d. I2 \& V# Q
  6. */
    1 a4 G" [/ k* Q
  7. % g  U' q, [" X4 L3 n* C7 d9 x
  8. #define SYSTICKPERIOD                    0.000001
    6 u, J/ M0 X+ V9 I. @& l
  9. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)
    & {; F# g% Z7 g+ U. c' f& |

  10. ( ~8 d. r2 z: ?- c' N
  11. /**
    7 i! S5 Y2 H/ _3 v2 ]; v9 H
  12.   * @brief  定时器2的初始化,,定时周期1uS
    ! F1 }5 a2 }$ g" W2 h
  13.   * @param  无2 ^9 V) ~7 Y) @- I: h# p, b
  14.   * @retval 无6 p; h: Y, Y% I9 x
  15.   */
    ! B/ ?  B' ~( x
  16. void TIM2_Init(void): R7 d. D5 ?8 ]1 s8 I
  17. {
    0 U" b* A* @) ~$ `
  18.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    5 D* O1 n9 H! x+ m: l  I' [

  19. : r6 Z6 ^; w1 |" ^
  20. /*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */
    # K+ s& b% o% g: E4 v( F! r
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    $ H4 a2 g! W5 R0 }  Z! m

  22. 1 g* {3 O4 \" C$ z
  23. /* Time base configuration */  }/ m: y' y) Z' ^# B+ ]& M' B9 m
  24.     TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;4 P: L, k6 C( i0 z* k% e2 q
  25.     TIM_TimeBaseStructure.TIM_Prescaler = 0;  k) T5 ]* \& C; E; x# d, {1 g# x
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;" T( I6 ^% j) Q, l
  27.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);6 E4 K) O0 D# I! Q; D9 I
  28. " [, J% |3 B) M2 J* D$ \) ?3 r
  29.     TIM_ARRPreloadConfig(TIM2, ENABLE);+ D) m2 o0 j7 Z. T  L) Y5 P
  30.   Z; f7 |3 f5 {$ |. Q& h0 V
  31. /* 设置更新请求源只在计数器上溢或下溢时产生中断 */
    0 c# F- L/ o1 j
  32.     TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global); , ^* T/ Y. `) \* y: [
  33.     TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    7 `) E* L) S+ e% O1 u3 ?
  34. }# X: E3 S( ]% }" T' P4 G% c1 k

  35. $ F# L3 O) W2 q( ?
  36. /**6 `3 }7 x4 o) A: C
  37.   * @brief   us延时程序,10us为一个单位  a; Q4 i0 R6 c, n
  38.   * @param  1 k4 B0 ?3 o/ \
  39.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us
    9 z9 _) Q; e" _# N& x# S
  40.   * @retval  无- O4 ]+ y5 t" B: M: l# w( i
  41.   */
    % W) i; K6 X! e2 C' a
  42. void Delay_us(__IO uint32_t nTime)
    ; c( a6 J+ m6 ~8 b2 v
  43. {     
    - Y' [  S  V  i2 l
  44. /* 清零计数器并使能滴答定时器 */
    $ \3 g" i$ [1 P' b1 d* R. a: E7 [& I
  45.     TIM2->CNT   = 0;  
    + @4 v. @! Y7 b% B- Q3 D) Z
  46.     TIM_Cmd(TIM2, ENABLE);     
    & t! o- N8 [. H3 `- v4 S9 y

  47. 8 f  S6 z( k# c( S& X
  48. for( ; nTime > 0 ; nTime--)
    % C+ N/ s  p% G+ K* h* y: ^- f3 \
  49.     {
    1 i" u4 }+ u! G) I
  50. /* 等待一个延时单位的结束 */2 A! _$ t1 Y9 }8 c3 B# C/ u5 _
  51. while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);
    * H6 s4 \) P4 y  Y' E$ [. L
  52.      TIM_ClearFlag(TIM2, TIM_FLAG_Update);
      O* X  G! a% {% B3 \0 B
  53.     }7 P9 ]* a2 e! p6 Q5 z( W

  54. + K- A) k2 _1 N) o  X) P+ j) B
  55.     TIM_Cmd(TIM2, DISABLE);/ G0 r: f$ Q1 D
  56. }
复制代码

; ^1 n) z& a1 S/ n
" K" A" S7 Z; {: V8 v: A

2 U- y: u7 d, |  w$ ]1 e在main函数中检验Delay_us的执行时间:, M- t, {) J7 Y7 }
  1. #include "stm32f10x.h". B/ B( q7 v  M* J
  2. #include "Timer_Drive.h"1 D9 k0 ?  Z0 I: F4 K& p+ o! A0 \
  3. #include "gpio.h"7 ~$ P# p. s. N
  4. #include "systick.h"+ T* Z2 ]. T( V4 I( E5 i
  5. / U7 J% u7 n" O" M6 W( n
  6. TimingVarTypeDef Time;! r9 J* g  d7 E2 i) K  F

  7. 5 E  F6 D4 [3 u; g
  8. int main(void)& w5 A; e! _" d. w
  9. {   
    " U3 v. {$ k' I# p
  10.     TIM2_Init();   
    ! O+ J/ C7 l) M7 f- `7 m
  11.     SysTick_Init();9 U( e* ?+ k5 y3 k. k& I% ~
  12.     SysTick_Time_Init(&Time);
    ) i  ]+ w4 \# {0 i/ A1 z7 U
  13. 3 V8 |# {! A, I" O8 u( c3 }6 |
  14. for(;;)
    ( v* |; B+ m5 g- i
  15.     {: @4 }& q: a0 Z' f1 R+ Y
  16.         SysTick_Time_Start(); " f, p. @+ P4 E' I( X& B
  17.         Delay_us(1000);- G2 e. O) i( @$ P
  18.         SysTick_Time_Stop();* o2 B7 Q7 {1 U$ R% r0 N2 X( S
  19.     }     
    0 h3 g! ?: e$ w* h
  20. }
复制代码

5 z2 q$ P( E  d, r. L* |0 u- r6 h3 e" V6 D/ d' ^
怎么去看检测结果呢?用调试的办法,打开调试界面后,将Time变量添加到Watch一栏中。然后全速运行程序,既可以看到Time中保存变量的变化情况,其中TimeWidthAvrage就是最终的结果。
, O1 r  x4 E! v' S6 {0 t- w: \, \/ h; \; o- V

* y/ b& [2 l4 H7 V 微信图片_20240524181458.png
) E! L- R0 p( M8 y  m1 M

) \2 f% {* u3 x* z1 y0 o  o: c* A/ r5 s! q, T3 u$ i6 t
4 @0 b; J* ~9 d/ x; x
    可以看到TimeWidthAvrage的值等于0x119B8,十进制数对应72120,滴答定时器的一个滴答为1/72M(s),所以Delay_us(1000)的执行时间就是72120*1/72M (s) = 0.001001s,也就是1ms。验证成功。   
& b  x% T: e7 E0 \
, S" p: G% d7 X4 [
7 n- i" R( x  ?; B: N0 X
备注:定时器方法输出检测结果有待改善,你可以把得到的TimeWidthAvrage转换成时间(以us、ms、s)为单位,然后通过串口打印出来,不过这部分工作对于经常使用调试的人员来说也可有可无。2 \9 y, i* X8 E

" N3 a, a/ L9 ?$ r. p

9 r9 ~- Z- Y  s两种方法对比
) W6 [4 p  j. z" ?, Z3 o软件测试方法   
3 ~' ]- i' I& H' ^# K操作起来复杂,由于在原代码基础上增加了测试代码,可能会影响到原代码的工作,测试可靠性相对较低。由于使用32位的变量保存systick的计数次数,计时的最大长度可以达到2^32/72M = 59.65 s。) X8 A/ g8 W- e) H# M" R% `

; ]5 O5 s  Q- B0 n. u. u$ r  M
  y, @! C1 Q- e( C+ }" }0 _& i( B9 v" N
示波器方法    1 _8 v8 I5 R/ q1 ~
操作简单,在原代码基础上几乎没有增加代码,测试可靠性很高。由于示波器的显示能力有限,超过1s以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。
6 w( U8 l2 [/ U
. U( N9 j6 t7 @1 t  j4 U' M* m& x( V' P: N& M0 G. h0 L
& k& T) y) P& n0 M7 h. j: b
转载自:单片机与嵌入式& U' F9 J& n: ^
如有侵权请联系删除3 Y/ B/ T; O7 f/ I1 x

. e4 W6 g! v) t' i
收藏 评论0 发布时间:2024-5-24 18:15

举报

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