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

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

[复制链接]
攻城狮Melo 发布时间:2024-5-24 18:15
前言    2 R9 t- h! }  S% T" c, l
测试代码的运行时间的两种方法:
  • 使用单片机内部定时器,在待测程序段的开始启动定时器,在待测程序段的结尾关闭定时器。为了测量的准确性,要进行多次测量,并进行平均取值。
  • 借助示波器的方法是:在待测程序段的开始阶段使单片机的一个GPIO输出高电平,在待测程序段的结尾阶段再令这个GPIO输出低电平。用示波器通过检查高电平的时间长度,就知道了这段代码的运行时间。显然,借助于示波器的方法更为简便。0 b% l0 y3 B3 N9 _3 j
% G6 l- A! c. |7 I8 n: V: k
  c/ R% Q& g. q. H( ]2 V: c' g6 Y7 q

! R" k) a4 i2 K! q! X借助示波器方法的实例   
  W" N! }9 U& H3 |$ }1 U8 {9 z! N! j3 N8 p8 e1 u
! F+ M* E! o" K7 r' j
Delay_us函数使用STM32系统滴答定时器实现:* T% J+ e. T: F* f
  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;}
复制代码
; ~0 }* o' \: N' H/ v
3 ~: z- {4 S- U& {! m1 ^, u

8 @% z7 g* n" p( ]* Q检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置:
% l6 |) f+ C4 e
  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);    }
复制代码
' w* a" S+ q! }" Q8 w7 x1 X

% e6 ?# |' b, Z在main函数中检验Delay_us的执行时间:
2 S. D7 n8 q0 w2 f6 L: f" t$ S2 m! R2 Z1 C& Z% @  }  p
/ @! f/ A; e1 L
微信图片_20240524181424.png % d$ t6 W1 a8 Y# L+ I% H
  f; m! z5 O; d$ N5 Z+ |, k3 U
+ o0 \9 _9 N* ^9 a! n
4 L: d0 M; P, K1 E
    示波器的观察结果:* q8 `# X: N" B7 O- [" m
微信图片_20240524181439.png
: i, e9 r4 r/ d6 L9 y
# C# o) `; A$ K: g
微信图片_20240524181443.png - D  V% S2 J- |& x

; z. }% `% f3 y+ ]$ V    可见Delay_us(100),执行了大概102us,而Delay_us(1)执行了2.2us。   
, x# _9 x: I9 ]" G) J8 j' l' r. U7 u
& d2 v4 p7 @" G3 p  t6 }& s
更改一下main函数的延时参数:
/ F9 A8 J, [  m
& }% W3 i# O7 B* l
5 I* s/ Q- M: G( c4 v
微信图片_20240524181448.png ' I0 Q( r& H, {# G0 Z# k

/ V, O  }8 A6 ^% V: Q. |0 Z6 q

& `& Q" r, \& E! i1 A8 t+ z. N    示波器的观察结果:& `) c& k3 f1 Y' o8 E; v
微信图片_20240524181451.png ! }- ]; a) g. {& \
微信图片_20240524181454.png
0 z+ r1 T7 T* H8 N- `0 z

4 D' ?6 ?: X6 v0 w4 D+ x* o    可见Delay_us(100),执行了大概101us,而Delay_us(10)执行了11.4us。   
' B. A5 d* p1 t" J. O: M
) D! i( m; m% e  b; X& v+ O

5 n+ I2 B" _# q; o  @: x结论:此延时函数基本上还是可靠的。6 u% D: p/ a$ g5 V2 ^
; a) ~% ?$ E( V3 s0 m, @
5 b" {- h0 U' y. q# Q3 W; e2 B% W' M
使用定时器方法的实例   
6 p/ D% L8 ^+ t# D, c
; {  [/ v+ Q7 c5 [

0 z0 O' ]7 S8 M# jDelay_us函数使用STM32定时器2实现:
! ?: l: @+ K- \/ ]5 f
  1. #include "timer.h"
    0 Q/ [& B: D# J9 W( h$ m* Q

  2. . C+ j2 W) {2 w- q$ k& `4 w1 ~
  3. /* SystemFrequency / 1000            1ms中断一次
    ' X/ J, d! f; A2 t/ k
  4. * SystemFrequency / 100000     10us中断一次; b; g* J* {. _! C5 C# J
  5. * SystemFrequency / 1000000         1us中断一次4 f! B% m1 z% `  ]
  6. */" p0 n, K. [: b# \, e

  7. + J1 o5 r5 R. I8 \
  8. #define SYSTICKPERIOD                    0.000001
    3 f7 V. v3 Y- j% p4 s
  9. #define SYSTICKFREQUENCY            (1/SYSTICKPERIOD)% C, g9 f+ }! z- a) ~
  10. ' e6 M5 |. B4 t+ E
  11. /**4 A; g- v# @- D1 E
  12.   * @brief  定时器2的初始化,,定时周期1uS
    1 H  V, V9 W4 j4 Y, ~& d+ X0 Q1 ~
  13.   * @param  无* M2 I5 j+ c; t
  14.   * @retval 无
    * P1 t2 L5 n6 ^, }' [) a2 t
  15.   */8 T" c' r' V3 S6 p- C
  16. void TIM2_Init(void)
    2 b5 b+ A* e& ?/ I; t
  17. {
    2 d0 L9 @8 L# w8 }6 \+ G
  18.     TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    $ X0 [# `% t: l) r5 g- ?& M

  19. 9 R" y. K3 V; Q( e8 w
  20. /*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */
    7 x, B1 Z( z3 Q- p  a* F* |
  21.     RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
    ' n$ v4 o$ E+ a7 K  ^; c
  22. * X6 h: W# [1 {. s( q- i0 Z; ]
  23. /* Time base configuration */
    & K1 }: b3 ^$ w) |# K7 L" x
  24.     TIM_TimeBaseStructure.TIM_Period = SystemCoreClock/SYSTICKFREQUENCY -1;
    ) t- B9 a6 t" h' O) y
  25.     TIM_TimeBaseStructure.TIM_Prescaler = 0;
      w7 ?7 v; w% }) e7 _' j8 N
  26.     TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;/ U0 ^0 Y4 _/ u" G; n( ^: v
  27.     TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);
    2 ^% \$ n+ R6 h0 d. Q# c

  28. 9 b" W7 p5 Q* [  h  W% Y8 N& S
  29.     TIM_ARRPreloadConfig(TIM2, ENABLE);; e/ D2 l. l% d4 V" h8 G+ `

  30. * E) p: a& f2 j
  31. /* 设置更新请求源只在计数器上溢或下溢时产生中断 */4 `: c1 d( `6 y% v8 J- n8 T
  32.     TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global); / e5 F" N- g8 y2 h8 }
  33.     TIM_ClearFlag(TIM2, TIM_FLAG_Update);
    # K0 e+ P! {, [3 E- M& O9 R
  34. }
    2 M) w8 R: J. ^" |* e

  35. % E# I! \  ^& r- U( o
  36. /**
    . c, {' T1 r  v, ^
  37.   * @brief   us延时程序,10us为一个单位
    ; k3 `! E& J' g8 U3 |, u
  38.   * @param  
    % Z; g) H0 I5 P5 h1 I
  39.   *        @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us
    0 L+ r# q8 S) Z- w0 i6 ^
  40.   * @retval  无
    " O- U! g* L0 R! \1 Z
  41.   */; \" q1 m3 f7 ?8 C( D
  42. void Delay_us(__IO uint32_t nTime)& [+ Z4 K( z8 R5 u6 m* `9 o
  43. {     2 t4 _  D0 l" z. I- s1 Q
  44. /* 清零计数器并使能滴答定时器 */( e. C+ q- @0 F# q  R; L
  45.     TIM2->CNT   = 0;  % V) d: ?2 s* Q2 U2 G0 T* Q
  46.     TIM_Cmd(TIM2, ENABLE);     
    # x  f  B$ r$ u0 o- N, X
  47. : |, N# |, [1 x6 N8 M( H
  48. for( ; nTime > 0 ; nTime--)# n  m. l- g7 K+ L  y/ d. c3 w
  49.     {$ [; F# n- m; z" ^- j/ P: W/ I: b
  50. /* 等待一个延时单位的结束 */5 q4 ?& J  X) n# D% I5 D
  51. while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);: z4 N( X% G4 B" _
  52.      TIM_ClearFlag(TIM2, TIM_FLAG_Update);$ @1 x" ]$ L* q
  53.     }$ i, Q- Q9 ?! o$ Z, ?
  54. ! ]6 n( V8 B+ q, L7 ?/ c' C$ ^
  55.     TIM_Cmd(TIM2, DISABLE);- J/ s% a6 N2 w% o( _
  56. }
复制代码

3 r8 L/ n) U! J
* V0 I6 Y' N2 q

( T8 r; S' A# j2 o在main函数中检验Delay_us的执行时间:' N0 E8 S0 m* m& T1 d
  1. #include "stm32f10x.h"
    , |7 d1 T& G" t+ M" Z8 H0 l4 y( ?
  2. #include "Timer_Drive.h"
    " o2 N! L- X! n. ]5 i: ^! ?2 Q
  3. #include "gpio.h"0 k% W- C" S' J) W; {2 i* @$ L
  4. #include "systick.h"7 k* d2 J3 O' j" i* E+ r
  5. ' E% M, Q6 ]- ^5 p0 I" E
  6. TimingVarTypeDef Time;
    ! A. t/ v; Y$ A/ X8 Z5 R0 X
  7. 1 M# b# i" G  b2 D" j  F3 g! s; H
  8. int main(void)
    % @# _* E9 E( N# q  P
  9. {    ( w0 O1 a0 `3 _0 o' P9 z- z8 Q
  10.     TIM2_Init();    1 c$ a7 L" E  G, m7 {4 X7 `
  11.     SysTick_Init();3 {/ `' G) k* K+ l* b, d3 L
  12.     SysTick_Time_Init(&Time);/ c6 _- K* }( K, ^. Q: X& n

  13. , O8 @: u( R* J4 v
  14. for(;;)
    1 `( ~7 L% ^3 }5 g# N: b3 U5 m
  15.     {/ _% y5 H+ n$ c# y  u& z9 |
  16.         SysTick_Time_Start(); 3 q6 F" g$ Q, C4 U8 ^
  17.         Delay_us(1000);. w$ J. V2 f4 I
  18.         SysTick_Time_Stop();
    2 K- o( Q9 U6 ]! H) N8 A% F
  19.     }     
    ) T- d0 _; a9 g
  20. }
复制代码
5 e7 v3 w7 z& t- K. [. |
- \4 R; V9 {  d- W% K' V
怎么去看检测结果呢?用调试的办法,打开调试界面后,将Time变量添加到Watch一栏中。然后全速运行程序,既可以看到Time中保存变量的变化情况,其中TimeWidthAvrage就是最终的结果。
2 @1 |! [1 |! ]( c/ @% X- ~3 A) z9 o* ~& I
" v7 s, Z  ^1 e8 V1 h
微信图片_20240524181458.png
$ j  M9 ~/ Q2 u8 V% v

/ c" u7 S! J7 f; P0 [
6 y0 i1 |& v/ R' ?, g
. y  _5 u& F  u7 u$ Q
    可以看到TimeWidthAvrage的值等于0x119B8,十进制数对应72120,滴答定时器的一个滴答为1/72M(s),所以Delay_us(1000)的执行时间就是72120*1/72M (s) = 0.001001s,也就是1ms。验证成功。   
; c3 O1 g4 w4 i1 o$ L$ J7 Z8 b' K. \$ |

9 Q3 i8 O% J( D+ s备注:定时器方法输出检测结果有待改善,你可以把得到的TimeWidthAvrage转换成时间(以us、ms、s)为单位,然后通过串口打印出来,不过这部分工作对于经常使用调试的人员来说也可有可无。
& x5 l8 W3 b3 Z' p
6 `& \  \4 t, ~+ M: C8 x& P

0 v" T* k( b1 u9 r2 e% _* x/ L两种方法对比
) w7 O  w) w4 R) j软件测试方法   
1 {6 i# R' D5 ^8 f7 M) A5 `! _  N操作起来复杂,由于在原代码基础上增加了测试代码,可能会影响到原代码的工作,测试可靠性相对较低。由于使用32位的变量保存systick的计数次数,计时的最大长度可以达到2^32/72M = 59.65 s。* w( u' ?  K; M# E

/ X7 [8 j6 p8 b- X
; N" v0 {( B$ j: A
示波器方法   
$ C; N0 D# h5 _, w9 e$ d操作简单,在原代码基础上几乎没有增加代码,测试可靠性很高。由于示波器的显示能力有限,超过1s以上的程序段,计时效果不是很理想。但是,通常的单片机程序实时性要求很高,一般不会出现程序段时间超过秒级的情况。& w  P. O7 C. b3 m. H
& @, n5 G, f6 W6 @6 f+ t- Y, O, ?
9 ?3 B" H) C0 z: |  |: a+ b
9 }9 y7 o8 o  a( u
转载自:单片机与嵌入式
' \2 ~9 \0 f: O* e& n如有侵权请联系删除, G# p/ }4 J7 u; p! W

+ z2 O1 r0 p9 `8 k4 z" r/ B2 B# n
收藏 评论0 发布时间:2024-5-24 18:15

举报

0个回答

所属标签

相似技术帖

官网相关资源

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