【STM32】定时器介绍&定时中断

使用的单片机机型为STM32F103C8T6

文章目录

定时器

TIM(Timer) 定时器

可以对输入的时钟进行计数,并在计数值达到设定值时触发中断

不仅具备基本的定时中断功能,还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能

根据复杂度和应用场景分为高级定时器、通用定时器、基本定时器三种类型

本STM32机型的定时器资源:TIM1,TIM2,TIM3,TIM4,即一个高级定时器,三个通用定时器

基本定时器

基本定时器只有 内部时钟(系统时钟) 这一时钟源

时钟信号先到达 触发控制器,控制选择如下二者功能之一:

  1. TRGO 为主模式触发 DAC ,用于输出模拟信号
  2. 时基单元:由预分频器,CNT计数器,自动重装寄存器组成。用于计时,计数,触发定时中断
  • 预分频器:用于对时钟源进行分频。如时钟频率为 72MHz,预分频器值为72,则输出到 CNT计数器的频率为72/72=1MHz。
  • CNT计数器:随时钟频率计数加一。如频率为1MHz,则每1us加一
  • 自动重装寄存器:类似阈值,当 CNT计数等于自动重装值时,触发定时器中断

通用定时器

通用定时器相比基础定时器看上去就复杂很多,主要多了如下功能:

  • 内外时钟源选择:内部时钟、外部输入
  • 输入捕获:测PWM,占空比
  • 输出比较:输出PWM。可用于控制舵机,直流电机
  • 编码器:测量编码器速度,方向
  • 主从触发模式功能:定时器联调

高级定时器

后续补充。。。。。。。。。。。。。。。。。

时钟源

时钟树

STM32的时钟树由多个时钟源和时钟分频组成,为STM32芯片提供各种时钟信号

STM32的基础时钟源有4个振荡器时钟:HSI(高速内部时钟)、HSE(高速外部时钟)、LSI(低速内部时钟)、LSE(低速外部时钟)。这是最底层的时钟来源

其中

  • 40kHz低速内部 LSI 振荡器,可用于驱动独立看门狗,或通过程序选择驱动 RTC(Real-Time Clock 实时时钟),用于从停机/待机模式下自动唤醒系统
  • 32.768kHz低速外部 LSE 振荡器也可驱动 RTC

我们常说的系统时钟,为SYSCLK,由三种不同的时钟源驱动:HSI、HSE、PLL

此机型的主频为72MHz,但 HSE 最高频率为16MHz,HSI 频率为 8MHz,都达不到主频。那系统主频是怎么来的呢?

其实就是将 HSI 或 HSE 接入 PLL,通过 PLL 倍频产生的

系统时钟通过选择器选择时钟源,输入源有 HSI(内部高速时钟)、HSE(外部高速时钟)、PLL锁相环

通常STM32启动时,先使用 HSI 内部时钟,同时 HSI 接入 PLL锁相环进行倍增,待倍增后的 PPLCLK 稳定后,主频再切换至 72MHz


外设时钟

输出到定时器的频率都为72MHz

APB1预分频虽然会降低频率,但通向定时器2-7,还会进行倍增,又会恢复至72MHz

APB2同理

定时中断

下图为通用定时器的定时中断结构

分为三个部分:时钟选择,时基单元,中断控制

时钟选择

计数器时钟可由下列时钟源提供:

  • RCC内部时钟,频率为72MHz
  • 外部时钟模式2:GPIO控制的外部输入触发 ETR
  • 外部时钟模式1:外部输入脚(TIx),用于输入捕获
  • 内部触发输入(ITRx):使用另一个定时器作为另一个定时器的预分频器,如可以配置一个定时器 Timer1 作为另一个定时器 Timer2 的预分频器,实现定时器联调

时基单元

预分频器,CNT计数器,自动重装寄存器组成

预分频器PSC

预分频器可以将计数器的时钟频率按1到65536之间的任意值进行分频。如系统频率为72MHz,预分频为2,则计数器接收的频率为72/2=36MHz

如此图示代表有影子寄存器,作用同缓冲器,可以在工作时被修改,新的预分频器参数在下一次更新事件到来时被采用,如下图

原先,预分频值为0,即不分频,计数器时钟等于系统时钟。但在计数器工作时,修改了预分频值,如果没有影子寄存器,那么新的分频就会立即应用,导致前后计数频率不同。使用影子寄存器,会先保存新的预分频值,在发生一次更新事件后才应用新的预分频值

  • 计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)

自动重装寄存器ARR

同样有影子寄存器,当 CNT计数器值等于 ARR 时,触发定时器中断

  • 计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)

如预分频PSC = 7200 - 1,代表计时器频率为72MHz/7200=1KHz,每1ms计时器加一

ARR = 1000 - 1,即1000次计数器加一才会溢出,即1000个1ms

最终效果就为计数器每1s溢出一次,频率为1Hz

CNT计数器

计数器有三种模式

  • 向上计数:计数器值从0计数加一到ARR,产生中断

  • 向下计数:计数器值从ARR计数减一到0,产生中断

  • 中央对齐:计数器从0计数加一到 ARR,产生中断;然后再计数减一到0,产生中断

计数器时序

编程实例

定时器内部时钟

  • 程序目标:实现计时功能,单位为s
  • 程序原理:使用定时器2,时钟源选择内部时钟,配置时基单元,实现每1s产生一次定时器中断,在中断处理函数中对计数加一

接线图如下:

定时器配置流程参照下图:分为时钟选择,时基单元,中断控制。相关函数声明在stm32f10x_tim.h

时钟选择

使用TIM2,因为为外设,需要开启时钟

c 复制代码
//开启TIM2的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

选择内部时钟源

c 复制代码
//配置时钟源
//配置内部时钟源为TIM2,若不配置,则默认为内部时钟
TIM_InternalClockConfig(TIM2);

时基单元

配置 PSC 和 ARR,实现每1s产生一次中断的效果

采用向上计数

c 复制代码
//初始化时基单元
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;			//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;		//向上计数,从0到重装值
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;					//重装值 - 1,因为从0开始,ARR的值
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;					//预分频器,PSC的值。如配置为72,即系统频率72MHz / 72 = 1MHz
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;				//重复计数器,高级定时器才会用到
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

中断控制

c 复制代码
//中断部分
//时基单元初始化完成后会发起一次更新事件,需要清理一下,避免影响复位
TIM_ClearFlag(TIM2, TIM_FLAG_Update);

//开启TIM2的更新中断
TIM_ITConfig(TIM2, TIM_FLAG_Update, ENABLE);

//中断优先级组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);

//NVIC中断嵌套初始化
NVIC_InitTypeDef NVIC_InitStructure;
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;						//中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;						//使能开关
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;			//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;					//响应优先级
NVIC_Init(&NVIC_InitStructure);

//开启TIM2
TIM_Cmd(TIM2, ENABLE);

PS:在TIM_TimeBaseInit中,完成时基单元初始化时,会产生一次更新中断,需要手动清除,否则会出现程序复位为1,而不是0的情况

完整初始化代码如下:

c 复制代码
/**
  * @brief		定时器初始化
  * @parm		无
  * @retval		无
  */
void Timer_Init(void)
{
	//开启TIM2的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	//配置时钟源
	//配置内部时钟源为TIM2,若不配置,则默认为内部时钟
	TIM_InternalClockConfig(TIM2);
	
	//初始化时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;			//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;		//向上计数,从0到重装值
	TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;					//重装值 - 1,因为从0开始,ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;					//预分频器,PSC的值。如配置为72,即系统频率72MHz / 72 = 1MHz
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;				//重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	//中断部分
	//时基单元初始化完成后会发起一次更新事件,需要清理一下,避免影响复位
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	
	//开启TIM2的更新中断
	TIM_ITConfig(TIM2, TIM_FLAG_Update, ENABLE);
	
	//中断优先级组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//NVIC中断嵌套初始化
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;						//中断通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;						//使能开关
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;			//抢占优先级
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;					//响应优先级
	NVIC_Init(&NVIC_InitStructure);
	
	//开启TIM2
	TIM_Cmd(TIM2, ENABLE);
}

中断处理函数的声明在startup_stm32f10x_md.s

c 复制代码
//定时器2中断处理函数
void TIM2_IRQHandler(void)
{
	if(TIM_GetITStatus(TIM2, TIM_FLAG_Update) == SET)//判断是否是更新中断
		Num++;
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
}

完整程序链接:【STM32】基础定时器

定时器外部时钟源

  • 程序目标:使用对射式红外传感器控制GPIO口,遮挡时输出低电平,TIM2以该GPIO为外部时钟源,实现计次
  • 程序原理:定时器初始化大抵相同,还需配置GPIO 和 外部时钟源

接线图如下:

首先开启相关外设时钟

c 复制代码
//开启TIM2的时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
//开启GPIO的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

配置GPIO

c 复制代码
//初始化GPIO
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &GPIO_InitStructure);

配置外部时钟源

c 复制代码
//配置时钟源
//配置外部时钟源
//								不分频				低电平或下降沿触发		  滤波
TIM_ETRClockMode1Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_Inverted, 0x0F);

后续时基单元和中断控制配置大抵相同

完整代码如下:

c 复制代码
/**
  * @brief		定时器初始化
  * @parm		无
  * @retval		无
  */
void Timer_Init(void)
{
	//开启TIM2的时钟
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	//开启GPIO的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	//初始化GPIO
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);
	
	//配置时钟源
	//配置外部时钟源
	//								不分频				低电平或下降沿触发		  滤波
	TIM_ETRClockMode1Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_Inverted, 0x0F);
	
	//初始化时基单元
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;			//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;		//向上计数,从0到重装值
	TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;					//重装值 - 1,因为从0开始,ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;					//预分频器,PSC的值。如配置为72,即系统频率72MHz / 72 = 1MHz
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;				//重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	//中断部分
	//时基单元初始化完成后会发起一次更新事件,需要清理一下,避免影响复位
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);
	
	//开启TIM2的更新中断
	TIM_ITConfig(TIM2, TIM_FLAG_Update, ENABLE);
	
	//中断优先级组
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	//NVIC初始化
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;						//中断通道
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;						//使能开关
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
	NVIC_Init(&NVIC_InitStructure);
	
	//开启TIM2
	TIM_Cmd(TIM2, ENABLE);
}

完整程序链接:【STM32】定时器外部时钟源计次


以上就是本篇博客的所有内容,感谢你的阅读

如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。

相关推荐
亦枫Leonlew34 分钟前
三维测量与建模笔记 - 5.3 光束法平差(Bundle Adjustment)
笔记·计算机视觉·三维重建·光束法平差
Lbs_gemini06031 小时前
C++研发笔记14——C语言程序设计初阶学习笔记12
c语言·开发语言·c++·笔记·学习
MarisolHu2 小时前
前端学习笔记-Vue篇-02
前端·vue.js·笔记·学习
先知demons4 小时前
uniapp开发微信小程序笔记10-触底加载
前端·笔记·微信小程序·小程序·uni-app
B1nna4 小时前
外卖开发(三)开发笔记——AOP实现实现公共字段填充、主键回显、抛异常和事务管理
笔记
我的老子姓彭4 小时前
C++学习笔记
c++·笔记·学习
lucy153027510795 小时前
【青牛科技】BISS0001高性能的传感信号处理集成电路芯片,广泛用于安防、自控等领域能
科技·单片机·智能家居·信号处理·安防·工控主板
白天看海5 小时前
40 基于单片机的温湿度检测判断系统
单片机·嵌入式硬件
电子设计师5 小时前
45 基于单片机的信号选择与温度变化
单片机·嵌入式硬件
zcb8496443715 小时前
27 基于51单片机的方向盘模拟系统
嵌入式硬件·51单片机·proteus·方向盘