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

单片机代码的运行时间如何测试?

[复制链接]
gaosmile 发布时间:2020-4-15 11:44
; U2 Q, D3 s6 \; }" f/ I
单片机编程者需要知道自己的程序需要花费多长时间、while周期是多少、delay延时是否真如函数功能描述那样精确延时。很多时候,我们想知道这些参数,但是由于懒惰或者没有简单的办法,将这件事推到“明天”。笔者提出了一种简便的测试方法,可以解决这些问题。

  B+ z) l  P! q  J/ H- H% l
测试代码的运行时间的思路:
使用单片机内部定时器,在待测程序段的开始启动定时器,在待测程序段的结尾关闭定时器。为了测量的准确性,要进行多次测量,并进行平均取值。

! c7 p) q& F, ^
借助示波器的方法是:在待测程序段的开始阶段使单片机的一个GPIO输出高电平,在待测程序段的结尾阶段再令这个GPIO输出低电平。用示波器通过检查高电平的时间长度,就知道了这段代码的运行时间。显然,借助于示波器的方法更为简便。
4 {" B+ O; Z3 t; E
以下内容为这两种方案的实例,以STM32为测试平台。如果读者是在另外的硬件平台上测试,实际也不难,思路都是一样的,自己可以编写对应的测试代码。
8 a/ r; T' c+ v' k
借助示波器方法的实例
$ n  T# W# C! z1 u1 I
Delay_us函数使用STM32系统滴答定时器实现

: O; _2 W7 U( ^! [6 l
#include “systick.h”

; i! m- H2 C- c) s4 _; I$ q
/* SystemFrequency / 10001ms中断一次
' n0 A7 ^$ h  t& L# Q+ ^! M
* SystemFrequency / 10000010us中断一次

6 ?! y# i! m% W8 F$ m' ]+ G
* SystemFrequency / 10000001us中断一次

+ Z7 H9 t9 u) B, M* M
*/

8 V; I; Q! z7 w: V/ I
#define SYSTICKPERIOD0.000001
4 b1 E7 Z5 T4 o3 q4 L5 d) n
#define SYSTICKFREQUENCY(1/SYSTICKPERIOD)

* t4 T6 v: o$ @( Z& J
/**

2 ^- w' A, e5 D+ W, B% B( f
* @brief 读取SysTIck的状态位COUNTFLAG
: l; Y# `7 s  c# u
* @param 无

" C0 A, e, o: H4 e
* @retval The new state ofUSART_FLAG (SET or RESET)。
2 F$ c6 r% [4 i- F( H& z
*/

& q* E+ s. z% G5 J, |+ J, H! G/ j: W
static FlagStatusSysTick_GetFlagStatus(void)

0 j) }$ G3 a; k. H- X
{
" M* V# G7 w7 X/ N7 x0 l8 G& z
if(SysTick-》CTRL&SysTick_CTRL_COUNTFLAG_Msk)

# f) w" J+ x2 S& S% f3 t5 _
{
7 f# s, s- j7 m8 j' `" X2 N
return SET;
9 C; k7 b$ X( I, [; z# `
}

5 p2 Y' ?( T9 [5 L6 a. c
else

) w; H) u; T& d8 {' b- q
{

, A6 s' i5 o, n% W& R& h: E) Z* m2 T
return RESET;

/ t+ v; A, f. v$ F- I  V
}
% ~3 T: b/ D( e0 V
}

, h5 O" g8 t/ [/ J. e
/**

, M: a, D0 _2 Z
* @brief 配置系统滴答定时器 SysTick
0 k+ a3 c. w" r, @9 }/ x
* @param 无

2 H; p7 X8 {, e6 F
* @retval 1 = faiLED, 0 = successful
) J) p: N: x2 y% |( \# B9 _& A
*/
9 r/ L" m9 E0 ^0 K5 |
uint32_t SysTick_Init(void)

% f  g  p  E) m
{

/ m4 O7 J# u: s2 a# `' a9 e
/* 设置定时周期为1us */

4 Q; M7 a! \$ K+ E- b& t# S9 l
if (SysTick_Config(SystemCoreClock/ SYSTICKFREQUENCY))
/ D* ~/ k- l/ G% u, N  ~
{
+ p7 D: |4 T9 z, o
/* Capture error */

6 `% P1 S. m5 g8 ]% F0 W0 J0 T8 v& _
return (1);

7 X; o8 x/ Y  x, S* x1 s8 J
}
, x- M6 a' _/ D* h' c
/* 关闭滴答定时器且禁止中断 */
/ E* K! G4 z: |& M
SysTick-》CTRL &= ~ (SysTick_CTRL_ENABLE_Msk | SysTick_CTRL_TICKINT_Msk);
9 @1 P3 N% q6 ^8 p1 a" z  E
return (0);
0 C) U3 g7 |7 Z2 @0 C
}
4 p6 z* }% Z3 a
/**

0 ~& Q" f) u4 l* Z8 u! P( {
* @brief us延时程序,10us为一个单位

* G2 m8 {( S; M" @: b
* @param
4 z1 C" C* ]2 i6 V6 O$ G: |
* @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us

9 r: i$ a7 Z8 @4 }
* @retval 无

: G- r* [, R8 ?( M
*/
& i" P& ~, ^) m3 V
void Delay_us(__IO uint32_t nTime)
$ a: ^% ~9 T7 {" C4 X# P
{
' s  x! x2 R6 G2 ~9 u3 N9 c
/* 清零计数器并使能滴答定时器 */
+ B! b) R& z! N5 O! `: e& V
SysTick-》VAL = 0;

, S/ H$ A3 J& c& X: Z7 R( ?
SysTick-》CTRL |=SysTick_CTRL_ENABLE_Msk;

5 Z! f3 V2 V, V1 r) Q; a) b9 L
for( ; nTime 》 0 ; nTime--)
; M2 ?; z, }) _3 r. W- J
{

- ?% n  n# O" w" C3 H  x+ _  r+ T6 ]
/* 等待一个延时单位的结束 */

  X, A' |( D- G6 W# H
while(SysTick_GetFlagStatus()!= SET);

) M2 A5 }, a: w4 N
}

1 v1 ]7 Q5 d9 ~5 r) F0 b. K4 V2 D* ]
/* 关闭滴答定时器 */

4 O6 J1 L5 v7 `! ^. s) O9 W
SysTick-》CTRL &= ~SysTick_CTRL_ENABLE_Msk;
. e  A8 U! z6 H( h- t1 r
}

9 n# l$ [0 f: b; P
检验Delay_us执行时间中用到的GPIO(gpio.h、gpio.c)的配置

; p1 s! Z% ~  w- i$ Y2 }
#ifndef __GPIO_H
3 E- A( K0 }) _) ^8 F5 Q4 e* Q7 q) M
#define __GPIO_H

. S/ g0 B: |3 i( V2 r
#include “stm32f10x.h”
  Q) U3 S" U* v1 M/ a( k
#define LOW 0
' b5 m2 r9 z2 G: @# L  A% n9 c4 `
#define HIGH 1
; K3 j7 b& T; ~$ I
/* 带参宏,可以像内联函数一样使用 */

* x: W. T6 c- \
#define TX(a) if (a) \
# N& V* W: l5 w# h$ x- t
GPIO_SetBits(GPIOB,GPIO_Pin_0);\

8 R4 o7 A2 J6 M$ {( ]
else \
6 i8 ^7 [" H- k6 t, Q6 e
GPIO_ResetBits(GPIOB,GPIO_Pin_0)

( c7 n5 J1 |2 |" c3 b7 _
void GPIO_Config(void);
% ]$ O) \6 e8 o" \  j! h
#endif
" ?( b' J: V  r
#include “gpio.h”
7 M' x& P3 G2 U( F; U8 I8 [) D
/**
/ U% m' o, U: l7 ]  D- m
* @brief 初始化GPIO

' X4 `, o3 Q5 o9 R8 w8 I2 T7 j
* @param 无
2 k& a6 d1 a% e+ E% U" `5 \9 U
* @retval 无

3 H( ~: h% F* S
*/

# p) J# s# N+ l9 \; L# a% X' w
void GPIO_Config(void)
. V7 ?6 n2 a5 z' c
{
$ L; K3 i- x! i8 V$ V7 S$ }
/*定义一个GPIO_InitTypeDef类型的结构体*/

  P' w: h/ j2 T4 w
GPIO_InitTypeDefGPIO_InitStructure;
$ T6 A# \0 m$ I9 e  L) d" a8 ^* S
/*开启LED的外设时钟*/
- X' C9 a% @5 ]& R0 y2 X
RCC_APB2PeriphClockCmd( RCC_APB2Periph_GPIOB, ENABLE);
0 P+ S1 a* \8 T+ U3 S5 I2 W$ k! X
GPIO_InitStructure.GPIO_Pin =GPIO_Pin_0;

) o( d, r! R$ r
GPIO_InitStructure.GPIO_Mode =GPIO_Mode_Out_PP;

0 j1 g, s; b* V
GPIO_InitStructure.GPIO_Speed= GPIO_Speed_50MHz;
% E$ T2 \4 Y  O. \. E6 i
GPIO_Init(GPIOB, &GPIO_InitStructure);
! D5 R" @7 v: h/ l
}

; @+ X, x: }# Y! q' p' ]7 N# ]
在main函数中检验Delay_us的执行时间

4 E4 ?. v( C1 h; H$ H
#include “systick.h”

: _4 r3 |+ k& T
#include “gpio.h”

9 \; q7 q0 L" s" b- ]9 V
/**
8 Z, k' O# S) t$ i# g( j
* @brief 主函数

+ V! s1 E6 L' q! Q0 s2 V8 S
* @param 无

4 q6 K2 ~  @8 \3 A. |+ j  d' b( T8 r
* @retval 无
8 u: G; e$ F6 {$ F) r# S$ P6 g$ F, N
*/

* V! ?4 I4 [& n# ~1 A1 G
int main(void)
/ k; n$ X$ B; s9 I3 ^
{
3 a  g7 @5 d4 \+ r& o# B
GPIO_Config();

5 k  T1 R5 E, Q6 y% n) m
/* 配置SysTick定时周期为1us */
" U  i& X9 W4 Q, H
SysTick_Init();

; L5 L8 }& @, }7 m) a
for(;;)

9 ?  ^% y3 b9 g+ i$ o/ ^
{

' J/ ?$ a4 p. I8 ^
TX(HIGH);
4 t" ~3 }' \5 ]. H
Delay_us(1);

8 v$ F* f0 E; ]
TX(LOW);

5 L3 p% l# l/ h3 k. M& B& V
Delay_us(100);
; d5 r  c( v7 c# u
}

0 Z% ^% N9 g8 h
}
% }& z" Q5 c/ j5 V1 U' m: \
示波器的观察结果
7 c2 b- b) C+ \2 f9 D

! k0 u1 g. @5 U% v) M4 ?
3 Y4 c* I. g: B0 H4 x. t
可见Delay_us(100),执行了大概102us,而Delay_us(1)执行了2.2us。

! l" A4 u  `$ @
更改一下main函数的延时参数
) n& s$ j, o; W( y4 a" ~6 k
int main(void)
6 w* z+ _& Q  u
{

$ F* g$ |' \) y
/* LED 端口初始化 */

& ]- t  W, l9 D& Y/ ]: L
GPIO_Config();
6 ]/ q& ^, G& A8 R0 Y* |, G8 @  ?
/* 配置SysTick定时周期为1us */
2 v4 c) G1 N& }8 f# L
SysTick_Init();

% J. X# r  l/ e* s( G( d
for(;;)
7 a% k1 Y2 u% l+ N% p0 m
{

: n! I9 w: |4 u  W& m: ^
TX(HIGH);

9 g  z: Z) J9 l, Z; Z) H, @9 s  C
Delay_us(10);
8 H6 }, d, b# q# a. L. l$ u9 W
TX(LOW);
- x# _( x2 ~0 j/ |+ M. d* y
Delay_us(100);
- m5 K: e8 d5 H( }6 H- n7 V
}

/ W; L) `0 A+ q- M3 g$ g7 f# v1 w* J! c8 J
}

5 ?# n6 O4 s% Z/ O. ]& p- U: K
示波器的观察结果
5 G. ~& L" c1 o
( H% @- ^9 ~8 q! g8 m

$ |8 o+ ?: N7 F# X
可见Delay_us(100),执行了大概101us,而Delay_us(10)执行了11.4us。
. B5 ]5 }( T; S
结论:此延时函数基本上还是可靠的。
" T8 q8 j- ]7 e. d7 R
使用定时器方法的实例

# O7 B6 `+ q3 g# z1 E
至于使用定时器方法,软件检测程序段的执行时间,程序实现思路见STM32之系统滴答定时器。笔者已经将检查软件的使用封装成库,使用方法在链接文章中也有介绍。我们这里只做一下简要的实践活动。

* J, @5 @+ C2 Y8 H
Delay_us函数使用STM32定时器2实现
5 P6 K; d$ d' l2 X7 ~. E
#include “timer.h”
% f" f- l5 d8 O* U$ q; t5 w
/* SystemFrequency / 1000 1ms中断一次
8 ^8 U- F$ W0 L/ J# @& r$ D. u
* SystemFrequency / 10000010us中断一次

2 d5 g4 ^+ Y0 l
* SystemFrequency / 10000001us中断一次
# d# x4 v0 `) @- W" F
*/

: z# J, O0 `% U4 y
#define SYSTICKPERIOD 0.000001

4 l  Y- I+ Z$ {- |
#define SYSTICKFREQUENCY (1/SYSTICKPERIOD)

; O. s; {+ [5 e, `7 n
/**
3 |; ^& r- d8 s! [& p
* @brief 定时器2的初始化,,定时周期1uS

+ V* A$ c( D5 M) D9 r! G$ t
* @param 无

% o. }6 D! s/ A- _1 P3 u
* @retval 无
0 k- [* e& O2 T) M
*/

$ ^, P5 [4 T1 E2 {
void TIM2_Init(void)
) B6 J2 \* ?% h
{
& r# B1 v4 S5 Z* H
TIM_TimeBaseInitTypeDefTIM_TimeBaseStructure;

+ I) v/ z/ V4 R
/*AHB = 72MHz,RCC_CFGR的PPRE1 = 2,所以APB1 = 36MHz,TIM2CLK = APB1*2 = 72MHz */
! Q7 s9 E* A7 x. {" h3 F4 Z
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
/ y4 G; Y3 e) e1 ?- s
/* Time base configuration */
( N% @! @: e+ s7 ]" W# {) z. l# s
TIM_TimeBaseStructure.TIM_Period= SystemCoreClock/SYSTICKFREQUENCY -1;
  z& F8 X/ Y; I$ |$ ?$ g# C
TIM_TimeBaseStructure.TIM_Prescaler= 0;

+ k" H% M+ @2 j$ _: k' Y' E
TIM_TimeBaseStructure.TIM_CounterMode= TIM_CounterMode_Up;
0 U- n( a! I2 D8 B: d0 u
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure);

; O  R0 F2 M, t8 F$ X& _
TIM_ARRPreloadConfig(TIM2, ENABLE);

' C- L6 p. ?2 f1 c6 y9 [9 Y  R& }" ]
/* 设置更新请求源只在计数器上溢或下溢时产生中断*/

) t7 Y6 F, G% X% s% R+ I1 F3 X
TIM_UpdateRequestConfig(TIM2,TIM_UpdateSource_Global);

, m! a$ J% Y; k& P) Y
TIM_ClearFlag(TIM2, TIM_FLAG_Update);

  S2 p: @! I$ q' Z& [
}

3 p8 q+ L2 R) q  a
/**

2 N2 y: c: [/ l( t
* @brief us延时程序,10us为一个单位

- k7 j) Q' X7 T! z1 ~
* @param

% T2 ~& z) a7 z$ u5 {* d7 x
* @arg nTime: Delay_us( 10 ) 则实现的延时为 10 * 1us = 10us

& d* M8 X* F! S$ |
* @retval 无

, P, T0 i& F/ E! w6 W2 ^5 [
*/

+ ^$ f! X! Y6 U/ A
void Delay_us(__IO uint32_t nTime)
" u# {0 @& M; u/ L4 v3 q
{

2 \2 v1 d. I' w
/* 清零计数器并使能滴答定时器 */

, l  A0 S6 a- {) O1 X# N' F
TIM2-》CNT = 0;

, b  V  W; z1 x6 h5 u, P0 x
TIM_Cmd(TIM2, ENABLE);

# d3 E8 v: g# p0 @) F% a
for( ; nTime 》 0 ; nTime--)

4 L! F$ u% g# K, ]2 ~% u* _7 K
{
7 Q9 i) l- d. \- q; z  H1 [& P6 t3 P
/* 等待一个延时单位的结束 */
- L# Q& f7 a# s
while(TIM_GetFlagStatus(TIM2, TIM_FLAG_Update) != SET);

1 `! l; {  p- D' @% h
TIM_ClearFlag(TIM2, TIM_FLAG_Update);
4 }8 `; x* _! g. {' w1 t% N" X
}
% V/ l6 n# B  y# e- H: J
TIM_Cmd(TIM2, DISABLE);

- T6 \& k: e' {( b4 x6 z+ g
}
+ y- I& X' D# R% B. L- p- ?
在main函数中检验Delay_us的执行时间
  h- l- \  N3 H- g; @0 s  ~% v0 w" I
#include “stm32f10x.h”
! ~. f1 t  b7 [
#include “Timer_Drive.h”
  b7 @) N3 l$ C2 @' [+ P
#include “gpio.h”
4 b2 E1 l. T2 Z
#include “systick.h”
/ \) V, n' `* q' D
TimingVarTypeDef Time;

6 f' \2 U" a1 O
int main(void)

7 J9 W. T2 I- K5 c
{

! Y  n! S2 a" {! Y2 }
TIM2_Init();
8 C5 x, n( _' Y/ g) w, Z- G. s, p: a7 g+ s
SysTick_Init();

' ~* X) i6 ^# z  M! v5 L. ?) b
SysTick_Time_Init(&Time);

- y5 L" _2 T) t  w( H0 w# d, T; z
for(;;)
, v( X3 i* `6 ?/ ~* s4 C" w& I4 Q8 s
{
+ T6 j! V3 y- Q2 s- T  |1 a- r
SysTick_Time_Start();

- u8 j2 |- X  r1 Z' N2 O# X6 @4 i
Delay_us(1000);
1 v+ u/ u" ]  z/ J3 B+ Q
SysTick_Time_Stop();
  i+ Z) t0 T8 D; J# m; T2 U; ]8 D$ T! z
}

. M( c  Q* a- I; M* d1 O* [3 x
}

( |& j3 x$ e! u  n. G- z" V+ Q

) j" ?% x+ H& U$ `$ @6 Z
赞 收藏 评论0 发布时间:2020-4-15 11:44

举报

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