STM32
直播中

凤毛麟角

8年用户 864经验值
私信 关注
[问答]

STM32的定时器中断实现步骤有哪些

定时器的工作原理是什么?
STM32的通用定时器是什么?有何特点?
STM32的定时器中断实现步骤有哪些?

回帖(1)

赵晨

2021-9-26 14:45:48
  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 usrn”,temp);//打印总的高点平时间
  TIM5CH1_CAPTURE_STA=0;//开启下一次捕获
  }
  总结
  以上就是我对定时器的理解,定时器的内容很多,很杂,很复杂,需要大家自己对一些寄存器进行深入的理解,最重要的是自己要上手写代码,复制粘贴是学习STM32的大忌,希望大家可以通过自己的刻苦努力,来实现自己能力的提升。
举报

更多回帖

发帖
×
20
完善资料,
赚取积分