STM32 定时器详解
吃了一个猛亏,自己理解花了大半天时间,结果一看代码发现巨简单
算了,把自己理解的放上来吧
目录
STM32 定时器详解
前言
一、定时器种类和区分
二、时钟源
三、计数过程
3.1 计数器时钟CK_CNT
3.2 计数器有关的三个寄存器
3.3 其他的寄存器
3.4 定时器计算时间
总结
前言
前面说过看门狗以及简单的延时功能,实际上STm32还有专门用于定时器的配置。定时器配套的功能有很多,比如定时发送 USART 数据、定时采集 AD数据等等。如果把定时器与 GPIO 结合起来使用的话可以实现非常丰富的功能,可以测量输入信号的脉冲宽度,可以生产输出波形。定时器生产 PWM 控制电机状态是工业控制普遍方法(STM32第六章-TIM定时器详解 – 知乎)
为什么要用计时器? 答:延时过程中,CPU时间被占用,无法进行其他任务,导致系统效率降低。延时时间越长,该 缺点便越明显,因此软件延时只适用于短暂延时,或简单项目。
这一块比较复杂:参考了很多博客( 【STM32】通用定时器的基本原理(实例:定时器中断)_stm32定时器中断设置中断变量_Yngz_Miao的博客-CSDN博客),最后整理总结如下:
一、定时器种类和区分
高级定时器(TIM1、TIM8);通用定时器(TIM2、TIM3、TIM4、TIM5);基本定时器(TIM6、TIM7)。
即:高级定时器具有捕获/比较通道和互补输出,通用定时器只有捕获/比较通道,基本定时器没有以上两者。
使用最多的是高级定时器和通用定时器
主要的区别见下:
二、时钟源
2.1 需要一个时钟源
定时器要实现计数必须有个时钟源,因为他需要一个计时的基础(很矛盾吧,但是内部能够提供一个最基本的RCC时钟可以基于此设计,也可以用外部的)。
基本定时器时钟只能来自内部时钟,高级控制定时器和通用定时器还可以选择外部时钟源或者直接来自其他定时器等待模式。
我们可以通过 RCC 专用时钟配置寄存器(RCC_DCKCFGR)的 TIMPRE位设置所有定时器的时钟频率,我们一般设置该位为默认值 0,使得表中可选的(通用定时器的时钟频率是由APB1的分频系数决定,如果APB1的预分频系数是1,则通用定时器的时钟频率等于APB1的时钟频率(不理解这句话可以看下面的RCC时钟部分))。
基本定时器只能使用内部时钟,当 TIM6 和 TIM7 控制寄存器 1(TIMx_CR1)的 CEN 位置 1时,启动基本定时器,并且预分频器的时钟来源就是 CK_INT(下面还有很多不同的说法)。
对于高级控制定时器和通用定时器的时钟源需要找控制器外部时钟、其他定时器等等模式,较为复杂,定时器来源:
1.系统RCC内部(RCC的CK_INT,这个可以由系统内部时钟IMx_CLK直接提供)
2.系统内外部的定时器(TIMX)
其中外部时钟有关的时钟源为
1.外部触发输入(也叫外部引脚输出TIMx_ETR)
2.内部触发输入(ITRx)
3.外部输入脚(TIx)
2.2 最常用的时钟源(默认设定)
关于来自系统(RCC)的时钟这里还需要在仔细说明一下(也即是 CK_INT来源再说明一下):在RCC(系统时钟)过程有这样的流程:
也就是说:由AHB时钟经过APB1预分频系数转至APB1时钟,再通过某个规定转至TIMxCLK时钟(TIMxCLK就是上面说的内部时钟CK_INT)。值得注意的是TIMxCLK时钟频率一定是APB1时钟频率(图中说了二分了就又会乘以二)
【APB1(RCCx系统中的时钟)->CK_CNT时钟】的过程
CK_PSC最终经过PSC预分频系数转至CK_CNT(就是下面这个计数器)。
原文链接:【STM32】通用定时器的基本原理(实例:定时器中断)_stm32定时器中断设置中断变量_Yngz_Miao的博客-CSDN博客
感谢大佬。
三、计数过程
3.1 计数器时钟CK_CNT
TIMxCLK经过PSC预分频器之后为CK_INT,作为CNT计数器的计数时钟。PSC可以对定时器时钟TIMxCLK(也就是CK_PSC)进行1~65535之间任何一个数进行分频,CK_CNT = TIMxCLK / (PSC + 1)。PSC的值设置于TIMx_PSC寄存器。
定时器的本质是计数器,即对一定周期(频率)的脉冲进行计数。(定时器的本质是计数器,即对一定周期(频率)的脉冲进行计数。_定时器为什么就是计数器_TYINY的博客-CSDN博客)
3.2 计数器有关的三个寄存器
要知道定时器的本质是计数器和三个寄存器有关就行了
定时器计数过程主要涉及三个寄存器( STM32-定时器详解_stm32定时器_KevinFlyn的博客-CSDN博客)
计数器寄存器(TIMx_CNT):向上计数、向下计数或者中心对齐计数;
预分频寄存器(TIMx_PSC):可将时钟频率按1到65535之间的任意值进行分频,可在运行时改变其设置值;
自动装载寄存器(TIMx_ARR):如果TIMx_CR1寄存器中的ARPE位为0,ARR寄存器的内容将直接写入影子寄存器;如果ARPE为1,ARR寄存器的那日同将在每次的更新时间UEV发生时,传送到影子寄存器;如果TIM1_CR1中的UDIS位为0,当计数器产生溢出条件时,产生更新事件。:【STM32】通用定时器的基本原理(实例:定时器中断)_stm32定时器中断设置中断变量_Yngz_Miao的博客-CSDN博客
3.3 其他的寄存器
当然,还有几个相关的寄存器需要记住
1.控制寄存器(TIMx_CR1)
作用:对计数器的计数方式、使能位等进行设置。
这里有ARPE位:自动重装载预装载允许位。ARPE=0时,TIMx_ARR寄存器没有缓冲;ARPE=1时,TIMx_ARR寄存器被装入缓冲器。
2.DMA/中断使能寄存器(TIMx_DIER)
DMA是一个临时的寄存器,定时器能够与DMA进行通信
该寄存器能够对DMA/中断使能进行配置。
3.4 定时器计算时间
通用定时器超时时间
超出(溢出)时间计算:Tout=(ARR+1)(PSC+1)/TIMxCLK。其中:Tout的单位为us,TIMxCLK的单位为MHz。
这里需要注意的是:PSC预分频系数需要加1,同时自动重加载值也需要加1。
为什么自动重加载值需要加1,因为从ARR到0之间的数字是ARR+1个;
为什么预分频系数需要加1,因为为了避免预分频系数不设置的时候取0的情况,使之从1开始。
这里需要和之前的预分频进行区分:由于通用定时器的预分频系数为1~65535之间的任意数值,为了从1开始,所以当预分频系数寄存器为0的时候,代表的预分频系数为1。而之前的那些预分频系数都是固定的几个值,比如1、4、8、16、32、64等等,而且可能0x000代表1,0x001代表4,0x010代表8等等。也就是说,一边是随意的定义(要从1开始),另一边是宏定义了某些值(只有特定的一些值)。
比如,想要设置超出时间为500ms,并配置中断,TIMxCLK按照系统默认初始化来(即72MHz),PSC取7199,由此可以计算出ARR为4999。
也就是说,在内部时钟TIMxCLK为72MHz,预分频系数为7199的时候,从4999递减至0的计数事件是500ms倒计时。
3.5 代码
代码:
概念很多,但是代码很少:
1.使能定时器时钟。RCC_APB1PeriphClockCmd();2. 初始化定时器,配置ARR,PSC。TIM_TimeBaseInit();3.开启定时器中断,配置NVIC。void TIM_ITConfig();NVIC_Init();4.使能定时器。TIM_Cmd();5.编写中断服务函数。TIMx_IRQHandler();
还有获取状态位的函数(这部分区别可以查看:STM32学习笔记—TIM_GetFlagStatus和TIM_GetITStatus两个固件库函数的区别_小重拌豆腐的博客-CSDN博客)
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);