TIM—基本定时器
本章参考资料:《 STM32F4xx 参考手册》、《 STM32F4xx 规格书》、库帮助文档
《 stm32f4xx_dsp_stdperiph_lib_um.chm》。
学习本章时,配合《 STM32F4xx 参考手册》 基本定时器章节一起阅读,效果会更佳,
特别是涉及到寄存器说明的部分。
特别说明,本书内容是以 STM32F42x 系列控制器资源讲解。
TIM 简介
定时器(Timer)最基本的功能就是定时了,比如定时发送 USART 数据、定时采集 AD
数据等等。如果把定时器与 GPIO 结合起来使用的话可以实现非常丰富的功能,可以测量
输入信号的脉冲宽度,可以生产输出波形。定时器生产 PWM 控制电机状态是工业控制普
遍方法,这方面知识非常有必要深入了解。
STM32F42xxx 系列控制器有 2 个高级控制定时器、 10 个通用定时器和 2 个基本定时器,
还有 2 个看门狗定时器。看门狗定时器不在本章讨论范围,有专门讲解的章节。控制器上
所有定时器都是彼此独立的,不共享任何资源。各个定时器特性参考表 31-1。
再看参考手册中的描述:
之前时钟章节的博客:
我们程序设定的分频系数是4和2,所以TIMx_CLK =2乘以PCLK1(因为 基本定时器是挂载在APB1上的) =90M。
基本定时器功能框图
基本定时器的功能框图包含了基本定时器最核心内容,掌握了功能框图,对基本定时
器就有一个整体的把握,在编程时思路就非常清晰,见图 31-1。
首先先看图 31-1 中绿色框内容,第一个是带有阴影的方框,方框内容一般是一个寄存
器名称,比如图中主体部分的自动重载寄存器(TIMx_ARR)或 PSC 预分频器(TIMx_PSC),
这里要特别突出的是阴影这个标志的作用,它表示这个寄存器还自带有影子寄存器,在硬
件结构上实际是有两个寄存器,源寄存器是我们可以进行读写操作,而影子寄存器我们是
完全无法操作的,有内部硬件使用。影子寄存器是在程序运行时真正起到作用的,源寄存
器只是给我们读写用的,只有在特定时候(特定事件发生时)才把源寄存器的值拷贝给它的
影子寄存器。多个影子寄存器一起使用可以到达同步更新多个寄存器内容的目的。
接下来是一个指向右下角的图标,它表示一个事件,而一个指向右上角的图标表示中
断和 DMA 输出。这个我们把它放在图中主体更好理解。图中的自动重载寄存器有影子寄
存器,它左边有一个带有“ U”字母的事件图标,表示在更新事件生成时就把自动重载寄
存器内容拷贝到影子寄存器内,这个与上面分析是一致。寄存器右边的事件图标、中断和
DMA 输出图标表示在自动重载寄存器值与计数器寄存器值相等时生成事件、中断和 DMA输出。
①时钟源
定时器要实现计数必须有个时钟源,基本定时器时钟只能来自内部时钟,高级控制定
时器和通用定时器还可以选择外部时钟源或者直接来自其他定时器等待模式。我们可以通
过 RCC 专用时钟配置寄存器(RCC_DCKCFGR)的 TIMPRE 位设置所有定时器的时钟频率,
我们一般设置该位为默认值 0,使得表 31-1 中可选的最大定时器时钟为 90MHz,即基本定
时器的内部时钟(CK_INT)频率为 90MHz。
基本定时器只能使用内部时钟,当 TIM6 和 TIM7 控制寄存器 1(TIMx_CR1)的 CEN 位
置 1 时,启动基本定时器,并且预分频器的时钟来源就是 CK_INT。对于高级控制定时器
和通用定时器的时钟源可以来找控制器外部时钟、其他定时器等等模式,较为复杂,我们
在相关章节会详细介绍。
②控制器
定时器控制器控制实现定时器功能,控制定时器复位、使能、计数是其基础功能,基
本定时器还专门用于 DAC 转换触发。
③计数器
基本定时器计数过程主要涉及到三个寄存器内容,分别是计数器寄存器(TIMx_CNT)、
预分频器寄存器(TIMx_PSC)、自动重载寄存器(TIMx_ARR),这三个寄存器都是 16 位有效
数字,即可设置值为 0 至 65535。
首先我们来看图 31-1 中预分频器 PSC,它有一个输入时钟 CK_PSC 和一个输出时钟
CK_CNT。输入时钟 CK_PSC 来源于控制器部分,基本定时器只有内部时钟源所以
CK_PSC 实际等于 CK_INT,即 90MHz。在不同应用场所,经常需要不同的定时频率,通
过设置预分频器 PSC 的值可以非常方便得到不同的 CK_CNT,实际计算为: fCK_CNT 等
于 fCK_PSC/(PSC[15:0]+1)。
图 31-2 是将预分频器 PSC 的值从 1 改为 4 时计数器时钟变化过程。原来是 1 分频,
CK_PSC 和 CK_CNT 频率相同。向 TIMx_PSC 寄存器写入新值时,并不会马上更新
CK_CNT 输出频率,而是等到更新事件发生时,把 TIMx_PSC 寄存器值更新到影子寄存器
中,使其真正产生效果。更新为 4 分频后,在 CK_PSC 连续出现 4 个脉冲后 CK_CNT 才产
生一个脉冲。
在定时器使能(CEN 置 1)时,计数器 COUNTER 根据 CK_CNT 频率向上计数,即每来
一个 CK_CNT 脉冲, TIMx_CNT 值就加 1。当 TIMx_CNT 值与 TIMx_ARR 的设定值相等
时就自动生成事件并 TIMx_CNT 自动清零,然后自动重新开始计数,如此重复以上过程。
为此可见,我们只要设置 CK_PSC 和 TIMx_ARR 这两个寄存器的值就可以控制事件生成的
时间,而我们一般的应用程序就是在事件生成的回调函数中运行的。在 TIMx_CNT 递增至
与 TIMx_ARR 值相等,我们叫做为定时器上溢。
自动重载寄存器 TIMx_ARR 用来存放于计数器值比较的数值,如果两个数值相等就生
成事件,将相关事件标志位置位,生成 DMA 和中断输出。 TIMx_ARR 有影子寄存器,可
以通过 TIMx_CR1 寄存器的 ARPE 位控制影子寄存器功能,如果 ARPE 位置 1,影子寄存
器有效,只有在事件更新时才把 TIMx_ARR 值赋给影子寄存器。如果 ARPE 位为 0,修改
TIMx_ARR 值马上有效。
为什么要加1,因为参考手册这样说的啊:
计数器、 自动重装载寄存器
定时器使能(CEN 置 1)后,计数器 CNT在CK_CNT 驱
动下向上计数,当 TIMx_CNT 值不 TIMx_ARR 的设
定值相等时就自动生成事件并 TIMx_CNT 自动清零,
然后自动重新开始计数,如此重复以上过程。
定时时间的计算如何实现0.5S的定时 ?
思考我们基本定时器的最小定时时间(除了零之外)是多少?分母最大,分子最小的时刻,1/90M hz=0.01111us=11nm;最大定时时间,分母最小,分子最大,65536/(90M/65536)=47.7s。
定时器初始化结构体详解
标准库函数对定时器外设建立了四个初始化结构体,基本定时器只用到其中一个即
TIM_TimeBaseInitTypeDef,该结构体成员用于设置定时器基本工作参数,并由定时器基本
初始化配置函数 TIM_TimeBaseInit 调用,这些设定参数将会设置定时器相应的寄存器,达
到配置定时器工作环境的目的。这一章我们只介绍 TIM_TimeBaseInitTypeDef 结构体,其
他结构体将在相关章节介绍。
初始化结构体和初始化库函数配合使用是标准库精髓所在,理解了初始化结构体每个
成员意义基本上就可以对该外设运用自如了。初始化结构体定义在 stm32f4xx_tim.h 文件中,
初始化库函数定义在 stm32f4xx_tim.c 文件中,编程时我们可以结合这两个文件内注释使用。
(1) TIM_Prescaler:定时器预分频器设置,时钟源经该预分频器才是定时器时钟,它设定
TIMx_PSC 寄存器的值。可设置范围为 0 至 65535,实现 1 至 65536 分频。
(2) TIM_CounterMode:定时器计数方式,可是在为向上计数、向下计数以及三种中心对
齐模式。基本定时器只能是向上计数,即 TIMx_CNT 只能从 0 开始递增,并且无需初
始化。
(3) TIM_Period:定时器周期,实际就是设定自动重载寄存器的值,在事件生成时更新到
影子寄存器。可设置范围为 0 至 65535。
(4) TIM_ClockDivision:时钟分频,设置定时器时钟 CK_INT 频率与数字滤波器采样时钟
频率分频比,基本定时器没有此功能,不用设置。
(5) TIM_RepetitionCounter:重复计数器,属于高级控制寄存器专用寄存器位,利用它可
以非常容易控制输出 PWM 的个数。这里不用设置。
虽然定时器基本初始化结构体有 5 个成员,但对于基本定时器只需设置其中两个就可
以,想想使用基本定时器就是简单。
编程实战:
//#define BASIC_TIM TIM6 //#define BASIC_TIM_CLK RCC_APB1Periph_TIM6 #define BASIC_TIM TIM7 #define BASIC_TIM_CLK RCC_APB1Periph_TIM7 #define BASIC_TIM_IRQn TIM7_IRQn #define BASIC_TIM_IRQHandler TIM7_IRQHandler //#define BASIC_TIM_IRQn TIM6_DAC_IRQn //#define BASIC_TIM_IRQHandler TIM6_DAC_IRQHandler
BH给的课后练习,历程使用TIM6,完成TIM7的基本定时器,更改中断源和中断服务函数名,这个不是单独只把之前历程中的6改成7就行,因为中断源有些变化,那么我们有没办法去掉这样的弊病呢?在我之前用山外库的时候,就做得很好,所以,我觉得,我需要写一个更好用的库函数版本:
通过自己用宏把后端给替换了,这样在编程的时候,就真的是只用6改成7就行,这样的库函数对新手使用上来说更方便。
初始化处先清除确保不受干扰,再开启更新中断,最后使能定时器:
中断服务函数中,先确保发生了中断,然后反转LED,最后清除中断发生标志,确保下一次中断服务函数正常工作。
又发现一点可以改进的地方:
这里还需要库函数使用者去计算时间,而作为专业的库函数,应该是自己写好一个基准,比如定时1ms的,然后我调用一个函数 dealy_ms(50);就表示精确延时50ms,这里也是可以通过库函数配置好一个最小时间基准例如ms级别的,通过形参传递参数,然后用这个形参倍数于之前的时间基准,这样的库函数,明显更好。
欢迎加入作者的小圈子
扫描下方左边二维码加入QQ交流群,扫描下方右边二维码关注个人微信公众号并获取更多隐藏干货,QQ交流群:816747642 微信公众号:Crystal软件学堂
![](https://images.cnblogs.com/cnblogs_com/yangguang-it/1907263/o_201227061707QQ20201227141844.png)
![](https://images.cnblogs.com/cnblogs_com/yangguang-it/1704156/o_200415115115qrcode_for_gh_5c7d1da35871_258.jpg)
作者:Crystal软件学堂 bilibili视频教程地址:https://space.bilibili.com/5782182 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在转载文章页面给出原文连接。 如果你觉得文章对你有所帮助,烦请点个推荐,你的支持是我更文的动力。 文中若有错误,请您务必指出,感谢给予我建议并让我提高的你。 |