九、TIM定时器中断

TIM(Timer)定时器

(1)定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断
(定时器有计数器的功能,当计数器的输入是一个准确可靠的基准时钟时,对基准时钟计数就是计时的过程。在STM32中,定时器的基准时钟一般是主频72MHz,若对72MHz计72个数,就是1MHz,1us的时间;若对72MHz计72000个数,就是1KHz,1ms的时间)
(2)16位计数器(用来执行计数定时的一个寄存器,每来一个时钟,计数器加一)、预分频器 (对计数器的时钟进行分频,让计数更加灵活)、自动重装寄存器(计数的目标值,就是想要多少个时钟申请中断)的时基单元。这些寄存器组成了定时器最核心的部分(时基单元)
(3)在72MHz计数时钟下可以实现最大59.65s的定时。2的16次方是65536,若预分频器设置最大,自动重装也设置最大,定时器的最大定时时间就是59.65s接近一分钟。(中断频率即每秒产生的中断数=每秒计数次数(72MHz/65536)/触发中断的计数值65536,取倒数就是中断周期/间隔时间59.65s)想要定时时间变长,可以使用定时器的级联模式(一个定时器的输出当作另一个定时器的输入,加在一起,最大定时时间=59.65*2的65536次方,八千多年)
(4)不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能.
(5)根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型在这里插入图片描述
STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4

基本定时器

在这里插入图片描述
预分频器(TIMx_PSC)、计数器(TIMx_CNT)、自动重装寄存器(TIMx_ARR)的时基单元(最基本的计数计时电路)
预分频器之前连接的是基准计数时钟的输入,由于基本定时器只能选择内部时钟,可以认为线直接连接到直接内部时钟CK_INT,来自于RCC_TIMxCLK,这里的频率值一般是系统的主频72MHz,故通向时基单元的计数基准频率就是72MHz。

预分频器PSC

时基单元中的预分频器可以对72MHz的计数时钟进行预分频,预分频器写0就是不分频或者1分频,输出频率=输入频率=72MHz;写1就是2分频,输出频率=输入频率/2=36MHz………故预分频器的值和实际分频系数相差1,实际分频系数=预分频器的值+1。
这个预分频器16位,最大值65535,就是65536分频。预分频器就是对输入的基准频率提前进行一个分频操作。

计数器CNT

计数器可以对预分频后的计数时钟进行计数,计数时钟每来一个上升沿计数器值加1,计数器也是16位的,值可以从0一直加到65535,再加就会回到0重新开始。故计数器的值在计时过程中会不断地自增运行,当自增运行到目标值时,产生中断,完成定时的任务。

自动重装寄存器ARR

自动重装寄存器是可以存储目标值的寄存器,也是16位,存储我们写入的计数目标,在运行过程中,计数值不断自增,自动重装值是固定目标,当计数值等于自动重装值时,就是计时时间到了,清零计数器,并产生中断信号,计数器开始下一次的计数计时
在这里插入图片描述
向上折的箭头代表这里产生中断信号,计数值=自动重装值产生的自动叫做“更新中断”,通往NVIC,再配置好NVIC的定时器通道,定时器的更新中断就可以得到CPU的响应了。
向下的箭头代表产生一个事件“更新事件”,不触发中断,但可以触发内部其他电路工作。

主从触发模式

TRGO主模式触发DAC功能,这个主从触发模式是STM32的一个特色,能让内部硬件在不受程序的控制下实现自动运行,可以减轻CPU的负担。
在使用DAC的时候,用DAC输出一段波形,需要每隔一段时间触发一次DAC,让其输出下一个电压点。(先设置一个定时器产生中断,每隔一段时间在中断程序中调用代码手动触发一次DAC转换,然后DAC输出。但是主程序处于频繁被中断,影响主程序的运行和其他中断的响应)
使用主模式将定时器的更新事件映射到触发输出TRGO(Trigger Out)的位置,TRGO直接接到DAC的触发转换引脚上,这样定时器的更新就不用再通过中断来触发DAC转换了,仅需要把更新事件通过主模式映射到TRGO,然后TRGO就会直接去触发DAC了。整个过程不需要软件的参与,实现了硬件的自动化,这就是主模式的作用

通用定时器

在这里插入图片描述
一、中间核心部分:时基单元(和基本定时器相同),对于基本定时器,其计数器的计数模式只有向上计数这一种。通用寄存器和高级寄存器支持向上、向下、中央三种计数方式。
向上计数(从0开始,向上自增计到重装值,清零同时申请中断);向下计数(从重装值开始,向下自减至0,回到重装值同时申请中断);中央对其的模式计数(从0开始,先向上自增,计到重装值申请中断,然后向下自减至0,再中断)
二、上半部分是内外时钟源选择和主从触发模式的结构。
时钟输入也就是内外时钟源选择,基本定时器只能选择内部时钟(系统频率72MHz);
通用定时器,时钟源不仅可以选择内部的72MHz时钟(最常用),还可选择外部时钟如下:
(1)ETR引脚(常用)。“外部时钟模式2”来自TIMx_ETR引脚上的外部时钟,ETR引脚位置参考引脚定义表(如图)可以在TIM2的ETR引脚也就是PA0上接一个外部方波时钟,因为外部引脚的时钟有些毛刺,配置内部的极性选择、边沿检测和预分频电路还有输入滤波电路,对外部时钟进行整形,对输入的波形进行滤波,滤波后的信号兵分两路,一路ETRF进入触发控制器,然后选择作为时基单元的时钟。这种情况适合想在ETR外部提供时钟或相对ETR时钟进行计数,将这个定时器当做计数器用。
(2)其他定时器。TRGI(Trigger In)主要用于触发输入来使用,可以触发定时器的从模式,也可以作为外部时钟来使用(“外部时钟模式1”),通过这一路的时钟有:1.ETR引脚的信号(可以通过两路当作时钟);2.ITR信号,这部分时钟信号来自其他定时器,这个主模式的输出TRGO可以通向其他定时器,接到其他定时器ITR引脚上,这里的ITR0到ITR3分别来自其他4个定时器的TRGO输出。通过这里可以实现定时器级联功能。3.CH1引脚的边沿。TI1F_ED,来自输入捕获的单元的CH1引脚,也就是从CH1引脚获得时钟,ED(Edge)是边沿的意思,就是通过这一路输入的时钟,上升沿和下降沿均有效。4.CH1引脚和CH2引脚。通过TI1FP1和TI2FP2获得时钟,TI1FP1是CH1引脚的时钟,TI1FP2是CH2引脚的时钟。
三、下右是输出比较电路,总共有四个通道,分别对应CH1到CH4的引脚,可以用于输出PWM波形,驱动电机;下左是输入捕获电路,也有四个通道,也对应CH1到CH4的引脚,可以用于测量输入方波的频率占空比等;下中的寄存器是捕获/比较寄存器,是输入捕获和输出比较电路共用的,由于输入捕获电路和输出比较电路不能同时使用,故寄存器和引脚都是共用的
缓冲寄存器,图中带有黑色阴影的寄存器都有影子寄存器这样的缓冲机制(下面详细介绍影子寄存器),如:预分频器、自动重装寄存器、捕获比较寄存器。

高级定时器

在这里插入图片描述
相比于通用定时器,右下部分增加了一些。
申请中断的地方增加了一个重复计数计次器,可以实现每隔几个计数周期才发生一次更新事件和更新中断,相当于对输出的更新信号又做了一次分频,提升了很多的定时时间。
这下面是高级定时器对输出比较模块的升级。DTG(Dead Time Generate)是死区生成电路(避免由于器件的不理想造成短暂的直通现象)
刹车输入的功能,为了给电机驱动提供安全保障,若外部引脚BKIN(Break IN)产生了刹车信号或内部时钟失效,产生了故障,控制电路就会自动切断电机的输出,防止发生意外

定时中断基本结构图

在这里插入图片描述
预分频器PSC、计数器CNT、自动重装寄存器ARR(时基单元),下面的运行控制是控制寄存器的一些位(如启动停止、向上向下计数等),操作这些寄存器可以控制时基单元的运行。
左边是为时基单元提供时钟的部分,可以选择RCC提供的内部时钟,也可以选择ETR引脚提供的外部时钟模式2,还可以选择触发输入当做外部时钟即时钟模式1(对应的有ETR外部时钟、ITRx其他定时器、TIx输入捕获通道),以上为定时器所有可选的时钟源。
右边,计时时间到,产生更新中断后的信号去向。
中断信号会先在状态寄存器里置一个中断标志位,标志位通过中断输出控制(时一个中断输出的允许位。由于定时器模块中很多地方都要申请中断,如更新申请中断、触发信号申请中断、输入捕获和输出比较匹配时都会申请中断,这些中断经过中断输出控制,需要的中断允许,不需要的中断禁止),到NVIC申请中断

预分频器时序

在这里插入图片描述
CK_PSC:预分频器的输入时钟,选内部时钟一般为72MHz,时钟不断运行
CNT_EN:计数器使能,高电平计数器正常运行,低电平计数器停止
CK_CNT:计数器时钟,既是预分频器的时钟输出又是计数器的时钟输入
一个计数周期的工作流程如下:
当计数器未使能,计数器时钟不运行,使能后前半段预分频器系数为1,计数器的时钟等于预分频器前的时钟;后半段预分频器的系数变为2,计数器的时钟变为预分频器前时钟的1/2
计数器寄存器:在计数器的驱动下,下面的计数器寄存器也跟随时钟的上升沿不断自增,在中间的位置FC之后,计数值变为0(ARR自动重装寄存器的自动重装值是FC)。当计数值计到和重装值相等,并且下一个时钟来临时,计数值才清零,同时,下面产生一个更新事件。
下面的三行时序描述了预分频器的一种缓冲机制:
预分频寄存器实际有两个:1.预分频控制寄存器用于读写,不直接决定分频系数;2.缓冲寄存器(影子寄存器)是真正起作用的寄存器,在某时刻将预分频器由0改成1,若在此时立即改变时钟的分频系数,会导致一个计数周期内前半部分和后半部分的频率不一样,为了防止计数中途更改数值造成错误,利用缓冲寄存器,这个变化等到本次计数周期结束时产生更新事件,预分频器的值才会被传递到缓冲寄存器里面去,才会生效。
最后预分频器内部也是靠计数来分频的,当预分频值为0时,预分频计数器就一直为0,直接输出原频率,当预分频为1时,计数器就0、1、0、1这样计数,在回到0的时候输出一个脉冲,这样输出频率就是输入频率的2分频。预分频值和实际分频系数之间有一个数的偏移,计数器计数频率:CK_CNT = CK_PSC / (PSC + 1) 。

计数器时序

在这里插入图片描述
CK_INT内部时钟72MHz
CK_CNT:计数器时钟,因为分频系数为2,故此频率是内部时钟频率的1/2。
计数器寄存器:计数器在时钟的上升沿自增,增到0036(ARR自动重装寄存器的值)发生溢出,之后再来一个上升沿,计数器清零。计数器溢出产生一个更新事件脉冲,还会置一个更新中断标志位UIF,此标志位置1就会申请中断,中断响应后需要在中断程序中手动清零。
计数器溢出(中断)频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)
计数器的ARR(自动重装寄存器)也有一个缓冲寄存器,且可以自己设置是否使用缓冲寄存器

计数器无预装时序(无缓冲寄存器)

通过设置ARPE位可以选择是否使用预装功能
在这里插入图片描述
计数中途突然更改自动重装寄存器,由FF改为36,计数值的目标值改变,计数到36之后,直接更新开始下一轮的计数。

计数器有预装时序(有缓冲寄存器)在这里插入图片描述

计数中途突然更改计数目标,由F5改为36,下面的影子寄存器起作用,计数的目标还是计数到F5才产生更新事件,同时要更改的36才被传递到影子寄存器里,在下一个计数周期这个更改的36才有效。
所以影子寄存器的目的是为了同步,让值的变化和更新事件同步发生,防止在运行途中更改造成错误。

RCC时钟树结构图在这里插入图片描述

这个时钟树就是STM32中用来产生和配置时钟,并把配置好的时钟发送到各个外设的系统。
时钟是所有外设运行的基础,需要最先配置,在程序主函数之前会执行一个SystemInit函数,就是用于配置这个时钟树。
左半部分是时钟的产生电路,右半部分是时钟的分配电路,中间SYSCLK是系统时钟72MHz。
时钟的产生电路中有四个震荡源:上面两个高速晶振提供系统时钟,AHB、APB1、APB2的时钟都是来源于这两个高速晶振,这里内部(RC)和外部(OSC)都有一个8MHz的晶振可以使用,外部的石英振荡器比内部的RC振荡器更加稳定,故一般使用外部晶振。
1.内部的8MHz高速RC振荡器(HSI RC);
2.外部的4-16MHz高速石英晶体振荡器(HSE OSC),也就是晶振,一般接8MHz;
3.外部的32.768KHz低速晶振(LSE OSC),一般给RTC提供时钟;
4.内部的40KHz低速RC振荡器(LSI RC),可以给看门狗提供时钟。
在这里插入图片描述
系统时钟,首先启动内部时钟(HIS RC),选择内部8MHz为系统时钟,暂时以8MHz运行;然后再启动外部时钟,进入PLL锁相环进行倍频,8MHz倍频9倍得到72Mhz,待锁相环输出稳定后,选择锁相环输出作为系统时钟,将系统时钟由8MHz切换为72MHz
若外部晶振出问题,系统时钟无法切换到72MHz,以内部8MHz运行,程序的时钟会慢大概10倍(精确来说是9倍)。CSS(时钟安全系统)负责切换时钟,可以检测外部时钟运行状态,一旦外部时钟失效,就会将外部时钟切换成内部时钟,保证系统时钟运行,防止程序卡死。(高级定时器中的刹车输入也有CSS(定时器介绍的地方有写),一旦检测到外部时钟失效,停止输出比较中的输出控制的电机,防止意外)在这里插入图片描述
时钟分配电路:首先系统时钟72MHz进入AHB总线,AHB的预分频器的分频系数在SystenInit里被配置成1,AHB的时钟就是72MHz;然后进入APB1总线,这里配置的分配系数是2,APB1总线的时钟为72/2=36MHz,下面若APB1预分频系数为一,则频率不变,否则频率乘二,定时器2~7的时钟还是72MHz。故高级定时器、通用定时器、基本定时器的内部基准时钟都是72MHz。此外,时钟输出的地方都有一个与门进行输出控制,控制位为外部时钟使能(RCC_APB2/1PeriphClockCmd),这个位置写1,让时钟能够通过与门,输出给外设。

定时器的库函数

1.void TIM_DeInit(TIM_TypeDef* TIMx);//恢复缺省配置
2.void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
//时基单元初始化,1:TIMx选择某个定时器,2:结构体包含配置时基单元的一些参数
3.void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
//将结构体变量赋成一个默认值
4.void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
//运行控制,使能计数器,对应定时中断基本结构图中的运行控制部分。TIMx选择定时器;NewState新状态(计数器的使能或失能)
5.void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
//中断输出控制,使能外设的中断输出信号,对应定时中断基本结构图中的中断输出控制部分。TIMx选择定时器;TIM-IT:选择配置哪个中断输出;NewState使能或失能

时钟源选择用下面的函数,对应定时中断基本结构图中的时基单元的时钟选择部分

1.void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
//选择内部时钟,参数TIMx
2.void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
//选择ITRx其他定时器的时钟,TIMx选择要配置的定时器;TIM_InputTriggerSource选择要接入哪个其他的定时器
3.void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
uint16_t TIM_ICPolarity, uint16_t ICFilter);
//选择TIx捕获通道的时钟。TIMx选择要配置的定时器;TIM_TIxExternalCLKSource选择TIx具体的某个引脚;  ICPolarity输入的极性;ICFilter滤波器
4.void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity ,  uint16_t ExtTRGFilter);
//选择ETR通过外部时钟模式1输入的时钟,ExtTRGPrescaler外部触发预分频器,对ETR的外部时钟再提前分频,ExtTRGPolarity极性  ExtTRGFilter滤波器
5.void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, 
 uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
//选择ETR通过外部时钟模式2输入的时钟,ExtTRGPrescaler外部触发预分频器,对ETR的外部时钟再提前分频,ExtTRGPolarity极性  ExtTRGFilter滤波器

(对于ETR输入的外部时钟,两个函数等效,若不需要触发输入的功能,两个函数可以互换)

6.void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,  uint16_t ExtTRGFilter);
//不是用来选择时钟,单独用来配置ETR引脚的预分频器、极性、滤波器

初始化结构体中的一些参数如预分频值和自动重装值等可能在初始化后还要更改,再调用初始化函数来改变参数值太麻烦,用一些单独的函数来更改参数值更加方便。

void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);
//单独写预分频值的函数。参数Prescaler要写入的预分频值,TIM_PSCReloadMode写入的模式(缓冲寄存器,写入的值在更新事件之后才有效,这个写入的模式,可以选择听从安排,在更新事件按生效;或者在写入后,手动产生一个更新事件,让这个值立即生效)
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);
//用于改变计数器的计数模式。TIM_CounterMode选择新的计数器模式
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);
//自动重装预装功能配置,NewState使能或失能,选择有预装和无预装,
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
//给计数器写入一个值,手动给一个计数值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);
//给自动重装寄存器写入一个值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
//获取当前计数器的值,查看计数器当前计到哪里,返回当前计数器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);
//获取当前预分频器的值,查看预分频器的值
//获取标志位和清除标志位
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
  • 39
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值