TIM定时中断
简介
定时器中断(Timer Interrupt,简称TIM)是一种硬件中断,由定时器(Timer)在预设的时间间隔后产生。定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断。这种中断允许系统在特定的时间间隔后执行特定的任务,而无需持续监视时间或状态。
定时器类型

定时器的类型分为3种,分别为基本定时器、通用定时器和高级定时器。其中基本定时器最简单功能最少,高级定时器最复杂功能最多。它们接的总线也不一样,基本定时器和通用定时器接在APB1总线上,而高级定时器接在APB2总线上。
基本定时器

基本定时器的作用是计时和计数。其中预分频器PSC连接到内部时钟,对内部时钟的频率进行分频。计数器CNT根据分频后的频率进行计数,当计数值达到预先设置好的自动重装值后,产生更新事件或中断。
通用定时器

时钟源选择
通用定时器有更多时钟源可以选择:
1.内部时钟源(CK_INT)是最常用的时钟源,主频72MHZ.。
2.外部时钟模式1,TI1FP1/TI1F_ED/TI2FP2,最终来自定时器独立通道(TIMx_CH1~4)
3.外部时钟模式2,外部触发输入(ETR)
4.内部触发输入(ITRx),使用一个定时器作为另一个定时器的预分频器,可以实现定时器的级联功能。
计数模式
通用定时器有三种计数模式,分别是:向上计数、向下计数和中央对齐计数。
向上计数模式

在向上计数模式中,计数器从0计数到自动加载值(TIMx_ARR计数器的内容),然后重新从0开始计数并且产生一个计数器溢出事件。
如果使用了重复计数器功能,在向上计数达到设置的重复计数次数(TIMx_RCR)时,产生更新事件(UEV);否则每次计数器溢出时才产生更新事件
计数器的时钟频率CK_CNT等于CK_PSC /(PSC[15:0]+1)。PSC包含了当更新事件产生时装入当前预分频器寄存器的值。
内部时钟的分频因子越大,计数器计数的周期也就越长。
当目标值达到目标值后,计数器溢出的同时产生更新时间并更新中断标志位。
向下计数模式

在向下模式中,计数器从自动装入的值(TIMx_ARR计数器的值)开始向下计数到0,然后从自动装入的值重新开始并且产生一个计数器向下的溢出事件/
同理。计数器的时钟频率CK_CNT等于CK_PSC /(PSC[15:0]+1)。PSC包含了当更新事件产生时装入当前预分频器寄存器的值。
内部时钟的分频因子越大,计数器计数的周期也就越长。
中央对齐模式

在中央对齐模式,计数器从0开始计数到自动加载的值(TIMx_ARR寄存器)−1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器下溢事件;然后再从0开始重新计数。
预装时序
计数器无预装时序时

计数器正在计数时,更改了自动加载寄存器的值,在无预装时序的情况下,计数器的目标值就会立刻从FF改为36。
计数器有预装时序时

计数器正在计数时,更改了自动加载寄存器的值,在有预装时序的情况下,计数器将继续计数直到达到原目标值溢出后,才会更改计数器的目标值。在自动加载影子寄存器中能观察到计数器的目标值变化。
项目制作
本次项目要使用TIM定时中断来计数,并在OLED屏上显示出来。
硬件介绍

OLED,即有机发光二极管( Organic Light Emitting Diode )。 OLED 由于同时具备自发光,不需背光源、对比度高、厚度薄、视角广、反应速度快、可用于挠曲性面板、使用温度范围广、构造及
制程较简单等优异之特性,被认为是下一代的平面显示器新兴应用技术。
该屏幕有以下特点:
1)0.96 寸 OLED 有黄蓝,白,蓝三种颜色可选;其中黄蓝是屏上 1/4 部分为黄光,下 3/4 为蓝;而且是固定区域显示固定颜色,颜色和显示区域均不能修改;白光则为纯白,也就是黑底白字;
蓝色则为纯蓝,也就是黑底蓝字。
2)分辨率为 128*64
3)多种接口方式;OLED 裸屏总共种接口包括:6800、8080 两种并行接口方式、3 线或 4 线的串行 SPI 接口方式、 IIC 接口方式(只需要 2 根线就可以控制 OLED 了!),这五种接口是通过屏上的 BS0~BS2 来配置的。
代码详解
配置Timer函数
根据定时中断基本结构图,使用RCC内部时钟配置Timer函数。

首先开启时钟,由于TIM2是接在APB1总线上的,故开启APB1时钟。(对应着“定时中断基本结构图”中的步骤1)
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
选择TIM2为内部时钟模式,这个代码可不写,因为不写的话TIM2默认为内部时钟模式。(对应着“定时中断基本结构图”中的步骤2)
TIM_InternalClockConfig(TIM2);
接着配置时基单元(对应着“定时中断基本结构图”中的步骤3)
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; //定义结构体变量
TIM_TimeBaseInitStructure.TIM_ClockDivision= TIM_CKD_DIV2; //用来配置滤波器时钟,不会影响到时基单元,根据它所给的参数随便填一个就好。
TIM_TimeBaseInitStructure.TIM_CounterMode= TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInitStructure.TIM_Period= 10000-1; //根据CK_CNT_OV=CK_PSC/(PSC+1)/(ARR+1)公式,将ARR配置为10000-1,使定时器每1s更新一次中断
TIM_TimeBaseInitStructure.TIM_Prescaler= 7200-1; //根据CK_CNT_OV=CK_PSC/(PSC+1)/(ARR+1)公式,将PSC配置为7200-1,使定时器每1s更新一次中断
TIM_TimeBaseInitStructure.TIM_RepetitionCounter= 0; //重复计数器在通用定时器用不到,高级定时器才会用到
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure); //将结构体变量交给TIM_TimeBaseInit
清除定时器更新标志位
TIM_ClearFlag(TIM2,TIM_FLAG_Update); //在TIM_TimeBaseInit函数末尾会产生更新事件,需要清除更新标志位,否则开启中断后就会立刻产生一个更新事件,也就是从1s开始计时。
使能TIM2中断(对应着“定时中断基本结构图”中的步骤4)
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
配置NVIC 优先级分组(对应着“定时中断基本结构图”中的步骤5)
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //配置其抢占优先级和相应优先级,对应着“定时中断基本结构图”中的步骤5
配置NVIC(对应着“定时中断基本结构图”中的步骤5)
NVIC_InitTypeDef NVIC_InitStructure; //定义结构体变量
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; //选择NVIC中的TIM2线路
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //使能NVIC中的TIM2线路
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2; //配置TIM2线路的抢占优先级为2
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; //配置TIM2线路的响应优先级为2
NVIC_Init(&NVIC_InitStructure);
TIM使能(对应着“定时中断基本结构图”中的步骤6)
TIM_Cmd(TIM2, ENABLE);
配置中断函数
void TIM2_IRQHandler()
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update)==SET) //判断TIM2是否触发中断
{
Sec++; //每隔1s进一次中断,Sec就会+1
if(Sec==60){Sec=0;Min++;}; //当Sec等于60s时,Min就会+1,Sec清零重新开始计数
TIM_ClearITPendingBit(TIM2, TIM_IT_Update); //清除中断标志位
}
}
在主函数中定义的"Sec"和"Min"两个变量,要在Timer函数中的顶部进行声明,否则无法跨文件使用变量
extern uint16_t Sec;
extern uint16_t Min;
完整Timer函数如下:
#include "stm32f10x.h" // Device header
extern uint16_t Sec;
extern uint16_t Min;
void Timer_Init(void)
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
TIM_InternalClockConfig(TIM2);
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision= TIM_CKD_DIV2;
TIM_TimeBaseInitStructure.TIM_CounterMode= TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period= 10000-1;
TIM_TimeBaseInitStructure.TIM_Prescaler= 7200-1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter= 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
TIM_ClearFlag(TIM2,TIM_FLAG_Update);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM2, ENABLE);
}
void TIM2_IRQHandler()
{
if(TIM_GetITStatus(TIM2, TIM_IT_Update)==SET)
{
Sec++;
if(Sec==60){Sec=0;Min++;};
TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
}
}
主函数
#include "stm32f10x.h" // Device header
#include "Delay.h"
#include "OLED.h"
#include "Timer.h"
uint16_t Sec;
uint16_t Min;
int main(void)
{
OLED_Init();
Timer_Init();
OLED_ShowString(1, 1, "Sec:");
OLED_ShowString(2, 1, "Min:");
while(1)
{
OLED_ShowNum(1, 5, Sec,5);
OLED_ShowNum(2, 5, Min,5);
}
}
成果展示

1959

被折叠的 条评论
为什么被折叠?



