记住一句话:
定时器就是定时器,作用就是做了一个钟表,时间到了就中断,类似于闹钟到时间就响,这里是到了时间就触发中断
大家一定要带着这句话去理解定时器中断,大方向不要偏,理解自然神。
一、定时器中断原理解释
定时器中断:最简单,最直接的解释
区别于外部中断是电平触发中断
定时器中断,举例来说,一个可以无限计时的设备,设置了计时的方式,还有计时的时间,然后每次到了一个固定的时间,就会执行提前写好的事件。
例如下面这个函数是定时器2定时中断:时间到了就会触发Num变量自增,LED1灯打开
/**
* 函 数:TIM2中断函数
* 参 数:无
* 返 回 值:无
* 注意事项:此函数为中断函数,无需调用,中断触发后自动执行
* 函数名为预留的指定名称,可以从启动文件复制
* 请确保函数名正确,不能有任何差异,否则中断函数将不能进入
*/
void TIM2_IRQHandler(void)
{
if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) //判断是否是TIM2的更新事件触发的中断
{
Num ++; //Num变量自增,用于测试定时中断
LED1_ON();
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除TIM2更新事件的中断标志位
//中断标志位必须清除
//否则中断将连续不断地触发,导致主程序卡死
}
}
二、中断时间的计算
根据系统时钟频率、计数周期、预分频器计算出最终的中断时间
三、计数器的三种方式
1. 向上计数模式 (Up Counting Mode)
工作原理:
- 计数器从0开始递增计数,直到达到自动重载寄存器(ARR)的值。
- 当计数器达到ARR值时,产生一个更新事件,并且计数器重新从0开始计数。
2. 向下计数模式 (Down Counting Mode)
工作原理:
- 计数器从自动重载寄存器(ARR)的值开始递减计数,直到达到0。
- 当计数器达到0时,产生一个更新事件,并且计数器重新从ARR值开始计数。
3. 中心对齐模式 (Center-Aligned Mode)
工作原理:
- 计数器先从0开始递增计数,直到达到自动重载寄存器(ARR)的一半(即ARR/2),然后开始递减计数,直到回到0。
- 当计数器达到ARR/2时,产生一个更新事件;当计数器回到0时,再产生一个更新事件。
- 这种模式实际上是双向计数,因此也称为双边沿计数。
四、时钟源配置
1. 内部时钟源
定时器默认的都是内部时钟源
- APB1总线上的定时器(如TIM2, TIM3, TIM4, TIM5, TIM6, TIM7, TIM12, TIM13, TIM14):
-
- 默认时钟源是PCLK1(APB1总线时钟)。
- 如果APB1预分频器设置为1,则PCLK1等于系统时钟(HCLK)。
- 如果APB1预分频器设置为其他值(如2或4),则PCLK1会相应地降低。
- APB2总线上的定时器(如TIM1, TIM8, TIM9, TIM10, TIM11):
-
- 默认时钟源是PCLK2(APB2总线时钟)。
- 如果APB2预分频器设置为1,则PCLK2等于系统时钟(HCLK)。
- 如果APB2预分频器设置为其他值(如2或4),则PCLK2会相应地降低。
2. 外部时钟源
虽然默认情况下定时器使用内部时钟源,但也可以配置为使用外部时钟源。外部时钟源可以来自以下几个途径:
- ETR引脚:外部触发输入引脚(External Trigger Input),可以直接作为定时器的时钟源。
- TIx引脚:定时器的通道输入引脚(例如TI1, TI2, TI3, TI4),可以通过配置用作外部时钟源。
- ITRx引脚:内部触发输入(Internal Trigger Input),可以是其他定时器的输出或其他内部信号。
3.配置外部时钟源
要配置定时器使用外部时钟源,可以使用以下函数之一:
TIM_ETRClockMode1Config
:配置外部触发模式1。TIM_ETRClockMode2Config
:配置外部触发模式2。
/*配置时钟源*/
TIM_InternalClockConfig(TIM2); //选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
五、定时器中断初始化
timer.c
#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);
}
}
*/
timer.h
#ifndef __TIMER_H
#define __TIMER_H
void Timer_Init(void);
#endif