【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