STM32---定时器知多少

STM32—定时器知多少

定时器的分类

定时器的分类
定时器的类型可以查看STM32F1XX中文参考手册的第14,15,16章节,里面对定时器的知识有详细的介绍。

STM32F1系列的开发板一共有8个定时器,按照功能的不同可以分为:

高级定时器(TIM1、TIM8)

通用定时器(TIM2、TIM3、TIM4、TIM5)

基本定时器(TIM6、TIM7)

看门狗定时器

SysTick定时器

他们之间的区别情况见下表:

在这里插入图片描述

定时器的工作原理

1 高级定时器简介
TIM1 和 TIM8 简介
高级控制定时器(TIM1 和 TIM8)包含一个 16 位自动重载计数器,该计数器由可编程预分频器驱动
此类定时器可用于各种用途,包括测量输入信号的脉冲宽度(输入捕获),或者生成输出波形(输出比较、PWM 和带死区插入的互补 PWM)。
使用定时器预分频器和 RCC 时钟控制器预分频器,可将脉冲宽度和波形周期从几微秒调制到几毫秒。
高级控制定时器(TIM1 和 TIM8)和通用 (TIMx) 定时器彼此完全独立,不共享任何资源。

14.2 TIM1 和 TIM8 主要特性
TIM1 和 TIM8 定时器具有以下特性:
● 16 位递增、递减、递增/递减自动重载计数器。
● 16 位可编程预分频器,用于对计数器时钟频率进行分频(即运行时修改),分频系数介于 1 到 65536 之间。
● 多达 4 个独立通道,可用于:
— 输入捕获
— 输出比较
— PWM 生成(边沿和中心对齐模式)
— 单脉冲模式输出
● 带可编程死区的互补输出。
● 使用外部信号控制定时器且可实现多个定时器互连的同步电路。
● 重复计数器,用于仅在给定数目的计数器周期后更新定时器寄存器。
● 用于将定时器的输出信号置于复位状态或已知状态的断路输入。
● 发生如下事件时生成中断/DMA 请求:
— 更新:计数器上溢/下溢、计数器初始化(通过软件或内部/外部触发)
— 触发事件(计数器启动、停止、初始化或通过内部/外部触发计数)
— 输入捕获
— 输出比较
— 断路输入
● 支持定位用增量(正交)编码器和霍尔传感器电路。
● 外部时钟触发输入或逐周期电流管理。

STM32F103中有TIM1,TIM8两个高级定时器,每一定时器都有
一个16位向上、向下、向上/下自动装载计数器

一个16位预分频器和四个独立从输入输出通道

每一个通道都可用于输入捕获、输出比较、PWM和单脉冲模式(除了基本定时器,高级定时器和通用定时器都能产生PWM)

单片机中没有时间的概念,因此定时器本质是一个以单位时间为准的计数器,计数值可以从0开始累加,也可以从一个设定值(ARR)的值递减,每隔一个固定的时间(由psc和时钟周期控制)计数器的值+1或-1,加到或减到头时会产生一个溢出信号,此时计数器的计数值清零或补充成初值,重新开始计数。

高级定时器框图

在这里插入图片描述
1 时钟源

由四个时钟来源,分别是

内部时钟源 CK_INT
外部时钟模式 1:外部输入引脚 TIx(x=1,2,3,4)
外部时钟模式 2:外部触发输入 ETR
内部触发输入(ITRx)

我们一般只用内部时钟源CK_INT,对于高级定时器,其时钟是由APB2总线时钟决定的
在这里插入图片描述

其他类型的定时器同理可以参考参考手册的RCC部分的时钟框图。

2 控制器

用来控制和发送命令的相关的寄存器有

CR1(control register 1 )控制寄存器1
CR2(control register 2 )控制寄存器2
SMCR(slave mode control register)从模式控制寄存器

CCER(capture/compare enable register)捕获比较寄存器

3 时基单元Time-base unit

在这里插入图片描述

时基单元的组成:
1)1-16位的预分频器 PSC
对控制计数器的时钟进行分频,分配系数可以为1~65536。

2)16位的计数器CNT
每经过一个计数器时钟周期这里变化1(+1或-1)。

(3)4-16位的自动重装载寄存器ARR
向上计数时,但计数器的值CNT从0计数到ARR的值的时候,会产生溢出中断(也叫更新中断,update interrupt),然后会清零重新计数向下计数时,当计数器的值CNT从ARR的值计数到0的时候会产生溢出中断,然后会重新填充ARR的值。
(4)8位的重复计数器RCR(高级定时器独有的,基本和基础定时器都没有)
向上计数时,我们把REO的值设置成10,如果有设置当计数器溢出的时候,那么此时不会产生中断,而是RCR的值+1,当加到10的时候才产生中断,向下 计数原理类似,只不过是从10减到0。

4 输入捕获

在这里插入图片描述
输入捕获可以对输入的信号的上升沿下降沿或者双边沿进行捕获,常用的有测量输入信号的脉宽和测量 PWM 输入信号的频率和占空比这两种。
输入捕获的大概原理:
输入引脚检测到电平跳变时(上升沿或下降沿),把计数器CNT的值锁存到捕获寄存器CCRx中,把前后两次捕获到的 CCR x寄存器中的值相减,就可以算出脉宽或者频率

(1)输入通道引脚
需要测量的信号从TIMx_CH1~4进入

(2)输入滤波器和边沿检测器
信号受到干扰时,滤波器可以对输入信号进行滤波,即进行重新采样。

滤波器的配置由 CR1 寄存器的位 CKD[1:0]和 CCMR1/2 的位 ICxF[3:0]控制。

边沿检测器用来设置信号在捕获的时候是什么边沿有效,可以是上升沿,下降沿,或者是双边沿,由 CCER 寄存器的位 CCxP 和 CCxNP 决定。
在这里插入图片描述

(3)捕获通道

捕获通道就是图中的 IC1/2/3/4,每个捕获/比较通道都有相对应的捕获寄存器 CCR1/2/3/4,当发生捕获的时候,计数器 CNT 的值就会被锁存到捕获寄存器中。

在这里插入图片描述
(4)预分频器

ICx 的输出信号会经过一个预分频器,用于决定发生多少个事件时进行一次捕获。具体的由寄存器 CCMRx 的位 ICxPSC 配置。

如果希望捕获信号的每一个边沿,则不分频。

在这里插入图片描述

(5)捕获寄存器
经过预分频器的信号 ICxPS 是最终被捕获的信号。

当发生捕获时(第一次),计数器CNT 的值会被锁存到捕获寄存器 CCR 中,还会产生 CCxI 中断,相应的中断位 CCxIF(在SR 寄存器中)会被置位,通过软件或者读取 CCR 中的值可以将 CCxIF 清 0。

如果发生第二次捕获(即重复捕获: CCR 寄存器中已捕获到计数器值且 CCxIF 标志已置 1),则捕获溢出标志位 CCxOF(在 SR 寄存器中)会被置位, CCxOF 只能通过软件清零。

5.输出比较

在这里插入图片描述

通过定时器的外部引脚对外输出控制信号。
有八种模式,主要使用的是其PWM1和PWM2模式。
具体使用哪种模式由寄存器 CCMRx 的位 OCxM[2:0]配置。

在这里插入图片描述
(1)比较寄存器
比较寄存器与捕获寄存器是同一个寄存器。

计数器 CNT 的值跟比较寄存器 CCR 的值相等的时候,输出参考信号 OCxREF 的信号的极性就会改变( OCxREF=1(高电平)称之为有效电平, OCxREF=0(低电平)称之为无效电平)。会产生比较中断 CCxI,相应的标志位 CCxIF(SR 寄存器中(状态寄存器,里面有各种状态或中断标记))会置位。

(2)死区发生器

在生成的参考波形 OCxREF 的基础上,可以插入死区时间,用于生成两路互补的输出信号 OCx 和 OCxN(OCxN的波形与OCx的相反)。

在这里插入图片描述

死区时间的大小具体由 BDTR 寄存器的位 DTG[7:0]配置

在这里插入图片描述

这里的T_ DTS是由内部时钟T_CK_INT经过分频得到的,由CR1寄存器上的CKD[9:8]位控制的。

在这里插入图片描述

(3)输出控制

(4)输出引脚

输出比较的输出信号最终是通过定时器的外部 IO 来输出的,分别为 CH1/2/3/4,其中
前面三个通道还有互补的输出通道 CH1/2/3N。

在这里插入图片描述

2 STM32的通用定时器

通用定时器功能特点描述

STM32的通用定时器是由一个可编程预分频器(PSC)驱动的16位自动重装载计数器(CNT)构成,可用于测量输入脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)等。

STM32的通用TIMx(TIM2、TIM3、TIM4 和 TIM5)定时器功能特点包括:

位于低速的APB1总线上(注意:高级定时器是在高速的APB2总线上);

16位向上、向下、向上/向下(中心对齐)计数模式,自动装载计数器(TIMx_CNT);

16位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数 为 1~65535 之间的任意数值;

4 个独立通道(TIMx_CH1~4),这些通道可以用来作为:
输入捕获
输出比较
PWM生成(边缘或中间对齐模式)
单脉冲模式输出

可使用外部信号**(TIMx_ETR)控制定时器**和定时器互连(可以用 1 个定时器控制另外一个定时器)的同步电路。

如下事件发生时产生中断/DMA(6个独立的IRQ/DMA请求生成器):
1 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
2 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
3 输入捕获
4 输出比较
5 支持针对定位的增量(正交)编码器和霍尔传感器电路
6 触发输入作为外部时钟或者按周期的电流管理

STM32 的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)等。

使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。
STM32 的每个通用定时器都是完全独立的,没有互相共享的任何资源

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

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

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

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

简单地理解三种计数模式,可以通过下面的图形:

在这里插入图片描述

通用定时器工作流程

在这里插入图片描述
对于这个定时器框图,分成四部分来讲:最顶上的一部分(计数时钟的选择)、中间部分(时基单元)、左下部分(输入捕获)、右下部分(PWM输出)。

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

内部时钟(TIMx_CLK)
外部时钟模式1:外部捕捉比较引脚(TIx)
外部时钟模式2:外部引脚输入(TIMx_ETR)
内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。

内部时钟源

在这里插入图片描述

从图中可以看出:由AHB时钟经过APB1预分频系数转至APB1时钟,再通过某个规定转至TIMxCLK时钟(即内部时钟CK_INT、CK_PSC)。最终经过PSC预分频系数转至CK_CNT。

那么APB1时钟怎么转至TIMxCLK时钟呢?除非APB1的分频系数是1,否则通用定时器的时钟等于APB1时钟的2倍。

例如:默认调用SystemInit函数情况下:SYSCLK=72M、AHB时钟=72M、APB1时钟=36M,所以APB1的分频系数=AHB/APB1时钟=2。所以,通用定时器时钟CK_INT=2*36M=72M。最终经过PSC预分频系数转至CK_CNT。

由于高级定时器与通用定时器的时钟不同,上面重点介绍时钟源,对于时基单元,计数模式,定时器相关的寄存器,以及控制寄存器都大体相同,大家可以参考高级定时器的寄存器介绍。

通用定时器超时时间

超出(溢出)时间计算:

Tout=(ARR+1)(PSC+1)/TIMxCLK

其中:Tout的单位为us,TIMxCLK的单位为MHz。

这里需要注意的是:PSC预分频系数需要加1,同时自动重加载值也需要加1。

为什么自动重加载值需要加1,因为从ARR到0之间的数字是ARR+1个;
为什么预分频系数需要加1,因为为了避免预分频系数不设置的时候取0的情况,使之从1开始。
这里需要和之前的预分频进行区分:由于通用定时器的预分频系数为1~65535之间的任意数值,为了从1开始,所以当预分频系数寄存器为0的时候,代表的预分频系数为1。而之前的那些预分频系数都是固定的几个值,比如1、4、8、16、32、64等等,而且可能0x000代表1,0x001代表4,0x010代表8等等。也就是说,一边是随意的定义(要从1开始),另一边是宏定义了某些值(只有特定的一些值)。

比如,想要设置超出时间为500ms,并配置中断,TIMxCLK按照系统默认初始化来(即72MHz),PSC取7199,由此可以计算出ARR为4999。

也就是说,在内部时钟TIMxCLK为72MHz,预分频系数为7199的时候,从4999递减至0的事件是500ms。

以上就是通用定时器的基本原理介绍,如有疑问可以参考STM32F4中文参考手册。

3 STM32基本定时器

基本定时器定时器(TIM6 和 TIM7)不仅可用作通用定时器以生成时基,还可以专门用于驱动数模转换器 (DAC)。实际上,此类定时器内部连接到 DAC 并能够通过其触发输出驱动 DAC
这些定时器彼此完全独立,不共享任何资源。

TIM6 和 TIM7 的主要特性
基本定时器(TIM6 和 TIM7)的特性包括:
● 16 位自动重载递增计数器。
● 16 位可编程预分频器,用于对计数器时钟频率进行分频(即运行时修改),分频系数介于 1 和 65536 之间。
● 用于触发 DAC 的同步电路。
● 发生如下更新事件时会生成中断/DMA 请求:计数器上溢.。

在这里插入图片描述

以上就是对定时器工作原理的基本介绍,如果还有疑问可以查询STM32F4XX中文参考手册,里面有详细的介绍和讲解。

定时器的应用

定时器的应用的应用场景有很多种,如下所示:

1 更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
2 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
3 输入捕获
4 输出比较
5 支持针对定位的增量(正 交)编码器和霍尔传感器电路
6 触发输入作为外部时钟或者按周期的电流管理

下面我重点给大家展示以下三个实验
1 定时器中断实验
2 PWM输出实验
3 输入捕获实验

定时器中断实验

接下来我们以通用定时器 TIM3 为实例,来说明要经过哪些步骤,才能达到这个要求,并产生中断。
1 )TIM3 时钟使能。
这里我们通过 APB1ENR 的第 1 位来设置 TIM3 的时钟,因为 Stm32_Clock_Init 函数里面把APB1的分频设置为2了,所以我们的TIM3时钟就是APB1时钟的2倍等于系统时钟(72M)
2 )设置 TIM3_ARR 和 和 TIM3_PSC 的值
通过这两个寄存器,我们来设置自动重装的值,以及分频系数。这两个参数加上时钟频率就决定了定时器的溢出时间。
3 )设置 TIM3_DIER 允许更 新中断
因为我们要使用 TIM3 的更新中断,所以设置 DIER 的 UIE 位为 1,使能更新中断。
4 )允许 TIM3 工作
光配置好定时器还不行,没有开启定时器,照样不能用。我们在配置完后要开启定时器,通过 TIM3_CR1 的 CEN 位来设置。
5 )TIM3 中断分组设置
在定时器配置完了之后,因为要产生中断,必不可少的要设置 NVIC 相关寄存器,以使能TIM3 中断。
6 )编写中断服务函数
在最后,还是要编写定时器中断服务函数,通过该函数来处理定时器产生的相关中断。在中断产生后,通过状态寄存器的值来判断此次产生的中断属于什么类型。然后执行相关的操作,我们这里使用的是更新(溢出)中断,所以在状态寄存器 SR 的最低位。在处理完中断之后应该向 TIM3_SR 的最低位写 0,来清除该中断标志。

定时器中断实现步骤:

1 使能定时器时钟。
RCC_APB1PeriphClockCmd();
② 初始化定时器,配置ARR,PSC。
TIM_TimeBaseInit();
③ 开启定时器中断,配置NVIC。
NVIC_Init();
④ 使能定时器。
TIM_Cmd();
⑥ 编写中断服务函数。
TIMx_IRQHandler();

void TIME3_Init(u16 arr,u16 psc)
{
		TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
		NVIC_InitTypeDef NVIC_InitStructure;

		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE);

		TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;//设置时钟分割:TDTS = Tck_tim
		TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up; //TIM向上计数模式
		TIM_TimeBaseStructure.TIM_Period=arr;//设置在下一个更新事件装入活动的自动重装载寄存器周期的值	
		TIM_TimeBaseStructure.TIM_Prescaler=psc;//设置用来作为TIMx时钟频率除数的预分频值
		TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
	
	  TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断

		NVIC_InitStructure.NVIC_IRQChannel=TIM3_IRQn;//TIM3中断
		NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;//IRQ通道被使能
		NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;  //先占优先级2级
		NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;//从优先级0级
		NVIC_Init(&NVIC_InitStructure);
	
	    TIM_Cmd(TIM3,ENABLE);	
}
void TIM3_IRQHandler(void)
{

		if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否		
			{
				LED1=!LED1;
				TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中断标志 
				}
}

PWM输出实验

本节要实现通过 TIM3_CH1 输出PWM 来控制 DS0 的亮度。下面我们介绍配置步骤:
1) )启 开启 TIM1 时钟,配置 PB5 为复用输出。要使用 TIM1,我们必须先开启 TIM1 的时钟(通过 APB2ENR 设置),这点相信大家看了这么多代码,应该明白了。这里我们还要配置 PB5 为复用输出,这是因为 TIM3_CH1 通道将使用 PB5 的复用功能作为输出。

2 ))设置 TIM3 的 的 ARR 和 和 PSC 。
在开启了 TIM3的时钟之后,我们要设置 ARR 和 PSC 两个寄存器的值来控制输出 PWM 的周期。当 PWM 周期太慢(低于 50Hz)的时候,我们就会明显感觉到闪烁了。因此,PWM 周期在这里不宜设置的太小。
3) )置设置 TIM3_CH1 的 的 PWM 模式 及通道方向。

接下来,我们要设置 TIM3_CH1 为 PWM 模式(默认是冻结的),因为我们的 DS0 是低电平亮,而我们希望当 CCR1 的值小的时候,DS0 就暗,CCR1 值大的时候,DS0 就亮,所以我们要通过配置 TIM1_CCMR1 的相关位来控制 TIM3_CH1 的模式。另外,我们要配置 CH1 为输出,所以要设置 CC1S[1:0]为 00(寄存器默认就是 0,所以这里可以省略)。

4) )能 使能 TIM3 的 的 CH1 输出,使能 TIM3。
接下来,我们需要开启 TIM3 的通道 1 的输出以及 TIM3的时钟。前者通过 TIM3_CCER来设置,是单个通道的开关,而后者则通过 TIM3_CR1 来设置,是整个 TIM13的总开关。只有设置了这两个寄存器,这样我们才可能在 TIM1 的通道 1 上看到 PWM 波输出。

5) ) 设置 MOE 输出,使能 PWM 输出。 。
普通定时器在完成以上设置了之后,就可以输出 PWM 了,但是高级定时器,我们还需要使能刹车和死区寄存器(TIM1_BDTR)的 MOE 位,以使能整个 OCx(即 PWM)输出。

6) )改 修改 TIM3_CCR1 来控制占空比。
最后,在经过以上设置之后,PWM 其实已经开始输出了,只是其占空比和频率都是固定的,而我们通过修改 TIM3_CCR1 则可以控制 CH1 的输出占空比。继而控制 DS0 的亮度。
通过以上 6 个步骤,我们就可以控制 TIM3 的 CH1 输出 PWM 波了。

简化上面的过程就是:
1 使能定时器14和相关IO口时钟。
使能定时器14时钟:RCC_APB1PeriphClockCmd();
使能GPIOF时钟:RCC_AHB1PeriphClockCmd ();
2 初始化IO口为复用功能输出。函数:GPIO_Init();
3 GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF; //复用功能
GPIOF9复用映射到定时器14
GPIO_PinAFConfig(GPIOF,GPIO_PinSource9,GPIO_AF_TIM14);
4 初始化定时器:ARR,PSC等:TIM_TimeBaseInit();
5 初始化输出比较参数:TIM_OC1Init();
6 使能预装载寄存器: TIM_OC1PreloadConfig(TIM14, TIM_OCPreload_Enable);
7 使能自动重装载的预装载寄存器允许位TIM_ARRPreloadConfig(TIM14,ENABLE);
8 使能定时器。
9 不断改变比较值CCRx,达到不同的占空比效果:TIM_SetCompare1();

void TIME3_PWM_Init(u16 arr,u16 psc)
{
	
		GPIO_InitTypeDef GPIO_InitStructure;
		TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
		TIM_OCInitTypeDef  TIM_OCInitStructure;//结构体声明

		RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE);	//使能定时器3时钟
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB  | RCC_APB2Periph_AFIO, ENABLE);  //使能GPIO外设和AFIO复用功能模块时钟

		GPIO_PinRemapConfig(GPIO_PartialRemap_TIM3, ENABLE); //Timer3部分重映射  TIM3_CH2->PB5    

		GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF_PP;
		GPIO_InitStructure.GPIO_Pin=GPIO_Pin_5;
		GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
		GPIO_Init(GPIOB,&GPIO_InitStructure);
	
	
		TIM_TimeBaseStructure.TIM_ClockDivision=0;
		TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
		TIM_TimeBaseStructure.TIM_Period=arr;
		TIM_TimeBaseStructure.TIM_Prescaler=psc;
		TIM_TimeBaseInit(TIM3,&TIM_TimeBaseStructure);
  

		TIM_OCInitStructure.TIM_OCMode=TIM_OCMode_PWM2;//PWM模式1或者模式2
		TIM_OCInitStructure.TIM_OCPolarity=TIM_OCPolarity_High;//比较输出极性
		TIM_OCInitStructure.TIM_OutputState=TIM_OutputState_Enable;//输出使能 OR失能
//		TIM_OCInitStructure.TIM_Pulse=100;//比较值,写CCRx
		TIM_OC2Init(TIM3, &TIM_OCInitStructure);  //根据T指定的参数初始化外设TIM3 OC2
		TIM_OC2PreloadConfig(TIM3, TIM_OCPreload_Enable);  //使能TIM3在CCR2上的预装载寄存器 
		TIM_Cmd(TIM3, ENABLE);  //使能TIM3
}





int main(void)
 {	
	delay_init();	    //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	uart_init(115200);
	LED_Init();		  	//初始化与LED连接的硬件接口
	BEEP_Init();
	TIME3_Init(4999,7199);
	TIME3_PWM_Init(899,0);
	KEY_Init();
    LCD_Init();

					 	
	while(1)
	{

      delay_ms(10);	 
		if(dir)
			led0pwmval++;
		else 
			led0pwmval--;

 		if(led0pwmval>500)
			dir=0;
		if(led0pwmval==0)
		dir=1;										 
		TIM_SetCompare2(TIM3,led0pwmval);		   
		
		}
		
}	

输入捕获实验

下面我们介绍输入捕获的配置步骤:

1 ))开启 TIM2 时钟,配置 PA0 为下拉输入。
要使用 TIM2,我们必须先开启 TIM2 的时钟(通过 APB1ENR 设置)。这里我们还要配置 PA0为下拉输入,因为我们要捕获 TIM2_CH1 上面的高电平脉宽,而 TIM2_CH1 是连接在 PA0 上
面的。
2 ))设置 TIM2 的 的 ARR 和 和 PSC
在开启了 TIM2 的时钟之后,我们要设置 ARR 和 PSC 两个寄存器的值来设置输入捕获的自动重装载值和计数频率。
3 ))设置 TIM2 的 的 CCMR1
TIM2_CCMR1 寄存器控制着输入捕获 1 和 2 的模式,包括映射关系,滤波和分频等。这里我们需要设置通道 1 为输入模式,且 IC1 映射到 TI1(通道 1)上面,并且不使用滤波(提高响应速度)器。
4) ) 设置 TIM2 的 的 CCER ,开启输入捕获,并设置为上升沿捕获。

TIM2_CCER 寄存器是定时器的开关,并且可以设置输入捕获的边沿。只有 TIM2_CCER寄存器使能了输入捕获,我们的外部信号,才能被 TIM2 捕获到,否则一切白搭。同时要设置好捕获边沿,才能得到正确的结果。
5) ) 设 置 TIM2 的 的 DIER ,使能捕获和更新中断,并编写中断服务函数
因为我们要捕获的是高电平信号的脉宽,所以,第一次捕获是上升沿,第二次捕获时下降沿,必须在捕获上升沿之后,设置捕获边沿为下降沿,同时,如果脉宽比较长,那么定时器就会溢出,对溢出必须做处理,否则结果就不准了。这两件事,我们都在中断里面做,所以必须开启捕获中断和更新中断。
设置了中断必须编写中断函数,否则可能导致死机。我们需要在中断函数里面完成数据处理和捕获设置等关键操作,从而实现高电平脉宽统计。
6 )设置 TIM2 的 的 CR1 ,使能定时器
最后,必须打开定时器的计数器开关,通过设置 TIM2_CR1 的最低位为 1,启动 TIM2 的计数器,开始输入捕获。
通过以上 6 步设置,定时器 2 的通道 1 就可以开始输入捕获了,同时因为还用到了串口输出结果,所以还需要配置一下串口。

输入捕获的一般配置步骤:

初始化定时器和通道对应IO的时钟。
初始化IO口,模式为复用:GPIO_Init();
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
设置引脚复用映射:
GPIO_PinAFConfig();
初始化定时器ARR,PSC
TIM_TimeBaseInit();
初始化输入捕获通道
TIM_ICInit();
如果要开启捕获中断,
TIM_ITConfig();
NVIC_Init();
使能定时器:TIM_Cmd();
编写中断服务函数:TIMx_IRQHandler();

代码如下:

void TIME2_CAP_Init(u16 arr,u16 psc)
{
    GPIO_InitTypeDef  GPIO_InitStructure; 
		TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
		TIM_ICInitTypeDef  TIM2_ICInitStructure;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);	 //使能TIM5时钟
		RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);	 //使能PA端口时钟
	
		GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
		GPIO_InitStructure.GPIO_Pin=GPIO_Pin_1;
		GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
		GPIO_Init(GPIOA, &GPIO_InitStructure);	 //设置为浮空输入
	
		TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;
		TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;
		TIM_TimeBaseStructure.TIM_Period=arr;
		TIM_TimeBaseStructure.TIM_Prescaler=psc;
		TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

		TIM2_ICInitStructure.TIM_Channel=TIM_Channel_2;
		TIM2_ICInitStructure.TIM_ICFilter=0x03;
		TIM2_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;
		TIM2_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;
		TIM2_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;
		TIM_ICInit(TIM2,&TIM2_ICInitStructure);
		
    TIM_Cmd(TIM2,ENABLE ); 
}


 int main(void)
 {	
	delay_init();	    //延时函数初始化	  
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	uart_init(115200);
	LED_Init();		  	//初始化与LED连接的硬件接口
	BEEP_Init();
	TIME3_Init(4999,7199);
	TIME3_PWM_Init(899,0);
	TIME2_Cap_Init(0XFFFF,72-1);	//以1Mhz的频率计数 
	KEY_Init();			 	
	while(1)
	{
        delay_ms(10);
		TIM_SetCompare2(TIM3,TIM_GetCapture2(TIM3)+1);

		if(TIM_GetCapture2(TIM3)==300)
			TIM_SetCompare2(TIM3,0);	
	 		 
 		if(TIM5CH1_CAPTURE_STA&0X80)//成功捕获到了一次上升沿
		{
		temp=TIM5CH1_CAPTURE_STA&0X3F;
			temp*=65536;//溢出时间总和
		temp+=TIM5CH1_CAPTURE_VAL;//得到总的高电平时间
			printf("HIGH:%d us\r\n",temp);//打印总的高点平时间
			TIM5CH1_CAPTURE_STA=0;//开启下一次捕获
		}

总结

以上就是我对定时器的理解,定时器的内容很多,很杂,很复杂,需要大家自己对一些寄存器进行深入的理解,最重要的是自己要上手写代码,复制粘贴是学习STM32的大忌,希望大家可以通过自己的刻苦努力,来实现自己能力的提升。

  • 6
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我可以为您解答关于STM32系统滴答定时器(systick)应用的问题。 系统滴答定时器(systick)是STM32中一个非常重要的计时器,它是一个24位的递减计数器,每当计数器减为0时,会触发一个中断。它通常用于操作系统的时间片轮转、延时函数的实现等。 在STM32中,我们可以通过如下代码使能系统滴答定时器: ```c // 使能系统滴答定时器 SysTick->CTRL |= SysTick_CTRL_ENABLE_Msk; ``` 然后,我们可以通过如下代码设置系统滴答定时器的时钟源和计数器值: ```c // 设置系统滴答定时器的时钟源和计数器值 SysTick->CTRL &= ~SysTick_CTRL_CLKSOURCE_Msk; // 使用处理器时钟作为时钟源 SysTick->LOAD = SystemCoreClock / 1000 - 1; // 设置计数器值为1ms ``` 在这段代码中,我们将系统滴答定时器的时钟源设置为处理器时钟,每次计数器减1代表1个处理器时钟周期的时间。然后我们将计数器的初值设置为`SystemCoreClock / 1000 - 1`,这里的`1000`代表我们希望系统滴答定时器每1ms中断一次。 最后,我们需要在中断服务函数中处理系统滴答定时器中断: ```c void SysTick_Handler(void) { // 处理系统滴答定时器中断 } ``` 在中断服务函数中,我们可以编写自己的代码来处理系统滴答定时器中断。例如,可以实现一个简单的延时函数: ```c void delay_ms(uint32_t ms) { uint32_t i; for (i = 0; i < ms; i++) { while (!(SysTick->CTRL & SysTick_CTRL_COUNTFLAG_Msk)); // 等待计数器减为0 } } ``` 这个函数可以让程序在执行到它的时候暂停一段时间,等待系统滴答定时器中断触发一定次数后再继续执行。 希望这些代码可以帮助您更好地理解STM32系统滴答定时器的应用。如果您还有其他问题,请随时提出。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值