定时器简介

TIM(Timer定时器)简介

在第一部分,我们主要讲的是定时器基本定时的功能,也就是定一个时间,然后让定时器每隔这个时间产生一个中断,来实现每隔一个固定时间执行一段程序的目的,比如你要做个时钟、秒表,或者使用一些程序算法的时候,都需要定时中断的功能,在第二部分,我们主要讲的是定时器输出比较的功能,输出比较这个模块最常见的用途就是产生PWM波形,用手驱动电机等设备,在这个部分,我们将会学习到,使用STM32输出的PWM波形,来驱动舵机和直流电机的例子,在第三部分,我们主要讲的是定时器输入捕获的功能,在这部分,我们将会学习使用输入捕获这个模块来实现测量方波频率的例子,在第四部分,我们再来学习一下定时器的编码器接口,使用这个编码器接口,能够更加方便地读取正交编码器的输出波形,在编码电机测速中,应用也是非常广泛的

使用定时器的外部时钟,可以提供一个更加精准的时钟来计时;或者也可以把外部时钟当做一个计数器,用来统计引脚上电平翻转的次数,毕竟定时器本质上就是一个计数器

定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断(定时器最基本的功能,就是定时触发中断,定时器也是一个计数器,当这个计数器的输入是一个准确可靠的基准时钟的时候,那它在对这个基准时钟进行计数的过程,实际就是计时的过程,比如在STM32中,定时器的基准时钟一般都是主频72MHz,如果我对72MHz计72个数,那就是1MHz也就是1us的时间)

16位计数器(这里计数器就是用来执行计数定时的一个寄存器,每来一个时钟,计数器加1)、预分频器(预分频器,可以对计数器的时钟进行分频,让这个计数更加灵活)、自动重装寄存器(自动重装寄存器就是计数的目标值,就是我想要计多少个时钟申请中断)的时基单元(这些寄存器构成了定时器最核心的部分,我们把这一块电路称为时基单元,这个时基单元里面的计数器、预分频器、自动重装寄存器都是16位的,2的6次方是65536,也就是如果预分频器设置最大,自动重装也设置最大,那定时器的最大的定时时间就是59.65s,接远一分钟),在72MHz计数时钟下可以实现最大59.65s的定时(如果你嫌这个时间还不够长,STM32的定时器还支持级联的模式,也就是一个定时器的输出,当做另一个定时器的输入,最大定时时间就是59.65s再乘以2次65536,8000多年)

不仅具备基本的定时中断功能,而且还包含内外时钟源选择(我们第一部分讲的就是这个定时中断和内外时钟源选择的功能)、输入捕获(第二部分)、输出比较(第三部分)、编码器接口(第四部分)、主从触发模式(第三部分)等多种功能(由手定时器的这个基本结构是非常通用的,很多模块电路都能用到,所以STM32的定时器上拓展了非常多的功能)

根据复杂度和应用场景分为了高级定时器(最复杂)、通用定时器(最常用,课程主要讲这个)、基本定时器(最简单)三种类型

类型 编号 总线 功能
高级定时器 TIM1、TIM8 APB2 拥有通用定时器全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能(这些功能主要是为了三相无刷电机的驱动设计的,本门课程不涉及)
通用定时器 TIM2、TIM3、TIM4、TIM5 APB1 拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能
基本定时器 TIM6、TIM7 APB1 拥有定时中断、主模式触发DAC的功能(这是STM32定时器的一大特色,就是这个主从触发模式,它能让内部的硬件在不受程序的控制下实现自动运行,在某些情景下将会极大地减轻CPU的负担)

编号:因为同一个芯片一般都有很多个定时器。所以TIM后面会跟一个数字

除了TIM18,在库函数中还出现了TIM911等等(这些用不到)

其中,高级定时器连接的是性能更高的APB2总线(这个在RCC开启时钟的时候要注意一下)

STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4(没有基本定时器,不同型号定时器的数量是不同的)

三种定时器的结构和功能

下面那三个寄存器构成时基单元,预分频器之前,连接的就是基准计数时钟的输入,最终来到了这个位置,由于基本定时器只能选择内部时钟,所以你可以直接认为,这根线直接连到了输入端的这里,也就是内部时钟CK_INT,内部时钟的来源是RCC_TIMxCLK,这里的频率值一般都是系统的主频72MHz,预分频器可以对这个72MHz的计数时钟进行预分频,寄存器写0那就是不分频(输出频率=输入频率=72MHz),写1就是二分频(输出频率=输入频率=36MHz),写2就是三分频,以此类推(这个预分频器是16位的,所以最大值可以写65535,也就是65536分频)这个计数器可以对预分频后的计数时钟进行计数,计数时钟每来一个上升沿,计数器的值就加1,这个计数器也是16位的,所以里面的值可以从0一直加到65535,如果再加的话计数器就会回到0重新开始,当自增到目标值时,产生中断,那就完成了定时的任务,所以现在还需要一个存储目标值的寄存器,那就是自动重装寄存器(也是16位)了,当计数值等于自动重装值时,也就是计时时间到了,就会产生一个中断信号,并且清零计数器,计数器自动开始下一次的计数计时

上图画的一个向上的折线箭头,就代表这里会产生中断信号,上面这种中断,我们一般把它叫作"更新中断",这个I就会通往NVIC,我们再配置好NVIC的定时器通道,那定时器的更新中断就能够得到CPU的响应了

向下的箭头,代表的是会产生一个事件,这里对应的事件就叫做"更新事件",更新事件不会触发中断。但可以触发内部其他电路的工作

主模式触发DAC的作用:这个用途就是在我们使用DAC的时候,可能会用DAC输出一段波形,那就需要每隔一段时间来触发一次DAC,让它输出下一个电压点,我们一般的思路是先设置一个定时器产生中断,每隔一段时间在中断程序中调用代码手动触发一次DAC转换,然后DAC输出,这样也是没问题的,但是这样会使主程序处手频繁被中断的状态,这会影响主程序的运行和其他中断的响应,所以定时器就设计了一个主模式,使用这个主模式可以把这个定时器的更新事件,映射到这个触发输出TRGO(Trigger Out)的位置,然后TRGO直接接到DAC的触发转换引脚上,定时器的更新就不需要再通过中断来触发DAC转换了,仅需要把更新事件通过主模式映射到TRGO,然后TRGO就会直接去触发DAC了,整个过程不需要软件的参与,实现了硬件的自动化,这就是主模式的作用

中间最核心的部分还是时基单元,不过对于通用定时器而言,这个计数器的计数模式就不止向上计数(计数器从0开始,向上自增,计到重装值,清零同时申请中断,然后循环)这一种了,通用定时器和高级定时器还支持向下计数模式(从重装值开始,向下自减,减到0后,回到重装值同时申请中断,然后循环)和中央对齐模式(就是从0开始,先向上自增,计到重装值,申请中断,然后再向下自减,减到0,再申请中断,然后循环)

时基单元上面那一坨结构偶就是内外时钟源选择(对于基本定时器而言,定时只能选择内部时钟。也就是系统频率72MHz,到了通用定时器这里,时钟源不仅可以选择内部的72MHz时钟,还可以选择外部时钟,第一个外部时钟就是来自TIMx_ETR引脚上的外部时钟,这个ETR(External)引脚的位置,可以参考一下引脚定义表,这里我们可以在这个TIM2的ETR引脚,也就是PA0上接一个外部方波时钟,然后配置一下内部的极性选择、边沿检测和预分频器电路,再配置一下输入滤波电路,这两块电路可以对外部时钟进行一定的整形,因为是外部引脚的时钟,所以难免会有毛刺,那这些电路就可以对输入的波形进行滤波,最后,滤波后的信号,兵分两路,上面一路ETRF进入触发控制器,紧跟着就可以选择作为时基单元的时钟了,如果你想在ETR外部引脚提供时钟,或者想对ETR时钟进行计数,就把这个定时器当做计数器来用的话,那就可以配置这一路的电路,在STM32中,这一路也叫做"外部时钟模式2")和主从触发模式的结构了

下面这里还有一路可以提供时钟,就是TRGI(Trigger In),这一路从名字上来看的话,它主要是用作触发输入来使用的,这个触发输入可以触发定时器的从模式,本小节我们讲的是这个触发输入作为外部时钟来使用的情况,暂且就可以把这个TRGI当做外部时钟的输入来看,当这个TRGI当做外部时钟来使用的时候,这一路就叫做"外部时钟模式1",那通过这一路的外部时钟都有哪些呢?第一个就是ETR引脚的信号(既可以通过上面一路来当作时钟,又可以通过下面这一路来当作时钟,两种情况对于时钟输入而言是等价的,只不过下面这一路输入会占用触发输入的通道而已),然后第二个,就是ITR信号,这一部分的时钟信号是来自其他定时器的,从右上可以看出,这个主模式的输出TRGO可以通向其他定时器,那通向其他定时器的时候,就接到了其他定时器的ITR引脚上来了,这个ITRO到ITR3分别来自其他4个定时器的TRGO输出

具体怎么连接的,手册有一张表

TIM2的ITR0是接在了TIM1的TRGO上的,ITR1接在了TIM8的TRGO上and so on

通过这一路我们就可以实现定时器级联的功能,比如我可以先初始化TIM3,然后使用主模式把它的更新事件映射到TRGO上,接着再初始化TIM2,这里选择ITR2,对应的就是TIM3的TRGO,然后后面再选择时钟为外部时钟模式1,这样TIM3的更新事件就可以驱动TIM2的时基单元,也就实现了定时器的级联

这里还可以选择TI1F_ED,这里连接的是输入捕获单无的CH1引脚,也就是从CH1引脚获得时钟,这里后缀加一个ED(Edge)就是边沿的意思,也就是通过这一路输入的时钟,上升沿和下降沿均有效

最后,这个时钟还能通过TI1FP1和TI2FP2获得,其中TI1FP1是连接到了这里,就是CH1引脚的时钟

外部时钟模式1的输入就介绍完了 ,总结一下就是,外部时钟模式1的输入可以是ETR引脚、其他定时器,CH1引脚的边沿,CH1引脚和CH2引脚,一般情况下外部时钟通过ETR引脚就可以了,对于时钟输入而言,最常用的还是内部的72MHz的时钟,如果要使用外部时钟,首选ETR引脚外部时钟模式2的输入,这一路最简单,最直接

右边那里就是定时器的主模式输出了,这部分的电路可以把内部的一些事件映射到这个TRGO引脚上,比如我们刚才讲基本定时器分析的,将更新事件映射到TRGO,用于触发DAC,这里也是一样,它可以把定时器内部的一些事件映射到这里来,用于触发其它定时器,DAC或者ADC,可见这个触发输出的范围是比基本定时器更广一些的

然后是下面的电路,这一部分主要包含了两块电路,右边这一块是输出比较电路,总共有四个通道,分别对应CH1到CH4的引脚,可以用于输出PWM波形,驱动电机,左边这一块是输入捕获电路,也是有四个通道,对应的也是CH1到CH4的引脚,可以用于测输入方波的频率等,中间这个寄存器是捕获/比较寄存器,是输入捕获和输出比较电路共用的,因为输入捕获和输出比较不能同时使用,所以这里的寄存器是共用的,引脚也是共用的,本节主要讲的是定时中断和内外时钟源选择,也用不到这部分电路

带黑色阴影的都带有影子寄存器这样的缓冲寄存器,并且这个缓冲寄存器是用还是不用,是可以自己设置的

高级定时器目前用不到,暂时咱们不学了

定时中断的基本结构

下面这里是运行控制,就是控制寄存器的一些位,比如启动停止,向上或者向下计数等等吗,我们操作这些寄存器就能控制时基单元的运行了,左边是为时基单元提供时钟的部分,这里可以选择RCC提供的内部时钟,也可以选择ETR引脚提供的外部时钟模式2,第一个程序定时器定时中断就是用的内部时钟这一路,第二个定时器外部时钟就是用的外部时钟模式2这一路,当然还可以选择这里的触发输入当做外部时钟,即外部时钟模式1,对应的有ETR外部时钟、ITRx其他定时器、TIx输入捕获通道;编码器模式一般是编码器独用的模式,普通的时钟用不到这个,右边这里,就是计时时间到,产生更新中断后的信号去向,那这里中断信号会先在状态寄存器里置一个中断标志位,这个标志位会通过中断输出控制,到NVIC申请中断,为什么会有一个中断输出控制呢?因为这个定时器模块有很多地方需要申请中断,不仅更新要申请中断,这里触发信号也会申请中断,还有下面的输入捕获和输出比较匹配时也会申请,这些中断都要经过中断输出控制,如果需要这个中断,那就允许,如果不需要,那就禁止,简单来说,这个中断输出控制就是一个中断输出的允许位

时基单元运行的一些细节问题

预分频器时序

第一行是CK_PSC,预分频器的输入时钟,一般是72MHz

下面的CNT_EN,计数器使能,高电平计数器正常运行,低电平计数器停止、

CK_CNT:计数器时钟,它既是预分频器的时钟输出,也是计数器的时钟输入

ARR自动重装值就是FC,当计数值计到和重装值相等,并目下一个时钟来临时,计数值才清零

下面那三行时序,这三行时序是什么意思呢?

这里描述的其实是这个预分频寄存器的一种缓冲机制,也就是这个预分频寄存器实际上是有两个,一个是预分频控制寄存器,供我们读写用的,它并不直接决定分频系数,另外还有一个缓冲寄存器(影子寄存器),这个缓冲寄存器才是真正起作用的寄存器,比如我们在某个时刻,把预分频寄存器由0改成了1,如果在此时立刻改变时钟的分频系数,那么就会导致,在一个计数周期内,前半部分和后半部分的频率不一样,计数计到一半,计数频率突然就会改变,这虽然一般并不会有什么问题,但是STM32的定时器比较严谨,设计了这个缓冲寄存器,当我在计数计到一半的时候改变了分频值,这个变化并不会立刻生效,而是会等到本次计数周期结束时,产生了更新事件,预分频寄存器的值才会被传递到缓冲寄存器里面去,才会生效,最后这里,预分频器内部实际上也是靠计数来分频的,当预分频值为0时,计数器就一直为0,直接输出原频率,当预分频值为1时,计数器就0、1、0、1、0,、1、0、1这样计数,回到0的时候,就输出一个脉冲,这样输出频率就是输入频率的二分频,预分频器的值和实际的分频系数之间有一个数的偏移

计数器计数频率: C K _ C N T = C K _ P S C / ( P S C + 1 ) CK\_CNT = CK\_PSC / (PSC + 1) CK_CNT=CK_PSC/(PSC+1)

计数器溢出频率: C K _ C N T _ O V = C K _ C N T / ( A R R + 1 ) = C K _ P S C / ( P S C + 1 ) / ( A R R + 1 ) CK\_CNT\_OV = CK\_CNT / (ARR + 1) = CK\_PSC / (PSC + 1) / (ARR + 1) CK_CNT_OV=CK_CNT/(ARR+1)=CK_PSC/(PSC+1)/(ARR+1)

用72MHz/(PSC+1)/(ARR+1)就能得到溢出频率

计数器无预装时序(无影子寄存器)

通过设置这个ARPE位,就河以选择是否使用预装功能

我突然更改了自动加载寄存器,就是自动重装寄存器,由FF改成了36,那计数值的目标值就由FF变成了36,所以这里计到36之后,就直接更新,开始下一轮计数

计数器有预装时序(有影子寄存器)

这个影子寄存器才是真正起作用的,引入影子寄存器的目的实际上是为了同步,就是让值的变化和更新事件同步发生,防止在运行途中更改造成错误

如果这张图里面没有影子寄存器的话,F5改到36立刻生效,但此时计数值已经到了F1,已经超过36了,所以会加到FFFF,然后再从0加到36

RCC时钟树

这个时钟树,就是STM32中用来产生和配置时钟,并且把配置好的时钟发送到各个外设的系统,时钟是所有外设运行的基础,所以时钟也是最先需要配置的东西,我们之前说过,程序中主函数之前还会执行一个SystemInit函数,这个函数就是用来配置这个时钟树的,这个结构看上去挺复杂的,配置起来还是比较麻烦的,不过好在ST公司已经帮我们写好了配置这个时钟树的SystemInit函数

左边的都是时钟的产生电路,右边的都是时钟的分配电路,中间的这个SYSCLK就是系统时钟72MHz,在时钟产生 电路有4个震荡源,分别是内部的8MHz高速RC振荡器,外部的4-16MHz高速石英晶体振荡器,也就是晶振,一般都是接8MHz,外部的32.768KHz低速晶振,这个一般是给RTC提供时钟的,最后是内部的40KHz低速RC振荡器,这个可以给看门狗提供时钟,上面这两个高速晶振,是用来提供系统时钟的,我们的AHB、APB2、APB1的时钟都是来源于这两个高速晶振,这里内部和外部都有一个8MHz的晶振,都是可以用的,只不过是外部的石英振荡器比内部的RC振荡器更加稳定,所以我们一般使用外部晶振,但是如果你系统很简单并且不需要那么精准的时钟,那也是可以使用内部RC振荡器的,这样就可以省下外部晶振的电路了,那在SystemInit函数里,ST是这样来配置时钟的,首先启动内部时钟,选择内部8MHz为系统时钟,暂时以内部的8MHz的时钟运行,然后在启动外部时钟,配置外部时钟走着一路(进入PLL锁相环进行倍频,8MHz倍频9倍,得到72MHz,等到锁相环输出稳定后,选择锁相环输出为系统时钟,这样就把系统时钟由8MHz切换为了72MHz);如果你的外部晶振出问题了,可能会导致一个现象,你会发现你程序的时钟大概慢了10倍(自己用定时器定了1s的时间,结果过了大概10s才进中断,如果外部晶振出问题了,系统时钟就无法切换到72MHz,那它就会以内部的8MHz运行,8M相比较72M,大概就慢了10倍 )

这里还有个CSS(Clock Security System),这个是时钟安全系统,它也是负责切换时钟的,它可以监测外部时钟的运行状态,一但外部时钟失效,它就会自动把外部时钟切换回内部时钟,保证系统时钟的运行,防止程序卡死造成事故,在高级定时器这里,也有CSS的身影,在这个刹车输入这里,一但CSS检测到外部时钟失效,这里通过或门,就会立刻反应到输出比较这里,让这个输出控制的电机立刻停止,防止意外,这就是STM32里面的一些安全保障措施

接下我们再看一下这右边的时钟分配电路,首先系统时钟72MHz进入AHB总线,AHB总线有个预分频器,在SystemInit里配置的分频系数为1,那AHB的时钟就是72MHz,然后进入APB1总线,这里配置的分频系数是2,所以APB1总线的时钟为72MHz/2=36MHz,通用定时器和基本定时器是接在APB1上的,而APB1的时钟是36MHz,按理说它们的时钟应该是36MHz,但是我们在说定时器的时候,一直都说的是所有定时器的时钟都是72MHz,原因在,下面还有一条支路,上面写的是, 如果APB1预分频系数=1,则频率不变,否则频率x2,然后再看右边,发现这一路是单独为定时器2-7开通的,因为那里预分频系数我们给的是2,所以这里频率要再乘以2,所以通向定时器2-7的时钟,就又回到了72MHz,所以三种定时器的时钟频率都是72MHz,当然前提是你不乱改它SystemInit里面的默认配置,要是改了,这里的时钟还得再另行分析

然后我们再看一下下面,APB2的时钟,这里给的分频系数为1,所以APB2的时钟和AHB一样,都是72MHz,这里接在APB2上的高级定时器也单开了一路,上面写的也是如果APB2预分频系数=1,则频率不变,否则频率x2,但是这里APB2的预分频系数就是1,所以频率不变,定时器1和8的时钟就是72MHz,那在这些时钟输出这里,都有一个与门进行输出控制,控制位写的是外部时钟使能,这就是我们在程序中写RCC_APB2/1PeriphClockCmd作用的地方,打开时钟,就是在这个位置写1,让左边的时钟能够通过与门输出给外设

剩下的还有一些给ADC、SDIO等等这些提供时钟的电路,因为目前用不上,后面自己看看

相关推荐
典则44 分钟前
STM32FreeRtos入门(四)——任务状态和调度
stm32·单片机·嵌入式硬件
充哥单片机设计1 小时前
【STM32项目开源】基于STM32的智能天然气火灾监控
stm32·单片机·嵌入式硬件
充哥单片机设计1 小时前
【STM32项目开源】基于STM32的智能仓库火灾检测系统
stm32·单片机·嵌入式硬件
就叫飞六吧3 小时前
普中stm32大Dap烧录流程
stm32
A9better3 小时前
嵌入式开发学习日志38——stm32之看门狗
stm32·嵌入式硬件·学习
小莞尔4 小时前
【51单片机】【protues仿真】基于51单片机智能路灯控制系统
c语言·stm32·单片机·嵌入式硬件·51单片机
辰哥单片机设计13 小时前
TT直流减速电机(STM32)
stm32
A9better13 小时前
嵌入式开发学习日志36——stm32之USART串口通信前述
stm32·单片机·嵌入式硬件·学习
思诺学长14 小时前
BMS(电池管理系统)的主要功能和架构简述
单片机·嵌入式硬件
czhaii14 小时前
全局不关总中断的 RTOS / CosyOS-II for STCAI MCU
单片机