STM32F4或者STM32F7有2个32位定时器,非常适合用来做高精度的时间测量,既可以保证精度,又可以保证量程,测试使用定时器5(32位定时器),实现1us精度的时间测量,函数格式类似于StopWatch类。
/*************************************************************************************************************
* 文件名 : StopWatch.c
* 功能 : 计时器
* 作者 : cp1300@139.com
* 创建时间 : 2020-02-09
* 最后修改时间 : 2020-02-09
* 详细: 通过32位的定时器5实现计时器功能,计时分辨率为1us;TIM5的时钟为APB1的时钟的两倍。
*************************************************************************************************************/
#include "StopWatch.h"
#include "system.h"
/******************************************************************************/
//定时器硬件设置
#define STOP_WATCH_TIMx TIM5 //定时器设置,只能使用定时器2或定时器5,这2个定时器是32位的
#define STOP_WATCH_TIM_DEV DEV_TIM5 //定时器的时钟使能设备选择
#define STOP_WATCH_TIM_IRQHandler TIM5_IRQHandler //定时器中断服务选择
#define STOP_WATCH_NVIC_IRQn IRQ_TIM5 //NVIC中断位置
/******************************************************************************/
static u32 g_StopWatchCycleCount = 0; //定时器周期计数器,在定时器的溢出中断中进行计数,一个周期为4000秒
#define STOP_WATCH_CYCLE 4000000000 //单位us,4000秒
//内部函数接口
static u32 StopWatch_GetTimeClockSpeed(void); //获取TIM2或TIM5的时钟速度
static void StopWatch_Init(void); //计时器初始化(会初始化定时器,并将定时器启动)
static void StopWatch_Start(STOP_WATCH_HANDLE *pHandle); //开始计数,记录开始值
static void StopWatch_Stop(STOP_WATCH_HANDLE *pHandle); //停止计数,记录结束值
static void StopWatch_Restart(STOP_WATCH_HANDLE *pHandle); //复位开始于结束值为0
static u32 StopWatch_GetElapsedMs(STOP_WATCH_HANDLE *pHandle); //获取过去的时间值,单位ms
static u64 StopWatch_GetElapsedUs(STOP_WATCH_HANDLE *pHandle); //获取过去的时间值,单位us
static void StopWatch_Close(void); //关闭全局计时器工具,会停止定时器,降低功耗
//定义一个全局的计时器工具
STOP_WATCH_TYPE g_mStopWatchClass =
{
StopWatch_Init, //计时器初始化(会初始化定时器,并将定时器启动)
StopWatch_Start, //开始计数,记录开始值
StopWatch_Stop, //停止计数,记录结束值
StopWatch_Restart, //复位开始于结束值为0
StopWatch_GetElapsedMs, //获取过去的时间值,单位ms
StopWatch_GetElapsedUs, //获取过去的时间值,单位us
StopWatch_Close, //关闭全局计时器工具,会停止定时器,降低功耗
};
/*************************************************************************************************************************
* 函数 : static u32 StopWatch_GetTimeClockSpeed(void)
* 功能 : 获取TIM2或TIM5的时钟速度
* 参数 : 无
* 返回 : 定时器时钟频率,单位Hz
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-02-09
* 最后修改时间 : 2020-02-09
* 说明 : 当APB1和APB2分频数为1的时候,TIM1、TIM8~TIM11的时钟为APB2的时钟,TIM2~TIM7、TIM12~TIM14的时钟为APB1的时钟;
而如果APB1和APB2分频数不为1,那么TIM1、TIM8~TIM11的时钟为APB2的时钟的两倍,TIM2~TIM7、TIM12~TIM14的时钟为APB1的时钟的两倍
*************************************************************************************************************************/
static u32 StopWatch_GetTimeClockSpeed(void)
{
u32 clock = 0;
u8 PPRE;
PPRE = (RCC->CFGR>>10)&0X7; //获取PPRE1 分频值
if(PPRE == 0) //分频数位0,直接等于APB1时钟
{
clock = SYS_GetAPB1ClockSpeed();
}
else //分频数不为1,时钟为APB1的2倍
{
clock = SYS_GetAPB1ClockSpeed()*2;
}
return clock;
}
/*************************************************************************************************************************
* 函数 : static void StopWatch_Init(void)
* 功能 : 计时器初始化(会初始化定时器,并将定时器启动)
* 参数 : 无
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-02-09
* 最后修改时间 : 2020-02-09
* 说明 :
*************************************************************************************************************************/
static void StopWatch_Init(void)
{
u32 psc;
if (((u32)STOP_WATCH_TIMx)!=((u32)TIM2) && ((u32)STOP_WATCH_TIMx) != ((u32)TIM5))
{
DEBUG("StopWatch初始化失败,无效的定时器!\r\n");
return;
}
psc = StopWatch_GetTimeClockSpeed(); //获取定时器时钟频率,设置定时器分频,时钟为1MHz
psc /= 1000000;
SYS_DeviceClockEnable(STOP_WATCH_TIM_DEV, TRUE); //使能定时器时钟
SYS_DeviceReset(STOP_WATCH_TIM_DEV); //定时器复位
//初始化配置
STOP_WATCH_TIMx->CR1 = 0; //关闭定时器,并复位相关设置,递增计数
STOP_WATCH_TIMx->CR1 |= BIT2; //只有溢出才会产生中断
STOP_WATCH_TIMx->CR2 = 0; //复位相关设置
STOP_WATCH_TIMx->PSC = psc-1; //预分频器设置,输入时钟为1MHz
STOP_WATCH_TIMx->CNT = 0; //初值为0
STOP_WATCH_TIMx->ARR = STOP_WATCH_CYCLE; //自动重装值
STOP_WATCH_TIMx->CR1 |= BIT7; //定时器自动重装使能
//UG重新初始化计数器,并产生一个更新事件。注意预分频器的计数器也被清0(但是预分频系数不变)。
//若在中心对称模式下或DIR=0(向上计数)则计数器被清0,若DIR=1(向下计数)则计数器取TIMx_ARR的值。
STOP_WATCH_TIMx->EGR |= BIT0; //更新UG=1
STOP_WATCH_TIMx->SR = ~STOP_WATCH_TIMx->SR; //清除中断标志位
STOP_WATCH_TIMx->DIER |= BIT0; //更新中断使能
STOP_WATCH_TIMx->DIER |= BIT6; //触发中断使能,2个一起设置才能使用中断
STOP_WATCH_TIMx->SR = 0; //清除中断
STOP_WATCH_TIMx->CR1 |= BIT0; //使能定时器
SYS_NVIC_IntEnable(STOP_WATCH_NVIC_IRQn, TRUE); //开启中断
}
/*************************************************************************************************************************
* 函数 : static void StopWatch_Start(STOP_WATCH_HANDLE *pHandle)
* 功能 : 开始计数,记录开始值
* 参数 : pHandle:计数句柄
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-02-09
* 最后修改时间 : 2020-02-09
* 说明 :
*************************************************************************************************************************/
static void StopWatch_Start(STOP_WATCH_HANDLE *pHandle)
{
SYS_DisableIrq(); //临时关闭中断
pHandle->StartTime = STOP_WATCH_TIMx->CNT; //获取当前的计数器值
pHandle->StartTime += g_StopWatchCycleCount; //加上溢出后的值
SYS_EnableIrq(); //开启中断
}
/*************************************************************************************************************************
* 函数 : static void StopWatch_Stop(STOP_WATCH_HANDLE *pHandle)
* 功能 : 停止计数,记录结束值
* 参数 : pHandle:计数句柄
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-02-09
* 最后修改时间 : 2020-02-09
* 说明 :
*************************************************************************************************************************/
static void StopWatch_Stop(STOP_WATCH_HANDLE *pHandle)
{
SYS_DisableIrq(); //临时关闭中断
pHandle->StopTime = STOP_WATCH_TIMx->CNT; //获取当前的计数器值
pHandle->StopTime += g_StopWatchCycleCount; //加上溢出后的值
SYS_EnableIrq(); //开启中断
pHandle->ElapsedUs = pHandle->StopTime - pHandle->StartTime; //计算时间
}
/*************************************************************************************************************************
* 函数 : static void StopWatch_Restart(STOP_WATCH_HANDLE *pHandle)
* 功能 : 复位开始于结束值为0
* 参数 : pHandle:计数句柄
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-02-09
* 最后修改时间 : 2020-02-09
* 说明 :
*************************************************************************************************************************/
static void StopWatch_Restart(STOP_WATCH_HANDLE *pHandle)
{
pHandle->ElapsedUs = 0; //清除
}
/*************************************************************************************************************************
* 函数 : static u32 StopWatch_GetElapsedMs(STOP_WATCH_HANDLE *pHandle)
* 功能 : 获取过去的时间值,单位ms
* 参数 : pHandle:计数句柄
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-02-09
* 最后修改时间 : 2020-02-09
* 说明 :
*************************************************************************************************************************/
static u32 StopWatch_GetElapsedMs(STOP_WATCH_HANDLE *pHandle)
{
return pHandle->ElapsedUs/1000;
}
/*************************************************************************************************************************
* 函数 : static u64 StopWatch_GetElapsedUs(STOP_WATCH_HANDLE *pHandle)
* 功能 : 获取过去的时间值,单位us
* 参数 : pHandle:计数句柄
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-02-09
* 最后修改时间 : 2020-02-09
* 说明 :
*************************************************************************************************************************/
static u64 StopWatch_GetElapsedUs(STOP_WATCH_HANDLE *pHandle)
{
return pHandle->ElapsedUs;
}
/*************************************************************************************************************************
* 函数 : static void StopWatch_Init(void)
* 功能 : 关闭全局计时器工具,会停止定时器,降低功耗
* 参数 : 无
* 返回 : 无
* 依赖 : 底层宏定义
* 作者 : cp1300@139.com
* 时间 : 2020-02-09
* 最后修改时间 : 2020-02-09
* 说明 :
*************************************************************************************************************************/
static void StopWatch_Close(void)
{
u32 psc;
if (((u32)STOP_WATCH_TIMx)!=((u32)TIM2) && ((u32)STOP_WATCH_TIMx) != ((u32)TIM5))
{
DEBUG("StopWatch关闭失败,无效的定时器!\r\n");
return;
}
SYS_DeviceReset(STOP_WATCH_TIM_DEV); //定时器复位
SYS_DeviceClockEnable(STOP_WATCH_TIM_DEV, FALSE); //关闭定时器时钟
SYS_NVIC_IntEnable(STOP_WATCH_NVIC_IRQn, FALSE); //中断关闭
}
//定时器中断处理函数
void STOP_WATCH_TIM_IRQHandler(void)
{
//uart_printf("中断了 0x%X\r\n", STOP_WATCH_TIMx->SR);
if(STOP_WATCH_TIMx->SR & BIT0) //溢出中断
{
STOP_WATCH_TIMx->SR = 0; //清除中断标志位
g_StopWatchCycleCount += STOP_WATCH_CYCLE; //溢出一次,计数器增加
}
STOP_WATCH_TIMx->SR = ~STOP_WATCH_TIMx->SR; //清除中断标志位-退出前再清除一次
}
|