单片机测量代码运行时间方法-STM32

Posted Z小旋

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单片机测量代码运行时间方法-STM32相关的知识,希望对你有一定的参考价值。

在实际程序的编写中,我们经常会对程序进行一个整体的复盘、优化,或者对算法的运行时间进行测量等等,那么怎么精确的测量我们程序的运行时间呢?

下面我们给出几种方法:

  • 1 使用Keil Debug功能
  • 2 使用逻辑分析仪或示波器等设备测量
  • 3 使用STM32自带定时器进行测量

1 使用Keil Debug功能

我们可以使用J-LINK或者ST-link 等仿真器,实现对代码运行时间的测量,首先要设置仿真器仿真的实际频率

首先点击Settings设置

然后点击Trace 设置我们芯片的系统频率,点击Teace Enable 使能

如果工作频率设置不正确,则会造成测量的时间不正确。

仿真器默认采用的是10MHz的工作频率

首先我们点击DEBUG模式

1.然后会跳转到main.c

我们可以看到从系统启动到main.c所需要的时间是0.00000367秒

2.我们在需要测量的程序段开始和结束的地方设置断点

3 通过起始断点和结束断点处读取到的时间,结束时间-起始时间,即可判断代码段执行的时间。


我们用500ms延时的流水灯来举例:

测量代码:

		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_0,GPIO_PIN_RESET);	//PB0置0
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET); //PB1置0
		delay_ms(500);


点击RUN 运行代码 到起始断点处停止

记录下此时时间 t1=0.00008590s

再次点击RUN 运行代码 到结束断点处停止

记录下此时时间 t2=0.50014558s

代码运行时间是: t2-t1=0.50014558-0.00008590=0.50005968s≈500ms

最后的0.00005968s 是运行上面两个LED高电平函数,所需要的CPU指令时间以及误差时间

总结:

使用keil的debug模式进行测量,非常方便,可以很随意的对任意段代码进行测量,并且误差很小,操作方便。

2 使用逻辑分析仪或示波器等设备测量

在待测程序段的开始阶段使单片机的一个GPIO输出高电平,在待测程序段的结尾阶段再令这个GPIO输出低电平。用示波器或者逻辑分析仪通过检查高电平的时间长度,就知道了这段代码的运行时间。

while(1)
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET); //PB1置1
		delay_ms(500);
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);	//PB1置0
		delay_ms(500);
	

延时500ms时波形如下:

修改延时为100ms:

while(1)
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET); //PB1置1
		delay_ms(100);
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);	//PB1置0
		delay_ms(100);
	

波形如下

总结:

使用示波器测量较为准确,缺点是需要单独的示波器或者逻辑分析仪等,示波器一般体积较大的还需要供电,并且还要连接GPIO口,还是有点麻烦的

3 使用STM32自带定时器进行测量

定时器本质上就是向上累加的计数器(如果配置成向上计数时),所以我们在测量开始的代码前面读取定时器的计数器,在结束测量的位置再读取定时器的计数器,获得两次的差值,这样就可以计算出这段代码的运行的时间。 这就是其原理

这里我们使用TIM3定时器

定时器初始化:

  • 定时器3初始化,我们使用STM32H7,定时器时钟为200M,分频系数为20000-1,
  • 所以定时器3的频率为200M/20000=10KHz 周期是T=1/F=1/10000=100us ,自动重装载为65535-1,
  • 那么定时器周期就是T=65535*100us=6.5535s
void TIM3_Init(u16 arr,u16 psc)
  
    TIM3_Handler.Instance=TIM3;                          //通用定时器3
    TIM3_Handler.Init.Prescaler=psc;                     //分频
    TIM3_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;    //向上计数器
    TIM3_Handler.Init.Period=arr;                        //自动装载值
    TIM3_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频因子
    HAL_TIM_Base_Init(&TIM3_Handler);
    
    HAL_TIM_Base_Start_IT(&TIM3_Handler); //使能定时器3和定时器3更新中断:TIM_IT_UPDATE    



 TIM3_Init(65535-1,20000-1); //定时器3初始化,定时器时钟为200M,分频系数为20000-1,
//所以定时器3的频率为200M/20000=10KHz  周期是T=1/F=1/10000=100us ,自动重装载为65535-1,
//那么定时器周期就是T=65535*100us=6.5535s


代码测试:

我们还是以LED流水灯翻转200ms为例,看下定时器测得的时间:


int  Start_count=0;  //开始计时定时器值
int  End_count=0;   //结束计时定时器值
int  Time;  //代码运行时间


int main(void)
	

	Cache_Enable();                		//打开L1-Cache
	HAL_Init();				        	//初始化HAL库
	Stm32_Clock_Init(160,5,2,4);  	    //设置时钟,400Mhz 

	LED_Init();							//初始化LED
    TIM3_Init(65535-1,20000-1);      	//定时器3初始化,定时器时钟为200M,分频系数为20000-1,
	//所以定时器3的频率为200M/20000=10KHz  周期是T=1/F=1/10000=100us ,
	//自动重装载为65535-1,那么定时器周期就是6.5535S
	
	//计数1次等于100us  单位0.1ms 
	
    while(1)
    
		//开始计时
		Start_count=__HAL_TIM_GET_COUNTER(&TIM3_Handler); //获取定时器的值,开始计时
		
       	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET); //PB1置1
		delay_ms(100);
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);	//PB1置0
		delay_ms(100);
		
		//结束计时
		End_count=__HAL_TIM_GET_COUNTER(&TIM3_Handler);  //获取定时器的值,结束计时	
		Time=(End_count-Start_count)/10;  //时间(ms)=结束值-开始值+定时器溢出次数*65535
		
	 



上面代码的思想就是在代码开始运行前,测得定时器的初始值,然后在代码结束的位置,再测得定时器的值,最终的代码运行时间:

Time=(End_count-Start_count)/10; //时间(ms)=结束值-开始值

测得时间为200ms

当然,如果我们的代码运行时间超过了6.5535秒,就要考虑定时器中断溢出的情况,我们的代码如下:



int  COUNT=0;  //定时器溢出次数
int  Start_count=0;  //开始计时定时器值
int  End_count=0;   //结束计时定时器值
int  Time;


int main(void)
	

	Cache_Enable();                		//打开L1-Cache
	HAL_Init();				        	//初始化HAL库
	Stm32_Clock_Init(160,5,2,4);  	    //设置时钟,400Mhz 
	delay_init(400);				//延时初始化
	LED_Init();							//初始化LED
    TIM3_Init(65535-1,20000-1);      	//定时器3初始化,定时器时钟为200M,分频系数为20000-1,
	//所以定时器3的频率为200M/20000=10KHz  周期是T=1/F=1/10000=100us ,
	//自动重装载为65535-1,那么定时器周期就是6.5535S
	
	//计数1次等于100us  单位0.1ms 
	
    while(1)
    
		//开始计时
		COUNT=0;  //每次开始计时将COUNT清零
		__HAL_TIM_SET_COUNTER(&TIM3_Handler,0); //获取定时器的值,开始计时
		
       	HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_SET); //PB1置1
		delay_ms(100);
		HAL_GPIO_WritePin(GPIOB,GPIO_PIN_1,GPIO_PIN_RESET);	//PB1置0
		delay_ms(100);
		
		//结束计时
		End_count=__HAL_TIM_GET_COUNTER(&TIM3_Handler);  //获取定时器的值,结束计时	
		Time=(End_count+COUNT*65535)/10;  //时间(ms)=结束值-开始值+定时器溢出次数*65535
		
	 

//定时器3中断服务函数调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)

    if(htim==(&TIM3_Handler))
    
       COUNT++;
    


上面代码的思想就是在代码开始运行前,定时器的计数值设置为0,然后在代码结束的位置,再测得定时器的值,如果代码运行时间过长,定时器中断中COUNT就加1,最终的代码运行时间:

Time=(End_count+COUNT65535)/10; //时间(ms)=结束值+定时器溢出次数65535

测得时间为200ms


总结:
这种方法比较方便的是代码的运行时间是一个变量,我们可以在程序中使用这个变量,这是很方便的,但是要占用一个定时器。


以上是关于单片机测量代码运行时间方法-STM32的主要内容,如果未能解决你的问题,请参考以下文章

单片机测量代码运行时间方法-STM32

获取STM32代码运行时间的技巧

基于STM32单片机的智能家居测量系统设计

STM32实现六轴姿态测量陀螺仪模块JY61P(标准库与HAL库实现)

STM32实现六轴姿态测量陀螺仪模块JY61P(标准库与HAL库实现)

STM32Cube STM32L053配置GPIO达到最低功耗详解含测量实际电流值