stm32 定时器中断

在上一篇 STM32外部中断的理解 中,我们讲述了stm32的外部中断,它是通过外部的一个中断信号作为中断源,对 CPU 进行申请中断处理的;此篇,我们再讲述下 stm32 的内部定时器中断。

1. 综述

stm32 定时器中断,是通过设置内部的定时器相关寄存器,然后定时器进行自增(自减)到某一个数之后,产生一个中断信号,由 cpu 进行处理。如下图:

其中,时基单元部分相关的寄存器就是我们需要设置的。

下图是基本定时器框图:

2. 名词解析

CK_PSC : 定时器的预分频器时钟源。这个值为系统时钟频率,在手册中,虽然 TIM2 是属于 APB1 外设,APB1 外设的时钟频率为36MHz,但是我们使用的库中,在 SystemInit 函数中,将所有的定时器都倍频到了72MHz,所以这个值为72MHz。
PSC : 定时器的预分频器寄存器(Prescaler Register)。在 stm32 中,预分频器用于将定时器的时钟源分频,以降低定时器的时钟频率。
CK_CNT : 定时器计数器的时钟源。CK_CNT = CK_PSC / (PSC + 1)
CNT : 定时器的计数器寄存器。用于记录定时器的计数值,根据定时器的时钟源逐渐递增或递减,计数器增加达到最大值时会自动重置为0,并触发相应的中断事件。CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)

ARR: 自动重装载寄存器(Auto-Reload Register)

3. 源码解析

我们这个实例演示了,通过定时器每隔1秒产生一个中断,并在 OLED 屏幕上进行显示。要达到这个功能,主要的核心是设置好 PSCARR

源码:

复制代码
#include "Timer.h"

uint16_t num = 0;

void Timer_Init(void)
{
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	
	TIM_InternalClockConfig(TIM2);
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1;
	TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1;
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);
	
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStruct);
	
	TIM_Cmd(TIM2, ENABLE);
}

uint16_t Timer_Get(void)
{
	return num;
}

void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) {
		num ++;
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}
3.1 使能 TIM2 外设时钟
复制代码
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

TIM2 属于 APB1 外设。

3.2 配置时钟源
复制代码
TIM_InternalClockConfig(TIM2);

TIM2 作为内部时钟源,并且时钟频率是72MHz。若不调用此函数,TIM默认也为内部时钟

3.3 配置时基单元
复制代码
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;		// 时钟分频 不分频
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;	// 计数器模式 向上计数
	TIM_TimeBaseInitStruct.TIM_Period = 10000 - 1;					// 计数周期,即 ARR 的值
	TIM_TimeBaseInitStruct.TIM_Prescaler = 7200 - 1;				// 预分频器,即 PSC 的值
	TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;				// 重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStruct);

ARR: 10000 - 1, PSC:7200 - 1, 此时我们可以计算出计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)

CK_CNT_OV = 72MHz/(7200 - 1 + 1)/(10000 - 1 + 1) = 1 Hz

即:1秒产生1次溢出

预分频器确定好后,我们就可以通过设置 ARR 值,来产生不同时间的定时器。

3.4 开启TIM2的更新中断
复制代码
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);	
3.5 配置 NVIC
复制代码
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStruct);

具体的解释参考:STM32外部中断的理解

3.6 TIM 使能
复制代码
TIM_Cmd(TIM2, ENABLE);

使能TIM2,定时器开始运行

3.7 中断处理
复制代码
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2, TIM_IT_Update) == SET) {
		num ++;
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

至此,基本上整个定时器中断的流程就解析完成了。

引用:
https://blog.uwenya.cc/1320.html

相关推荐
南棱笑笑生41 分钟前
20250602在荣品的PRO-RK3566开发板的Android13下打开HDMI显示
单片机·嵌入式硬件
茯苓gao1 小时前
STM32G4 电机外设篇(四)DAC输出电流波形 + CAN通讯
stm32·单片机·嵌入式硬件
嵌入式之入坑笔记4 小时前
怎么快速判断一款MCU能否跑RTOS系统
单片机·嵌入式硬件
c7_ln4 小时前
USART 串口通信全解析:原理、结构与代码实战
stm32·单片机·usart
长流小哥5 小时前
STM32:CAN总线精髓:特性、电路、帧格式与波形分析详解
网络·stm32·单片机·嵌入式硬件·信息与通信
田甲5 小时前
RGB888色彩格式转RGB565格式
c语言·单片机·嵌入式硬件
悟空胆好小5 小时前
ST MCU CAN模块--TTCAN模式浅析
单片机·嵌入式硬件
真的想上岸啊10 小时前
学习STC51单片机25(芯片为STC89C52RCRC)
单片机·嵌入式硬件·学习
Invinciblenuonuo12 小时前
STM32八股【10】-----stm32启动流程
stm32·单片机·嵌入式硬件
hahaha601614 小时前
RK3588和FPGA桥片之间IO电平信号概率性不能通信原因
单片机·嵌入式硬件·fpga开发