STM32定时器实现计时功能

前言

        本文章使用STM32F103C8T6标准库实现TIM1的定时功能,代码如下:

#include "stm32f10x.h"                  // Device header
#include "led_drv.h"

static void timInit(uint32_t periodUs)
{
	/*使能定时器时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
	/*定时器复位*/
	TIM_DeInit(TIM1);
	/*定时器配置*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);
	TIM_TimeBaseInitStruct.TIM_Prescaler = SystemCoreClock / 1000000 - 1;//输入给计数器的时钟频率为1Mhz,周期1us
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period = periodUs - 1;
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStruct);
	TIM_ClearFlag(TIM1, TIM_FLAG_Update);
	/*使能定时器更新中断*/
	TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
	/*NVIC配置*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//一个工程只分组一次
	
	NVIC_InitTypeDef  NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel = TIM1_UP_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
	NVIC_Init(&NVIC_InitStruct);
	/*定时器使能*/
	TIM_Cmd(TIM1, ENABLE);
	TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
}

/**
***********************************************************
* @brief	定时器驱动初始化
* @param
* @return 
***********************************************************
*/
void TimDrvInit(void)
{
	timInit(1000);
}

/**
***********************************************************
* @brief	定时器1更新中断服务函数
* @param
* @return 
***********************************************************
*/
void TIM1_UP_IRQHandler(void)
{
	if ( TIM_GetITStatus(TIM1, TIM_IT_Update) )
	{
//		static uint32_t s_counter;
//		static uint8_t flipFlag = 1;
//		s_counter ++;
//		if (s_counter >= 1000 && flipFlag)
//		{
//			s_counter = 0;
//			flipFlag = 0;
//			TurnOnLed(LED_BLUE);
//			TurnOnLed(LED_YELLOW);
//			TurnOnLed(LED_WHITE);
//		}
//		else if (s_counter >= 1000 && flipFlag == 0)
//		{
//			s_counter = 0;
//			flipFlag = 1;
//			TurnOffLed(LED_BLUE);
//			TurnOffLed(LED_YELLOW);
//			TurnOffLed(LED_WHITE);
//		}
		TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
	}
}

一 基本定时框图

        来自RCC的TIM1CLK的频率是72MHz,PSC预分频器是一个16位的寄存器,所以分频系数可以选择的范围是0~65535,假如设置预分频系数为0,那就是不分频(1分频),CK_CNT == TIM1CLK / 1;假如预分频系数PSC为71, 那就是72分频,CK_CNT == TIM1CLK / 72 == 1MHz。

        CNT计数器是一个周期计数器,CK_CNT每来一个时钟周期就±1,假如CK_CNT == 1MHz,那么周期t = 1/f = 1us,也就是说CNT计数器每计数一次就是1us。

        这时,如果自动重装载寄存器设置为999,那么每1000us即1ms产生一次更新中断,如此实现定时的功能。为什么是999重装值而不是1000呢, 假如我需要4us产生一次中断,那我设置重装寄存器的值为4, 计数器从0-1计数1次, 从1-2计数2次, 从2-3计数3次,从3-4计数4次, 从4-0计数5次,为0产生更新中断,所以需要减1。

 

二 代码实现

2.1 驱动初始化

        只讲一些要点,不逐一进行分析。

        ①TIM_TimeBaseStructInit这个函数是为了对结构体初始化,因为TIM_TimeBaseInitStruct是一个局部变量,我们实现定时器1的定时功能并不会用到它的所有成员,而局部变量不赋初值其值是不确定的,所以需要进行初始化。

        ②SystemCoreClock / 1000000 - 1;使用这个作为预分频系数,假如系统时钟是36MHz,同样得到CK_CNT为1MHz。

        ③TIM_CounterMode_Up为向上计数模式,关于计数模式网上资料很多,可自行查阅。

static void timInit(uint32_t periodUs)
{
	/*使能定时器时钟*/
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_TIM1, ENABLE);
	/*定时器复位*/
	TIM_DeInit(TIM1);
	/*定时器配置*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_TimeBaseStructInit(&TIM_TimeBaseInitStruct);
	TIM_TimeBaseInitStruct.TIM_Prescaler = SystemCoreClock / 1000000 - 1;//输入给计数器的时钟频率为1Mhz,周期1us
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period = periodUs - 1;
	TIM_TimeBaseInit(TIM1, &TIM_TimeBaseInitStruct);
	TIM_ClearFlag(TIM1, TIM_FLAG_Update);
	/*使能定时器更新中断*/
	TIM_ITConfig(TIM1, TIM_IT_Update, ENABLE);
	/*NVIC配置*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//一个工程只分组一次
	
	NVIC_InitTypeDef  NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel = TIM1_UP_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 0;
	NVIC_Init(&NVIC_InitStruct);
	/*定时器使能*/
	TIM_Cmd(TIM1, ENABLE);
	TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
}

 2.2 中断服务函数

        唯一要注意的就是更换定时器后记得更换中断服务函数,进入中断后,不要忘记清除中断标志位。

/**
***********************************************************
* @brief	定时器1更新中断服务函数
* @param
* @return 
***********************************************************
*/
void TIM1_UP_IRQHandler(void)
{
	if ( TIM_GetITStatus(TIM1, TIM_IT_Update) )
	{
		/*实现功能*/
		TIM_ClearITPendingBit(TIM1, TIM_IT_Update);
	}
}

### 使用 STM32 定时器实现计时功能 为了使用 STM32定时器实现计时功能,可以按照以下方法进行配置: #### 配置定时器参数 首先,在 `SystemClock_Config` 函数之后调用定时器初始化函数。这里以 TIM2 为例说明如何设置定时器用于倒计时。 ```c void TIM2_Init(void) { __HAL_RCC_TIM2_CLK_ENABLE(); // 使能定时器时钟[^1] TIM_HandleTypeDef htim2; htim2.Instance = TIM2; // 设置定时器工作模式为向上计数模式 htim2.Init.CounterMode = TIM_COUNTERMODE_UP; // 设定预分频系数,假设系统时钟频率为72MHz,则此处设定为7199可得到大约1ms的时间间隔 htim2.Init.Prescaler = 7199; // 自动重装载值设为所需毫秒数减去1,例如要延时500ms则应填入499 htim2.Init.Period = 499; if (HAL_TIM_Base_Init(&htim2) != HAL_OK) { Error_Handler(); } // 开启更新中断 HAL_TIM_Base_Start_IT(&htim2); } ``` 此段代码完成了对 TIM2 的基本初始化操作,并开启了定时器的更新中断以便处理时间到达后的动作[^2]。 #### 中断服务程序定义 当指定时间内发生溢出事件时会触发相应的中断请求,因此还需要编写对应的中断服务子程序来进行响应并执行特定的任务逻辑。 ```c // 定义全局变量记录剩余次数 uint8_t countdown_times = 5; // 倒计时总次数 void TIM2_IRQHandler(void) { if (__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE)) { if(__HAL_TIM_GET_IT_SOURCE(&htim2,TIM_IT_UPDATE)){ __HAL_TIM_CLEAR_IT(&htim2, TIM_IT_UPDATE); // 执行一次倒计时结束的操作 printf("Countdown %d\n",countdown_times--); if(countdown_times==0){ // 关闭定时器 HAL_TIM_Base_Stop_IT(&htim2); // 可在此处加入其他完成倒计时后的处理语句... } } } } ``` 上述代码片段展示了如何通过检测到定时器更新事件的发生而减少一个单位的倒计数值直至达到零为止;一旦倒计时完毕即停止该定时器的工作流程[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值