STM32笔记:学习TIM定时器的使用方法

1、TIM简介

1.1 基本定时器

时基单元:由自动重装载寄存器、预分频器、计数器组成。

来自RCC的TIMxCLK:一般是系统的主频,72MHz。

预分频器(16位):对进来的频率进行分频,写0,不分频,输出72MHz。写1,2分频,输出36MHz。以此类推。

计数器(16位):对预分频器后的计数时钟进行计数。

自动重装载寄存器(16位):写入的计数目标。

向上的箭头(UI):表示这里会产生中断信号。计数值等于自动重装值产生的中断,一般叫做“更新中断”,更新中断之后会通往NVIC,再配置好NVIC的定时器通道,那定时器的更新中断就可以得到CPU的响应。

向下的箭头(U):表示这里会产生一个事件。对应的事件叫做“更新事件”,更新事件不会触发中断,但会触发内部的其他电路的工作。

主模式触发DAC功能:定时器设计了一个主模式,使用主模式可以把定时器的更新事件映射到触发输出TRGO(Trigger Out)的位置,TRGO直接接到DAC的触发转换引脚上。定时器的更新就不需要通过中断来触发DAC转换了。

基本定时器仅支持向上计数模式。

1.2 通用定时器

基本定时器支持向上计数、向下计数、中央对齐的计数模式。

时钟源选择:可以选择内部时钟,也可以选择外部时钟。外部时钟:TIMx_ETR(Externala)引脚上的外部时钟。

外部时钟模式2。ETRF:经过ETRF进入触发控制器,就可以选择作为时基单元的时钟了。这一路叫做“外部时钟模式2”。

外部时钟模式1。TRGI:主要用作触发输入。当TRGI当作外部时钟来使用时,这一路叫做“外部时钟模式1”。通过这一路的时钟有:①ETR的引脚信号,②ITR信号,ITR0-ITR3分别来自其他4个定时器的TRGO输出。可以用作定时器的级联。③TI1F_ED(Edge,边沿)信号:连接的是输入捕获单元的CH1引脚。④TI1FP1信号:连接到CH1引脚的时钟。TI2FP2信号:连接到CH2引脚的时钟。

ITR信号:

编码器接口:可以读取正交编码器的输出波形。

TRGO:定时器的主模式输出,可以把内部的一些事件映射到TRGO引脚上。

输出比较电路:图右下角,有四个通道,CH1–CH4。可以用于输出PWM波形,驱动电机等。

输入捕获电路:图左下角,有四个通道,CH1–CH4。可以用于测量输入方波信号的频率等。

捕获/比较寄存器:是输入捕获和输出比较电路共用的。输入捕获和输出比较不能共用,因此寄存器和引脚也是共用的。

输入滤波器原理:可以滤掉信号的抖动干扰。在一个固定的时钟频率f下进行采样,如果连续N个采样点都为相同的电平,代表输入信号稳定,即可输出采样值。如果N个采样值不全都相同,说明信号有抖动,此时保持上一次的输出或者直接输出低电平。采样频率f和采样点数N都是滤波器的参数。采样频率f可以是由①内部时钟直接而来,也可以是②内部时钟+分频而来,分配多少,由参数TIM_ClockDivision 决定。TIM_ClockDivision 见5.3配置时基单元代码。

1.3 高级定时器

重复次数计数器:可以实现每隔几个周期,才发生一次更新事件和更新中断。

DTG(Dead Time Generate):死区生产电路。右边输出控制有两个互补的信号,可以输出一对互补的PWM波,可以用于驱动三相无刷电机。如:四轴飞行器、电动车的后轮、电钻等。

BRK:刹车输入功能,为了给电机驱动提供安全保障。如果外部引脚BKIN(Break IN)产生了刹车信号,或者内部时钟失效,产生了故障,控制电路会自动切断电机的输出。

2、定时中断基本结构

PSC(Prescaler):预分频器

CNT(Counter):计数器

ARR(Auto Reload Register):自动重装载寄存器

运行控制:控制寄存器的一些位。如启动停止、向上或向下计数等。

计时时间到产生中断的去向:中断信号会先在状态寄存器里置一个中断标志位,这个标志位会通过中断输出控制,到NVIC申请中断。

中断输出控制:因为定时器很多地方需要申请中断,如:更新申请中断,触发信号也会申请中断,输入捕获和输出比较匹配时也会申请中断。就是中断输出的允许位,需要某个中断,就允许一下。

3、时序图

3.1 预分频器时序图

CK_PSC:预分频器的输入时钟,内部时钟一般是72MHz。

CNT_EN:计数器使能,高电平计数器正常运行,低电平计数器停止。

CK_CNT:预分频器的时钟输出,也是计数器时钟的输入。

一个计数周期的工作流程:开始时,计数器不使能,定时器时钟不运行。计数器使能后,前半段,预分频器系数为1,定时器时钟等于预分频前的时钟;后半段,预分频器系数为2,定时器时钟等于预分频前时钟的一半。在定时器时钟的驱动下,计数器寄存器跟随时钟的上升沿不断自增,ARR自动重装值为FC。当计数值计到和重装值相等,并且下一个时钟来临时,计数器寄存器的值才清0。同时产生一个更新事件(UEV)。

下三行时序:预分频控制寄存器,供我们读写用的,并不直接决定分频系数。预分频缓冲器或者影子寄存器:真正起作用的寄存器。当在计数中间改变预分频器的值(预分频控制寄存器),不会马上更新,要等到计数到自动重装值,并产生更新事件后,才会更改预分频器的值(预分频缓冲器)。

3.2 计数器时序

 

CK_INT:内部时钟72MHz。

计数器无预装时序:没有缓冲寄存器的情况。

计数器有预装时序:有缓冲寄存器的情况。

通过设置ARPE位,可以选择是否使用预装功能。

4、 RCC时钟树

左边是时钟的产生电路,右边是时钟的分配电路。

4.1 时钟产生电路

有四个震荡源:

        ①内部的8MHz高速RC振荡器;

        ②OSC_OUT,OSC_IN:外部的4-16MHz高速石英晶体振荡器,也就是晶振,一般都是接8MHz;外部的石英振荡器比内部的RC振荡器更加稳定。一般使用外部晶振。

        ③OSC32_IN,OSC32_OUT:外部的32.768KHz低速晶振,一般给RTC提供时钟。

        ④LSI RC:内部的40KHz低速RC振荡器,可以给看门狗提供时钟。

        高速时钟晶振用于提供系统时钟,如AHB、APB2、APB1都是来源于这两个高速晶振。

在SystemInit函数里,ST配置时钟:①首先启动内部时钟,选择内部8MHz为系统时钟,暂时以内部8MHz的时钟运行。②然后再启动外部时钟,经过PLLXTPRE、PLLSRC,进入PLLMUL(PLL锁相环)进行倍频,8MHz倍频9倍,得到72MHz。等到锁相环输出稳定后,选择锁相环输出为系统时钟。系统时钟就从8MHz切换成了72MHz。

CSS(Clock Security System):时钟安全系统,负责切换时钟。可以监测外部时钟的运行状态,一但外部时钟失效,就会自动把外部时钟切换回内部时钟,保证系统时钟的运行,防止程序卡死造成事故。

4.2 时钟分配电路

①AHB总线有个预分频器,在SystemInit里配置的分频系数为1,故AHB总线的时钟为72MHz。

②然后进入APB1总线,这里配置的分频系数为2,故APB1总线的时钟为36MHz。

故基本定时器、通用定时器的时钟都为72MHz。

③APB2总线,在这里配置的分频系数为1,故APB1总线的时钟为72MHz。

故高级定时器的时钟都为72MHz。

④外设时钟使能,就是程序中RCC_APB2/1PeriphClockCmd的地方。

5、TIM中断初始化代码

根据定时中断基本结构来一步一步初始化定时器中断代码:

(1)开启外部时钟

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);

(2)选择时基单元的时钟

①选择内部时钟作为时基单元时钟

TIM_InternalClockConfig(TIM2);

定时器上电后默认使用的就是内部时钟,可以省略。

②选择ETR外部时钟作为时基单元时钟

TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);

选择外部时钟作为时基单元时钟,可能需要配置GPIO,视情况而定。配置GPIO就需要开启外部时钟以及引脚等配置。

(3)配置时基单元

TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;
TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;
TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);

TIM_Period ,ARR自动重装器的值

TIM_Prescaler ,PSC预分频器的值

TIM_RepetitionCounter ,重复计数器的值

因为TimeBaseInit里面有上述那句话,为啥要上述那句?

因为预分频器是有缓冲寄存器的,写的值只有在更新事件时,才会真正起作用,这里为了让值立刻起作用,手动生成了一个更新事件,这样预分频器的值就有效了。但同时带来了副作用:更新事件和更新中断是同时发生的,更新中断会置更新中断标志位,一旦程序初始化完,更新中断就会立刻进入,会导致刚一上电,就立刻进中断。

TIM_ClearFlag(TIM2, TIM_FLAG_Update);

因此需要增加上述手动清除更新中断标志位代码。

(4)使能更新中断

TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);

(5)配置NVIC

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);

(6)启动定时器

TIM_Cmd(TIM2, ENABLE);

物联沃分享整理
物联沃-IOTWORD物联网 » STM32笔记:学习TIM定时器的使用方法

发表评论