zoukankan      html  css  js  c++  java
  • STM32F103单片机学习—— 通用定时器

    版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/fengshuiyue/article/details/79150724

    本篇重点记录的是STM32F1的通用定时器。
    STM32F103ZE有8个定时器,其中2个高级定时器(TIM1、TIM8),4个通用定时器(TIM2、TIM3、TIM4、TIM5),2个基本定时器(TIM6、TIM7)。下表是对这8个定时器的详细描述。

    定时器种类 位数 计数器模式 产生DMA请求 捕获/比较通道 互补输出 特殊应用场景
    高级定时器
    (TIM1,TIM8)
    16 向上、向下、向上/下 可以 4 带死区控制盒紧急刹车,
    可应用于PWM电机控制
    通用定时器
    (TIM2~TIM5)
    16 向上、向下、向上/下 可以 4 通用。定时计数,PWM输出,
    输入捕获,输出比较
    基本定时器
    (TIM6,TIM7)
    16 向上、向下、向上/下 可以 0 主要应用于驱动DAC

    上表中可看出STM32F103ZE定时器都是16位的,捕获/比较通道有4个,计数模式包括3种(向上计数、向下计数、中央对齐(向上/向下)计数)。

    在此对计数模式做一个解释
    ①向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新从0开始计数并且产生一个计数器溢出事件。
    ②向下计数模式:计数器从自动装入的值(TIMx_ARR)开始向下计数到0,然后从自动装入的值重新开始,并产生一个计数器向下溢出事件。
    ③中央对齐模式(向上/向下计数):计数器从0开始计数到自动装入的值-1,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。
    计数

    1、通用计时器概述

    通用计数器TIMx(TIM2~TIM5)定时器的特点包括:

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

    通用计时器框图如下:
    通用计时器

    从图中我们可以看到通用计时器由时钟、时基单元、输入电路、输出电路构成,下面将会对这四块分别做介绍。

    2、通用计数器 时钟的选择

    通用计数器

    上图总结为计数器的时钟有8种选择:

    • 内部RCC提供的时钟:TIMxCLK(CK_INT)
      CK_INT(内部时钟)值的计算:
      从下图可知如果是APB1的分频系数是1,则通用定时器的时钟=APB1的时钟;否则(APB1的分频系数不是1)通用寄存器的时钟=APB1时钟*2;
      CK_INT
      如:默认使用SystemInit函数的情况下,SYSCLK=72M,AHB时钟=72M,APB1时钟=36M,APB1=AHBAPB1=2APB1的分频系数=AHB时钟APB1时钟=2 ,所以通用定时器时钟CK_INT=2*36M=72M。

    • 内部触发器输入口1~4(ITR1、ITR2、ITR3、ITR4),用一个定时器作为另一定时器的分频

    • 外部捕捉比较引脚,引脚1(TI1FP1或TI1F_ED)、引脚2(TI2FP2)
    • 外部引脚:ETR(使能/禁止位、可编程设定极性、4位外部触发过滤器、外部触发分频器[分频器关闭、二分频、四分频、八分频])

    计数器时钟可以由下列时钟源提供(该内容意思同上):
    内部时钟(CK_INT)
    外部时钟模式1:外部输入脚(TIx)
    外部时钟模式2:外部触发输入(ETR)
    内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时器Timer1而作为另一个定时器Timer2的预分频器。

    3、时基单元

    时基单元

    从上图中我们可看到定时器的构成:

    1)计数寄存器(TIMx_CNT)

    该寄存器计数模式为3种,向上计数、向下计数和对齐计数
    计数寄存器

    2)预分频器寄存器(TIMx_PSC)

    可将时钟频率按1到65536之间的任意值进行分频,可在运行时改变其设置值
    预分频寄存器

    3)自动装载寄存器(TIMx_ARR)

    如果TIMx_CR1寄存器中的ARPE位为0,ARR寄存器的内容将直接写入影子寄存器;如果ARPE为1,ARR寄存器的内容将再每次的更新事件UEV发生时,传送到影子寄存器;
    如果TIMx_CR1中的UDIS位为0,当计数器产生溢出条件时,产生更新事件;
    自动重装载寄存器

    4)控制寄存器1(TIMx_CR1)

    控制寄存器1

    5)DMA中断使能寄存器(TIMx_DIER)

    DMA中断使能寄存器

    6)定时器中断实现步骤

    时基单元为我们提供了定时的功能,我们利用该功能实现如下示例程序的编写:
    通过定时器中断配置,实现每500ms中断一次,通过定时中断实现LED灯闪烁。
    ① 使能定时器时钟。
    RCC_APB1PeriphClockCmd();
    ② 初始化定时器,配置ARR,PSC(即配置自动装载寄存器TIMx_ARR和预分频寄存器值TIMx_PSC)
    TIM_TimeBaseInit(TIM_TypeDef* TIMx, IM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

    ARR、PSC如何确定
    1)我们知道计数器ARR溢出后会产生更新中断,我们以中心对齐模式的时序图来说明,如下图:
    中心对称
    上图中CK_INT前面已说过,其频率由APB1来决定,若使用默认时钟SystemInit初始化的话,CK_INT=72MHz。
    CK_CNT如何确定,我们看下图
    CK_CNT
    CK_INT=APB1APB12CK_INT=APB1或APB1∗2
    上述公式为何计数器ARR和时钟分频PSC都要加1,因为这两个值是配置在寄存器中的,其实从0开始计数,故要加1。

    根据上面导出的Tout的公式,结合本小节开头的需求,中断时间设置为500ms,我们可使用默认的系统频率,则Fck_psc=CK_INT=72MHz,则

    500ms=ARR+1T=ARR+1PSC[15:0]+172000500(ms)=(ARR+1)∗T=(ARR+1)∗(PSC[15:0]+1)72000

    设PSC=7199,则ARR=4999,该需求的ARR,PSC我们将会以此值去配置。

    ③ 开启定时器中断,配置NVIC。
    void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
    NVIC_Init();
    ④ 使能定时器。
    TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
    ⑤ 编写中断服务函数。
    TIMx_IRQHandler();

    7)程序编写

    //通用定时器3中断初始化
    //这里时钟选择为APB1的2倍,而APB1为36M
    //arr:自动重装值。
    //psc:时钟预分频数
    //这里使用的是定时器3!
    void TIM3_Int_Init(u16 arr,u16 psc)
    {
        TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
        NVIC_InitTypeDef NVIC_InitStructure;
    
        RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM3, ENABLE); //时钟使能
    
        //定时器TIM3初始化
        TIM_TimeBaseStructure.TIM_Period = arr; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值   
        TIM_TimeBaseStructure.TIM_Prescaler =psc; //设置用来作为TIMx时钟频率除数的预分频值
        TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
        TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
        TIM_TimeBaseInit(TIM3, &TIM_TimeBaseStructure); //根据指定的参数初始化TIMx的时间基数单位
    
        TIM_ITConfig(TIM3,TIM_IT_Update,ENABLE ); //使能指定的TIM3中断,允许更新中断
    
        //中断优先级NVIC设置
        NVIC_InitStructure.NVIC_IRQChannel = TIM3_IRQn;  //TIM3中断
        NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0;  //先占优先级0级
        NVIC_InitStructure.NVIC_IRQChannelSubPriority = 3;  //从优先级3级
        NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道被使能
        NVIC_Init(&NVIC_InitStructure);  //初始化NVIC寄存器
    
    
        TIM_Cmd(TIM3, ENABLE);  //使能TIMx                     
    }
    //定时器3中断服务程序
    void TIM3_IRQHandler(void)   //TIM3中断
    {
        if (TIM_GetITStatus(TIM3, TIM_IT_Update) != RESET)  //检查TIM3更新中断发生与否
        {
        TIM_ClearITPendingBit(TIM3, TIM_IT_Update  );  //清除TIMx更新中断标志 
        LED1=!LED1;
        }
    }
    //LED1闪烁的周期为500ms,LED0闪烁的周期为200ms,看到的现象为LED1闪烁慢,LED0闪烁快
    int main(void)
    {       
    
        delay_init();            //延时函数初始化    
        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
    
            LED_Init();              //LED端口初始化
    
        TIM3_Int_Init(4999,7199);//10Khz的计数频率,计数到5000为500ms  
            while(1)
        {
            LED0=!LED0;
            delay_ms(200);         
        }
    }    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56

    STM32通用定时器在此篇仅记录了 定时器基本的概念和时基单元的功能编程,由于篇幅的限制,通用定时器的输入和输出的功能将再下篇介绍。

  • 相关阅读:
    5.线性回归算法
    4.K均值算法--应用
    3.K均值算法
    机器学习2
    机器学习1
    第十五次作业
    第十三次作业
    第十一次作业
    P1250 种树
    P1516 青蛙的约会
  • 原文地址:https://www.cnblogs.com/CodeWorkerLiMing/p/12007430.html
Copyright © 2011-2022 走看看