STM32 通用定时器

发布于: 2018年05月16日 12:33:32 | 分类: STM32 | 浏览: 184

STM32 通用定时器TIMx简介

    通用定时器是一个通过可编程预分频器(PSC)驱动的16位自动装载计数器(CNT)构成。它适用于多种场合,包括测量输入信号的脉冲长度(输入捕获)或者产生输出波形(输出比较和PWM)。使用定时器预分频器和RCC时钟控制器预分频器,脉冲长度和波形周期可以在几个微秒到几个毫秒间调整。每个定时器都是完全独立的,没有互相共享任何资源。它们可以一起同步操作

TIMx主要功能

1,16位向上、向下、向上/向下自动装载计数器
2,16位可编程(可以实时修改)预分频器,计数器时钟频率的分频系数为1~65536之间的任意数值
3,4个独立通道:
    1),输入捕获
    2),输出比较
    3),PWM生成(边缘或中间对齐模式)
    3),单脉冲模式输出
4,使用外部信号控制定时器和定时器互连的同步电路
5,如下事件发生时产生中断/DMA
    1),更新:计数器向上溢出/向下溢出,计数器初始化(通过软件或者内部/外部触发)
    2),触发事件(计数器启动停止初始化或者由内部/外部触发计数)
    3),输入捕获
    4),输出比较
6,支持针对定位的增量(正交)编码器和霍尔传感器电路
7,触发输入作为外部时钟或者按周期的电流管理

通用TIMx (TIM2、 TIM3、 TIM4TIM5)定时器功能

通用定时器框图

TIMx功能描述

    时基单元

    可编程通用定时器的主要部分是一个16位计数器和与其相关的自动装载寄存器。这个计数器可以向上计数向下计数或者向上向下双向计数。此计数器时钟由预分频器分频得到。计数器自动装载寄存器预分频器寄存器可以由软件读写,在计数器运行时仍可以读写。

计数器寄存器(TIMx_CNT)
预分频器寄存器 (TIMx_PSC)
自动装载寄存器 (TIMx_ARR)

时基单元

    自动装载寄存器是预先装载的,写或读自动重装载寄存器将访问预装载寄存器。根据在TIMx_CR1寄存器中的自动装载预装载使能位(ARPE)的设置,预装载寄存器的内容被立即或在每次的更新事件UEV时传送到影子寄存器。当计数器达到溢出条件(向下计数时的下溢条件)并当TIMx_CR1寄存器中的UDIS位等'0'时,产生更新事件。更新事件也可以由软件产生。随后会详细描述每一种配置下更新事件的产生。

    计数器由预分频器的时钟输出CK_CNT驱动,仅当设置了计数器TIMx_CR1寄存器中的计数器使能位(CEN)时, CK_CNT才有效。真正的计数器使能信号CNT_EN是在CEN的一个时钟周期后被设置。预分频器描述预分频器可以将计数器的时钟频率按165536之间的任意值分频。它是基于一个(在TIMx_PSC寄存器中的)16位寄存器控制的16位计数器。这个控制寄存器带有缓冲器,它能够在工作时被改变。新的预分频器参数在下一次更新事件到来时被采用。

预分频器运行时,更改计数器参数的例子

预分频器的参数从1变到2时的计数器时序图

预分频器的参数从1变到4时的计数器时序图

    计数器模式

向上计数模式

    在向上计数模式中,计数器从0计数到自动加载值(TIMx_ARR计数器的内容),然后重新从0开始计数并且产生一个计数器溢出事件。每次计数器溢出时可以产生更新事件,在TIMx_EGR寄存器中(通过软件方式或者使用从模式控制器)设置UG位也同样可以产生一个更新事件。

    设置TIMx_CR1寄存器中的UDIS位,可以禁止更新事件;这样可以避免在向预装载寄存器中写入新值时更新影子寄存器。在UDIS位被清0之前,将不产生更新事件。但是在应该产生更新事件时,计数器仍会被清0,同时预分频器的计数也被请0(但预分频系数不变)。此外,如果设置了TIMx_CR1寄存器中的URS位(选择更新请求),设置UG位将产生一个更新事件UEV,但硬件不设置UIF标志(即不产生中断或DMA请求);这是为了避免在捕获模式下清除计数器时,同时产生更新和捕获中断。

    当发生一个更新事件时,所有的寄存器都被更新,硬件同时(依据URS位)设置更新标志位(TIMx_SR寄存器中的UIF位)。

        ● 预分频器的缓冲区被置入预装载寄存器的值(TIMx_PSC寄存器的内容)。

        ● 自动装载影子寄存器被重新置入预装载寄存器的值(TIMx_ARR) 

TIMx_ARR=0x36时计数器在不同时钟频率下的动作

计数器时序图,内部时钟分频因子为1

计数器时序图,内部时钟分频因子为2

计数器时序图,内部时钟分频因子为4

计数器时序图,内部时钟分频因子为N

计数器时序图,当ARPE=0时的更新事件(TIMx_ARR没有预装入)

计数器时序图,当ARPE=1时的更新事件(预装入了TIMx_ARR)

向下计数模式

    向下模式中,计数器从自动装入的值(TIMx_ARR计数器的值)开始向下计数到0,然后从自动装入的值重新开始并且产生一个计数器向下溢出事件。每次计数器溢出时可以产生更新事件,在TIMx_EGR寄存器中(通过软件方式或者使用从模式控制器)设置UG位,也同样可以产生一个更新事件。

    设置TIMx_CR1寄存器的UDIS位可以禁止UEV事件。这样可以避免向预装载寄存器中写入新值时更新影子寄存器。因此UDIS位被清为0之前不会产生更新事件。然而,计数器仍会从当前自动加载值重新开始计数,同时预分频器的计数器重新从0开始(但预分频系数不变)。
    此外,如果设置了TIMx_CR1寄存器中的URS位(选择更新请求),设置UG位将产生一个更新事UEV但不设置UIF标志(因此不产生中断和DMA请求),这是为了避免在发生捕获事件并清除计数器时,同时产生更新和捕获中断。当发生更新事件时,所有的寄存器都被更新,并且(根据URS位的设置)更新标志位(TIMx_SR
存器中的UIF位)也被设置。

    ● 预分频器的缓存器被置入预装载寄存器的值(TIMx_PSC寄存器的值)。
    ● 当前的自动加载寄存器被更新为预装载值(TIMx_ARR寄存器中的内容)。自动装载在计数器重载入之前被更新,因此下一个周期将是预期的值。

TIMx_ARR=0x36时,计数器在不同时钟频率下的动作

计数器时序图,内部时钟分频因子为1

计数器时序图,内部时钟分频因子为2

计数器时序图,内部时钟分频因子为4

计数器时序图,内部时钟分频因子为N

计数器时序图,当没有使用重复计数器时的更新事件

中央对齐模式(向上/向下计数)

    中央对齐模式,计数器从0开始计数到自动加载的值(TIMx_ARR寄存器)-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器下溢事件;然后再从0开始重新计数。在这个模式,不能写入TIMx_CR1中的DIR方向位。它由硬件更新并指示当前的计数方向。可以在每次计数上溢和每次计数下溢时产生更新事件;也可以通过(软件或者使用从模式控制器)设置TIMx_EGR寄存器中的UG位产生更新事件。然后,计数器重新从0开始计数,预分频器也重新从0开始计数。
设置TIMx_CR1寄存器中的UDIS位可以禁止UEV事件。这样可以避免在向预装载寄存器中写入新值时更新影子寄存器。因此UDIS位被清为0之前不会产生更新事件。然而,计数器仍会根据当前自动重加载的值,继续向上或向下计数。

    此外,如果设置了TIMx_CR1寄存器中的URS位(选择更新请求) ,设置UG位将产生一个更新事件UEV但不设置UIF标志(因此不产生中断和DMA请求),这是为了避免在发生捕获事件并清除计数器时,同时产生更新和捕获中断。当发生更新事件时,所有的寄存器都被更新,并且(根据URS位的设置)更新标志位(TIMx_SR寄存器中的UIF位)也被设置。

    ● 预分频器的缓存器被加载为预装载(TIMx_PSC寄存器)的值。
    ● 当前的自动加载寄存器被更新为预装载值(TIMx_ARR寄存器中的内容)。如果因为计数器溢出而产生更新,自动重装载将在计数器重载入之前被更新,因此下一个周期将是预期的值(计数器被装载为新的值)。

内部时钟分频因子为1TIMx_ARR=0x6

内部时钟分频因子为2

内部时钟分频因子为4TIMx_ARR=0x36

内部时钟分频因子为N

ARPE=1时的更新事件(计数器下溢)

ARPE=1时的更新事件(计数器溢出)

时钟源

● 内部时钟(CK_INT)
● 外部时钟模式1:外部输入脚(TIx)
● 外部时钟模式2:外部触发输入(ETR)
● 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器
Timer1而作为另一个定时器Timer2的预分频器

计数器时钟时钟源

内部时钟源(CK_INT)

    如果禁止了从模式控制器(TIMx_SMCR寄存器的SMS=000),则CENDIR(TIMx_CR1寄存器)和UG位(TIMx_EGR寄存器)是事实上的控制位,并且只能被软件修改(UG位仍被自动清除)。只要CEN位被写成1,预分频器的时钟就由内部时钟CK_INT提供。

下图显示了控制电路和向上计数器在一般模式下,不带预分频器时的操作

一般模式下的控制电路,内部时钟分频因子为1

外部时钟源模式1

    TIMx_SMCR寄存器的SMS=111时,此模式被选中。计数器可以在选定输入端的每个上升沿或下降沿计数。

TI2外部时钟连接例子 

1. 配置TIMx_CCMR1寄存器CC2S=01,配置通道2检测TI2输入的上升沿
2. 配置TIMx_CCMR1寄存器的IC2F[3:0],选择输入滤波器带宽(如果不需要滤波器,保持IC2F=0000)注: 捕获预分频器不用作触发,所以不需要对它进行配置
3. 配置TIMx_CCER寄存器的
CC2P=0,选定上升沿极性
4. 配置TIMx_SMCR寄存器的SMS=111,选择定时器外部时钟模式1
5. 配置TIMx_SMCR寄存器中的TS=110,选定TI2作为触发输入源
6. 设置TIMx_CR1寄存器的CEN=1,启动计数器当上升沿出现在TI2,计数器计数一次,且TIF标志被设置。在TI2的上升沿和计数器实际时钟之间的延时,取决于在TI2输入端的重新同步电路。

配置向上计数器在T12输入端的上升沿计数步骤

外部时钟模式1下的控制电路

外部时钟源模式2

    选定此模式的方法为:令TIMx_SMCR寄存器中的ECE=1计数器能够在外部触发ETR的每一个上升沿或下降沿计数。下图是外部触发输入的框图

1.本例中不需要滤波器,置TIMx_SMCR寄存器中的ETF[3:0]=0000
2. 设置预分频器,置TIMx_SMCR寄存器中的ETPS[1:0]=01
3. 设置在ETR的上升沿检测,置TIMx_SMCR寄存器中的ETP=0
4. 开启外部时钟模式2,置TIMx_SMCR寄存器中的ECE=1
5. 启动计数器,置TIMx_CR1寄存器中的CEN=1
计数器在每2ETR上升沿计数一次。

配置在ETR下每2个上升沿计数一次的向上计数器步骤


ETR的上升沿和计数器实际时钟之间的延时取决于在ETRP信号端的重新同步电路 

外部时钟模式2下的控制电路 

内部触发输入(ITRx

    使用 A 定时器作为 B 定时器的预分频器(A B 提供时钟)。这些时钟,具体选择哪个可以通过 TIMx_SMCR 寄存器的相关位来设置。这里的 CK_INT时钟是从 APB1 倍频的来的,STM32 中除非 APB1 的时钟分频数设置为 1,否则通用定时器 TIMx的时钟是 APB1 时钟的 2 倍,当 APB1 的时钟不分频的时候,通用定时器 TIMx 的时钟就等于APB1 的时钟。这里还要注意的就是高级定时器的时钟不是来自 APB1,而是来自 APB2 的。

实验

    今天的实验,将使用定时器产生中断,然后在中断服务函数里面翻转 LED上的电平,来指示定时器中断的产生。接下来以通用定时器 TIM3 为实例,来说明要经过哪些步骤,才能达到这个要求,并产生中断。

1,TIM3 时钟使能
2,初始化定时器参数,自动重装值, 分频系数,计数方式
3,设置 TIM3_DIER 允许更新中断
4,TIM3 中断优先级设置
5,允许 TIM3 工作,也就是使能 TIM3  
6,编写中断服务函数

一般步骤

硬件设计

    本实验用到的硬件资源有:指示灯 DS0 DS1, 定时器 TIM3本章将通过 TIM3 的中断来控制 DS1 的亮灭, DS0 DS1 的电路在前面已经有介绍了。而TIM3属于 STM32 的内部资源,只需要软件设置即可正常工作。

软件设计

timer.h

#ifndef __TIMER_H
#define __TIMER_H
#include "sys.h"

void TIM3_Int_Init(u16 arr,u16 psc);

#endif

timer.c

#include "timer.h"
#include "led.h"

//通用定时器中断初始化
//这里时钟选择为APB1的2倍,而APB1为36M
//arr:自动重装值。
//psc:时钟预分频数
//这里使用的是定时器3!
void TIM3_Int_Init(u16 arr, u16 psc) {

    TIM_TimeBaseInitTypeDef  TIM_TimeBaseInitTypeDef;
    NVIC_InitTypeDef NVIC_InitTypeDef;

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能

    TIM_TimeBaseInitTypeDef.TIM_Period      = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值     计数到5000为500ms
    TIM_TimeBaseInitTypeDef.TIM_Prescaler   = psc; //设置用来作为TIMx时钟频率除数的预分频值  10Khz的计数频率  
    TIM_TimeBaseInitTypeDef.TIM_ClockDivision = 0; //设置时钟分割:TDTS = Tck_tim
    TIM_TimeBaseInitTypeDef.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
    TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitTypeDef); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

    TIM_ITConfig(TIM3, TIM_IT_Update, ENABLE );//使能或者失能指定的TIM中断

    NVIC_InitTypeDef.NVIC_IRQChannel    = TIM3_IRQn;            //TIM3中断
    NVIC_InitTypeDef.NVIC_IRQChannelPreemptionPriority  = 0;    //先占优先级0级
    NVIC_InitTypeDef.NVIC_IRQChannelSubPriority         = 3;    //从优先级3级
    NVIC_InitTypeDef.NVIC_IRQChannelCmd = ENABLE;               //IRQ通道被使能
    NVIC_Init(&NVIC_InitTypeDef);  //根据NVIC_InitStruct中指定的参数初始化外设NVIC寄存器

    TIM_Cmd(TIM3, ENABLE);  //使能TIMx外设

}

void TIM3_IRQHandler() {   //TIM3中断
    if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET) {//检查指定的TIM中断发生与否:TIM 中断源

        TIM_ClearITPendingBit(TIM3, TIM_IT_Update );  //清除TIMx的中断待处理位:TIM 中断源 
        LED1=!LED1;
    }
}

led.h

#ifndef __LED_H
#define __LED_H 
#include "sys.h"

#define LED0 PAout(8)   // PA8
#define LED1 PDout(2)   // PD2  

void LED_Init(void);//初始化
                        
#endif

led.c

#include "led.h"

//初始化PB5和PE5为输出口.并使能这两个口的时钟         
//LED IO初始化
void LED_Init(void)
{
    GPIO_InitTypeDef  GPIO_InitStructure;
       
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA|RCC_APB2Periph_GPIOD, ENABLE);   //使能PA,PD端口时钟
    
    GPIO_InitStructure.GPIO_Pin     = GPIO_Pin_8;            //LED0-->PA.8 端口配置
    GPIO_InitStructure.GPIO_Mode    = GPIO_Mode_Out_PP;      //推挽输出
    GPIO_InitStructure.GPIO_Speed   = GPIO_Speed_50MHz;      //IO口速度为50MHz
    GPIO_Init(GPIOA, &GPIO_InitStructure);               //根据设定参数初始化GPIOA.8
    GPIO_SetBits(GPIOA,GPIO_Pin_8);                          //PA.8 输出高

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_2;                //LED1-->PD.2 端口配置, 推挽输出
    GPIO_Init(GPIOD, &GPIO_InitStructure);                  //推挽输出 ,IO口速度为50MHz
    GPIO_SetBits(GPIOD,GPIO_Pin_2);                          //PD.2 输出高 
}

main.c

#include "led.h"
#include "delay.h"
#include "sys.h"
#include "timer.h"

int main(int argc, char const *argv[]) {

    delay_init();                   //延时函数初始化
    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); // 设置中断优先级分组2
    LED_Init();                     //初始化与LED连接的硬件接口
    TIM3_Int_Init(4999,7199);       //10Khz的计数频率,计数到5000为500ms
     while(1)
    {
        LED0 = !LED0;
        delay_ms(200);
    }
}

效果

    LED0大约200ms闪烁一次,LED1大约500ms闪烁一次。

标签: STM32
本文为作者原创文章,转载时请务必声明出处并添加指向此页面的链接。
分享:
发表评论

目前您尚未登录,请 登录 后进行评论

评论信息