STM32中TIM定时中断功能深度解析
目录
1.引入
1.1 简介
TIM(Timer)定时器
定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时
不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
所谓定时器,就是闹钟,时间到后你就要做某些事。有 2 个要素:时间、做事,换成程序员的话就是:超时时间、函数。
比如以按键为例,使用定时器可以解决抖动现象,未超时就发生中断,就重新计时,当抖动停止了,超过定时的时间,就可以取调用中断函数去处理稳定后的按键中断事件。(注:上图的提到的函数是linux开发板的,但是原理其实是差不多的)
1.2 类型
类型 | 编号 | 总线 | 功能 |
---|---|---|---|
高级定时器 | TIM1、TIM8 | APB2 | 拥有通用定时器全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能 |
通用定时器 | TIM2、TIM3、TIM4、TIM5 | APB1 | 拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能 |
基本定时器 | TIM6、TIM7 | APB1 | 拥有定时中断、主模式触发DAC的功能 |
STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4
1.2.1 基本定时器
预分频器:
CNT计数器:
自动重装载寄存器:
三者构成了时基单元。
同时还提到了主模式触发DAC的功能,主从触发模式是stm32的一大特色,它能让内部的硬件在不受程序的控制下实现自动运行。
在框图中有个部分写着至DAC,在我们使用DAC的时候,可能会用DAC(数字模拟信号转换器)输出一段波形,那么就需要每隔一段时间来触发一次DAC,让它输出下一个电压点。
知道了定时器后我们可能会想到先设置一个定时器,每隔一段时间产生中断,在中断函数中去让DAC进行转换、输出。虽然这样没问题,但是会造成CPU负担,因为需要中断的次数不少,主程序也老是处于被频段中断的状态。这时候就可以使用主模式触发DAC
也就是将更新事件(U)映射到TRGC(Trogger Out)的位置,然后接到DAC转换器上,这样就可以采用事件触发的方式去通知DAC进行转换。
其实就是和Linux开发板中的MSI或MSI-X的消息中断方式是有点相似的,不是采用传统的INTx引脚中断触发方式,而是event消息来触发中断。虽然stm32这种不算是中断,但其实也是用event事件来让某些设备开始工作。
1.2.2 通用定时器
拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能
中间的还是时基单元,和基本定时器是一样的功能。不过对于CNT计数器,在这里不是只有向上计数的模式了(自增),在这里还支持:
1. 触发控制单元 (Trigger Control Unit)
这个模块用于生成和选择不同的触发信号,包括:
外部触发输入 (ETR):可以从外部引脚获取触发信号,通过 ETRP 控制输入极性,经过输入滤波器后,得到触发信号 ETRF
。
TRGO 输出信号:将触发信号输出至 DAC 或 ADC,进行协调同步。
从模式控制:允许定时器在主从模式下工作,通过从模式寄存器配置触发信号(例如复位、使能、向上/向下计数等)。
对于TRGI,获得时钟,其中除了TRC的ITR使用级联定时器的方式还有通过TIIF_ED,也就是通过CH1引脚获得时钟,这种方式获得时钟上升沿和下降沿都有效。除此之外TRGI除了TRC,还能通过TI1FP1和TI2FP2获得,分别连接到IC1和IC2。
其实主要就是时钟的来源,除了基本定时器的来自系统RCC的内部时钟,还能有其它方式:
2. 输入捕获单元 (Input Capture Unit)
通用定时器支持四路输入捕获通道(TI1、TI2、TI3、TI4),用于捕获外部输入信号的事件,主要用于频率测量、信号周期或占空比测量等。
每个输入捕获通道具有以下功能组件:
3. 输出比较单元 (Output Compare Unit)
通用定时器的输出比较单元支持四个输出通道(OC1、OC2、OC3、OC4),用于比较 CNT 计数器和预设的比较值 (CCRx),主要用于产生 PWM 信号、输出定时事件等。
输出比较单元通过比较 CNT 计数器和指定的比较值 (CCRx) 来控制输出行为,支持多种模式:
4. CNT 计数器
这是整个定时器的核心部件,负责记录时钟的计数值。计数器有多种计数模式:
5. 自动重装载寄存器 (ARR)
6. 预分频器 (PSC)
7. 中断与 DMA 事件
8. 刹车功能 (Break Input, BRK)
BRK
引脚直接触发,用于紧急停止输出。常用于电机控制场景,检测到过流或故障时可以迅速停机,保护电路和设备。1.2.3 高级定时器
高级定时器在功能上相较于通用定时器更为复杂,主要用于电机控制等要求较高的场景。以下是高级定时器的详细结构功能解析:
1. 触发控制单元 (Trigger Control Unit)
2. 时基单元 (Counter and ARR)
3. 输入捕获单元 (Input Capture Unit)
4. 输出比较单元 (Output Compare Unit)
高级定时器支持四路输出比较单元,用于 PWM 信号生成和定时输出。
每个输出比较通道配有比较寄存器 (CCRx) 和比较模式配置,可以生成多种 PWM 信号:
输出比较单元还配备死区控制器 (DTG),用于在双向控制场景下添加死区时间,以避免上下桥驱动器的短路。
5. 刹车输入单元 (Break Input)
6. 死区时间控制单元 (Dead-Time Generator, DTG)
7. 主从模式控制 (Master-Slave Mode Control)
8. 事件生成与中断
9.重复计数器 (Repetition Counter, REP)
相比通用定时器,主要就是多了死区时间控制单元 (Dead-Time Generator, DTG)、刹车输入单元 (Break Input)、重复计数器 (Repetition Counter, REP)。
1.3 定时中断基本结构图
看了上面的介绍下后再来看这个基本结构体,其实一目了然了。就是对于通用定时器来说,时钟可以有多种来源,
1.4 时序
1.4.1 PSC预分频器
计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)
CK_PSC:输入的未分频的计数时钟。
CNT_EN:计时器使能,高电平正常运行,低电平停止运行。
CK_CNT:分频后的计数时钟,图中以红虚线可以看出分为了两个频率,前半和未分频的时钟频率一样,也就是PSC=1;后半段则产生的分频,PSC是2,CK_CNT = CK_PSC / (2 + 1) = CK_PSC / 3
计数器寄存器:顾名思义,进行计数的,当计数达到目标值就产生中断。计数的频率和分频后的时钟频率是正比的。可以看出红线左边的自动重装值就是FC,计数达到后清0,FC –> 00
更新事件(UEV):计时器计数达到重装值了,同时下一个时钟来了,分频后进入到CNT计数器,产生一个更新事件(图中唯一一个高电平),开始新的计时。
预分频控制寄存器( PSC Register) ):供我们读写用的。比如将0改成了1,那么在时钟周期结束后,预分配缓冲器也会从0–>1,此时预分频率计数器就会按照0、1、0、1来输出,一个0、1就对应到一个分频器输出的CK_CNT,也就是对应到一个计数。到1 CK_CNT就输出高电平。
预分频缓冲器:真正起作用的寄存器,如果没有这个寄存器,我们在某个时间内对预分频控制寄存器寄存器进行写入,把0改成1,此时还在进行着一个周期的时钟,这就会导致该周期的时钟的前半部分和后半部分频率不一样了,这样是不行的。因此就有了预分频缓冲器,即使我们对某一个正在进行的时钟周期内对预分频控制寄存器寄存器改变了值,也会等到该周期结束了,才对预分频缓冲器的值进行更改,产生新的事件,开始新的时钟周期。
1.4.2 计时器时序
计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)
1.4.3 计数器无预装时序
就是自动重装载寄存器是否启用了缓冲寄存器,其实和PSC预分频器的缓冲器道理是一样的。
对于有阴影部分的器件,其都是有缓冲寄存器的功能的。
对于没有启用缓冲器的重装载寄存器,在某一个时钟周期内(红虚线左边),对目标计数进行了更新,也就是原本是FF改为了36,那么原来该时钟周期的计数目标是到FF才产生申请中断,到由于更改了,计数到36就申请中断,计数清0
这和PSC是一样的,如果不开启缓冲器,那么就会导致在某一个周期内产生新的分频,造成时钟的计数频率不一样。
1.4.4 计数器有预装时序
这种在某一个周期内更改了目标计数,只会在下一个时钟周期生效,不会影响到当前的周期
1.5 RCC时钟树
红色左边的都是时钟的产生电路,右边的则是时钟的分配电路。中间的SYSCLK就是系统的时钟72MHz,不过时钟一开始输出的并不是72MHz,在时钟产生电路中,是有4个振荡源,从上往下分别是:
对于前两个是给系统提供戏中的,AHB、APB1、APB2总线的时钟都是由其提供的。一个内部一个外部,都是可以使用的,1只不过外部的石英晶体振荡器会比内部的RC振荡器更稳定。
对于系统内部的时钟,是有提供的init函数来进行初始化的,其思路是这样的,先启用的第一个8 MHz HSI RC作为系统时钟,之后再去配置第二个外部4-16 MHz HSE OSC,等其经过PLLMUL锁相环进行倍频,8MHz倍频9倍,也就是得到稳定的72MHz后,再启用其作为系统的时钟,原本的第一个时钟关闭,实现了从8MHz切换到了72MHz。
所以如果在某些时刻,明明你定的时钟是1s,结果大约10s后才发生中断,那么就有可能此时使用的还是第一个内部的8MHz的高速RC振荡器作为系统的时钟,第二个时钟的晶振可能出现问题了。
2.基本使用
2.1 步骤
按照这个结构体去进行配置定时器:
- RCC开启时钟,在模块程序编写的时候都是基本会执行的。打开后定时器的基准时钟和整个外设时钟都会打开
- 选择时基单元的时钟源,这里我们选择的是RCC内部时钟,因此配置为内部时钟模式
- 配置时基单元:PSC预分频器、CNT计数器、ARP自动重装器,这一部分用结构体配置就好了
- 配置中断输出控制,允许更新中断输出到NVCI
- 配置NVIC,在NVIC中打开定时器中断的通道,并配置一个优先级
- 运行控制配置,也就是使能定时器
- 定义定时器中断函数
2.2 相关函数
按照配置步骤,去调用相关的库函数,有哪些库函数可以去查看stm32f10x_tim.h文件
库函数手册中也已经详细的讲解了。下面根据配置步骤,给出所需要的相关函数:
- RCC开启时钟,在模块程序编写的时候都是基本会执行的。打开后定时器的基准时钟和整个外设时钟都会打开
- 选择时基单元的时钟源,这里我们选择的是RCC内部时钟,因此配置为内部时钟模式
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
将定时器的时钟源配置为内部时钟,即系统时钟(比如72 MHz)。
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
配置定时器 TIMx 的外部时钟源为某个内部触发源(ITRx)。
TIM_InputTriggerSource 用于指定触发源(如TRGI信号)。
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource, uint16_t TIM_ICPolarity, uint16_t ICFilter);
配置定时器 TIMx 的外部时钟为 TIx 引脚的输入信号。
参数包括输入通道、极性(上升沿或下降沿触发)以及输入滤波器配置。
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
配置定时器 TIMx 的外部时钟模式1,使用 ETR(外部触发)作为时钟源。
可以设置 ETR 的预分频、极性和滤波器。
oid TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
配置定时器 TIMx 的外部时钟模式2,使用 ETR(外部触发)作为时钟源。
与模式1类似,但具体行为略有不同,通常用于更复杂的同步需求。
- 配置时基单元:PSC预分频器、CNT计数器、ARP自动重装器,这一部分用结构体配置就好了
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
初始化定时器的基本时间基配置。
TIM_TimeBaseInitStruct 是一个结构体,包含定时器的预分频系数、计数模式、计数器周期、时钟分频、重复计数等参数。、
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
将 TIM_TimeBaseInitTypeDef 结构体初始化为默认值。
------------------------------------------------------------------------------
//下面是和时基单元另外相关的函数,基础知识在上面对定时器的介绍的时候也讲过:
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
配置定时器的预分频器值 Prescaler。
TIM_PSCReloadMode 指定是否立即加载新的分频值(一般为 TIM_PSCReloadMode_Immediate)。
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
设置定时器的计数器值,即直接对 CNT 寄存器赋值。
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
配置自动重装载寄存器(ARR)的预装载功能,控制 ARR 值的更新方式。
若启用预装载,ARR 值将在下一个更新事件时加载到计数器。
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
设置定时器的计数器值,即直接对 CNT 寄存器赋值。
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);
设置自动重装载值(ARR),用于控制计数周期的长度。
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
获取当前计数器的值(CNT寄存器的值)。
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);
获取当前的预分频器值(PSC寄存器的值)。
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
获取定时器的特定标志状态(如更新标志、触发标志等)。返回标志的状态是设置还是清除。
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
清除指定的定时器标志,将对应的标志位复位。
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
检查指定的定时器中断是否发生,返回中断状态。
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
清除指定的定时器中断挂起位,即重置中断标志,使得该中断能够再次触发。
- 配置中断输出控制,允许更新中断输出到NVCI
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
配置定时器的中断功能,使能或禁用指定的中断。
该函数用于控制定时器的特定中断源的使能状态。当 NewState 为 ENABLE 时,开启指定的中断源;当 NewState 为 DISABLE 时,关闭指定的中断源。
- 配置NVIC,在NVIC中打开定时器中断的通道,并配置一个优先级。 — 这一部分的函数在之前讲解NVIC的基本一致。
- 运行控制配置,也就是使能定时器
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
用于开启或关闭指定的定时器。当 NewState 为 ENABLE 时,定时器开始运行;当 NewState 为 DISABLE 时,定时器停止运行。
- 定义定时器中断函数
2.3 结构体
以下是结构体 TIM_TimeBaseInitTypeDef
的成员变量的中文注释,并附上详细的解释:
typedef struct
{
uint16_t TIM_Prescaler; /*!< 指定用于分频定时器时钟的预分频器值。
此参数可以是 0x0000 到 0xFFFF 之间的一个数值 */
uint16_t TIM_CounterMode; /*!< 指定计数器模式。
此参数可以是 @ref TIM_Counter_Mode 的一个值 */
uint16_t TIM_Period; /*!< 指定将在下一个更新事件时装载到活动自动重装载寄存器中的周期值。
此参数必须是 0x0000 到 0xFFFF 之间的一个数值 */
uint16_t TIM_ClockDivision; /*!< 指定时钟分频系数。
此参数可以是 @ref TIM_Clock_Division_CKD 的一个值 */
uint8_t TIM_RepetitionCounter; /*!< 指定重复计数器的值。每当 RCR 递减计数器达到零时,生成一个更新事件并从 RCR 值 (N) 重新开始计数。
在 PWM 模式下,这意味着 (N+1) 对应于:
- 边沿对齐模式下的 PWM 周期数
- 中心对齐模式下的半个 PWM 周期数
此参数必须是 0x00 到 0xFF 之间的一个数值。
@note 此参数仅对 TIM1 和 TIM8 有效 */
} TIM_TimeBaseInitTypeDef;
TIM_Prescaler
(预分频器):
CK_CNT = CK_PSC / (TIM_Prescaler + 1)
,其中 CK_PSC
为输入的时钟频率。预分频值越大,CK_CNT
的频率就越低。通过控制预分频器,可以更灵活地控制定时器的计数速度。TIM_CounterMode
(计数器模式):
TIM_Counter_Mode
的枚举值,常见的值包括:TIM_CounterMode_Up
TIM_CounterMode_Down
TIM_CounterMode_CenterAligned1、TIM_CounterMode_CenterAligned2、TIM_CounterMode_CenterAligned3
TIM_Period
(周期):
TIM_Period
的值后会产生更新事件(UEV),然后计数器会重置为 0。周期值的大小决定了计数器溢出所需的时间,因此可以通过调整周期值来设置计时周期。TIM_ClockDivision
(时钟分频):
TIM_Clock_Division_CKD
的枚举值,通常包括以下选项:0
):TIM_CKD_DIV1
1
):TIM_CKD_DIV2
2
):TIM_CKD_DIV4
TIM_RepetitionCounter
(重复计数器):
2.4 编写
2.4.1 定时器内部中断
User:
System:
Hardware:
#include "stm32f10x.h" // Device header
/**
* 函 数:定时中断初始化
*/
void Timer_Init(void)
{
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟
/*配置时钟源*/
TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
/*时基单元初始化*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1; //计数周期,即ARR的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1; //预分频器,即PSC的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
/*中断输出配置*/
TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定时器更新标志位
//TIM_TimeBaseInit函数末尾,手动产生了更新事件
//若不清除此标志位,则开启中断后,会立刻进入一次中断
//如果不介意此问题,则不清除此标志位也可
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //开启TIM2的更新中断
/*NVIC中断分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2
//即抢占优先级范围:0~3,响应优先级范围:0~3
//此分组配置在整个工程中仅需调用一次
//若有多个中断,可以把此代码放在main函数内,while循环之前
//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
/*NVIC配置*/
NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //选择配置NVIC的TIM2线
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //指定NVIC线路的抢占优先级为2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1
NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设
/*TIM使能*/
TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行
}
/* 定时器中断函数,可以复制到使用它的地方
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
*/
对72MHz(72000000Hz)进行7200的分频,就是10KHz的频率,也就是1/10K s记一次数,那么计数10000次不就是刚好1s,所以这个时钟中断是每1s申请一次中断。
2.4.2 定时器外部中断
User:
Hardware:
System:
#include "stm32f10x.h" // Device header
/**
* 函 数:定时中断初始化
* 参 数:无
* 返 回 值:无
* 注意事项:此函数配置为外部时钟,定时器相当于计数器
*/
void Timer_Init(void)
{
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //开启TIM2的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //开启GPIOA的时钟
/*GPIO初始化*/
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure); //将PA0引脚初始化为上拉输入
/*外部时钟配置*/
TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);
//选择外部时钟模式2,时钟从TIM_ETR引脚输入
//注意TIM2的ETR引脚固定为PA0,无法随意更改
//最后一个滤波器参数加到最大0x0F,可滤除时钟信号抖动
/*时基单元初始化*/
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; //时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; //计数器模式,选择向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10 - 1; //计数周期,即ARR的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1; //预分频器,即PSC的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0; //重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元
/*中断输出配置*/
TIM_ClearFlag(TIM2, TIM_FLAG_Update); //清除定时器更新标志位
//TIM_TimeBaseInit函数末尾,手动产生了更新事件
//若不清除此标志位,则开启中断后,会立刻进入一次中断
//如果不介意此问题,则不清除此标志位也可
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE); //开启TIM2的更新中断
/*NVIC中断分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置NVIC为分组2
//即抢占优先级范围:0~3,响应优先级范围:0~3
//此分组配置在整个工程中仅需调用一次
//若有多个中断,可以把此代码放在main函数内,while循环之前
//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
/*NVIC配置*/
NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //选择配置NVIC的TIM2线
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //指定NVIC线路使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //指定NVIC线路的抢占优先级为2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //指定NVIC线路的响应优先级为1
NVIC_Init(&NVIC_InitStructure); //将结构体变量交给NVIC_Init,配置NVIC外设
/*TIM使能*/
TIM_Cmd(TIM2, ENABLE); //使能TIM2,定时器开始运行
}
/**
* 函 数:返回定时器CNT的值
* 参 数:无
* 返 回 值:定时器CNT的值,范围:0~65535
*/
uint16_t Timer_GetCounter(void)
{
return TIM_GetCounter(TIM2); //返回定时器TIM2的CNT
}
/* 定时器中断函数,可以复制到使用它的地方
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
{
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
*/
作者:憧憬一下