STM32单片机学习分享三-TIM定时中断(仅为记录个人学习,有错误可以指出,谢谢)

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);
	}
}

成果展示

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值