stm32教程:TIM定时器详解 & TIM时钟计时代码模板

早上好啊,大佬们,这一期我们继续我们stm32基本功能的学习,今天来一个大家伙 —— 定时器。定时器的功能十分强大,是我们学习stm32道路上绕不开的一个大石,但是相信有了前面学习的经验和基础,大家也能很好的掌握这里的内容。

不知道这个样子的开头这对于我的stm32的读者会不会有点新,因为确实好久没写这个专栏了,哈哈哈(掩饰)

目录

一、定时器原理

二、定时器分类

三、计数器模式(重要)

四、定时器的基本结构

五、定时器的工作模式

六、通用定时器工作原理(重要)

1. 时钟源选择

2. 时基单元

计数器溢出频率:

CK_CNT_OV = CK_CNT / (ARR + 1)  =  CK_PSC / (PSC + 1) / (ARR + 1)

 无预装时序

有预装时序

3. 输入捕获和输出比较

4. 中断和事件生成

5. 触发控制器

6. 编码器接口

七、代码模板


一、定时器原理

STM32 的定时器本质上是一个计数器,它可以对内部时钟源或外部时钟源进行计数。当计数器的值达到预设的自动重载值(ARR)时,会产生一个更新事件(UEV)。通过配置,可以使这个更新事件触发中断,从而在中断服务函数中执行特定的任务。

二、定时器分类

不同系列的 STM32 芯片包含的定时器数量和类型有所不同,对于我们的stmc8t6来说呢,它具有

  • 高级控制定时器:1 个,即 TIM1。具备最丰富的功能,支持向上、向下、向上 / 向下(中心对齐)计数模式,可用于产生带死区控制的互补 PWM 信号,常用于电机控制等需要高精度控制的领域。
  • 通用定时器:3 个,分别是 TIM2、TIM3 和 TIM4。功能较为全面,可实现定时、计数、PWM 输出、输入捕获等多种功能,应用场景广泛,例如工业自动化、仪器仪表等。
  • 基本定时器:2 个,为 TIM6 和 TIM7。功能相对简单,主要用于基本的定时功能,可产生定时中断,为系统提供周期性的定时信号。
定时器分类
定时器种类位数计数器模式产生DMA请求捕获/比较通道互补输出特点
高级定时器16向上、向下、向上及向下可以4
  • 具有丰富的功能和强大的控制能力,支持多种计数模式,包括向上计数、向下计数和中心对齐计数模式。
  • 能够产生带死区控制的互补 PWM 输出,适用于需要高精度电机控制的应用场景。
通用定时器16向上、向下、向上及向下可以4
  • 功能较为全面,具备基本的定时、计数、PWM 输出和输入捕获等功能。
  • 支持多种时钟源选择,包括内部时钟、外部时钟和定时器级联等方式。
基本定时器16向上、向下、向上及向下可以0
  • 功能相对简单,主要用于基本的定时功能。
  • 没有输入捕获、输出比较和 PWM 输出等复杂功能。

在实际使用中,我们最常用的就是通用定时器,它也能够满足我们日常使用中90%的情况了。所以下面就根据它来继续讲解。

三、计数器模式(重要)

通用定时器可以向上计数向下计数向上向下双向计数模式。

向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。

向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。

中央对齐模式(向上/向下计数):计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。

四、定时器的基本结构

定时器的基本结构包括以下几个部分:

  • 时基单元:包括计数器(CNT)、预分频器(PSC)和自动重装寄存器(ARR)。

    • 计数器(CNT):用于对时钟信号进行计数。

    • 预分频器(PSC):用于将输入时钟分频,降低计数频率。

    • 自动重装寄存器(ARR):设置计数器的上限值,当计数器达到 ARR 值时,会触发更新事件(Update Event)。

  • 时钟源:定时器可以使用内部时钟(如系统时钟)或外部时钟作为计数源。

  • 中断和事件生成:定时器可以在计数器溢出、捕获事件、比较匹配等情况下生成中断或事件。

五、定时器的工作模式

STM32 的定时器支持多种工作模式,常见的模式包括:

  • 定时器模式:定时器对时钟信号进行计数,达到设定值时触发中断或事件。

  • 输入捕获模式:用于测量外部信号的脉冲宽度或频率。

  • 输出比较模式:用于生成 PWM 信号或控制输出电平。

  • PWM 模式:生成脉宽调制信号,常用于电机控制、LED 调光等应用。

  • 编码器模式:用于读取旋转编码器的信号,测量旋转方向和速度。

六、通用定时器工作原理(重要)

主要分成了四个部分:
时钟产生器部分;时基单元部分;输入捕获部分;输出比较部分

1. 时钟源选择

通用定时器的时钟源可以来自内部时钟(CK_INT)或外部时钟(ETR)。内部时钟通常由系统时钟(如 72MHz)提供,而外部时钟可以通过 ETR 引脚输入。

  • 内部时钟(CK_INT):定时器使用系统时钟作为计数源,经过预分频器(PSC)分频后,提供给计数器(CNT)进行计数。

  • 外部时钟(ETR):外部时钟信号通过 ETR 引脚输入,经过极性选择、边沿检测和预分频器处理后,作为计数器的时钟源。

2. 时基单元

时基单元是定时器的核心部分,包括计数器(CNT)、预分频器(PSC)和自动重装寄存器(ARR)。

  • 预分频器(PSC):用于将输入时钟分频,降低计数频率。例如,如果系统时钟为 72MHz,预分频器设置为 7199,则计数器的时钟频率为 72MHz / (7199 + 1) = 10kHz。

  • 计数器(CNT):对分频后的时钟信号进行计数。计数器可以向上计数、向下计数或中央对齐计数。

计数器溢出频率
CK_CNT_OV = CK_CNT / (ARR + 1)  =  CK_PSC / (PSC + 1) / (ARR + 1)
  • 自动重装寄存器(ARR):设置计数器的上限值。当计数器达到 ARR 值时,会触发更新事件(Update Event),并重新从 0 开始计数。

 无预装时序

有预装时序

3. 输入捕获和输出比较

通用定时器支持输入捕获和输出比较功能,用于测量外部信号的脉冲宽度或生成 PWM 信号。

  • 输入捕获:当外部信号触发捕获事件时,计数器的当前值会被锁存到捕获/比较寄存器(CCR)中。通过比较两次捕获的值,可以计算出信号的脉冲宽度或频率。

  • 输出比较:通过比较计数器(CNT)和捕获/比较寄存器(CCR)的值,可以控制输出引脚的电平。例如,当 CNT < CCR 时,输出高电平;当 CNT >= CCR 时,输出低电平。通过调整 CCR 的值,可以生成不同占空比的 PWM 信号。

4. 中断和事件生成

定时器可以在多种情况下生成中断或事件,如计数器溢出、捕获事件、比较匹配等。

  • 更新事件:当计数器达到 ARR 值时,会触发更新事件,并生成更新中断(Update Interrupt)。

  • 捕获/比较事件:当捕获事件或比较匹配发生时,会触发相应的中断或事件。

5. 触发控制器

触发控制器用于控制定时器的启动、停止和复位。它可以通过软件触发或外部触发信号来控制定时器的运行。

  • 软件触发:通过设置控制寄存器(CR)中的相关位,可以启动、停止或复位定时器。

  • 外部触发:通过外部触发信号(如 ETR 引脚)可以启动或停止定时器。

6. 编码器接口

通用定时器还支持编码器接口模式,用于读取旋转编码器的信号。编码器接口可以自动根据编码器的正交信号控制计数器的增减,从而测量旋转方向和速度。

七、代码模板

相信看到代码模板,这里才是大伙最关注的地方。

这里我给出两个模板,一个是内部时钟定时器的,一个是外部时钟定时器的。这一期就只介绍模板, 后面还会出一些用定时器实现的一些模块。例如,舵机,电机之类的。

步骤

1. 开时钟

2. 配置时钟源,也就是选择内部时钟还是外部时钟。

3. 时基单元初始化

4. 中断输出配置

5. NVIC配置,这里和外部中断里所说的NVIC是一个东西。

6.TIM使能,也就是打开定时器

内部时钟定时器

初始化函数

这里编码格式是 ANSI,在网页里会会乱码

#include "stm32f10x.h"                  // Device header
#include "Timer.h"


void Timer_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;				//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;				//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				//将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元	
	
	/*中断输出配置*/
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位
																//TIM_TimeBaseInit函数末尾,手动产生了更新事件
																//若不清除此标志位,则开启中断后,会立刻进入一次中断
																//如果不介意此问题,则不清除此标志位也可
	
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//指定NVIC线路的抢占优先级为2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

中断函数

void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

外部时钟定时器

对于外部时钟基本就是和内部时钟是一样的,唯一的差别就是两者的所记的数的不同。

外部时钟比较类似之前讲的外部中断的内容。

初始化函数

#include "stm32f10x.h"                  // Device header
#include "Timer.h"


void Timer_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	/*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);						//将PA0引脚初始化为上拉输入
	
	/*外部时钟配置*/
	TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);
																//选择外部时钟模式2,时钟从TIM_ETR引脚输入
																//注意TIM2的ETR引脚固定为PA0,无法随意更改
																//最后一个滤波器参数加到最大0x0F,可滤除时钟信号抖动
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;					//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;				//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				//将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元	
	
	/*中断输出配置*/
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位
																//TIM_TimeBaseInit函数末尾,手动产生了更新事件
																//若不清除此标志位,则开启中断后,会立刻进入一次中断
																//如果不介意此问题,则不清除此标志位也可
																
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//指定NVIC线路的抢占优先级为2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

中断函数

void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

尾声

OKk,这一期主要是说一下定时器的原理,然后给大家写了一下定时器的代码模板。

后面会陆续给大家带来一些使用定时器实现的模块,让大家能够快速的掌握并运用定时器。

感谢大伙观看,别忘了三连支持一下

大家也可以关注一下我的其它专栏,同样精彩喔~

下期见咯~

请添加图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值