文章目录
- 前言
- 一、TIM定时器简介
- 1.1 定时器的基本功能
- 1.2 时基单元
- 1.3 定时器的其他功能
- 1.4 TIM定时器的分类
- 二、TIM定时器的内部结构
- 2.1 基本定时器
- 2.2 通用定时器
- 2.3 高级定时器
- 三、定时中断的基本结构
- 四、时序图
- 4.1 预分频器时序
- 4.2 计数器时序
- 4.3 计数器无预装时序
- 4.4 计数器有预装时序
- 五、时钟树
前言
提示:本文主要用作在学习江科大自化协STM32入门教程后做的归纳总结笔记,旨在学习记录,如有侵权请联系作者
本文主要探讨STM32中功能最强大、结构最复杂的一个外设 TIM(Timer)定时器,主要包括定时器的基本概念、分类以及定时中断基本结构等,本文还对基本定时器、通用定时器以及高级定时器的内部结构进行了详细的分析,为更好地理解与使用定时器打下了坚实的基础。
一、TIM定时器简介
1.1 定时器的基本功能
TIM(Timer)定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断,这就是定时器最基本的功能,也就是定时触发中断。 同时也可以看出来,定时器就是一个计数器。当这个计数器的输入是一个准确可靠的基准时钟的时候,那它在对这个基准时钟进行计数的过程,实际上就是计时的过程。
1.2 时基单元
STM32的定时器拥有16位计数器、预分频器和自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时。 这里的计数器就是用来执行计数定时的一个寄存器,每来一个时钟,计数器加1。预分频器,可以对计数器的时钟进行分频,让这个计数更加灵活。自动重装寄存器就是计数的目标值,就是我想要计多少个时钟申请中断。这些寄存器构成了定时器最核心的部分,我们把这一块电路称为时基单元。
这个时基单元里面的计数器、预分频器和自动重装寄存器都是16位的,2的16次方是65536,也就是如果预分频器设置最大,自动重装也设置最大,那定时器的最大定时时间就是59.65s,接近1分钟。那这个是怎么算的呢?就是72M / 65536 / 65536,得到的是中断频率,然后取倒数,就是59.65秒多,这就是最大的定时时间。
1.3 定时器的其他功能
STM32的定时器不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口以及主从触发模式等多种功能。由于定时器的这个结构是非常通用的,很多模块电路都能用到,所以STM32的定时器上扩展了非常多的功能。
1.4 TIM定时器的分类
STM32的定时器根据复杂度和应用场景分为了高级定时器、通用定时器和基本定时器三种类型。其中,高级定时器最为复杂,通用定时器中等复杂、最常用,基本定时器最简单。
类型 | 编号 | 总线 | 功能 |
---|---|---|---|
高级定时器 | TIM1、TIM8 | APB2 | 拥有通用定时器的全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能 |
通用定时器 | TIM2、TIM3、TIM4、TIM5 | APB1 | 拥有基本定时器的全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口以及主从触发模式等功能 |
基本定时器 | TIM6、TIM7 | APB1 | 拥有定时中断、主模式触发DAC的功能 |
- STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4
注意:不同型号的芯片定时器的数量是不一样的,在操作这个外设之前一定要查一下有没有这个外设
二、TIM定时器的内部结构
2.1 基本定时器
首先,这一部分有三个最重要的寄存器,分别是预分频器PSC、计数器CNT和自动重装寄存器,它们构成了最基本的计数计时电路,所以这一块电路叫作时基单元。
预分频器前连接的就是基准计数时钟的输入。由于基本定时器只能选择内部时钟,所以你可以直接认为预分频器之前的这根线直接连到了输入端这里,也就是内部时钟CK_INT。内部时钟的来源是RCC的TIMxCLK,这里的频率值一般都是系统的主频72MHz,所以通向时基单元的计数基准频率就是72M。
再来看时基单元,首先是预分频器,它可以对这个72MHz的计数时钟进行预分频。比如这个寄存器写0,那就是不分频或者说是1分频,这时候输出频率 = 输入频率 = 72MHz。如果预分频器写1,那就是2分频,输出频率 = 输入频率 / 2 = 36MHz。如果写2,那就是3分频,以此类推。所以预分频器的值和实际的分频系数相差了1。即实际分频系数 = 预分频器的值 + 1。 这个预分频器是16位的,所以最大值可以写65535,也就是65536分频。这就是预分频器,就是对输入的基准频率提前进行一个分频的操作。
然后是计数器,这个计数器可以对预分频后的计数时钟进行计数。计数时钟每来一个上升沿,计数器的值就加1。这个计数器也是16位的,所以里面的值可以从0一直加到65535,如果再加的话,计数器就会回到0重新开始计数。 所以计数器的值在计时过程中会不断地自增运行,当自增运行到目标值时产生中断,那就完成了定时的任务。所以现在还需要一个存储目标值的寄存器,那就是自动重装寄存器了。
自动重装寄存器也是16位的,它存的就是我们写入的计数目标。在运行的过程中,计数值不断自增,自动重装值是固定的目标,当计数值等于自动重装值时,也就是计时时间到了,那它就会产生一个中断信号并且清零计数器,计数器自动开始下一次的计数计时。
在这里图上画的一个向上的折线箭头,就代表这里会产生中断信号。像这种计数值等于自动重装值产生的中断,我们一般把它叫作更新中断。这个更新中断之后就会通往NVIC,我们再配置好NVIC的定时器通道,那定时器的更新中断就能够得到CPU的响应了。
这里向下的箭头,代表的是会产生一个事件,这里对应的事件就叫作更新事件。更新事件不会触发中断,但可以触发内部其他电路的工作。
以上这些就是定时器定时中断的全部流程了。从基准时钟,到预分频器再到计数器。计数器计数自增,同时不断地与自动重装寄存器进行比较,他俩值相等时,即计时时间到,这时会产生一个更新中断和更新事件,CPU响应更新中断,就完成了我们定时中断的任务了。
在这里简单介绍一下这个主模式触发DAC的功能。STM32定时器的一大特色,就是这个主从触发模式,它能让内部的硬件在不受程序的控制下实现自动运行,如果能把这个主从触发模式掌握好,那在某些情景下将会极大地减轻CPU的负担。
这个主模式触发DAC有啥用呢?这个用途就是在我们使用DAC的时候,可能会用DAC输出一段波形,那就需要每隔一段时间就来触发一次DAC让它输出下一个电压点。如果用正常的思路来实现的话,就是先设置一个定时器产生中断,每隔一段时间在中断程序中调用代码手动触发一次DAC转换,然后DAC输出。这样也是没问题的,但是这样会使主程序处于频繁被中断的状态,这会影响主程序的运行和其他中断的响应。所以定时器就设计了一个主模式,使用这个主模式可以把定时器的更新事件映射到这个触发输出TRGO(Trigger Out)的位置,然后TRGO直接接到DAC的触发转换引脚上。这样,定时器的更新就不需要再通过中断来触发DAC转换了,仅需要把更新事件通过主模式映射到TRGO,然后TRGO就会直接去触发DAC了,整个过程不需要软件的参与,实现了硬件的自动化,这就是主模式的作用。
2.2 通用定时器
再来看一下通用定时器,相比较于基本定时器,通用定时器的结构就复杂多了。首先是中间最核心的这一部分还是时基单元,这部分结构和基本定时器是一样的,由预分频器、计数器和自动重装寄存器构成。每部分的工作流程和基本定时器也是一样的,预分频器对时钟进行预分频,计数器自增计数,当计数值达到自动重装值时,计数值清零同时产生更新中断和更新事件。
不过对于通用计时器而言,这个计数器的计数模式就不止向上计数这一种了。前面的分析我们都是用向上计数的模式,也就是计数器从0开始,向上自增,计到重装值清零同时申请中断,然后开始下一轮,依次循环。除了向上计数这一种模式外,通用定时器和高级定时器还支持向下计数模式和中央对齐模式。
向下计数模式就是从重装值开始向下自减,减到0之后回到重装值同时申请中断,然后继续下一轮,依次循环。中央对齐模式就是从0开始,先向上自增计到重装值,申请中断,然后再向下自减,减到0再申请中断,然后继续下一轮,依次循环。
总结一下就是,基本定时器仅支持向上计数模式,通用定时器和高级定时器不仅支持向上计数模式,还支持向下计数模式以及中央对齐模式。不过我们最常用的还是向上计数模式。
然后我们再来看一下上面这部分结构,这些就是内外时钟源选择和主从触发模式的结构了。
我们先看一下内外时钟源选择。对于基本定时器而言,定时只能选择内部时钟,也就是系统频率72MHz,而通用定时器时钟源不仅可以选择内部时钟,还可以选择外部时钟。具体都有哪些呢?
第一个外部时钟就是来自TIMx_ETR引脚上的外部时钟。 这个ETR引脚的位置,可以参考一下引脚定义表。可以看到这里有TIM2_CH1_ETR,意思就是这个TIM2的CH1和ETR都是复用在了这个位置,也就是PA0引脚。下面还有CH2、CH3、CH4和其他定时器的一些引脚,也都可以在这里找到。
那这里,我们可以在这个TIM2的ETR引脚,也就是PA0上接一个外部方波时钟。然后配置一下内部的极性选择、边沿检测和预分频器电路,再配置一下输入滤波电路(这些电路可以对外部时钟进行一定的整形,因为是外部引脚的时钟,所以难免会有些毛刺,那这些电路就可以对输入的波形进行滤波,同时也可以选择一下极性和预分频器)
最后,滤波后的信号兵分两路,上面一路ETRF进入触发控制器,紧跟着就可以选择作为时基单元的时钟了。如果你想在ETR外部引脚提供时钟,或者想对ETR时钟进行计数,把这个定时器当作计数器来用的话,那就可以配置这一路的电路,在STM32中,这一路也叫做“外部时钟模式2”。
除了外部ETR引脚可以提供时钟外,下面这里还有一路可以提供时钟,就是TRGI(Trigger In)。 这一路从名字上来看的话,它主要是用作触发输入来使用的,这个触发输入可以触发定时器的从模式。关于触发输入和从模式这里暂时先不细讲,我们先来讲一下这个触发模式作为外部时钟来使用的情况。暂且可以把这个TRGI当作外部时钟的输入来看,当这个TRGI当作外部时钟来使用的时候,这一路就叫作“外部时钟模式1”。那通过这一路的外部时钟都有哪些呢?
往左看,第一个,就是ETR引脚的信号。这里ETR引脚既可以通过上面这一路来当作时钟,又可以通过下面这一路来当作时钟,两种情况对于时钟输入而言是等价的,只不过是下面这一路输入会占用触发输入的通道而已。
然后第二个,就是ITR信号,这一部分的时钟信号是来自其他定时器的。 从右边可以看出,这个主模式的输出TRGO可以通向其他定时器,那通向其他定时器的时候,就接到了其他定时器的ITR引脚上来了。这个ITR0到ITR3分别来自其他4个定时器的TRGO输出。
至于具体的连接方式是怎么样的,可以看一下手册里如下的这一张表。这里可以看到,TIM2的ITR0是接在了TIM1的TRGO上的,ITR1接在了TIM8,ITR2接在了TIM3,ITR3接在了TIM4,其他定时器也都可以参照一下这个表,这就是ITR和定时器的连接关系。
通过这一路我们就可以实现定时器级联的功能。比如我们可以先初始化TIM3,然后使用主模式把它的更新事件映射到TRG0上,接着再初始化TIM2,这里选择ITR2,对应的就是TIM3的TRGO,然后后面再选择时钟为外部时钟模式1,这样TIM3的更新事件就可以驱动TIM2的时基单元,也就实现了定时器的级联。
那我们继续看,这里还可以选择TI1F_ED,这里连接的是输入捕获单元的CH1引脚,也就是从CH1引脚获得时钟。这里后缀加一个ED(Edge)就是边沿的意思,也就是通过这一路输入的时钟,上升沿和下降沿均有效。最后,这个时钟还能通过TI1FP1和TI2FP2获得,其中TI1FP1是连接到了CH1引脚的时钟,TI2FP2连接到了CH2引脚的时钟。
以上就是外部时钟模式1的输入的全部内容了。总结一下就是,外部时钟模式1的输入可以是ETR引脚、其他定时器、CH1引脚的边沿、CH1引脚和CH2引脚。 这还是比较复杂的,一般情况下外部时钟通过ETR引脚就可以了,设置这么复杂的输入,不仅仅是为了扩大时钟输入的范围,更多的还是为了某些特殊应用场景而设计的。对于时钟输入而言,最常用的还是内部的72MHz的时钟,如果要使用外部时钟,首选ETR引脚外部时钟模式2的输入,这一路最简单,最直接。
这里还有一块没讲到,下面这个是定时器的一个编码器接口,可以读取正交编码器的输出波形。然后右边还有一个定时器的主模式输出,这部分电路可以把内部的一些事件映射到这个TRGO引脚上,比如刚刚分析的基本定时器,将更新事件映射到TRGO用于触发DAC。这里也是一样,它可以把定时器内部的一些事件映射到这里来,用于触发其他定时器、DAC或者ADC。可见这个触发输出的范围是比基本定时器更广一些的。
那上面这部分就差不多了,我们再看一下下面这一部分,这一部分主要包含了两块电路。
右边这块是输出比较电路,总共有四个通道,分别对应CH1到CH4的引脚,可以用于输出PWM波形驱动电机。左边这一块是输入捕获电路,也是有四个通道,对应的也是CH1到CH4的引脚,可以用于测量输入方波的频率等。中间这个寄存器是捕获/比较寄存器,是输入捕获和输出比较电路共用的,因为输入捕获和输出比较不能同时使用,所以这里的寄存器是共用的,引脚也是共用的。
输入捕获和输出比较这部分电路先暂时不讲,那到这里,有关通用定时器的内容就讲完了。
2.3 高级定时器
最后,我们再简单地看一下高级定时器。对比通用定时器的结构,高级定时器这里,左上的这一大部分都没有变化,主要改动的就是右边的这几个部分。
第一个是这里,申请中断的地方,增加了一个重复次数计数器。有了这个计数器之后,就可以实现每隔几个计数周期才发生一次更新事件和更新中断。原来的结构是每个计数周期完成后都会发生更新,现在有个计数器在这里,可以实现每隔几个周期再更新一次,这就相当于对输出的更新信号又做了一次分频。那对于高级定时器的话,我们之前计算的最大定时时间59秒多,在这里就还需要再乘一个65536,这就又提升了很多的定时时间了。这就是这个重复次数计数器的工作流程。
然后下面这里的这些,就是高级定时器对输出比较模块的升级了,这些内容大概了解一下就行了,现在还不必深入。
三、定时中断的基本结构
我们来看一下定时中断和内外时钟源选择所涉及的结构,以下这个就是定时中断的基本结构图。
那首先,中间最重要的还是PSC(Prescaler)预分频器、CNT(Counter)计数器、ARR(AutoReloadRegister)自动重装器这三个寄存器构成的时基单元。下面这里是运行控制,就是控制寄存器的一些位,比如启动停止、向上或向下计数等等,我们操作这些寄存器就能控制时基单元的运行了。
左边是为时基单元提供时钟的部分,这里可以选择RCC提供的内部时钟,也可以选择ETR引脚提供的外部时钟模式2。当然也可以选择这里的触发输入当作外部时钟,即外部时钟模式1,对应的有ETR外部时钟、ITRx其他定时器、TIx输入捕获通道,这些就是定时器的所有可选的时钟源了。
最后这里还有个编码器模式,这一般是编码器独用的模式,普通的时钟用不到这个。
接下来右边这里,就是计时时间到,产生更新中断后的信号去向。在这里,如果是高级定时器的话,还会多一个重复计数器,不过我们暂时就不考虑了。那这里,中断信号会先在状态寄存器里置一个中断标志位,这个标志位会通过中断输出控制,到NVIC申请中断。为什么会有一个中断输出控制呢?因为这个定时器模块有很多地方都要申请中断(不仅更新要申请中断,触发信号也会申请中断,输入捕获和输出比较匹配时也会申请中断),所以这些中断都要经过中断输出控制。如果需要这个中断,那就允许,如果不需要,那就禁止。简单来说,这个中断输出控制就是一个中断输出的允许位,如果需要某个中断,就允许一下。
四、时序图
4.1 预分频器时序
计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)
4.2 计数器时序
计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)
4.3 计数器无预装时序
4.4 计数器有预装时序
五、时钟树
这个时钟树,就是STM32中用来产生和配置时钟,并且把配置好的时钟发送到各个外设的系统。时钟是所有外设运行的基础,所以时钟也是最先需要配置的东西,在执行主程序之前还会执行一个SystemInit函数,这个函数的作用就是配置时钟树的。