STM32F4开发指南(库函数版)ALIENTEK探索者STM32F407开发板教程200第十三章定时器

STM32F4开发指南(库函数版) ALIENTEK Explorer STM32F407开发板教程 200 第十三章定时器中断实验 在本章中,我们将向您介绍如何使用STM32F4的通用定时器。STM32F4的定时器功能非常强大。有TIME1 TIME8等高级定时器,TIME2~TIME5、TIM9~TIM14等通用定时器,TIME6 TIME7等基本定时器,共有14个定时器。在本章中,我们将使用 TIM3 的定时器中断来控制 DS1 的反转,并在 main 函数中使用 DS0 的反转来指示程序正在运行。在本章中,我们选择一个难度适中的通用定时器来介绍。本章将分为以下几个部分: 13.1 STM32F4通用定时器介绍13. 2 硬件设计 13.3 软件设计 13.4 下载验证 13.1 STM32F4 通用定时器简介 STM32F4 通用定时器包含一个 16 位或 32 位自动重载计数器 (CNT),由可编程频率转换器 (PSC) 驱动器预分频。STM32F4的通用定时器可用于:测量输入信号的脉冲长度(输入捕捉)或产生输出波形(输出比较和PWM)。使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以从几微秒调整到几毫秒。由可编程频率转换器 (PSC) 驱动器预分频。STM32F4的通用定时器可用于:测量输入信号的脉冲长度(输入捕捉)或产生输出波形(输出比较和PWM)。使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以从几微秒调整到几毫秒。由可编程频率转换器 (PSC) 驱动器预分频。STM32F4的通用定时器可用于:测量输入信号的脉冲长度(输入捕捉)或产生输出波形(输出比较和PWM)。使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以从几微秒调整到几毫秒。

STM32F4的每个通用定时器都是完全独立的,彼此之间不共享任何资源。STM3 的常用 TIMx(TIM2~TIM5 和 TIM9~TIM14) 定时器功能包括:1)16 位/32 位(TIM2 和 TIM5) 仅向上、向下、向上/向下) 自动加载计数器(TIMx_CNT),注意:TIM9~TIM14只支持向上(递增)计数模式。2)16位可编程(可实时修改)预分频器(TIMx_PSC),计数器时钟频率分频系数为65535之间的任意值。独立通道(TIMx_CH1~4、TIM9~TIM14最多2个通道),这些通道可用作:A.输入捕捉B.输出比较C.PWM生成(边沿或中心对齐)模式),注意:TIM9~TIM14 不支持中心对齐模式 D. 单脉冲模式输出4) 可以使用外部信号(TIMx_ETR)来控制定时器和定时器互连(一个定时器可以用来控制另一个定时器5)当以下事件发生时产生中断/DMA(TIM9~TIM14不支持DMA): A.更新:计数器上溢/下溢、计数器初始化(通过软件或内部/外部触发)) B. 触发事件(计数器启动、停止、初始化或通过内部/外部触发计数) C. 输入捕捉 D. 输出比较 E. 支持增量用于定位的(正交)编码器和霍尔传感器电路(TIM9~ TIM14 不支持) F. 触发输入作为外部时钟或按周期进行电流管理(TIM9~TIM14 不支持) 由于STM32F4 的通用定时器比较复杂,这里不做介绍,请直接参考《STM32F4xx中文参考手册》。第 392 页,

接下来介绍与我们本章实验密切相关的几个通用定时器的寄存器(TIM2~TIM5的寄存器下面都介绍,TIM9~TIM14的寄存器略有不同,详细请参考《STM32F4xx中文参考手册》相应章节)。首先是控制寄存器1(TIMx_CR1),各个寄存器的说明如图13.1.1:STM32F4开发指南(库函数版)ALIENTEK Explorer STM32F407开发板教程201 图13.1.1 TIMx_CR1寄存器说明 本实验中我们只使用了TIMx_CR1的最低位,即计数器使能位,该位必须置1才能启动定时器计数。 下一个,我们将介绍与我们的章节密切相关的第二个寄存器:DMA/中断使能寄存器(TIMx_DIER)。该寄存器为16位寄存器,其位描述如图13.1.2所示: 图13.1.2 TIMx_DIER寄存器描述这里我们也只关心它的第 0 位,也就是更新中断使能位,本章使用定时器更新中断,所以该位应设置为 1 以使能因更新事件而产生的中断。接下来我们看一下与本章相关的第三个寄存器:预分频寄存器(TIMx_PSC)。该寄存器用于设置时钟分频,然后作为计数器的时钟提供给计数器。其位描述如图 13.1.2 所示: 图 13.1.2 TIMx_DIER 寄存器描述 这里我们也只关心它的第 0 位,即更新中断使能位,本章使用定时器更新中断,因此该位应设置为 1 以使能由于更新事件引起的中断。接下来我们看一下与本章相关的第三个寄存器:预分频寄存器(TIMx_PSC)。该寄存器用于设置时钟分频,然后作为计数器的时钟提供给计数器。其位描述如图 13.1.2 所示: 图 13.1.2 TIMx_DIER 寄存器描述 这里我们也只关心它的第 0 位,即更新中断使能位,本章使用定时器更新中断,因此该位应设置为 1 以使能由于更新事件引起的中断。接下来我们看一下与本章相关的第三个寄存器:预分频寄存器(TIMx_PSC)。该寄存器用于设置时钟分频,然后作为计数器的时钟提供给计数器。因此该位应设置为 1 以启用由于更新事件引起的中断。接下来我们看一下与本章相关的第三个寄存器:预分频寄存器(TIMx_PSC)。该寄存器用于设置时钟分频,然后作为计数器的时钟提供给计数器。因此该位应设置为 1 以启用由于更新事件引起的中断。接下来我们看一下与本章相关的第三个寄存器:预分频寄存器(TIMx_PSC)。该寄存器用于设置时钟分频,然后作为计数器的时钟提供给计数器。

该寄存器每一位的描述如图13.1.3: 图13.1.3 TIMx_PSC寄存器每一位的描述这里,定时器的时钟源是 4 1)内部时钟(CK_INT)2)外部时钟模式 1:外部输入引脚(TIx)3)外部时钟模式 2:外部触发输入(ETR),仅 TIM2、@ >TIM3、TIM4 4)内部触发输入 (ITRx):使用 A 定时器作为 B 定时器的预分频器(A 提供时钟)。选择这些时钟中的哪一个可以通过 TIMx_SMCR 寄存器中的相关位来设置。这里的CK_INT时钟是从APB1倍增的,除非APB1的时钟分频器设置为1(一般不是1),否则通用定时器TIMx的时钟为APB1时钟,而APB1时钟不分频时,通用定时器TIMx的时钟等于APB1的时钟。这里还要注意,高级定时器和TIM9~TIM11的时钟不是来自APB1什么是定时器中断函数,而是来自APB2。这里介绍一下TIMx_CNT寄存器,它是定时器的计数器,这个寄存器存放的是当前定时器的计数值。接下来介绍自动重载寄存器(TIMx_ARR),物理上对应2个寄存器。一种是程序员可以直接操作的,一种是程序员不可见的。这个不可见的寄存器在STM32F4开发指南(库函数版)ALIENTEK Explorer STM32F407开发板教程202中称为影子寄存器”

事实上,真正起作用的是影子寄存器。根据TIMx_CR1寄存器中APRE位的设置:当APRE=0时,可以随时将预加载寄存器的内容传送到影子寄存器中,此时两者是连通的;并且当 APRE=1 时,在每个更新事件 (UEV) 处,在将预加载寄存器 (ARR) 的内容传输到影子寄存器之前。自动重载寄存器的描述如图 13.1.4: 图 13.1.4 TIMx_ARR 寄存器的描述 最后,我们的寄存器要介绍的是:状态寄存器(TIMx_SR)。该寄存器用于标记当前是否发生了与定时器相关的各种事件/中断。该寄存器每一位的描述如图13.1.5: 图1< @3.1.5 TIMx_SR寄存器各位说明这些位的详细说明请参考《STM32F4xx中文参考手册第429章,我们将使用定时器产生中断,然后在中断服务函数中翻转DS1上的电平,指示定时器中断的产生。接下来,我们以通用定时器TIM3为例,说明必须采取哪些步骤才能达到这个要求并产生中断。在这里,我们描述了如何通过库函数来实现每个步骤。首先要提的是,定时器相关的库函数主要集中在固件库文件stm32f4xx_tim.h stm32f4xx_tim.c文件中。请参考《STM32F4xx中文参考手册》第429章,我们会使用定时器产生中断,然后在中断服务函数中翻转DS1上的电平来指示定时器中断的产生。接下来,我们以通用定时器TIM3为例,说明必须采取哪些步骤才能达到这个要求并产生中断。在这里,我们描述了如何通过库函数来实现每个步骤。首先要提的是,定时器相关的库函数主要集中在固件库文件stm32f4xx_tim.h stm32f4xx_tim.c文件中。请参考《STM32F4xx中文参考手册》第429章,我们会使用定时器产生中断,然后在中断服务函数中翻转DS1上的电平来指示定时器中断的产生。接下来,我们以通用定时器TIM3为例,说明必须采取哪些步骤才能达到这个要求并产生中断。在这里,我们描述了如何通过库函数来实现每个步骤。首先要提的是,定时器相关的库函数主要集中在固件库文件stm32f4xx_tim.h stm32f4xx_tim.c文件中。我们以通用定时器 TIM3 为例来说明必须采取哪些步骤才能达到这个要求并产生中断。在这里,我们描述了如何通过库函数来实现每个步骤。首先要提的是,定时器相关的库函数主要集中在固件库文件stm32f4xx_tim.h stm32f4xx_tim.c文件中。我们以通用定时器 TIM3 为例来说明必须采取哪些步骤才能达到这个要求并产生中断。在这里,我们描述了如何通过库函数来实现每个步骤。首先要提的是,定时器相关的库函数主要集中在固件库文件stm32f4xx_tim.h stm32f4xx_tim.c文件中。

定时器配置步骤如下: 1)TIM3 时钟使能。TIM3挂载在APB1下,所以我们通过APB1总线下的使能函数来使能TIM3。调用的函数是:RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3,ENABLE); ///启用TIM3时钟2)初始化定时器参数,设置自动重载值、分频系数、计数方式等。在库函数中,定时器的初始化参数通过初始化函数TIM_TimeBaseInit实现: voidTIM_TimeBaseInit( TIM_TypeDef*TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct); 第一个参数是判断是哪个定时器,比较容易理解。第二个参数是定时器初始化参数结构指针。结构类型为 TIM_TimeBaseInitTypeDef。让’ s看一下这个结构体的定义:typedef struct STM32F4开发指南(库函数版)ALIENTEK Explorer STM32F407开发板教程203 uint16_tTIM_Prescaler;uint16_t TIM_CounterMode;uint16_t TIM_Period;uint16_t TIM_ClockDivision;uint8_t TIM_RepetitionCounter;TIM_TimeBaseInitTypeDef; 该结构体共有 5 个成员变量。需要注意的是,只有前四个参数对通用定时器有用,最后一个参数 TIM_RepetitionCounter 只对高级定时器有用,这里不多解释。TIM_TimeBaseInitTypeDef; 该结构体共有 5 个成员变量。需要注意的是,只有前四个参数对通用定时器有用,最后一个参数 TIM_RepetitionCounter 只对高级定时器有用,这里不多解释。TIM_TimeBaseInitTypeDef; 该结构体共有 5 个成员变量。需要注意的是,只有前四个参数对通用定时器有用,最后一个参数 TIM_RepetitionCounter 只对高级定时器有用,这里不多解释。

第一个参数TIM_Prescaler用于设置分频系数,如上所述。第二个参数 TIM_CounterMode 用于设置计数模式。如上所述,它可以设置为向上计数、向下计数和居中对齐。比较常用的是上计数模式TIM_CounterMode_Up和下计数模式TIM_CounterMode_Down。第三个参数是设置自动重载计数周期值,前面已经解释过了。第四个参数用于设置时钟分频因子。针对TIM3初始化初始化代码格式: TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure; TIM_TimeBaseStructure.TIM_Period 5000;TIM_TimeBaseStructure.TIM_Prescaler =7199; TIM_TimeBaseStructure.TIM_ClockDivision TIM_CKD_DIV1;TIM_TimeBaseStructure.TIM_CounterMode TIM_CounterMode_Up;TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); <

因为我们要使用TIM3的更新中断,所以寄存器的对应位可以使能更新中断。在库函数中,定时器中断使能通过TIM_ITConfig函数实现: void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState); 第一个参数是选择定时器编号,比较容易理解,取值是TIM1~TIM17。第二个参数非常关键,用于指示我们启用的定时器中断类型。定时器中断的种类很多,包括更新中断TIM_IT_Update、触发中断TIM_IT_Trigger、输入捕捉中断等。第三个参数很简单,就是禁用或启用。比如我们要开启TIM3的更新中断,格式为:TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE 4) TIM3 中断优先级设置。定时器中断使能后,由于要产生中断,所以必须设置NVIC相关寄存器,设置中断优先级。如何使用NVIC_Init函数设置中断优先级我之前已经解释过很多次了,这里不再重复解释。5)允许TIM3工作,即开启TIM3。只配置定时器 还没有,定时器没有开启,还是不能用。即启用 TIM3。只配置定时器 还没有,定时器没有开启,还是不能用。即启用 TIM3。只配置定时器 还没有,定时器没有开启,还是不能用。

配置完成后我们需要开启定时器,定时器由 TIM3_CR1 的 CEN 位设置。固件库中开启定时器的函数是通过TIM_Cmd函数实现voidTIM_Cmd(TIM_TypeDef* TIMx,FunctionalState NewState)。这个功能非常简单。比如我们要开启定时器3,方法是:TIM_Cmd(TIM3, ENABLE); //启用TIMx外设6)写中断服务函数。STM32F4开发指南(库函数版) ALIENTEK Explorer STM32F407开发板教程 204 最后需要编写定时器中断服务函数,通过该函数处理定时器产生的相关中断。中断产生后,产生的中断类型由状态寄存器的值决定。然后进行相关操作,我们在这里使用更新(溢出)中断,所以它位于状态寄存器 SR 的最低位。处理完中断后,向 TIM3_SR 的最低位写 0 清除中断标志。在固件库函数中,用来读取中断状态寄存器的值来判断中断类型的函数是: ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t) 该函数的作用是判断定时器的中断类型是否为TIM_IT TIMx 被中断。

比如我们需要确定(TIM_GetITStatus(TIM3, TIM_IT_Update) RESET){}固件库中清除中断标志位的函数为: void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT) 这个函数的作用是清除定时器 TIMx TIM_IT 标志位的中断。使用起来非常简单。比如TIM3的溢出中断发生后,我们需要清除中断标志位,方法是:TIM_ClearITPendingBit(TIM3, TIM_IT_Update 这里需要说明一下,固件库还提供了两个函数来判断定时器状态和清除定时器状态标志位的函数TIM_GetFlagStatus和TIM_ClearFlag,它们的作用与前面两个函数类似,只有TIM_GetITStatus函数会先判断中断是否使能,然后中断标志位被使能后进行判断,而 TIM_GetFlagStatus 则直接用来判断状态标志位。通过以上步骤什么是定时器中断函数,就可以达到我们的目的,利用通用定时器的更新中断来控制DS1 13.2 硬件设计 本实验用到的硬件资源有: 指示灯 DS0 和 DS1 定时器 TIM3 这个本章将通过TIM3的中断来控制DS1的开/关,DS1直接连接PF10,前面已经介绍过了。

TIM3属于STM32F4的内部资源,只需要软件设置即可正常工作。13.3 软件设计 打开我们的CD-ROM 实验 8 定时器中断实验 可以看到我们项目中HARDWARE 下的time.c 文件(包括头文件time.h)比以前多了一个。这两个文件都是我们自己写的。同时还介绍了定时器相关的固件库函数文件stm32f4xx_tim.c和头文件stm32f4xx_tim.h。现在让我们看看我们的 time.c 文件。timer.c文件的代码如下: //通用定时器3中断初始化 //arr: auto-reload value。psc:clock prescaler //定时器溢出时间的计算方法:Tout=((arr+1)*(psc+1))/Ft us. //Ft=定时器工作频率,单位:Mhz //定时器3在这里使用!无效 TIM3_Int_Init(u16 arr,u16 psc) TIM_TimeBaseInitTypeDefTIM_TimeBaseInitStructure; NVIC_InitTypeDef NVIC_InitStructure; STM32F4开发指南(库函数版)ALIENTEK Explorer STM32F407开发板教程205 RCC_APB1PeriphClockCmd(RCCABLE_APB1Periph_TIM3,ENCABLE); Enable TIM3 clock TIM_TimeBaseInitStructure.TIM_Period arr;//自动重载值 TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //定时器分频 TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; 启用);Enable TIM3 clock TIM_TimeBaseInitStructure.TIM_Period arr;//自动重载值 TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //定时器分频 TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; 启用);Enable TIM3 clock TIM_TimeBaseInitStructure.TIM_Period arr;//自动重载值 TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //定时器分频 TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //向上计数模式 TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;

TIM3_Int_Init函数就是执行我们上面介绍的5个步骤,让TIM3开始工作,开启中断。这里我们使用标签~来标记定时器初始化的五个步骤。该函数的参数用于设置 TIM3 的溢出时间。因为APB1的时钟在系统初始化SystemInit函数中已经初始化为4分频,所以APB1的时钟为42M,从STM32F4的内部时钟树图来看(图4.3.1.1) 知道:当 APB1 的时钟分频器为 1 时,TIM2~7 和 TIM12~14 的时钟为 APB1 的时钟,如果 APB1 的时钟分频器不为 1,则时钟TIM2~7和TIM12的~14的时钟频率会是APB1时钟的两倍,所以TIM3时钟为84M,并且根据我们设计的arr和psc的值,可以计算出中断时间。计算公式如下: Tout= ((arr+1)*(psc+1))/Tclk; 其中: Tclk:TIM3的输入时钟频率(单位为Mhz)。 Tout:TIM3溢出时间(单位是us).Timer.h头文件内容比较简单,这里就不解释了。STM32F4开发指南(库函数版)ALIENTEK Explorer STM32F407开发板教程206 最后我们看一下主要的函数代码如下: intmain(void) NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//设置系统中断优先级分组 2delay_init(168); //初始化延时函数 LED_Init(); //初始化LED端口TIM3_Int_Init(5000-1,8400 -1);//定时器时钟84M,分频系数为8400,

这里,500ms的定时器持续时间就是这样计算出来的。定时器的时钟是84Mhz,分频系数是8400,所以分频后的计数频率是84Mhz/8400=10KHz,然后计数到5000,所以持续时间是5000/10000 =0.5s ,即 500 毫秒。13.4 下载验证 完成软件设计后,我们将编译好的文件下载到Explorer STM32F4开发板,看看运行结果是否和我们写的一致。如果没有错误,我们会看到DS0连续闪烁(每400ms),DS1也在闪烁,但闪烁时间比DS0慢(1s时间)。

© 版权声明
THE END
喜欢就支持一下吧
点赞0
分享
评论 抢沙发

请登录后发表评论