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

 1.整体方案

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

2.效果展示

3.快速使用

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

TOOL_RUNTM_Init();

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

void TIM6_DAC_IRQHandler(void)

{

if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)

{

TOOL_RUNTM_Irq();

TIM6->SR = ~ TIM_FLAG_UPDATE;

}

}

(3)设定定时器参数

/*定时器时钟源频率*/

#define RUN_TIME_SOURSE_FREQ (SystemCoreClock/2)

/*定时器计时寄存器读取接口*/

#define RUN_TIME_CNT (TIM6->CNT)

/*为了防止中断频率过高,所以采用降低计数频率方式来做*/

#define RUN_TIME_PRECALER 4

/* 基础定时器为16位,最大值为 2^16 = 65536 ,此处为了方便计算,取值 60000 */

#define RUN_TIME_PERIOD 60000

/* 中断记录次数*/

#define RUN_TIME_IRQ_NUMBER 200

/* 计数方向选择*/

#define RUN_TIME_UP 1

#define RUN_TIME_DOWN 2

#define RUN_TIME_COUNT_DIRE RUN_TIME_UP

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

RunTime_T Test;

TOOL_RUNTM_Start(&Test);

(5)执行被测试函数

bsp_DelayMS(10);

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

(7)定义一个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;

/*
*********************************************************************************************************
*	函 数 名: TIM6_DAC_IRQHandler
*	功能说明: TIM6定时中断服务程序
*	返 回 值: 无
*********************************************************************************************************
*/
void TIM6_DAC_IRQHandler(void)
{
	if((TIM6->SR & TIM_FLAG_UPDATE) != RESET)
	{
		TOOL_RUNTM_Irq();
		TIM6->SR = ~ TIM_FLAG_UPDATE;
	}
}

/**
***********************************************************************************************************                  
* @name   		BSP_TIPWM_SetTIMforInt
* @brief     	tim init
* @param        null
* @return       null                      
***********************************************************************************************************
**/
void Tim6Init(void)
{
	TIM_HandleTypeDef   TimHandle = {0};
	
	/*The TIM clock was enabled */
	__HAL_RCC_TIM6_CLK_ENABLE();

	TimHandle.Instance = TIM6;
	TimHandle.Init.Prescaler         = RUN_TIME_PRECALER-1;
	TimHandle.Init.Period            = RUN_TIME_PERIOD-1;	
	TimHandle.Init.ClockDivision     = 0;
	TimHandle.Init.CounterMode       = TIM_COUNTERMODE_UP;
	TimHandle.Init.RepetitionCounter = 0;
	TimHandle.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
	HAL_TIM_Base_Init(&TimHandle);

	/* Description The timer was disabled  */
	__HAL_TIM_ENABLE_IT(&TimHandle, TIM_IT_UPDATE);
	
    HAL_NVIC_SetPriority(TIM6_DAC_IRQn, 2, 0);
    HAL_NVIC_EnableIRQ(TIM6_DAC_IRQn);		
	
	HAL_TIM_Base_Start(&TimHandle);
}

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

	bsp_InitTimer();
	//启动一个硬件定时器
	Tim6Init();
	
	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;

6.源文件

.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/2)
/*定时器计时寄存器读取接口*/
#define RUN_TIME_CNT (TIM6->CNT)
/*为了防止中断频率过高,所以采用降低计数频率方式来做*/
#define RUN_TIME_PRECALER    	 4
/* 基础定时器为16位,最大值为 2^16 = 65536 ,此处为了方便计算,取值 60000*/
#define RUN_TIME_PERIOD  	 60000
/* 中断记录次数*/
#define RUN_TIME_IRQ_NUMBER 200
/* 计数方向选择*/
#define RUN_TIME_UP 1
#define RUN_TIME_DOWN 2
#define RUN_TIME_COUNT_DIRE RUN_TIME_UP

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 1】1、stm32通过滴答定时器进行代码运行时间检测,ns级精度-CSDN博客

  • 9
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是在STM32CubeIDE中使用TIM2配置50Hz定时器的步骤: 1. 打开STM32CubeIDE并创建一个新项目。 2. 在“Pinout & Configuration”中,选择您要使用的STM32器件并配置GPIO引脚以控制定时器。 3. 在“Project Manager”视图中,右键单击项目并选择“Properties”。 4. 在“C/C++ Build”中,选择“Settings”。 5. 在“Tool Settings”选项卡中,选择“MCU Settings”。 6. 在“TIM”中,选择您要使用的定时器(例如TIM2)。 7. 设置“Prescaler”和“Period”以生成50Hz的PWM信号。 例如,如果您使用的是72MHz的系统时钟,则可以将“Prescaler”设置为1440,将“Period”设置为2000。 8. 在“NVIC”中,启用定时器中断。 9. 在代码中初始化定时器,并启动它。 10. 使用定时器中断处理程序来生成50Hz的PWM信号。 以下是使用HAL库的示例代码: ```c #include "stm32f4xx_hal.h" TIM_HandleTypeDef htim2; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_TIM2_Init(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_TIM2_Init(); HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_1); while (1) { } } void HAL_TIM_PWM_PulseFinishedCallback(TIM_HandleTypeDef *htim) { if(htim->Instance == TIM2) { HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_0); } } void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; /** Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 8; RCC_OscInitStruct.PLL.PLLN = 336; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 7; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2; RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_5) != HAL_OK) { Error_Handler(); } } static void MX_TIM2_Init(void) { TIM_MasterConfigTypeDef sMasterConfig = {0}; TIM_OC_InitTypeDef sConfigOC = {0}; htim2.Instance = TIM2; htim2.Init.Prescaler = 1440; htim2.Init.CounterMode = TIM_COUNTERMODE_UP; htim2.Init.Period = 2000; htim2.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; if (HAL_TIM_PWM_Init(&htim2) != HAL_OK) { Error_Handler(); } sMasterConfig.MasterOutputTrigger = TIM_TRGO_RESET; sMasterConfig.MasterSlaveMode = TIM_MASTERSLAVEMODE_DISABLE; if (HAL_TIMEx_MasterConfigSynchronization(&htim2, &sMasterConfig) != HAL_OK) { Error_Handler(); } sConfigOC.OCMode = TIM_OCMODE_PWM1; sConfigOC.Pulse = 1000; sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH; sConfigOC.OCFastMode = TIM_OCFAST_DISABLE; if (HAL_TIM_PWM_ConfigChannel(&htim2, &sConfigOC, TIM_CHANNEL_1) != HAL_OK) { Error_Handler(); } HAL_TIM_PWM_RegisterCallback(&htim2, HAL_TIM_PWM_PULSE_FINISHED_CB_ID, HAL_TIM_PWM_PulseFinishedCallback); } static void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); GPIO_InitStruct.Pin = GPIO_PIN_0; GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; GPIO_InitStruct.Pull = GPIO_NOPULL; GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } ``` 这个示例代码将在PA0引脚上生成50Hz PWM信号,并在每个PWM周期结束时调用“HAL_TIM_PWM_PulseFinishedCallback”函数。 在“HAL_TIM_PWM_PulseFinishedCallback”函数中,将PA0引脚的状态切换。 您可以根据自己的需要修改此功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值