【Tool 1】使用STM32滴答定时器实现代码运行时间检测,实现纳秒级精度

1.整体方案

该方案主要使用滴答定时器的计数来进行运行时间的检测。stm32滴答定时器的时钟源可以选择为内核时钟,该定时器为24位,可以记到2^24=16,777,216,是一个比较大的数字,本人所使用的stm32h743时钟频率为400mhz,每2.5ns会产生一次计数,精度很高。如果把让定时器在一个比较长的时间之后再产生溢出,溢出之后再进行计次,可以得到一个精度比较高的软件定时器。在执行程序的开始前获取该定时器的值,再在该执行程序执行结束后获取该定时器的值,两个时间的差值就是该段程序的运行时间,

2.效果展示

3.快速使用

(1)执行TOOL_RUNTM_Init函数,进行运行模块检测初始化

TOOL_RUNTM_Init(SystemCoreClock);

(2)将“TOOL_RUNTM_Irq”函数放到滴答定时器中断函数“SysTick_Handler”中

void SysTick_Handler(void)

{

TOOL_RUNTM_Irq();

}

(3)创建RunTime_T类型结构体,运行TOOL_RUNTM_Start函数,获取开始时间

RunTime_T Test;

TOOL_RUNTM_Start(&Test);

(4)执行被测试函数

bsp_DelayMS(10);

(5)运行TOOL_RUNTM_Stop函数,获取结束时间

(6)定义一个uint64_t 变量,获取运行时间,以ns为单位

time = TOOL_RUNTM_CurGet(&Test);

4.完整快速使用代码

RunTime_T TestStr;
uint64_t time_ns = 0;
RunTimeTrans_T TestOut;
uint64_t max_run = 0;
uint64_t min_run = 0;

/*
*********************************************************************************************************
*	函 数 名: SysTick_Handler
*	功能说明: 系统嘀嗒定时器中断服务程序。启动文件中引用了该函数。
*	形    参:  无
*	返 回 值: 无
*********************************************************************************************************
*/
void SysTick_Handler(void)
{
	TOOL_RUNTM_Irq();
}

/*
*********************************************************************************************************
*	函 数 名: main
*	功能说明: c程序入口
*	形    参: 无
*	返 回 值: 错误代码(无需处理)
*********************************************************************************************************
*/
int main(void)
{	
	bsp_Init();		/* 硬件初始化 */

	SysTick_Config(RUN_TIME_PERIOD); /* 将滴答定时器初始化为10000000*/
	//启动一个硬件定时器
	bsp_SetTIMforInt(TIM6,1000,2,0);
	
	TOOL_RUNTM_Init();
	
	/*获取运行时间检测精度,也就是最小检测时间*/
	min_run = TOOL_RUNTM_PreciGet(&TestStr);
	/*获取最大可记的运行时间*/
	max_run = TOOL_RUNTM_MaxTime(&TestStr);
	
	/*将最大运行时间进行转换*/
	TestOut = TOOL_RUNTM_Convert(max_run);

	
	/* 进入主程序循环体 */
	while (1)
	{	
		/*开始计时*/
		TOOL_RUNTM_Start(&TestStr);
		bsp_DelayMS(10);
		/*结束计时*/
		TOOL_RUNTM_Stop(&TestStr);
		/*获取当前运行时间*/
		time_ns = TOOL_RUNTM_CurGet(&TestStr);
		time_ns = time_ns;
		/*将运行时间进行转换*/
		TestOut = TOOL_RUNTM_Convert(time_ns);

	}
}

5.各个接口说明及代码分析

(1)TOOL_RUNTM_Init:

调用“SysTick_Config”函数进行滴答定时器初始化

(2)TOOL_RUNTM_BaseReset:

清零“RunTimeTrans_T”类型的结构体

(3)TOOL_RUNTM_PreciGet:

获取当前运行时间检测的精度,以ns为单位返回

(4)TOOL_RUNTM_Start

开始运行时间检测

(5)TOOL_RUNTM_Stop

停止运行时间检测

(6)TOOL_RUNTM_CurGet

获取本次运行时间

(7)TOOL_RUNTM_AvgGet

获取平均运行时间

(8)TOOL_RUNTM_Irq

中断执行函数

(9)TOOL_RUNTM_MaxTime

获取最大检测时间

(10)TOOL_RUNTM_Convert

将获取到的ns为单位的运行时间解析为“RunTimeTrans_T”类型的时间

typedef struct
{
    float RunTimeNs; //ns
    uint16_t RunTimeUs;//us
    uint16_t RunTimeMs;//ms
    uint16_t RunTimeS;//S
}RunTimeTrans_T;

7.源文件

.c

/*
*********************************************************************************************************
*
*	模块名称 : 运行时间统计模块
*	文件名称 : tool_run_time.c
*	版    本 : V1.1
*	说    明 : 统计程序的运行时间
*
*	修改记录 :
*		版本号  日  期        作者     说明
*		V1.0    2024-1-3 StrongerSun  正式发布
*
*	Copyright (C), 2015-2020
*
*********************************************************************************************************
*/
#include "stm32h7xx_hal.h"
#include "bsp.h"
#include "tool_run_time.h"


static __IO RunTimeBase_T s_tTimeRunCount;
static __IO uint32_t s_ulSourceTime = 0;
static __IO float s_TimeRunPreci = 0; /* 计时精度,以ns为单位 */



/*
*********************************************************************************************************
*	函 数 名: BSP_RUNTM_Init
*	功能说明: 初始化运行时间功能
*	形    参:_sys_clock:系统时钟
*	返 回 值: 无
*********************************************************************************************************
*/
void TOOL_RUNTM_Init(void)
{
	s_ulSourceTime = RUN_TIME_SOURSE_FREQ/RUN_TIME_PRECALER;
	s_TimeRunPreci = 1/(float)s_ulSourceTime*1000000000;/* 计算计时精度,以ns为单位 */

}

/*
*********************************************************************************************************
*	函 数 名: BSP_RUNTM_BaseReset
*	功能说明: 基础机构体数据清零
*	形    参:_sys_clock:系统时钟
*	返 回 值: 无
*********************************************************************************************************
*/
void TOOL_RUNTM_BaseReset(RunTimeTrans_T *_obj)
{
	_obj->RunTimeNs = 0;
	_obj->RunTimeS = 0;
	_obj->RunTimeMs = 0;
	_obj->RunTimeS = 0;

}

/*
*********************************************************************************************************
*	函 数 名: BSP_RUNTM_PreciGet
*	功能说明: 获取运行时间统计的精度,以ns为单位返回
*	形    参:无
*	返 回 值: TimeRunBase_T:运行时间统计基础结构体
*********************************************************************************************************
*/
float TOOL_RUNTM_PreciGet(RunTime_T *_obj)
{
	return s_TimeRunPreci;
}

/*
*********************************************************************************************************
*	函 数 名: BSP_RUNTM_Start
*	功能说明: 开始统计
*	形    参{_obj:目标结构体
*	返 回 值: 无
*********************************************************************************************************
*/
void TOOL_RUNTM_Start(RunTime_T *_obj)
{
	/* 这些变量在Systick中断中被改写,因此需要关中断进行保护 */
	DISABLE_INT();  	/* 关中断 */
	/* 根据定时器方向取得计数值 */
	#if(RUN_TIME_COUNT_DIRE == RUN_TIME_UP)
	s_tTimeRunCount.ReloadReg = RUN_TIME_CNT;
	#elif (RUN_TIME_COUNT_DIRE == RUN_TIME_DOWN)
	s_tTimeRunCount.ReloadReg = RUN_TIME_PERIOD - RUN_TIME_CNT;
	#endif
	_obj->Start = s_tTimeRunCount;
	ENABLE_INT();  		/* 开中断 */
}

/*
*********************************************************************************************************
*	函 数 名: BSP_RUNTM_Stop
*	功能说明: 结束统计
*	形    参{_obj:目标结构体
*	返 回 值: 无
*********************************************************************************************************
*/
void TOOL_RUNTM_Stop(RunTime_T *_obj)
{
	uint32_t start = 0,stop = 0;


	DISABLE_INT();  	/* 关中断 */
	/* 这个变量在Systick中断中被改写,因此需要关中断进行保护 */
	/* 因此定时器向下计数,所以需要相减,获得真实值 */
	/* 首先获取定时器值,防止之后的运算计入计入运行时间 */
	#if(RUN_TIME_COUNT_DIRE == RUN_TIME_UP)
	s_tTimeRunCount.ReloadReg = RUN_TIME_CNT;
	#elif (RUN_TIME_COUNT_DIRE == RUN_TIME_DOWN)
	s_tTimeRunCount.ReloadReg = RUN_TIME_PERIOD - RUN_TIME_CNT;
	#endif
	_obj->Stop = s_tTimeRunCount;
	ENABLE_INT();  		/* 开中断 */
	start = _obj->Start.TimeCount*RUN_TIME_PERIOD +_obj->Start.ReloadReg;
	stop = _obj->Stop.TimeCount*RUN_TIME_PERIOD +_obj->Stop.ReloadReg;

	if (stop >= start)
	{
		_obj->CurCount = stop - start;
	}
	else
	{
		_obj->CurCount = RUN_TIME_IRQ_NUMBER*RUN_TIME_PERIOD - start + stop;
	}
	if(_obj->AvgCount != 0)
	{
		_obj->AvgCount = (_obj->AvgCount+_obj->CurCount)/2;
	}
	else
	{
		_obj->AvgCount = _obj->CurCount;
	}
	
	
}

/*
*********************************************************************************************************
*	函 数 名: BSP_RUNTM_CurGet
*	功能说明: 获取本次运行时间
* 	形参:_obj:目标结构体
*	返 回 值: 无
*********************************************************************************************************
*/
uint64_t TOOL_RUNTM_CurGet(RunTime_T *_obj)
{
	return (uint64_t)(_obj->CurCount * s_TimeRunPreci);
}

/*
*********************************************************************************************************
*	函 数 名: BSP_RUNTM_AvgGet
*	功能说明:获取平均运行时间,以ns为单位
* 	形参:_obj:目标结构体
*	返 回 值: 无
*********************************************************************************************************
*/
uint64_t TOOL_RUNTM_AvgGet(RunTime_T *_obj)
{
	return (uint64_t)(_obj->AvgCount * s_TimeRunPreci);
}

/*
*********************************************************************************************************
*	函 数 名: BSP_RUNTM_AvgGet
*	功能说明:获取平均运行时间,以ns为单位
* 	形参:_obj:目标结构体
*	返 回 值: 无
*********************************************************************************************************
*/
void TOOL_RUNTM_Irq(void)
{
	/* 不处理此变量的回绕,使自动从0开始计数*/
	s_tTimeRunCount.TimeCount++;
	/* 考虑进行平均值计算时可能会导致数据超过uin32_t ,所以此处选择使用100,防止数据溢出*/
	if(s_tTimeRunCount.TimeCount >= RUN_TIME_IRQ_NUMBER)
	{
		s_tTimeRunCount.TimeCount = 0;
	}
}

/*
*********************************************************************************************************
*	函 数 名: BSP_RUNTM_MaxTime
*	功能说明:最大计时时间
* 	形参:_obj:目标结构体
*	返 回 值: 无
*********************************************************************************************************
*/
uint64_t TOOL_RUNTM_MaxTime(RunTime_T *_obj)
{
	return s_TimeRunPreci*RUN_TIME_PERIOD *RUN_TIME_IRQ_NUMBER;
}

/*
*********************************************************************************************************
*	函 数 名: BSP_RUNTM_Convert
*	功能说明:将ns转换为s,ms,us,ns
* 	形参:_tim:ns为单位的运行时间
*	返 回 值: 解析出来的结构体
*********************************************************************************************************
*/
RunTimeTrans_T TOOL_RUNTM_Convert(uint64_t _tim)
{
	RunTimeTrans_T result;
	
	TOOL_RUNTM_BaseReset(&result);

	result.RunTimeNs = _tim%1000;
	result.RunTimeUs = _tim/1000%1000;
	result.RunTimeMs = _tim/1000/1000%1000;
	result.RunTimeS = _tim/1000/1000/1000;
	
	return result;
}

/***************************** (END OF FILE) *********************************/

.h

/*
*********************************************************************************************************
*
*	模块名称 : 运行时间记录模块
*	文件名称 : tool_run_time.h
*	版    本 : V1.0
*	说    明 : 头文件
*
*	Copyright (C), 2012-2013
*
*********************************************************************************************************
*/

#ifndef __TOOL_RUN_TIME_H
#define __TOOL_RUN_TIME_H

#include "stdint.h"
#include "stm32h7xx_hal.h"
#include "bsp.h"



/*定时器时钟源频率*/
#define RUN_TIME_SOURSE_FREQ (SystemCoreClock)
/*定时器计时寄存器读取接口*/
#define RUN_TIME_CNT (SysTick->VAL)
/*为了防止中断频率过高,所以采用降低计数频率方式来做*/
#define RUN_TIME_PRECALER  1
/* 滴答定时器为24位,最大值为 2^24 = 16777216 ,此处为了方便计算,取值 10000000*/
#define RUN_TIME_PERIOD  	 10000000
/* 中断记录次数*/
#define RUN_TIME_IRQ_NUMBER 200
/* 计数方向选择*/
#define RUN_TIME_UP 1
#define RUN_TIME_DOWN 2
#define RUN_TIME_COUNT_DIRE RUN_TIME_DOWN

typedef struct
{
	__IO float RunTimeNs; //ns
	__IO uint16_t RunTimeUs;//us
	__IO uint16_t RunTimeMs;//ms
	__IO uint16_t RunTimeS;//S
}RunTimeTrans_T;

typedef struct
{
	__IO uint32_t ReloadReg;//开始寄存器时间
	__IO uint8_t TimeCount;//计时时间 ,此处采用uint8_t 主要考虑用一个32位的数值可以放下
}RunTimeBase_T;

typedef struct
{
	__IO RunTimeBase_T Start;//开始时间
	__IO RunTimeBase_T Stop;//结束时间
	__IO uint32_t CurCount; //当前计次
	__IO uint32_t AvgCount; //平均计次
}RunTime_T;


void TOOL_RUNTM_Init(void);
void TOOL_RUNTM_BaseReset(RunTimeTrans_T *_obj);
float TOOL_RUNTM_PreciGet(RunTime_T *_obj);
void TOOL_RUNTM_Start(RunTime_T *_obj);
void TOOL_RUNTM_Stop(RunTime_T *_obj);
uint64_t TOOL_RUNTM_CurGet(RunTime_T *_obj);
uint64_t TOOL_RUNTM_AvgGet(RunTime_T *_obj);
void TOOL_RUNTM_Irq(void);
uint64_t TOOL_RUNTM_MaxTime(RunTime_T *_obj);
RunTimeTrans_T TOOL_RUNTM_Convert(uint64_t _tim);



extern RunTime_T run_time_test;






#endif /* __BSP_RUN_TIME_H */

/***************************** (END OF FILE) *********************************/

7.测试工程

链接:StrongerSun/tool run time – 码云 – 开源中国 (gitee.com)

8.联系本人

如代码使用有问题,可以发邮件到 3060793968@qq.com 

9.基础定时器实现方式:

【tool 2】stm32通过基础定时器进行代码运行时间检测,ns级精度-CSDN博客

作者:StrongerSun

物联沃分享整理
物联沃-IOTWORD物联网 » 【Tool 1】使用STM32滴答定时器实现代码运行时间检测,实现纳秒级精度

发表回复