stm32—时钟、定时器和看门狗
1. 时钟
什么是时钟呢?
一个可以产生周期性信号的设备
什么是周期性信号?
1 ----- ----- -----0 ----- ----- -----
所以时钟信号就是周期性变化的信号
关于时钟我们有两个比较重要的概念需要理解:
T:时钟周期,最小重复的信号单元的时间长度,基本单位为s(秒)F:时钟频率,1s内有多少个重复的信号单元(1s振动多少次),单位Hz
===> T * F == 1s
例子:
F = 200Hz 意味着 1s 振动 200次
---> 每次振动的时间是 1 / 200 s == 5ms
2. 为什么需要时钟
时钟最主要的作用是用来同步信号用的
什么是同步呢?
就比如我们的左腿和右腿实际上就需要进行同步,两腿需要协同工作,左腿迈一步然后右腿迈一步,依次重复,人就能正常前行。对应在我们的机器上也是一样的,机器的运行是由很多器件协同工作完成的,当一个器件完成分给它的工作时,理应通知别的器件它完成了,轮到你干活了,等你干完我再继续干。类似于左腿迈完步子了,要等右腿迈完左腿再迈。比如:在A和B进行数据的收发时,A发完数据后应该等待B去接收,等B接收完了之后A再继续发,这种就是同步通信
而我们的M4中大部分时序逻辑电路需要同步,那设备之间怎么实现同步?
这就需要用到我们的时钟信号了
设备是怎么根据时钟信号怎么实现同步的呢?来看电路以及时序图:
如上电路图中假设A端和B端的时序变化如下:
理论上A&B端的电平变化应该如上图所示,输出端(A&B)的电平变换应该要与B端的一致,但是实际上却是下面这样:
为什么会有这样的现象呢?主要是因为A和B输入到&门时,输出需要经过一段的反应时间,虽然这个时间比较短。那么在B端电平变化后,不能立马去读取输出端的电平,应该要等待一段时间后再去读取,那么应该要等待多长时间?此时间段实际上是可以获取到的,我们只需要对电路进行如下改动:
电路修改后, C端的电平的变化如下:
很明显,C端的高电平时期就是&门处理数据的时间(记为de_t),这段时间是不正常的,正常情况下C端应该一直是低电平(当&门处理数据没有延时的时候),我们将C端时序中凸起的地方就称之为“毛刺”。“毛刺”时期是不正常的,我们应该要略过它,略过的意思是指当B的信号发生改变时我们不应该立马去读输出值,而是应该等待一段时间,等电路(如:&门)将数据处理完毕后,再去读取输出值此时才是准确的怎么略过?此时就需要用到我们的时钟信号了
当我们在电路中加上REG后,在REG内部的触发器(假设触发器上升沿触发)的作用下,D端的电平变化如图:
通过上面的时序图可以得知,D此时输出的是一条干净的,没有“毛刺”的信号,那么通过上面这种现象我们可以总结出电路中处理“毛刺”的解决方案:
- 输入信号只能在Clock低跳变(下降沿)时改变,在Clock为高电平时保持不变
- T > 2 * de_t (周期必须大于2倍时延)
3. 时钟信号是怎么产生的呢?
在自然界中有一些物体天生就会产生摆动(振动) ---> 石英晶体
如果想利用石英晶体规则地、周期性的产生方波信号,需要一些电路来保证:
晶振电路:频率一般会比较小,如:12M、8M...
但是石英晶振难以满足现代计算机的高频需要,如:CPU它的频率会很高
那么我们就有“分频 / 倍频”电路:
分频:把输入频率变小
倍频:把输入频率变大
比如:在M4中接触的比较多的有PLL:锁相环电路
4. STM32F4xx时钟树(时钟系统)
查看<STM32F4xx中文参考手册.pdf>第六章(复位和时钟控制)第二节(时钟)第107页可以得到M4的时钟树(见图<时钟树.png>)
时钟树中有几个关键名词:LSI: Low Speed Internal 内部低速时钟 (32 kHz RC振荡器)
LSE: Low Speed External 外部低速时钟 (32.768KHz)
HSI: High Speed Internal 内部高速时钟 (16 MHz RC振荡器)
HSE: High Speed External 外部高速时钟 (8MHz)
内部时钟: 由内部集成的RC震荡电路产生
外部时钟: 由晶振产生通过GEC-M4原理图可知:
HSE_VALUE = 8M(外部高速时钟8M,取决于所接晶振大小)
从HSE出发沿着线路往右边走,会来到SW选择器,选择器一共有三路输入,分别为HSE/HSI/PLLCLK(锁相环时钟),选择这三者之一作为系统时钟来使用,我们的系统时钟最高可以达到168Mhz。很明显HSE/HSI提供不了168Mhz的频率,那么则由锁相环提供
而锁相环的输入是由HSI和HSE二选一之后进行M分频后得到的。在我们M4中我们选择的是HSE,也就是8M进行M分频后输入锁相环
即 SYSCLK = PLLCLK <--- 168M
= (HSE / M) * N / P
= (8Mhz / M ) * N / P
所以PLLCLK是由M和N以及P决定,实际上这三者的值可以在代码中指定:
M ---> 代码中用PLL_M表示 ---> 8分频(根据HSE而来,目的将HSE分频为1M)
N ---> 代码中用PLL_N表示 ---> 336倍频(336M)
P ---> 代码中用PLL_P表示 ---> 2分频
选择器选择HSE/HSI/PLLCLK三者之一作为SYSCLK系统时钟后,接着就可以来到AHB总线,可以通过多个预分频器配置AHB频率、高速APB(APB2)和低速APB(APB1)频率
AHB BUS = PLLCLK / (AHB Prescaler) // AHB Prescaler AHB总线的预分频
APBx BUS = AHB BUS CLK / (APBx Prescaler) // APBx Prescaler APBx总线的预分频
AHB总线时钟最大值为168M;
低速APB(APB1)最大值为42M,高速APB(APB2)最大值为84M
来到APBx总线上后,我们的定时器就挂载在APBx总线上,那我们的定时器的时钟频率是多少呢?
通过时钟树我们可以得知定时器的时钟频率分为两种:
- if (APBx presc == 1) 的意思是如果APB预分频值为1,那么定时器时钟频率等于APB的时钟频率;
- else 的意思就是如果APB预分频器值不为1,那么定时器时钟频率等于两倍的APB的时钟频率
如果APB1 CLK = 42M = AHB BUS CLK / (APB1 Prescaler)
APB1 Prescaler = 168 / 42 = 4
则位于APB1总线上的定时器时钟频率为 42M*2 = 84M
如果APB2 CLK = 84M = AHB BUS CLK / (APB2 Prescaler)
APB2 Prescaler = 168 / 84 = 2
则位于APB2总线上的定时器时钟频率为 84M*2 = 168M
5. 修改固件库时钟相关代码
因为ST公司提供固件库的时候,不知道其他公司设计的板子会采用多少频率的HSE晶振,因此只提供了最大值配置
GECM4采用的是8M HSE,所以需要修改
1)修改 HSE_VALUE 为 8M
stm32f4xx.h --> 144行
2)修改 PLL 相关
PLL_M 8 (371行)
PLL_N 336 (不需要改)
PLL_P 2 (不需要改)
system_stm32f4xx.c
上述修改需根据硬件电路的设计而来!!!
6. 定时器
timer:定时器就是用来定时的器件
在STM32上,一般来说,定时器由三部分组成:
时基单元、输入捕获单元、输出比较单元
1. 时基单元:Time Bese Unit
定时器的基本单元,所有定时器都具备的单元
时基单元 = 计数器 + 重载计数值寄存器 + 定时器预分频器 组成
时基单元工作原理:
将计数器设置为一个值按照一定的时钟频率递减到0,或按照一定的时钟频率从0递增到某个值,当计数器溢出后,可以产生一个溢出事件/中断以此来达到定时的功能
组成时基单元的三个器件的作用:
1)定时器预分频器(TIMx_PSC)
用来将定时器的总线时钟进行分频,提供一个合适的频率,给计数器去计数。分频系数介于1到65536之间,是一个16位的寄存器
2)重载计数值寄存器(TIMx_ARR)
用来设定计数值,设为N值。如果自动重装载数值为0,则计数器停止
3)计数器(TIMx_CNT)
按照预分频器得到的频率,从0递增到N,或者从N递减到0,并且可以在溢出后,产生定时器中断/事件
溢出的含义为:
如果为递增计数,当计数值达到N时产生溢出
如果为递减计数,当计数值达到0时产生溢出
比如:如果为递增计数,从0开始在一定的时钟频率下开始加1,一直加,加到N时,此时完成计数,就会溢出,产生定时器中断/事件
那么我们如何知道计数器溢出产生中断时,到底花费了多少时间呢?
我们知道计数器每做一次运算(+1运算)是需要花费时间的,那么我们只需要将计数器每+1所花费的时间求出来t,那么产生中断的时间应该就为:
(N+1) * t // N+1指做了N+1次运算
// 从0递增到N或者从N递减到0是N次运算
// 此时通过重装载从N回到0或从0回到N又是一次运算
// 相当于秒钟计数从0加到59秒再加1又回到0
计数器每+1花费的时间到底是多少?首先我们要搞清楚定时器中断产生的流程:
(依据于<STM32F4xx中文参考手册>第17章<基本定时器>中图188<基本定时器框图>):
通过如上流程图可知计数器每+1所花费的时间是跟输入计数器的时钟频率有关系的
举个例子:
假设TIMEx是位于APB1总线上,那么Fin = 84M hz
为了方便计算一般情况下TIMEx_PSC设置为83
则计数器时钟频率为Fcnt = Fin / (TIMEx_PSC + 1) = 84M / 84 = 1M
此时计数器的时钟周期Tcnt = 1 / Fcnt = 1 / 1M = 1us
此时即意味着计数器每过1us加1
所以产生定时器中断的时间为Vt = (N + 1)* Tcnt = (N + 1)us
2. 输入捕获单元
可以对一个或多个输入信号进行处理
有些定时器不具备输入捕获单元
具体可以捕捉多少个输入信号需要看你的定时器有几个通道Channel
有什么用呢?
比如: 可以计算输入信号的频率
输入信号经过"输入捕获阶段"(数字滤波,多路复用,预分频,去噪等等),到信号检测,当检测到需要的信号状态(上升沿变化/下降沿变化)变化时,就会把定时器时基单元中的TIMx_CNT计数器值,锁定到"输入捕获寄存器"中
这样就可以根据预先设定的定时器参数(时钟频率,N值等等),就可以计算出从开始捕获到锁定这个信号所花费的时间了t
那么进而我们就可以求出输入信号的频率 = 1/t
3. 输出比较单元
可以输出一个或多个信号
有些定时器没有输出比较单元
具体可以输出多少个信号,就需要看你的定时器有几个通道Channel
输出比较:
定时器可以向对应的GPIO引脚(复用功能)输出一个电平状态
并且可以根据"输出比较寄存器(TIMx_CCR)"中的值,来翻转输出的电平状态
比如:
TIMx_CCR > TIMx_CNT 向GPIO引脚输出一个低电平
TIMx_CCR <= TIMx_CNT 向GPIO引脚输出一个高电平
典型应用: 输出PWM波
注意:
输入捕获和输出比较共用一个寄存器
因此同一个定时器的输入捕获和输出比较功能不能同时使用