TIM简介
TIM(Timer)定时器 定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
定时器类型
基本定时器框图的讲解
首先下图中有三个重要部分预分频器,计数器,自动重装在寄存器,这三个部分构成了最基本的计数计时电路,所以这一块电路叫做时基单元,预分频器之前连接的就是基准计数的时钟的输入,最终来到了触发控制器的位置,由于基本定时器智能选择内部时钟作为输入,所以我们可以直接认为触发控制器的输出线直接连接到内部时钟CK_INT,内部时钟来源是RCC_TIMxCLK,这里的频率值一般都是系统的主频72MHZ,所以通向时基单元的主频是72MHZ,假如PSC预分频器设置位0,也就是一分频则输出频率等于72MHZ,预分频器写1那就是2分频,输出频率=输入频率/2=36MHz,如果写2就是3分频,输出=输入/3,以此类推,所以预分频器的值和实际的分频系数相差了1,即实际分频系数=预分频器的值+1,预分频器是16位的所以可以写65535,也就是65536分频,预分频器就是对输入的基准频率提前进行一个分频的操作,然后是计数器,计数器可以对预分频后的计数时钟进行计数,计数器每来一个上升沿(电平由低到高的进行切换),计数器的值就加1,计数器也是16位所以计数器的值可以从0加到65535,如果在家的话计数器就会回到0重新开始,计数器的值会在计数的过程中不断自增,直至达到自动重装在寄存器存放的值时,此时产生了中断,完成了任务,计数器清零重新自增,下图中UI箭头为产生的中断信号(更新中断)这个更新中断会通往NVIC我们再配置好NVIC的定时器通道,定时器的更新中断就能得到CPU的响应了,U箭头为更新事件信号,更新事件不会触发中断但是会触发内部其他电路的工作,这些就是定时器定时中断的流程。
主模式触发DAC功能
STM32定时器的一大特点,就是这个主从触发模式,它能让内部硬件在不受程序的控制下实现自动运行,如果能把这个主从触发模式掌握好,那在某些情况下,减少CPU的损耗,
主模式触发DAC这个用途时在我们使用DAC的时候,可能会用DAC输出一段波形,那就需要每隔一段时间来触发DAC,让他输出下一个电压点,如果用正常的思路来实现,先设置一个定时器产生中断,每个一段时间在终端程序中调用代码手动触发一次DAC转换,然后DAC输出,但是会使主程序一直处于频繁中断的状态下,影响主程序运行和其他中断响应,所以定时器设计了一个主模式,使用主模式可以把定时器的更新事件映射到TRGO(Tigger Out触发输出)的位置TRGO直接接到DAC的触发转换引脚上,这样定时器的更新就不需要通过中断触发DAC转换了,仅需要把更新事件通过主模式映射到TRGO,然后TRGO就会直接触发DAC了,整个过程不需要软件参与,实现了硬件的自动化,这就是主模式的作用。
通用定时器框图的讲解
通用定时器,和高级定时器支持向下计数模式,向下计数模式就是从重装值开始,向下自减减到0后回到重装值同时申请中断,然后继续下一轮一次循环,这就是向下计数模式,还有中央对齐的计数模式,从0开始向上自增,自增到重装值,然后申请中断,向下自建,减到0,在申请中断,上部分结构就是内外时钟源选择,和主从触发模式的结构,对于基本定时器而言,定时只能选择内部时钟,也就是系统频率72MHz。
通用定时器不仅可以选择72Mz时钟,还可以选择外部时钟,具体有,TIMx_ETR引脚上的外部时钟,我们可以在这个TIM2的ETR引脚,也就是PA0上接一个外部方波时钟,然后配置一下内部的机型选择,边沿检测和预分频器电路,配置输入滤波电路,这两块电路乐意对外部时钟电路进行一定整形,因为外部引脚的时钟,会存在毛刺,电路对输入的波形进行滤波,经过滤波的信号分成两路上面一路ETRF进入触发控制器,紧接着就可以选择进入时基单元的时钟。
假如你想在ETR外部引脚提供时钟,或者想对ETR时钟进行计数,把这个定时器当作定时器来用的话我们可以配置这一路,下面还有一路可以提供时钟,TRGI主要时用作触发输入来使用的,这个触发输入可以触发定时器的从模式(后面会讲到这个模式)。我们可以把这个TRGI当作外部时钟模式1,两种情况对于时钟输入而言是等价的,只不过下面这一路输入会占用触发输入的通道而已,还有ITR信号这些信号是来自其他定时器,从触发控制器的右侧看主模式TRGO可以通向其他定时器,那通向其他定时器的时候,就接到了其他定时器的ITR引脚上了从ITR0到ITR3分别来自其他四个定时器的TRGO输出,通过此路我们可以实现定时器级联的功能,例如我们可以初始化TIM3,然后使用主模式把它的更新事件映射到TRGO上,接着在初始化TIM2,这里选择ITR2,对应的就是TIM3的TRGO,在选择时钟为外部时钟模式1,TIM3的更新事件就可以驱动TIM2的时基单元,也就实现了定时器的级联。
我们还可以选择TI1F_ED,这里连接的是这里输入捕获单元的CH1引脚,ED是边沿的意思,通过此路的时钟上升沿下降沿都有效,始终还可以通过T1FP1(CH1引脚时钟),TIFP2(CH2引脚时钟)来获得,时钟产生的部分介绍完毕。
下图中时基单元下方主要包含了两块电路,右下方主要是输出比较电路,总共有四个通道,分别对应CH1-CH4的引脚,左面是输入捕获电路,对应的也是CH1-CH4引脚,可以用来测输入方波的频率等,中间的寄存器是捕获/比较寄存器,是输入捕获和输出比较电路公用的,输入捕获和输出比较不能同时使用,所以寄存器公用,引脚也公用。
高级定时器框图详解
申请中断的地方增加了一个重复次数计数器,有了这个计数器之后,就可以实现每个几个计数周期才发生一次更新事件和更新中断,原来的结构是每个计数周期完成后都会发生更新,现在有个计数器在这里,可以实现每隔几个周期在更新一次,这个就相当于对输出的更新信号又做了一次分频,下面还对输出比较模块进行了升级,DTG是死区升级电路,右边的输出引脚由原来的一个变成了可以输出一对互补的PWM波,这些电路是为了驱动三项无刷电机的
定时中断流程
本文主要介绍定时中断和外部时钟源选择,中间最重要的就是时基单元,由下图三个部分组成,运行控制就是一些控制寄存器的位,例如启动停止,向上或向下计数等等,我们操作这些寄存器就能控制实际单元的运行了。左边是为了时基单元提供时钟的部分,这里可以选择左边那几个方式提供时钟具体流程上一段详细介绍了。计时时间到,会产生一个更新中断最右边是更新中断的去向,如果是高级定时器的话会多一个重复计数器,中断信号会在状态寄存器里设置一个中断标志位,这个标志位会通过中断输出控制,到NVIC申请中断。
预分频器时序图
第一行是CK_PSC,预分频器的输入时钟,内部时钟一般是72MHz,CNT_EN计数器使能,高电平计数器正常运行,低电平计数器停止,CK_CNT,前半部分预分频系数是1时钟频率等于CK_PSC的时钟频率,后半段系数为2,计数器时钟变为预分频器时钟一半,在计数器时钟驱动下下面的计数器寄存器也跟随时钟的上升沿不断自增,在中间位置FC之后,计数值为0,可以推断出ARR自动重装值就是FC,当计数值和重装值相等,并且下一个时钟来临时,计数值才清0,同时产生一个更新事件,下面还有三行时序,描述预分频寄存器的一种缓冲机制,预分频寄存器实际有两个,一个是我们读写用的,他并不直接决定分频系数,使本轮计数完成改变的分频值才会生效。
RCC时钟树框图详解
程序主函数执行前需要执行一个SystemInit函数,这个函数就是用来配置时钟树的,左边为时钟产生电路,右边为时钟的分配电路,中间的SYSCLK就是系统时钟72MHz,在时钟产生电路中有四个震荡源,分别内部高速8MHz,外部4-16MHz高速石英晶体振荡器,也就是晶振,一般接8MHz,外部32.768KHz低速晶振,这个一般是给RTC提供时钟的,最后是内部40KHz低速RC振荡器,这个可以给看门狗提供时钟,上面两个高速晶振,是用来提供系统时钟的,AHB,APB2,APB1都是来源于这两个高速晶振,这里内部和外部都有1个8MHz晶振,都可以用,外部石英振荡器比RC振荡器要更加稳定,所以一般使用外部晶振,系统简单可以使用内部RC震荡时钟。
在SystemInit函数中首先胡启动内部时钟,选择内部8MHz作为系统时钟,暂时以8MHz运行,然后启动外部时钟,配置外部时钟走PLL锁相环进行倍频,8MHz倍频9倍,得到72MHz,等到锁相环输出稳定后,选择锁相环输出为系统时钟,这样就可以把系统时钟有8MHz切换为72MHz。当外部晶振损坏导致程序的时钟会慢大概10倍,外部晶振损坏系统时钟无法切换到72MHz,CSS是为了防止程序卡死。
右边时钟分配电路,系统时钟72MHz进入AHB总线,AHB总线有个预分频器,在SystemInit函数中,配置的分频系数为1,AHB时钟为72MHz,进入APB1总线配置分频系数为2,APB1时钟为72/2=36MHz,下面还一路单独为定时器开通的一路将APB1时钟增加到72MHz,APB2同理。ADC其他电路原理也是如此。
TIM寄存器配置及详解
控制寄存器 1(TIMx_CR1)
这个寄存器我们经常只使用这两位
- 第4位:选择计数方式
- 第0位:使能计数器
DMA/中断使能寄存器(TIMx_DIER)
这个寄存器只使用第0位
- 第0位:更新中断
状态寄存器(TIMx_SR)
状态寄存器我们使用第0位
- 第0位:用来更新中断标记
预分频器(TIMx_PSC)
该寄存器用设置对时钟进行分频,然后提供给计数器,作为计数器的时钟,定时器的时钟源有4个
- 内部时钟(CK_INT)
- 外部时钟模式1:外部输入脚(TIx)
- 外部时钟模式2:外部触发输入(ETR)
- 内部触发输入(ITRx):使用A定时器作为B定时器的预分频器
如上四个时钟源由TIMx_SMCR寄存器设置。
自动重装载寄存器(TIMx_ARR)
TIM中断寄存器配置
具体的位运算讲解在前面GPIO配置中详解,可以区前面的文章参考
void TIM4_NVIC(void) //TIM4中断号为30
{
SCB->AIRCR|=0x05FA0500; //写入钥匙和分组2
NVIC->ISER[0]|=0x40000000; //TIM4中断使能
NVIC->IP[30]|=15<<4; //TIM4 抢占3 次优先级3
}
// ARR:重装载值
// PSC:预分频数
// 定时器4初始化
void TIM4_Init(unsigned int ARR,unsigned int PSC)
{
RCC->APB1ENR|=1<<2; //TIM4时钟使能
TIM4->ARR=ARR; //重装载值
TIM4->PSC=PSC; //预分频数
TIM4->DIER|=1<<0; //允许更新中断
TIM4->CR1|=0x01; //使能定时器 4
TIM4_NVIC();
}
//定时器4中断服务函数
void TIM4_IRQHandler(void)
{
if(TIM4->SR&(1<<0)) //溢出中断
{
LED1=~(LED1); //绿灯反转
}
TIM4->SR&=~(1<<0); //清除中断标志位
}
TIM中断库函数配置
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);//使能
TIM_InternalClockConfig(TIM2);//开启内部时钟
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//初始化定时器结构体
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//指定时钟分频使输出时钟稳定
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;向上计数
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;//ARR装载值配置
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;//预分频系数设置
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;//重复计数器设置
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);//初始化这个定时器
TIM_ClearFlag(TIM2, TIM_FLAG_Update);//清除标志位
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//使能中断
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);//配置中断分组
NVIC_InitTypeDef NVIC_InitStructure;//中断结构体定义
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;//选择中断通道
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;//使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;//设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;//设置响应优先级
NVIC_Init(&NVIC_InitStructure);//初始化中断
TIM_Cmd(TIM2, ENABLE);//定时器使能