目录
TIM简介
定时器类型
基本定时器的结构图
时基单元
预分频器
计数器
自动重装寄存器
主模式触发DAC的功能
通用定时器的结构图
计数器的计数模式
内外时钟源选择和主从触发模式的结构
外部时钟模式2
外部时钟模式1
其他部分
输出比较电路
输入捕获电路
高级定时器的结构图
定时中断基本结构图
预分频器时序
计数器时序
计数器无预装时序
计数器有预装时序
RCC时钟树的结构图
时钟产生电路
时钟分配电路
声明:本专栏是本人跟着B站江科大的视频的学习过程中记录下来的笔记,我之所以记录下来是为了方便自己日后复习。如果你也是跟着江科大的视频学习的,可以配套本专栏食用,如有问题可以QQ交流群:963138186
本节我们来学习一下STM32中功能最强大、结构最复杂的定时器。
因为定时器的内容很多,我们将总共分为四个部分讲解:
在第一部分,主要讲定时器基本定时的功能。也就是定一个时间,然后让定时器每隔这个时间产生一个中断,来实现每隔一个固定时间执行一段程序的目的。比如要做个时钟秒表,或者使用一些程序算法的时候,都需要用到定时中断的这个功能。
在第二部分,主要讲定时器输出比较的功能。输出比较这个模块,最常见的用途就是产生PWM波形。用于驱动电机等设备,在这个部分我们将会学习到使用STM32输出的PWM模型来驱动舵机和直流电机的例子。
在第三部分,主要讲的是定时器输入捕获的功能。在这部分,我们将会学习使用输入捕获这个模块来实现测量方波频率的例子。
在第四部分,我们再来学习一下定时器的编码器接口。使用这个编码器接口能够更加方便的读取正交编码器的输出波形,在编码电机测速中应用也是非常广泛的。
TIM简介
TIM(TIMer)定时器
定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断。
定时器就是一个计数器,当这个计数器的输入是一个准确可靠的基准时钟的时候,那它在对这个基准时钟进行计数的过程,实际上就是计时的过程。比如在STM32中定时器的基准时钟一般都是主频72MHz,当对72MHz计72个数,那就是1MHz,也就是一微秒的时间。如果记72000个数,那就是1KMhz,也就是一毫秒的时间。
STM32的定时器拥有16位计数器(这里计数器就是用来执行计数定时的一个寄存器,每来一个时钟计数器加1)、预分频器(可以对计数器的时钟进行分频,让这个计数更加灵活)、自动重装寄存器的时基单元(就是计数的目标值,就是想要计多少个时钟申请中断),这些寄存器构成了定时器最核心的部分,我们把这一块电路称为时基单元,这些寄存器都是16位的,2的16次方是65536,在72MHz计数时钟下可以实现最大59.65s的定时,接近1分钟。72M除以65536再除以65536得到的就是中断频率,然后取倒数,就是59.65s。如果你嫌这个还不够长,STM32的定时器,还支持级联的模式。也就是一个定时器的输出,当做另一个定时器的输入,这样加一起最大的定时时间就是59.65秒,再乘两次65536,结果大概是八千多年,如果还嫌短,那就再继续级联一个定时器.....
不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
根据复杂度和应用场景分为了高级定时器(最复杂)、通用定时器(我们主要讲这个)、基本定时器三种类型
定时器类型
STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4,也就是一个高级定时器和三个通用定时器,没有基本定时器。
在库函数中还出现了TM9、10、11等等,但是这些一般都用不到。只需要知道一下TIM1到8定时器就好了。
基本定时器还可以和DAC联合使用。这三种定时器是由高级到低级向下兼容的,高级定时器拥有通用定时器的全部功能,通用定时器又有基本定时器的全部功能。
注意一下不同的型号定时器的数量是不同的,在操作这个外设之前一定要查一下它是不是有这个外设,别操作到了不存在的外设。
接下来我们就依次来看一下高级定时器,通用定时器和基本定时器的结构图。
基本定时器的结构图
基本定时器可以完成定时中断和主模式触发DAC的功能。首先下面这部分由三个重要的寄存器组成了时基单元。
时基单元
预分频器
预分频器之前连接的就是基准计数时钟的输入,最终来到了这个位置。
由于基本定时器只能选择内部时钟,所以可以直接认为这根线直接连到了输入端的这里,也就是内部时CK_INT。
内部时钟的来源是RCC 的TMxCLK,这里的频率值一般都是系统的主频72MHz,所以通向时基单元的计数基准频率就是72M。
预分频器可以对这个72MHz的计数时钟进行预分频。比如这个寄存器写0,就是不分频,或者说是1分频,这时候输出频率等于输入频率等于七十二兆赫兹。如果预分频器写1,就是二分频,输出频率等于输入频率除以二,等于三十六兆赫兹。如果写2就是三分频,输出等于输入除以三,以此类推。
所以,预分频器的值和实际的分频系数,相差了1,即实际分频系数等于预分频器的值加1。这个预分频器是16位的,所以最大值可以写65535,也就是65536分频。
这就是预分频器,就是对输入的基准频率提前进行一个分频的操作。
计数器
计数器可以对预分频后的计数时钟进行计数。计数时钟每来一个上升沿,计数器的值就加1。这个计数器也是十六位的,所以里面的值可以从0一直加到65535。如果再加的话,计数器就会回到0重新开始。所以计数器的值在计时过程中会不断的自增运行。当自增运行到目标值时,产生中断,就完成了定时的任务。
所以现在还需要一个存储目标值的寄存器,就是自动重装寄存器了。
自动重装寄存器
自动重装寄存器也是十六位的,它存的就是我们写入的计数目标。在运行的过程中,计数值不断自增。
自动重装值是固定的目标,当计数值等于自动重装值时,就是计时时间到了,它就会产生一个中断信号,并且清零计数器,计数器自动开始下一次的计数计时。
在这里图上画的一个向上的折线箭头,就代表这里会产生中断信号,像这种计数值等于自动重装值产生的中断,我们一般把它叫做“更新中断”。这个更新中断之后,就会通往NVIC,我们再配置好NVIC的定时器通道,定时器的更新中断就能够得到CPU的响应了。
这里向下的箭头代表的是会产生一个事件。这里对应的事件就叫做“更新事件”,更新事件不会触发中断,但可以触发内部其他电路的工作。
以上这些就是定时器,定时中断的全部流程了:从基准时钟、到预分频器、再到计数器,计数器计数自增,同时不断地与自动重装寄存器进行比较,它俩值相等时即计时时间到,这时会产生一个更新中断和者更新事件,CPU响应更新中断就完成了我们定时中断的任务了。
到这里定时中断和时基单元的工作流程就已经讲完了。
之后我们简单的介绍一下这个主模式触发DAC的功能。
主模式触发DAC的功能
STM32定时器的一大特色就是这个主从触发模式。它能让内部的硬件在不受程序的控制下实现自动运行。如果能把这个主从触发模式掌握好,在某些情景下将会极大的减轻CPU的负担。这个模式我们后面还会再详细讲的,这里先简单了解一下。
这个主模式触发DAC有啥用?
它用途就是在我们使用DAC的时候,可能会用DAC输出一段波形,就需要每隔一段时间来触发一次DAC,让它输出下一个电压点。如果用正常的思路来实现的话,就是先设置一个定时器产生中断。每隔一段时间在中断程序中调用代码手动触发一次DAC转换,然后DAC输出。这样也是没问题的,但是这样会使主程序处于频繁被中断的状态,这会影响主程序的运行和其他中断的响应。
所以定时器就设计了一个主模式,使用这个主模式,可以把这个定时器的更新事件,映射到这个触发输出TRGO的位置,然后TRGO直接接到DAC的触发转换引脚上,这样定时器的更新就不需要再通过中断来触发DAC转换了。仅需要把更新事件通过主模式映射到TRGO,然后TRGO就会直接去触发DAC,整个过程不需要软件的参与,实现了硬件的自动化,这就是主模式的作用。
当然除了这个主模式外,还有更多的硬件自动化的设计,这些我们后续再继续讲。
有关基本定时器的部分,我们就讲完了。
接下来我们再看一下通用定时器到通用定时器。
通用定时器的结构图
中间最核心的这一部分还是时基单元,这部分结构和基本定时器是一样的。
不过对于通用定时器而言,这个计数器的计数模式就不止向上计数这一种了。之前解释的都是用的向上计数的模式。也就是计数器从零开始向上自增,计到重装值清零,同时申请中断,然后开始下一轮依次循环。
计数器的计数模式
除了这种向上计数的模式外,通用定时器和高级定时器还支持向下计数模式和中央对齐模式。
向下计数模式就是从重装值开始,向下自减,减到零之后,回到重装值,同时申请中断,然后继续下一轮依次循环。
中央对齐的计数模式就是从零开始先向上自增,计到重装值,申请中断,然后再向下自减减到零再申请中断,然后继续下一轮依次循环。
总结一下,就是基本定时器仅支持向上计数这一种模式,通用定时器和高级定时器支持向上计数、向下计数、中央对齐这三种模式。
不过我们最常用的还是向上计数模式,所以其他两种模式大家就了解一下
内外时钟源选择和主从触发模式的结构
然后我们再来看一下上面的这部分结构,这些就是内外时钟源选择和主从触发模式的结构。
对于基本定时器而言,定时器只能选择内部时钟,也就是系统频率七十二兆赫兹。
而通用定时器不仅可以选择内部的七十二兆赫兹时钟,还可以选择外部时钟。
第一个外部时钟就是来自TIMx_ETR引脚上的外部时钟
TIMx_ETR引脚的位置可以参考一下引脚定义表:
这个TIM2_CH1_ETR的意思就是这个TIM2的CH1和ETR都是复用在了这个位置,也就是PA0引脚。
其他定时器的一些引脚也都可以在这里找到。
外部时钟模式2
在这个TIM2的ETR引脚,也就是PA0上接一个外部方波时钟,然后配置一下内部的极性选择、边沿检测和预分频器电路,再配置一下输入滤波电路(这些电路可以对外部时钟进行一定的整形,因为是外部引脚的时钟,所以难免会有些毛刺,则这些电路就可以对输入的波形进滤波,同时也可以选择一下极性和预分频器)。
最后滤波后的信号兵分两路,上面一路ETRF进入触发控制器,紧跟着就可以选择作为时基单元的时钟了。
如果你想在ETR外部引脚提供时钟,或者想对ETR时钟进行计数,把这个定时器当做计数器来用的话,就可以配置这一路的电路:在STM32中,这一路也叫做外部时钟模式2。
外部时钟模式1
除了外部ETR引脚可以提供时钟外,下面这里还有一路可以提供时钟,就是TRGI(Trigger In)。
这一路从名字上来看的话,它主要是用作触发输入来使用的。这个触发输入可以触发定时器的从模式。关于触发输入和从模式,我们后续再讲。暂且可以把这个TRGI当做外部时钟的输入来看。把这个TRGI当做外部时钟来使用的时候,这一路就叫做外部时钟模式1。
通过这一路的外部时钟都有哪些?
第一个就是ETR引脚的信号。这里ETR引脚既可以通过上面这一路来当做时钟,又可以通过下面这一路来当做时钟,两种情况对于时钟输入而言是等价的,只不过是下面这一路输入会占用触发输的通道而已。
然后第二个就是ITR信号这一部分的时钟信号是来自其他定时器的。从右边可以看出,这个主模式的输出TRGO可以通向其他定时器。通向其他定时器的时候,就接到了其他定时器的ITR引脚上来了。
这个ITR0到ITR3分别来自其他四个定时器的TRGO输出。至于具体的连接方式是怎么样的?
可以看手册的这一张表:
这里可以看到TIM2的ITR0是接在了TIM1的TRGO上的。ITR1接在了TM8,ITR2接在了TM3,ITR3接在了TM4,其他定时器也都可以参照一下这个表,这就是ITR和定时器的连接关系。
通过这一路,我们就可以实现定时器级联的功能。
比如我们可以先初始化TM3,然后使用主模式把它的更新事件映射到TRGO上。接着再初始化TM2,这里选择ITR2,对应的就是TM3的TRGO,然后后面再选择时钟为外部时钟模式1,这样TM3的更新事件就可以驱动TM2的时基单元,也就实现了定时器的级联。
我们继续看这里,还可以选择TI1F_ED,这里连接的是输入捕获单元的CH1引脚,
也就是从CH1引脚获得时钟,这里后缀加一个ED(Edge)就是边沿的意思,
也就是通过这一路输入的时钟上升沿和下降沿均有效。
最后这个时钟还能通过TI1FP1和TI2FP2获得,其中TI1FP1是连接到了这里,就是CH1引脚的时钟,
TI2FP2连接到了这里,就是CH2引脚的时钟。
到这里外部时钟模式1的输入就介绍完了.
总结:外部时钟模式1的输入可以是ETR引脚、其他定时器、CH1引脚的边沿,CH1引脚和CH2引脚。
一般情况下,外部时钟通过ETR引脚就可以了。其他设置这么复杂的输入不仅仅是为了扩大时钟输的范围,更多的还是为了某些特殊应用场景而设计的,比如为了定时器的级联而设计的这一部分:
下面这一部分,我们之后讲输入捕获和测频率时还会继续讲的,到时候就会明白它为什么要这样设计的。现在这些电路你大概了解一下就行了。
对于时钟输入而言,最常用的还是内部的七十二兆赫兹的时钟。如果要使用外部时钟,首选ETR引脚外部时钟模式2的输入,这一路最简单,最直接。
有关时钟输入的部分到这里就讲完了。
其他部分
最后这个是定时器的一个编码器接口,可以读取正交编码器的输出波形,这个我们后续也会再讲。
然后接下来右边这里就是定时器的主模式输出。
这部分电路可以把内部的一些事件映射到这个TRGO引脚上。比如我们刚才讲基本定时器时,将更新事件映射的TRGO用于触发DAC。这里也是一样,我们可以把定时器内部的一些事件映射到这里来,用于触发其他定时器、DAC或者ADC。可见这个触发输出的范围是比基本定时器更广一些的。
我们再看下面这部分,这一部分主要包含了两块电路。
输出比较电路
右边这一块是输出比较电路,总共有四个通道。分别对应CH1到Ch4的引脚,可以用于输出PWM波形驱动电机。
输入捕获电路
左边这一块是输入捕获电路,也是有四个通道,对应的也是CH1到Ch4的引脚。可以用于测量输入方波的频率等。
中间这个寄存器是捕获比较寄存器,是输入捕获和输出比较电路共用的。因为输入捕获和输出比较不能同时使用,所以这里的寄存器是共用的,引脚也是共用的。
有关输入捕获和输出比较,这部分电路我们留到之后再具体分析。
有关通用定时器的内容暂时就讲到这里。
高级定时器的结构图
对比通用定时器的结构,高级定时器这里左上的这一大部分都没有变化。
主要改动的就是右边这几个部分
申请中断的地方增加了一个重复次数计数器。有了这个计数器之后,就可以实现每隔几个计数周期才发生一次更新事件和更新中断。原来的结构是每个计数周期完成后都会发生更新。现在有个计数器,在这里可以实现每隔几个周期再更新一次,这就相当于对输出的更新信号又做了一次分频。
我们之前计算的最大定时时间五十九秒多,对于高级定时器的话,在这里就还需要再乘一个65536,这就又提升了很多的定时时间了。这就是这个重复计数器的工作流程。
然后下面这里的这些就是高级定时器,对输出比较模块的升级了。
这些内容大家有个印象就行了,现在还不必深入了解。
这个DTG(Dead Time Generate)是死区生成电路。
右边这里的输出引脚由原来的一个变为了两个互补的输出,可以输出一对互补的PWM波。
这些电路是为了驱动三相无刷电机的,三相无刷电机还是比较常用的。比如四轴飞行器,电动车的后轮,电钻等里面都可能是这个三相无刷电机。因为三相无刷电机的驱动电路,一般需要三个桥壁臂,每个桥壁两个大功率开关管来控制,总共需要六个大功率开关管来控制。所以这里的输出PWM引脚的前三路就变为了互补的输出。而第四路却没什么变化,因为三相电机只需要三路就行了。
为了防止互补输出的PWM驱动桥臂时,在开关切换的瞬间,由于器件的不理想,造成短暂的直通现象。所以前面这里就加上了死区生成电路,在开关切换的瞬间,产生一定时长的死区,让桥臂的上下管全都关断,防止直通现象。
最后一部分就是刹车输入的功能。这个是为了给电机驱动提供安全保障的。
如果外部引脚BKIN(Break IN)产生了刹车信号,或者内部时钟失效产生了故障,那么控制电路就会自动切断电机的输出,防止意外的发生,这就是刹车输入的功能。
到这里这个表里的每个功能都大概介绍了
定时中断基本结构图
这是对上面三种定时器结构的简化图。我们来着重看一下本节的两个任务(定时中断和内外时钟源选择)所涉及的结构。
在本节示例程序里,第一个定时器定时中断就是用的内部时钟这一路。第二个定时器外部时钟就是用的外部时钟模式2这一路。
当然还可以选择这里的触发输入,当做外部时钟,即外部时钟模式1。
这些就是定时器的所有可选的时钟源了。
最后还有个编码器模式,这一般是编码器独用的模式,普通的时钟用不到这个。
接下来右边这里就是计时时间到产生更新中断后的信号去向。在这里,如果是高级定时器的话,在中断输出控制前还会多一个重复计数器。这个注意一下,不过我们暂时就不考虑了。
这里中断信号会先在状态寄存器里置一个中断标志位。这个标志位会通过中断输出控制到NVIC申请中断
为什么会有一个中断输出控制?
因为这个定时器模块有很多地方都要申请中断,比如前面这个图里不仅更新要申请中断,这里触发信号也会申请中断,还有下面的输入捕获和输出比较匹配时也会申请。所以这些中断都要经过中断输出控制,如果需要这个中断,就允许,如果不需要,就禁止。简单来说,这个中断输出控制就是一个中断输出的允许位。如果需要某个中断,就记得允许一下。
接下来我们再来看几个时序图,研究一下时基单元运行的一些细节问题。
预分频器时序
这是预分频器的参数从1变到2时,计数器的时序图
第一行是CK_PSC,预分频器的输入时钟,就是这个图里的这个位置,选内部时钟的话,一般是七十二兆赫兹。
第二行的CNT_EN,计数器使能,高电平则计数器正常运行,低电平则计数器停止。
第三行是CK_CNT,计数器时钟,就是这个位置,它既是预分频器的时钟输出,也是计数器的时钟输入。
这里可以看到,开始时计数器未使能,计数器时钟不运行。然后使能后,前半段预分频器系数为1,计数器的时钟等于预分频器前的时钟。
后半段预分频器系数变为2,计数器的时钟就也变为预分频器前时钟的一半了。
在计数器时钟的驱动下,下面计数器寄存器也跟随时钟的上升沿不断自增。在中间的这个位置FC之后,计数值变为0了。这里虽然没写,但是可以推断出ARR自动重装值就是FC。
当计数值计到和重装值相等,并且下一个时钟来临时,计数值才清零。同时下面这里产生一个更新事件。这就是一个计数周期的工作流程。
下面这些时序描述的其实是这个预分频寄存器的一种缓冲机制,也就是这个预分频寄存器实际上是有两个
预分频控制寄存器供我们读写用的,它并不直接决定分频系数。
预分频缓冲寄存器,或者说是影子寄存器,缓冲寄存器和影子寄存器这两个说法其实是一个意思。这个缓冲寄存器才是真正起作用的寄存器,比如我们在某个时刻,把预分频寄存器由0改成了1。如果在此时立刻改变时钟的分频系数,就会导致这里在一个计数周期内,前半部分和后半部分的频率不一样。这里计数记到一半,计数频率突然就会改变了。这虽然一般并不会有什么问题,但是STM32的定时器比较严谨。设计了这个缓冲寄存器。这样当计数记到一半的时候,改变了分频值,这个变化并不会立刻生效,而是会等到本次计数周期结束时产生了更新事件,预分频寄存器的值才会被传递到缓冲寄存器里面去,才会生效。
所以在这里看到,即使在计数中途改变了预分频值,计数频率仍然会保持为原来的频率
直到本轮计数完成,在下一轮计数时,改变后的分频值才会起作用。
预分频器内部实际上也是靠计数来分频的。当预分频值为0时,计数器就一直为0,直接输出原频率。当预分频值为0时,计数器就01010101这样计数,再回到0的时候,输出一个脉冲。这样输出频率就是输入频率的二分频。
预分频器的值和实际的分频系数之间有一个数的偏移,有这样一个公式计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)。
计数器时序
内部时钟分频因子为2,就是分频系数为2。
第一行是内部时钟,七十二兆赫兹。
第二行是时钟使能,高电平就启动。
第三行是计数器时钟,因为分频系数为二,所以这个频率是第一行内部时钟频率除二。
第四行计数器在时钟每个上升沿自增。当增到0036的时候发生溢出,计到36之后再来一个上升沿,计数器清零。计数器溢出(第五行),产生一个更新事件脉冲(第六行)。
另外还会置一个更新中断标志位UIF(最后一行),这个标志位只要置1了就会去申请中断,然后中断响应后,需要在中断程序中手动清零。
这就是计数器的工作流程。
这里也有个式子,计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1),把之前那个式子(计数器计数频率:CK_CNT = CK_PSC / (PSC + 1))代进去,就是CK_CNT_OV= CK_PSC / (PSC + 1) / (ARR + 1)
这就是我们在计算定时时间的一个式子。用七十二兆赫兹除以 (PSC + 1)再除ARR + 1就能得到溢出频率。如果想算溢出时间,就只需要再取个倒数就行了。
刚才说了预分频器为了防止计数中途更改数值,造成错误,设计了缓冲寄存器。所以计数器肯定也少不了这样的设计了。
像这样带一个黑色阴影的寄存器都是有影子寄存器这样的缓冲机制的,包括预分频器自动重装寄存器和下面的捕获比较寄存器,所以计数的这个ARR自动重装寄存器也是有一个缓冲寄存器的。
并且这个缓冲寄存器是用还是不用,是可以自己设置的。
下面计数器无预装时序就是没有缓冲寄存器的情况。
而再下面这个有预装时序,就是有缓存寄存器的情况。
通过设置这个ARPE位就可以选择是否使用预装功能。
我们先看一下无预装的情况。
计数器无预装时序
在这里,计数器正在进行自增计数,突然更改了自动加载寄存器,即自动重装计存器。由DD改成了26计数值的目标值就由FF变成了36。所以这里计到36之后,就直接更新,开始下一轮计数。
再看一下下面这个图有预装的情况
计数器有预装时序
在计数的中途,突然把计数目标由F5改成了36。可以看到下面有个影子计存器,这个影子计存器才是真正起作用的,它还是F5。所以现在计数的目标还是计到F5,产生更新事件。
这时,要更改的36才被传递到影子寄存器,在下一个计数周期,这个更改的36才有效。
所以可以看出,引入这个影子寄存器的目的实际上是为了同步,就是让值的变化和更新事件同步发生。防止在运行途中更改造成错误,在这个例子也可以看出,如果这里不使用影子寄存器的话,F5改到36立刻生效。但此时计数值已经到了F1已经超过36了,F1只能增加,但它的目标却是36,比它还小,这样F1就只能一直加到FFFF,再回到零,再加到36才能产生更新,这就会造成一些小问题。当然如果你不介意这样的问题的话,就不用管这些细节了。毕竟STM32设计出来要考虑到各种各样的情况,所以做的比较严谨。
RCC时钟树的结构图
这个时钟树就是STM32中用来产生和配置时钟,并且把配置好的时钟发送到各个外设的系统。
时钟是所有外设运行的基础,所以时钟也是最先需要配置的东西。
我们之前说过,程序中主函数之前还会执行一个SystemInit函数,这个函数就是用来配置这个时钟树的。
这个结构看上去挺复杂的,配置起来还是比较麻烦的。不过ST公司已经帮我们写好了配置这个时钟树的SystemInit的函数。
从这里画一个界限,左边的都是时钟的产生电路,右边的都是时钟的分配电路。
中间的这个system clock就是系统时钟72MHz。
先看产生电路部分
时钟产生电路
在时钟产生电路,有四个振荡源。
分别是内部的8MHz高速RC振荡器。
外部的4-16MHz高速石英晶体振荡器,也就是晶振,一般都是接8MHz。
外部的32.768MHz低速晶振,这个一般是给RTC提供时钟的
最后是内部的40KHz低速RC振荡器,这个可以给看门狗提供时钟。
这两个高速晶振是用来提供系统时钟的,AHB、APB2、APB1的时钟都是来源于这两个高速晶振。
这里内部和外部都有一个8MHz的晶振,都是可以用的。只不过是外部的石英振荡器比内部的RC振荡器更加稳定,所以一般我们都用外部晶振,但是如果你系统很简单,而且不需要精确的时钟,也是可以使用内部RC震荡器的,这样就可以省下外部晶振的电路了。
在SystemInit函数里,ST是这样来配置时钟的:首先启动内部时钟,选择内部8MHz为系统时钟,暂时以内部8MHz的时钟运行,然后再启动外部时钟,配置外部时钟走这一路,进入PLL锁相环进行倍频,8MHz倍频9倍得到七十二兆赫兹。等到锁相环输出稳定后,选择锁相环输出为系统时钟。这样就把系统时钟由八兆赫兹切换为了七十二兆赫兹,这是ST配置的流程。
如果你的外部晶振出问题了,可能会导致一个现象,你会发现程序的时钟慢了大概十倍。比如你用定时器定一个一秒的时间,结果过了大概十秒才进中断。这个问题就出在这里,如果外部晶振出问题了,系统时钟就无法切换到七十二兆赫兹。它就会以内部的八兆赫兹运行,八兆相比于七十二兆,大概就慢了十倍。
另外这里还有一个CSS(Clock Security System),这个是时钟安全系统,它也是负责切换时钟的。它可以监测外部时钟的运行状态,一旦外部时钟失效,它就会自动把外部时钟切换回内部时钟,保证系统时钟的运行,防止程序卡死造成事故。
另外在这个高级定时器这里也有这个css,在这个刹车输入这里,一旦CSS检测到外部时钟失效,这里通过或门就会立刻反映到输出比较。这里让这个输出控制的电机立刻停止,防止意外,这就是这个STM32里面的一些安全保障措施。
左边的生成电路看完后,接下来我们看一下右边的时钟分配电路。
时钟分配电路
首先,系统时钟七十二兆赫兹进入AHB总线,AHB总线有个预分频器,在System_Init里配置的分频系数为1。AHB的时钟就是七十二兆赫兹,然后进入APB1总线,这里配置的分频系数是2,所以APB1总线的时钟为七十二兆赫兹除以二,等于三十六兆赫兹。
现在大家可能会有个问题,就是我们刚才说通用定时器和基本定时器是接在APB1上的,而APB1的时钟是三十六兆赫兹,按理说他们的时钟应该是三十六兆赫兹,但是我们在学定时器的时候,一直都说的是所有的定时器的时钟都是七十二兆赫兹,这是为什么?原因就在这里:
这下面还有一条支路上面写的是,如果APB1预分频系数等于1则频率不变,否则频率乘2。
然后再看右边发现这一路是单独为定时器2~7开通的。因为预分频系数我们给的是2,所以这里频率要再乘二。所以通向定时器2~7的时钟就又回到了七十二兆赫兹。
所以这里就可以有个结论,无论是高级定时器还是通用定时器,还是基本定时器,它们的内部基准时钟都是七十二兆赫兹,这个就给我们的使用带来了方便,不用再考虑不同定时器的时钟不一样的问题了。当然前提是你不乱改它System_Init里面的默认配置,要是改了这里的时钟,还得再另行分析。
然后我们再看一下下面APB2的时钟,这里给的分频系数为1,所以APB2的时钟和AHB一样,都是七十二兆赫兹,这里接在APB2上的高级定时器也单开了一路。上面写的也是,如果APB2预分频系数等于1则频率不变,否则频率乘二。但是这里APB2的预分频系数就是1,所以频率不变,定时器1和8的时钟就是七十二兆赫兹。
在这些时钟输出这里都有一个与门进行输出控制。
控制位写的是外部时钟使能,这就是我们在程序中写RCC_APB2或者APB1 PeriphClockCmd外设时钟控制作用的地方。打开时钟那一个步骤就是在这个位置写1让左边的时钟能够通过与门输出给外设。
有关时钟树的内容就讲到这里,剩下的还有一些给ADC、SDIO等等这些提供时钟的电路,大家就自己看看了。
想深入研究的话,也可以再看看手册。
温馨提示:如果理论部分的知识有些看不懂的,可以先略过,当下不必太纠结,重要的还是代码部分学习如果应用每个模块。
本节的内容就全部完成了,下一节来开始写定时中断和内外时钟源选择的代码。
QQ交流群:963138186
本篇就到这里,下篇继续!欢迎点击下方订阅本专栏↓↓↓