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

相关推荐
不悔哥1 天前
开源OV-Watch:怎么做一个智能手表
单片机·开源·嵌入式
XiHongShi20161 天前
STM32F407 RTC定时器例程,建议保存
stm32·单片机·学习
优信电子1 天前
AIT1001 非接触式红外测温模块 STM32 驱动开发实战:从引脚到代码全解析
stm32·嵌入式·温度传感器·红外测温·非接触式测温·ait1001
深圳老胡2 天前
STM32F407 控制 L6470 步进电机驱动 —— 控制过程简介
笔记·stm32·单片机·嵌入式硬件·代码规范
wuyk5552 天前
《WiFi 嵌入式物联网开发全套实战》| 第 16 章 ESP32 AP+STA 双模共存原理与工程坑点
网络·stm32·物联网
女神下凡2 天前
嵌入式设计的各种存储芯片的硬件/软件设计规范,非常全面。
arm开发·单片机·嵌入式硬件
梅梅9662 天前
一款mipi转lvds的lcd调试(LT8912B)
单片机·计算机外设·电脑·显示器·pd芯片
开发笔记-阿牛2 天前
蓝牙音乐灯拆解,又一次拆到蓝牙音乐灯芯片CK6865L
人工智能·单片机·嵌入式硬件·音视频·音频
从零开始的嵌入式之旅2 天前
day47
arm开发·经验分享·笔记·嵌入式硬件
恒锐丰科技林技术员2 天前
EG1164 高压大电流升压同步整流芯片解析
经验分享·嵌入式硬件·硬件工程