14 . 定时器中断实验

Posted 孤帆工作室

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了14 . 定时器中断实验相关的知识,希望对你有一定的参考价值。

定时器中断实验

我们介绍了STM32F1 的外部中断,我们来学习下定时器中断。STM32F1 的定时器功能非常强大,其包含 2 个基本定时器(TIM6、TIM7)、4 个通用定时器(TIM2-TIM5)和2 个高级定时器(TIM1、TIM8),共计8 个 。我们以通用定时器为例进行讲解,学会STM32F1 定时器的使用。要实现的功能是:通过TIM4 中断控制D2 指示灯闪烁,主函数控制D1 指示灯闪烁。

定时器介绍

STM32F1 的定时器非常多,基本定时器的功能最为简单,类似于51 单片机内定时器。通用定时器是在基本定时器的基础上扩展而来,增加了输入捕获与输出比较等功能。高级定时器又是在通用定时器基础上扩展而来,增加了**可编程死区互补输出、重复计数器、带刹车(断路)**功能,这些功能主要针对工业电机控制方面。高级定时器不做过多介绍,主要针对常用的通用定时器进行讲解,如需对定时器深入了解,可以参考《STM32F10x 中文参考手册》-13、14、15 定时器章节,这么大篇幅对定时器的介绍,足见STM32F10x 定时器之重要性。如果学会了使用通用定时器,那么你也学会了基本定时器用法,至于高级定时器,只是比通用定时器多了那几个功能,你只要把对应的那几个功能对照中文参考手册看下即可。

通用定时器简介

STM32F1 的通用定时器包含一个16 位自动重载计数器(CNT),该计数器由可编程预分频器(PSC)驱动。STM32F1 的通用定时器可用于多种用途,包括测量输入信号的脉冲宽度(输入捕获)或者生成输出波形(输出比较和PWM)等。使用定时器预分频器和RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。STM32F1 的每个通用定时器都是完全独立的,没有互相共享的任何资源。STM32F1 的通用定时器TIMx (TIM2-TIM5 )具有如下功能:
(1)16 位向上、向下、向上/向下自动装载计数器(TIMx_CNT)。
(2)16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为1~65535 之间的任意数值。
(3)4 个独立通道(TIMx_CH1-4),这些通道可以用来作为:
A.输入捕获
B.输出比较
C. PWM 生成(边缘或中间对齐模式)
D.单脉冲模式输出
(4)可使用外部信号(TIMx_ETR)控制定时器,且可实现多个定时器互连(可以用1 个定时器控制另外一个定时器)的同步电路。
(5)发生如下事件时产生中断/DMA 请求:
A.更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
B.触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
C.输入捕获
D.输出比较
(6)支持针对定位的增量(正交)编码器和霍尔传感器电路
(7)触发输入作为外部时钟或者按周期的电流管理
通用定时器结构框图
其实那些通用定时器功能,是由通用定时器内部结构所致。内部结构框图如图19.1.1 所示:(大家可以对照中文参考手册中基本定时器和高级定时器的结构框图对比下这几部分,自然就明白它们的功能差别)

在通用定时器结构框图中我们可以看到有两种奇怪的箭头,它们的意义如下:

我们把通用定时器结构框图分成5 个子模块,按照顺序依次进行简单介绍。
(1)标号1:时钟源通用定时器的时钟来源有4 种可选:
A.内部时钟(CK_INT)
B.外部时钟模式1:外部输入引脚TIx(x=1,2,3,4)
C.外部时钟模式2:外部触发输入ETR
D.内部触发输入(ITRx(x=0,1,2,3))
通用定时器时钟来源这么多,具体选择哪个可以通过TIMx_SMCR 寄存器的相关位来设置,定时器相关寄存器的介绍可以参考《STM32F10x 中文参考手册》-通用定时器-寄存器章节详细了解。这里的CK_INT 时钟是从APB1 倍频得来的,除非APB1 的时钟分频数设置为1(一般都不会是1),否则通用定时器TIMx的时钟是APB1 时钟的2 倍,当APB1 的时钟不分频的时候,通用定时器TIMx的时钟就等于APB1 的时钟。这里还要注意的就是高级定时器的时钟不是来自APB1,而是来自APB2,这个在库文件stm32f10x_rcc.h 也可以查找到,如下:

通常我们都是将内部时钟(CK_INT)作为通用定时器的时钟来源,而且通用定时器的时钟是APB1 时钟的2 倍,即APB1 的时钟分频数不为1。所以通用定时器的时钟频率是72MHz。
(2)标号2:控制器
通用定时器控制器部分包括触发控制器、从模式控制器以及编码器接口。触发控制器用来针对片内外设输出触发信号,比如为其它定时器提供时钟和触发DAC/ADC 转换。从模式控制器可以控制计数器复位、启动、递增/递减、计数。编码器接口专门针对编码器计数而设计。
(3)标号3:时基单元
通用定时器时基单元包括3 个寄存器,分别是计数器寄存器(TIMx_CNT)、预分频器寄存器(TIMx_PSC)、自动重载寄存器(TIMx_ARR)。高级定时器中还有一个重复计数寄存器(TIMx_RCR),通用和基本定时器是没有的。通用定时器这三个寄存器都是16 位有效。而高级定时器的TIMx_RCR 寄存器是8 位有效。在这个时基单元中,有个预分频器寄存器(TIMx_PSC),用于对计数器时钟频率进行分频,通过寄存器内的相应位设置,分频系数值可在1 到65536 之间。由于从模式控制寄存器具有缓冲功能,因此预分频器可实现实时更改,而新的预分频比将在下一更新事件发生时被采用。在时基单元中,还有个计数寄存器(TIMx_CNT),通用定时器计数方式有向上计数、向下计数、向上向下计数(中心对齐计数)。下面分别来介绍下这几种计数方式:
1.向上计数
在向上(递增)计数模式下,计数器从0 开始计数,每来一个CK_CNT 脉冲计数器就加1,直到等于自动重载值(TIMx_ARR 寄存器的内容),然后重新从0开始计数并生成计数器上溢事件。每次发生计数器上溢时会生成更新事件(UEV),或将TIMx_EGR 寄存器中的UG 位置1(通过软件或使用从模式控制器)也可以生成更新事件。通过软件将TIMx_CR1 寄存器中的UDIS 位置1 可禁止UEV 事件。这可避免向预装载寄存器写入新值时更新影子寄存器。在UDIS 位写入0 之前不会产生任何更新事件。不过,计数器和预分频器计数器都会重新从0 开始计数(而预分频比保持不变)。此外,如果设置TIMx_CR1 寄存器中相应的中断位置1,也会产生中断事件。
2.向下计数
在向下(递减)计数模式下,计数器从自动重载值( TIMx_ARR 寄存器的内容)开始递减计数到0,然后重新从自动重载值开始计数并生成计数器下溢事件。每次发生计数器下溢时会生成更新事件,或将TIMx_EGR 寄存器中的UG 位置1(通过软件或使用从模式控制器)也可以生成更新事件。通过软件将TIMx_CR1寄存器中的UDIS 位置1 可禁止UEV 更新事件。这可避免向预装载寄存器写入新值时更新影子寄存器。在UDIS 位写入0 之前不会产生任何更新事件。不过,计数器会重新从当前自动重载值开始计数,而预分频器计数器则重新从0 开始计数(但预分频比保持不变)。此外,如果设置TIMx_CR1 寄存器中相应的中断位置1,也会产生中断事件。
3.向上向下计数(中心对齐计数)
在中心对齐模式下,计数器从0 开始计数到自动重载值( TIMx_ARR 寄存器的内容)-1,生成计数器上溢事件;然后从自动重载值开始向下计数到1 并生成计数器下溢事件。之后从0 开始重新计数,如此循环执行。每次发生计数器上溢和下溢事件都会生成更新事件。在时基单元中,还有个自动重载寄存器(TIMx_ARR),该寄存器是用来放与CNT 计数器比较的值。自动重载寄存器(TIMx_ARR)的控制受TIMx_CR1 寄存器中ARPE 位决定,当ARPE=0 时,自动重载寄存器(TIMx_ARR)不进行缓冲,寄存器内
容直接传送到影子寄存器。当APRE=1 时,在每一次更新事件( UEV)时,才把预装载寄存器(ARR)的内容传送到影子寄存器。
(4)标号4:输入捕获
输入捕获可以对输入的信号的上升沿,下降沿或者双边沿进行捕获,通常用于测量输入信号的脉宽、测量PWM 输入信号的频率及占空比。在输入捕获模式下,当相应的ICx 信号检测到跳变沿后,将使用捕获/比较寄存器(TIMx_CCRx)来锁存计数器的值。发生捕获事件时,会将相应的CCxIF 标志( TIMx_SR 寄存器)置1,并可发送中断或DMA 请求(如果已使能)。如果发生捕获事件时CCxIF 标志已处于高位,则会将重复捕获标志CCxOF( TIMx_SR寄存器)置1。可通过软件向CCxIF 写入0 来给CCxIF 清零,或读取存储在TIMx_CCRx 寄存器中的已捕获数据。向CCxOF 写入0 后会将其清零。输入捕获单元由输入通道、输入滤波器和边沿检测器、输入捕获通道、预分频器、捕获/比较寄存器等组成。从框图中可以看到,通用定时器的输入通道有4 个TIMx_CH1/2/3/4。通常也把这个4 个通道称为TI1/2/3/4,如果后面出现此类称呼就表明是通用定时器的4 个输入通道。这些通道都对应到STM32F1 引脚上,可以把被检测信号输入到这4 个通道中进行捕获。当输入的信号存在高频干扰时,可以使用输入滤波器进行滤波,具体滤波原理可以参考《STM32F10x 中文参考手册》通用定时器捕获/比较通道章节。边沿检测器是用来设置信号在捕获时哪种边沿有效,可以为上升沿、下降沿、双边沿,具体是由TIMx_CCER 寄存器相应位设置。输入捕获通道就是框图中的IC1/2/3/4,每个捕获通道都有对应的捕获寄存器TIMx_CCR1/2/3/4。如果发生捕获的时候,CNT 计数器的值就会被锁存到捕获寄存器中。这里我们要搞清楚输入通道和捕获通道的区别,输入通道是用来输入信号的,捕获通道是用来捕获输入信号的通道,一个输入通道的信号可以同时输入给两个捕获通道。比如输入通道TI1 的信号经过滤波边沿检测器之后的TI1FP1和TI1FP2 可以进入到捕获通道IC1 和IC2,在前面的框图中也可以看到信号箭头的流向。ICx 的输出信号会经过一个预分频器,用于决定产生多少个事件时进行一次捕获。具体由寄存器TIMx_CCMRx 的位ICxPSC 配置,如果希望捕获信号的每一个边沿,则把预分频器系数设置为1。经过预分频器的信号ICxPS 是最终被捕获的信号,当发生第一次捕获时,计数器CNT 的值会被锁存到捕获/比较寄存器
CCR 中(此时使用捕获寄存器功能),还会产生CCxI 中断,相应的中断位CCxIF(在SR 寄存器中)会被置位,通过软件或者读取CCR 中的值可以将CCxIF 清0。如果发生第二次捕获(即重复捕获:CCR 寄存器中已捕获到计数器值且CCxIF
标志已置1),则捕获溢出标志位CCxOF(在SR 寄存器中)会被置位,CCxOF 只能通过软件清零。
(5)输出比较
输出比较就是通过定时器的外部引脚对外输出控制信号,可以输出有效电平、无效电平、翻转、强制变为无效电平、强制变为有效电平、PWM1 和PWM2等模式,具体使用哪种模式由寄存器CCMRx 的位OCxM[2:0]配置。其中PWM 模式是输出比较中的特例,使用的也最多。从框图中可以看到,输出比较单元与输入捕获单元共用了捕获/比较寄存器,只不过在输出比较的时候使用的是比较寄存器功能。当计数器CNT 的值与比较寄存器CCR 的值相等的时候,输出参考信号OCxREF 的信号的极性就会改变,并且会产生比较中断CCxI,相应的标志位CCxIF(SR 寄存器中)会置位。然后OCxREF 再经过一系列的控制之后就成为真正的输出信号OC1/2/3/4,最终输出到对应的管脚TIMx_CH1/2/3/4。

通用定时器配置步骤

接下来我们介绍下如何使用库函数对通用定时器进行配置。这个也是在编写程序中必须要了解的。具体步骤如下:(定时器相关库函数在stm32f10x_tim.c和stm32f10x_tim.h 文件中)
(1)使能定时器时钟
定时器实验,我们使用的是通用定时器TIM4,我们知道TIM4 是挂接在APB1 总线上的,所以可以使用APB1 总线时钟使能函数来使能TIM4,调用的库函数如下:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);// 使能TIM4 时钟

(2)初始化定时器参数,包含自动重装值,分频系数,计数方式等要使用定时器功能,必须对定时器内相关参数初始化,其库函数如下:

voidTIM_TimeBaseInit(TIM_TypeDef*TIMx,TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

函数中第一个参数是用来确定哪个定时器,例如TIM4;第二个参数是一个结构体指针变量,结构体类型是TIM_TimeBaseInitTypeDef,其内包含了定时器初始化的成员变量。下面我们就来看下这个结构体:

typedef struct

uint16_t TIM_Prescaler; //定时器预分频器
uint16_t TIM_CounterMode; //计数模式
uint32_t TIM_Period; //定时器周期
uint16_t TIM_ClockDivision; //时钟分频
uint8_t TIM_RepetitionCounter; //重复计数器
 TIM_TimeBaseInitTypeDef;

TIM_TimeBaseInitTypeDef 结构体内含有5 个成员变量,前4 个在通用定时器中会使用到,最后一个是在高级定时器中才会用到。下面就来简单介绍下每个成员变量的功能:

TIM_Prescaler:定时器的预分频器系数,时钟源经过该预分频器后输出的才是定时器时钟,设置值范围:0-65535,分频系数由于是除数,分母不能为0,所以会自动加1,最后实现1-65536 分频。
TIM_CounterMode:定时器计数方式,前面讲解过,可以设置为向上、向下、中心对齐计数方式。比较常用的是向上计数模式(TIM_CounterMode_Up)和向下计数模式(TIM_CounterMode_Down)。
TIM_Period:设置定时器自动重载计数周期值,在事件产生时更新到影子寄存器。可设置范围为0 至65535。
TIM_ClockDivision:时钟分频因子,设置定时器时钟CK_INT 频率与数字滤波器采样时钟频率分频比。
TIM_RepetitionCounter:重复计数器,通过此参数可以非常简单的控制
PWM 输出个数。此成员只针对于高级定时器配置,基本定时器与通用定时器不用设置。

了解结构体成员功能后,就可以进行配置,例如:

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_Period=1000; //自动装载值
TIM_TimeBaseInitStructure.TIM_Prescaler=35999; //分频系数
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;

//设置向上计数模式

TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);

(3)设置定时器中断类型,并使能对定时器中断类型和使能设置的函数如下:void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
第一个参数用来选择哪个定时器,例如TIM4。
第二个参数用来设置定时器中断类型,定时器的中断类型非常多,包括更新中断TIM_IT_Update、触发中断TIM_IT_Trigger、输入捕获中断等等。
第三个参数用来使能或者失能定时器中断类型,可以为ENABLE 和DISABLE。

例如我们要使能定时器TIM4 更新中断,调用函数如下:

TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); //开启定时器中断

(4)设置定时器中断优先级,使能定时器中断通道在上一步我们已经使能了定时器的更新中断,只要使用到中断,就必需对
NVIC 初始化,NVIC 初始化库函数是NVIC_Init(),这个在前面讲解STM32 中断时就已经介绍过,不清楚的可以回过头看下。
(5)开启定时器
前面几个步骤已经将定时器配置好,但还不能正常使用,只有开启定时器了才能让它正常工作,开启定时器的库函数如下:
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
第一个参数是用来选择定时器。
第二个参数是用来使能或者失能定时器,也就是开启或者关闭定时器功能。同样可以选择ENABLE 和DISABLE。
例如我们要开启TIM4,那么调用此函数如下:

TIM_Cmd(TIM4,ENABLE); //开启定时器

(6)编写定时器中断服务函数
最后我们还需要编写一个定时器中断服务函数,通过中断函数处理定时器产生的相关中断。定时器中断服务函数名在STM32F1 启动文件内就有,TIM4 中断函数名如下:

TIM4_IRQHandler

因为定时器的中断类型有很多,所以进入中断后,我们需要在中断服务函数开头处通过状态寄存器的值判断此次中断是哪种类型,然后做出相应的控制。库函数中用来读取定时器中断状态标志位的函数如下:

ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);

此函数功能是判断TIMx 的中断类型TIM_IT 是否产生中断,例如我们要判断TIM4 的更新(溢出)中断是否产生,可以调用此函数:

if(TIM_GetITStatus(TIM4,TIM_IT_Update))

...//执行TIM4 更新中断内控制

如果产生更新中断,那么调用TIM_GetITStatus 函数后返回值为1,就会进入到if 函数内执行中断控制功能程序。否则就不会进入中断处理程序。在编写定时器中断服务函数时,最后都会调用一个清除中断标志位的函数,如下:

void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

里面的两个参数功能和前面读取定时器中断状态标识位函数一样,例如我们要清除TIM4 的更新中断标志位,调用函数如下:

TIM_ClearITPendingBit(TIM4,TIM_IT_Update);

固件库中还有两个函数是用来读取状态标志位以及清除中断标志位,函数分别为TIM_GetFlagStatus 和TIM_ClearFlag。
这两个函数作用与前面两个函数类似,只不过TIM_GetITStatus 函数会先判断中断是否使能,使能了才会读取中断状态标志位,而TIM_GetFlagStatus 函数是直接判断状态标志位。将以上几步全部配置好后,我们就可以正常使用定时器中断了。

硬件设计

本章硬件电路非常简单,只使用到开发板上的LED(D1 和D2),至于TIM4它属于STM32F1 芯片内部的资源。开发板上LED 模块电路在前面已经介绍,这里不多说。

软件设计

所要实现的功能是:通过TIM4 的更新中断控制D2 指示灯间隔500ms秒状态取反,主函数控制D1 指示灯不断闪烁。程序框架如下:
(1)初始化TIM4,并使能更新中断等
(2)编写TIM4 中断函数
(3)编写主函数
在前面介绍定时器配置步骤时,就已经讲解如何初始化定时器。在APP 工程组中可以看到添加time.c 文件, 在StdPeriph_Driver 工程组中添加了stm32f10x_tim.c 库文件。定时器操作的库函数都放在stm32f10x_tim.c 和stm32f10x_tim.h 文件中,所以使用到定时器就必须加入stm32f10x_tim.c 文件,同时还要包含对应的头文件路径。

TIM4 初始化函数
要使用定时器中断,我们必须先对它进行配置。TIM4 初始化代码如下:

/****************************************************************
***************
* 函数名: TIM4_Init
* 函数功能: TIM4 初始化函数
* 输入: per:重装载值
psc:分频系数
* 输出: 无
*****************************************************************
**************/
void TIM4_Init(u16 per,u16 psc)

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
NVIC_InitTypeDef NVIC_InitStructure;
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM4,ENABLE);//使能TIM4时钟
TIM_TimeBaseInitStructure.TIM_Period=per; //自动装载值
TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系数
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//设置向上计数模式
TIM_TimeBaseInit(TIM4,&TIM_TimeBaseInitStructure);
TIM_ITConfig(TIM4,TIM_IT_Update,ENABLE); //开启定时器中断
TIM_ClearITPendingBit(TIM4,TIM_IT_Update);
NVIC_InitStructure.NVIC_IRQChannel = TIM4_IRQn;//定时器中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;// 抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =3; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);
TIM_Cmd(TIM4,ENABLE); //使能定时器

在TIM4_Init()函数中,首先使能TIM4 时钟,其次配置定时器结构体TIM_TimeBaseInitStructure,使能TIM4 的更新中断,为了防止定时器中断状态标志位默认值的影响,我们清除下TIM4 的更新中断标志,然后配置相应的NVIC并使能对应中断通道,我们将TIM4 的抢占优先级设置为2,响应优先级设置为3。最后就是对开启TIM4。这一过程在前面步骤介绍中已经提了。TIM4_Init()函数有两个参数,用来设置定时器的自动装载值和分频系数,方便大家修改定时时间。其实如果你会使用通用定时器TIM4,那么其他通用定时器都一样,如果要使用基本定时器,那就更简单,因为通用定时器已经包含了基本定时器的功能。至于高级定时器大家可以参考中文参考手册了解,配置过程只是增加一个复位计数寄存器的配置。

TIM4 中断函数
初始化TIM4 后,中断就已经开启了,当TIM4 内计数器CNT 发生更新(溢出)事件,就会产生一次中断,具体代码如下:

/****************************************************************
***************
* 函数名: TIM4_IRQHandler
* 函数功能: TIM4 中断函数
* 输入: 无
* 输出: 无
*****************************************************************
**************/
void TIM4_IRQHandler(void)

if(TIM_GetITStatus(TIM4,TIM_IT_Update))

led2=!led2;

TIM_ClearITPendingBit(TIM4,TIM_IT_Update);

为了确认定时器是否发生更新中断,调用了读取定时器中断状态标志位函数TIM_GetITStatus(TIM4,TIM_IT_Update),如果确实产生更新中断,那么就会执行if 内的语句,从而控制D2 指示灯状态取反。最后要记得清除下定时器中断状态标志位。

主函数
编写好定时器初始化和中断服务函数后,接下来就可以编写主函数了,代码如下:

/****************************************************************
***************
* 函数名: main
* 函数功能: 主函数
* 输入: 无
* 输出: 无
*****************************************************************
**************/
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "time.h"
int main()

u8 i;
SysTick_Init(72);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组分2 组
LED_Init();
TIM4_Init(1000,36000-1); //定时500ms
while(1)

i++;
if(i%20==0)

led1=!led1;

delay_ms(10);


主函数实现的功能很简单,首先初始化对应的硬件端口时钟和IO 口,然后调用我们前面编写的TIM4 的初始化函数,这里我们设定定时器自动重装载值为1000,预分频系数为36000-1,这里减1 是因为定时器预分频器内部会自动加1,所以如果要进行36000 分频的话,就传递35999。最后进入while 循环语句,不断让D1 指示间隔200ms 闪烁。初始化后,定时器开始工作,计数器CNT 从0 开始计数,每来一个定时器时钟,计数器值就会累加一次,当计数到1000 也就是累积计数1000 次,这个时候定时器就发生溢出并产生更新中断,每产生一次更新中断时间是500ms。有的朋友就会问,这个定时500ms 是怎么计算的?很简单,我们知道TIM4 是挂接在APB1 总线上的,而APB1 的时钟是36MHz,前面介绍定时器就说过,如果APB1 时钟分频系数为1,TIM2-7 的时钟即为APB1总线的时钟,否则就是APB1 总线时钟的2 倍,即72MHz。再根据刚才设计的自动重装载值和预分频系数就可以计算出定时时间,计算公式如下:

Tout= ((per)*(psc+1))/Tclk;

Tclk 是定时器的时钟频率值,72M,per 和psc 是我们传递的参数值,Tout是定时器产生中断的时间,单位是us。将数据代入即可得到产生定时更新中断时间是500ms。

以上是关于14 . 定时器中断实验的主要内容,如果未能解决你的问题,请参考以下文章

14 . 定时器中断实验

14 . 定时器中断实验

STM32-定时器详解

定时器最大接口时钟我定时时钟区别

STM32定时器中断周期

STM32F4 定时器 - 预分频器或周期值必须除以二才能得到我期望的结果