STM32学习笔记--定时器

目录:

一、定时器基础知识

1.STM32定时器简介
(1)STM32家族的定时器众多,按照核内、核外标准大致分为两部分:核内定时器+ 外设定时器
  1)核内定时器:Systick系统滴答定时器
  2)外设定时器: 特定应用定时器+常规定时器
    •特定应用定时器:2个看门狗定时器
    •常规定时器:基本定时器、通用定时器、高级定时器
(2)STM32一共有8个16位的常规定时器,其中两个基本定时器TIM6和TIM7,四个通用定时器TIM2~TIM 5,两个高级定时器TIM1和TIM8:
STM32学习笔记--定时器
(3) STM32定时器功能强大,具有定时、信号频率测量、信号的PWM测量、PWM输出、电机控制 、编码器接口等功能。

2.定时器计数模式
STM32学习笔记--定时器
(1)向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且由计数器的溢出产生一个更新事件。
(2)向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并由计数器向下溢出产生一个更新事件。
(3)中间对齐模式(向上/向下计数):计数器从0开始计数到(自动装入的值ARR-1),产生一个计数器溢出的更新事件,然后向下计数到1并且产生一个计数器溢出的更新事件;然后再从0开始重新计数。

二、基本定时器(TIM6和TIM7)

基本定时器TIM6和TIM7只具备最基本的定时功能,当累加的时钟脉冲数超过预定值时,能触发中断或DMA请求。 由于在芯片内部与DAC外设相连,可通过触发输出驱动DAC,也可作为其他定时器的时钟基准,基本定时器拥有16位可编程(可实时修改)预分频器,用于对输入的时钟按系数为1~65536之间的任意数值分频。
STM32学习笔记--定时器
  这两个基本定时器使用的时钟源都是TIMxCLK(内部时钟CK_INT),时钟源经过PSC预分频器输入至脉冲计数器TIMx_CNT,基本定时器只能工作在向上计数模式,在重装载寄存器TIMx_ARR中保存的是定时器的溢出值。
  工作时,脉冲计数器TIMx_CNT由时钟触发进行计数,当TIMx_CNT的计数值X等于重装载寄存器TIMx_ARR中保存的数值N时,产生溢出事件,可触发DMA请求,然后TIMx_CNT的值重新被置为0,重新向上计数。
两个基本定时器的时基单元:
● 计数器寄存器(TIMx_CNT)
STM32学习笔记--定时器
● 预分频寄存器(TIMx_PSC)
STM32学习笔记--定时器
● 自动重装载寄存器(TIMx_ARR)
STM32学习笔记--定时器

三、通用定时器(TIM2~TIM5)

通用定时器通过可编程预分频器驱动的16位自动装载计数器构成,可以工作在向上、向下、向上向下双向三种计数模式;除了基本定时功能外,通用定时器还拥有输出比较、输入捕获、PWM输出等功能,主要用于测量输入脉冲的频率、脉冲宽度以及输出PWM脉冲的场合;此外通用定时器还具有编码器接口;通用定时器结构图如下:
STM32学习笔记--定时器

1.功能特点

(1) 位于低速的APB1总线上(APB1)
(2) 16 位向上、向下、向上/向下(中心对齐)计数模式,自动装载计数(TIMx_CNT)。
(3) 16 位可编程(可以实时修改)预分频器(TIMx_PSC),计数器时钟频率的分频系数为 1~65535 之间的任意数值。
(4) 4 个独立通道(TIMx_CH1~4),这些通道可以用来作为:
  1) 输入捕获
  2) 输出比较
  3) PWM 生成(边缘或中间对齐模式)
  4) 单脉冲模式输出
(5) 可使用外部信号(TIMx_ETR)控制定时器和定时器互连(可以用 1 个定时器控制另外一个定时器)的同步电路。
(6)如下事件发生时产生中断/DMA(6个独立的IRQ/DMA请求生成器):
  1)更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
  2) 触发事件(计数器启动、停止、初始化或者由内部/外部触发计数)
  3) 输入捕获
  4) 输出比较
  5) 支持针对定位的增量(正交)编码器和霍尔传感器电路
  6) 触发输入作为外部时钟或者按周期的电流管理
(7)STM32 的通用定时器可以被用于:测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和 PWM)等。
(8)使用定时器预分频器和 RCC 时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。
(9)STM32 的每个通用定时器都是完全独立的,没有互相共享的任何资源。

2.时钟源

STM32学习笔记--定时器
(1) 内部时钟(CK_INT):时钟信号来自芯片时钟系统给到定时器的时钟
(2)外部时钟模式1—外部输入通道(TIx):时钟信号来自于定时器的输入通道TIMx_CH1/2/3
(3) 外部时钟模式2—外部触发输入 (引脚ETR):时钟信号来自于定时器的特定输入通道TIMx_ETR,只有1 个
(4)内部触发输入(ITRx):主定时器的触发输出连接到从定时器内部的触发输入并作为时钟源。
  定时器时钟 TIMxCLK,即内部时钟CK_INT,经 APB1 预分频器后分频提供,如果APB1 预分频系数等于 1,则频率不变,否则频率乘以 2,库函数中 APB1预分频的系数是2,即 PCLK1=36M,所以定时器时钟 TIMxCLK=36 * 2=72M。

3.时基单元

(1) 计数器寄存器(TIMx_CNT)
STM32学习笔记--定时器
  一个 16 位的计数器,分别有向上计数模式、向下计数模式和*对齐计数模式。最大计数值为 65535。当计数器达到溢出条件(上溢/下溢)并当TIMx_CR1寄存器中的UDIS位等于’0’时(允许更新),产生更新事件,并重新开始计数。
(2) 预分频器寄存器 (TIMx_PSC)
STM32学习笔记--定时器
  有一个输入时钟CK_PSC 和一个输出时钟CK_CNT。输入时钟CK_PSC 就是上面时钟源的输出,输出CK_CNT 则用来驱动计数器CNT 计数。通过设置预分频器PSC 的值可以得到不同的CK_CNT , 实际计算为:fCK_CNT 等于fCK_PSC/(PSC[15:0]+1),可以实现1 至65536 分频。PSC包含了当更新事件产生时装入当前预分频器寄存器的值。
(3) 自动装载寄存器 (TIMx_ARR)
STM32学习笔记--定时器
  一个 16 位的寄存器,这里面装着计数器能计数的最大数值。该寄存器在物理上实际对应着2个寄存器。一个是程序员可以直接操作的,另外一个是程序员看不到的(影子寄存器)。事实上真正起作用的是影子寄存器。根据TIMx_CR1寄存器中ARPE位的设置:ARPE=0时,ARR寄存器没有缓冲,此时2者是连通的,预装载寄存器的内容可以随时传送到影子寄存器;而ARPE=1时,ARR寄存器的内容被装入缓冲器,在每一次更新事件(UEV)时,才把预装在寄存器的内容传送到影子寄存器。

4.输入捕获

  捕捉外部触发信号、事件;触发中断或DMA请求,配合计数器对输入信号的周期或频率进行测量。输入捕获可以对输入的信号的上升沿,下降沿或者双边沿进行捕获,常用的有测量输入信号的脉宽和测量PWM输入信号的频率和占空比这两种。
(1)工作原理
  输入捕获的大概的原理就是,当捕获到信号的跳变沿的时候,把计数器CNT的值锁存到捕获/比较寄存器CCR 中,把前后两次捕获到的CCR 寄存器中的值相减,就可以算出脉宽或者频率。如果捕获的脉宽的时间长度超过你的捕获定时器的周期,就会发生溢出这个我们需要做额外的处理。
  一句话总结工作过程就是:通过检测TIMx_CHx上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕获。
  具体工作原理如下:
1)脉冲宽度测量
STM32学习笔记--定时器
  假定定时器工作在向上计数模式,图中 t1-t2 时间,就是我们需要测量的高电平时间。测量方法如下:首先设置定时器通道 x 为上升沿捕获,这样,t1 时刻,就会触发捕获并清零 CNT,并设置通道 x为下降沿捕获,这样到 t2 时刻,又会发生捕获事件,得到此时的 CNT 值,记为 CCRx2。这样,根据定时器的计数频率,我们就可以算出 t1-t2 的时间,从而得到高电平脉宽。在 t1-t2 之间,可能产生 N 次定时器溢出,这就要求我们对定时器溢出,做处理,防止高电平太长,导致数据不准确。如图所示,t1-t2之间,CNT计数的次数等于:N * ARR+CCRx2,有了这个计数次数,再乘以 CNT 的计数周期,即可得到 t2-t1 的时间长度,即高电平持续时间。
2)PWM频率、占空比测量
STM32学习笔记--定时器
  要测量的PWM波通过GPIO引脚输入到定时器的脉冲检测通道,其时序图为图中的TI1。把脉冲计数器TIMx_CNT配置为向上计数,重装载寄存器TIMx_ARR的N值足够大。
  在输入脉冲TI1的上升沿到达时,触发IC1和IC2输入捕获中断,这时将TIMx_CNT的值复位为0,于是TIMx_CNT的值X在TIMx_CLK的驱动下从0开始不断累加,直到TI1出现下降沿,触发IC2捕获事件,将CNT的值存储到TIMx_CRR2,然后CNT继续累加,直到TI2出现第二次上升沿,触发IC1捕获事件,将此时的CNT值存储到TIMx_CRR1。
  很明显,TIMx_CCR1(加1)的值乘以TIMx_CLK的周期,就可以得到PMW波的脉冲周期,TIMx_CCR2(加1)的值乘以TIMx_CLK的周期,就可以的得到PWM波高电平时间,有了这两个数值就可以算出PWM波的频率和占空比了。
(2)库函数配置
  以捕获TIM2_CH1(PA0) 高电平脉宽为例。
  先设置输入捕获为上升沿检测,记录发生上升沿的时候 TIM2_CNT 的值。然后配置捕获信号为下降沿捕获,当下降沿到来时,发生捕获,并记录此时的 TIM2_CNT 值。样,前后两次 TIM2_CNT 之差,就是高电平的脉宽,同时 TIM2 的计数频率我们是知道的,从而可以计算出高电平脉宽的准确时间。
  库函数配置如下:
1)开启 TIM2 时钟,配置 PA0 为下拉输入
  要使用 TIM2,我们必须先开启 TIM2 的时钟。这里我们还要配置 PA0 为下拉输入,因为我们要捕获 TIM2_CH1 上面的高电平脉宽,而 TIM2_CH1 是连接在 PA0 上面的。

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能 TIM2 时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能 GPIOA 时钟

2)初始化 TIM2,设置 TIM2 的 ARR 和 PSC。
  在开启了 TIM2 的时钟之后,我们要设置 ARR 和 PSC 两个寄存器的值来设置输入捕获的自动重装载值和计数频率。这在库函数中是通过 TIM_TimeBaseInit 函数实现的。

TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;
TIM_TimeBaseStructure.TIM_Period = arr; //设定计数器自动重装值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; // TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //TIM 向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据指定的参数初始化 Tim2

3)设置 TIM2 的输入比较参数,开启输入捕获
  输入比较参数的设置包括映射关系,滤波,分频以及捕获方式等。这里我们需要设置通道 1为输入模式,且 IC1 映射到 TI1(通道 1)上面,并且不使用滤波(提高响应速度)器,上升沿捕获。库函数是通过 TIM_ICInit 函数来初始化输入比较参数的:

void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);

  结构体 TIM_ICInitTypeDef 的定义:

typedef struct
{
 uint16_t TIM_Channel;
 uint16_t TIM_ICPolarity;
 uint16_t TIM_ICSelection;
 uint16_t TIM_ICPrescaler;
 uint16_t TIM_ICFilter;
} TIM_ICInitTypeDef;

  参数 TIM_Channel 很好理解,用来设置通道。我们设置为通道 1,为TIM_Channel_1。参 数 TIM_ICPolarit 是 用 来 设 置 输 入 信 号 的 有 效 捕 获 极 性 , 这 里 我 们 设 置 为TIM_ICPolarity_Rising,上升沿捕获。同时库函数还提供了单独设置通道 1 捕获极性的函数为:

TIM_OC1PolarityConfig(TIM2,TIM_ICPolarity_Falling),

  这表示通道 1 为上升沿捕获,我们后面会用到,同时对于其他三个通道也有一个类似的函数,使用的时候一定要分清楚使用的是哪个通道该调用哪个函数,格式为 TIM_OCxPolarityConfig()。参数 TIM_ICSelection 是用来设置映射关系,我们配置 IC1 直接映射在 TI1 上,选择TIM_ICSelection_DirectTI。参 数 TIM_ICPrescaler 用来设置输入捕获分频系数,我们这里不分频,所以选中TIM_ICPSC_DIV1,还有 2,4,8 分频可选。参数 TIM_ICFilter 设置滤波器长度,这里我们不使用滤波器,所以设置为 0。

TIM_ICInitTypeDef TIM2_ICInitStructure;
TIM5_ICInitStructure.TIM_Channel = TIM_Channel_1; //选择输入端 IC1 映射到 TI1 上
TIM5_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; //上升沿捕获
TIM5_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; //映射到 TI1 上
TIM5_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; //配置输入分频,不分频
TIM5_ICInitStructure.TIM_ICFilter = 0x00;//IC1F=0000 配置输入滤波器 不滤波
TIM_ICInit(TIM2, &TIM2_ICInitStructure);

4)使能捕获和更新中断(设置 TIM2 的 DIER 寄存器)
  因为我们要捕获的是高电平信号的脉宽,所以,第一次捕获是上升沿,第二次捕获时下降沿,必须在捕获上升沿之后,设置捕获边沿为下降沿,同时,如果脉宽比较长,那么定时器就会溢出,对溢出必须做处理,否则结果就不准了。这两件事,我们都在中断里面做,所以必须开启捕获中断和更新中断。这里我们使用定时器的开中断函数TIM_ITConfig 即可使能捕获和更新中断:

TIM_ITConfig( TIM2,TIM_IT_Update|TIM_IT_CC1,ENABLE);//允许更新中断和捕获中断

5)设置中断分组,编写中断服务函数
  设置中断分组的方法前面多次提到这里我们不做讲解,主要是通过函数 NVIC_Init()来完成。分组完成后,我们还需要在中断函数里面完成数据处理和捕获设置等关键操作,从而实现高电平脉宽统计。在中断服务函数里面,开始的时候要进行中断类型判断,在中断结束的时候要清除中断标志位。使用到的函数分别为 TIM_GetITStatus()函数和 TIM_ClearITPendingBit()函数。

if (TIM_GetITStatus(TIM2, TIM_IT_Update) != RESET){}//判断是否为更新中断
if (TIM_GetITStatus(TIM2, TIM_IT_CC1) != RESET){}//判断是否发生捕获事件
TIM_ClearITPendingBit(TIM2, TIM_IT_CC1|TIM_IT_Update);//清除中断和捕获标志位

6)使能定时器(设置 TIM2 的 CR1 寄存器)
  最后,必须打开定时器的计数器开关, 启动 TIM5 的计数器,开始输入捕获。

TIM_Cmd(TIM2,ENABLE ); //使能定时器 2

5.PWM输出

(1)工作原理
  脉冲宽度调制(PWM),是英文“Pulse Width Modulation”的缩写,简称脉宽调制,是利用微处理器的数字输出来对模拟电路进行控制的一种非常有效的技术。简单一点,就是对脉冲宽度的控制。
  STM32 的定时器除了 TIM6 和 7。其他的定时器都可以用来产生 PWM 输出。其中高级定时器 TIM1 和 TIM8 可以同时产生多达 7 路的 PWM 输出。而通用定时器也能同时产生多达 4路的 PWM 输出,这样,STM32 最多可以同时产生 30 路 PWM 输出!
  PWM输出的具体工作原理如下:
  通用定时器可以利用GPIO引脚进行脉冲输出,在配置为比较输出、PWM输出功能时,捕获/比较寄存器TIMx CCR被用作比较功能,下面把它简称为比较寄存器。
  这里直接举例说明定时器的PWM输出工作过程:若配置脉冲计数器TIMx_CNT为向上计数,而重载寄存器TIMx ARR被配置为N,即TIMx_ CNT的当前计数值数值X在TIMxCLK时钟源的驱动下不断累加,当TIMx_ CNT的数值X大于N时,会重置TIMx_ CNT数值为0并重新计数。
  而在TIMx CNT计数的同时,TIMx CNT的计数值X会与比较寄存器TIMx CCR预先存储的数值A进行比较。当脉冲计数器TIMxCNT的数值X小于比较寄存器TIMxCCR的值A时,输出高电平(或低电平)↓相反地,当脉冲计数器的数值X大于或等于比较寄存器的值A时,输出低电平(或高电平)。
  如此循环,得到的输出脉冲周期就为重载寄存器TIMxARR存储的数值(N+1)乘以触发脉冲的时钟周期,其脉冲宽度则为比较寄存器TIMx_ CCR的值A乘以触发脉冲的时钟周期,即输出PWM的占空比为A/(N+1)。
STM32学习笔记--定时器
  图中为重载寄存器TIMx ARR被配置为N=8,向上计数;比较寄存器TIMx CCR的值被设置为4、8、大于8、等于0时的输出时序图。图中OCxREF即为GPIO引脚的输出时序、CCxIF为触发中断的时序。
(2)库函数配置
  以TIM1_CH1 输出PWM 为例:
1)开启 TIM2 时钟,配置 PA8 为复用输出。
  先开启 TIM2 的时钟,配置 PA8 为复用输出,这是因为 TIM2_CH1 通道将使用 PA8 的复用功能作为输出。库函数使能 TIM2 时钟的方法是:

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE); //使能定时器 2 时钟

  设置 PA8 为复用功能,这里简单列出 GPIO 初始化的一行代码:

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP; //复用推挽输出

2)设置 TIM2 的 ARR 和 PSC。
  设置 ARR 和 PSC 两个寄存器的值来控制输出 PWM 的周期。通过 TIM_TimeBaseInit 函数实现的,调用的格式为:

TIM_TimeBaseStructure.TIM_Period = arr; //设置自动重装载值
TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置预分频值
TIM_TimeBaseStructure.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up; //向上计数模式
TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据指定的参数初始化 TIMx 的

3)设置 TIM1_CH2的 PWM 模式及通道方向, 使能 TIM2 的 CH1 输出。
  设置 TIM2_CH1 为 PWM 模式(默认是冻结的),通过配置 TIM2_CCMR1 的相关位来控制 TIM2_CH1 的模式。通过函数 TIM_OC1Init()~TIM_OC4Init()来设置,使用的是通道 1,所以使用的函数是 TIM_OC1Init()。

void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);

  结构体 TIM_OCInitTypeDef的定义:

typedef struct
{
 uint16_t TIM_OCMode;
 uint16_t TIM_OutputState;
 uint16_t TIM_OutputNState; */
 uint16_t TIM_Pulse;
 uint16_t TIM_OCPolarity;
 uint16_t TIM_OCNPolarity;
 uint16_t TIM_OCIdleState;
 uint16_t TIM_OCNIdleState;
} TIM_OCInitTypeDef;

  参数 TIM_OCMode 设置模式是 PWM 还是输出比较,这里是 PWM 模式。参数 TIM_OutputState 用来设置比较输出使能,也就是使能 PWM 输出到端口。参数 TIM_OCPolarity 用来设置极性是高还是低。其他的参数 TIM_OutputNState,TIM_OCNPolarity,TIM_OCIdleState 和 TIM_OCNIdleState 是高级定时器 TIM1 和 TIM8 才用到的。
  具体代码:

TIM_OCInitTypeDef TIM_OCInitStructure;
TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_PWM2; //选择 PWM 模式 2
TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable; //比较输出使能
TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_High; //输出极性高
TIM_OC1Init(TIM2, &TIM_OCInitStructure); //初始化 TIM2 OC1

4)使能 TIM2。

TIM_Cmd(TIM2, ENABLE); //使能 TIM2

5)修改 TIM2_CCR1 来控制占空比。
  在经过以上设置之后,PWM 其实已经开始输出了,只是其占空比和频率都是固定的,而我们通过修改 TIM2_CCR1 则可以控制 CH1 的输出占空比。在库函数中,修改 TIM2_CCR1 占空比的函数是:

void TIM_SetCompare1(TIM_TypeDef* TIMx, uint16_t Compare1);

四、高级定时器(TIM1和TIM8)

  TIM1和TIM8是两个高级定时器,它们具有基本、通用定时器的所有功能,还具有三相6步电机的接口、刹车功能(break function) 及用于PWM驱动电路的死区时间控制等,使得它非常适合于电机的控制。如图为高级定时器结构。
STM32学习笔记--定时器
  相比于通用定时器,主要多出了BRK、DTG两个结构,因而具有了死区时间的控制功能。
  首先,介绍一下死区时间:在H桥、三相桥的PWM驱动电路中,上下两个桥臂的PWM驱动信号是互补的,即上下桥臂轮流导通,但实际上为了防止出现上下两个臂同时导通(会造成短路),在上下两臂切换时留一小段时间,上下臂都施加关断信号,这个上下臂都关断的时间称为死区时间。
  STM32的高级定时器可以配置出输出互补的PWM信号,并且在这个PWM信号中加人死区时间,为电机的控制提供了极大的便利。下图中的OCxREF为参考信号(可理解为原信号),OCx和OCxN为定时器通过GPIO引脚输出的PWM互补信号。
STM32学习笔记--定时器
  若不加入死区时间,当OCxREF出现下降沿,OCx同时输出下降沿,OCxN 则同时输出相反的上升沿,即这三个信号的跳变是同时的。
  加入死区时间后,当OCxREF出现下降沿,OCx同时输出下降沿,但OCxN则过了一小段延迟再输出上升沿,OCxREF出现上升沿后,OCx要经过- -段延时再输出上升沿。假如OCx、OCxN分别控制上、下桥臂,有了延迟后,就不容易出现上、下桥臂同时导通的情况。这个延迟时间与PWM信号驱动的电子器件特性相关,从事工控领域的读者对此应该比较熟悉。
  在保证不出现短路的情况下,死区时间越短越好。
STM32学习笔记--定时器

注:定时器相关寄存器及功能见芯片中文参考手册13.4,14.4以及15.4小节。
参考文章: 1.通用定时器的介绍
        2.STM32——高级定时器、通用定时器、基本定时器 的区别

上一篇:STM32CubeMX+Keil+Proteus仿真定时器中断控制LED灯


下一篇:008_STM32CubeMX定时器中断