STM32定时器(TIM)基本定时功能详解

本文主要通过介绍定时器基本结构去学习如何使用定时器进行定时。

一、定时器基本介绍

定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断,从而达到计时功能。

本文从通用定时器介绍。本文所使用芯片为 STM32F103C8T6,拥有TIM1-TIM4 四个定时器资源。

1.1、基本定时器结构 

图.2为基本定时器结构

 红色框选部分为时基单元。

【1】内部时钟(CK_INT)输入到PSC预分频器,预分频器会对输入时钟进行分频。如PSC=0时为1分频,即CK_CNT=CK_INT/1,  PSC=1时,CK_CNT=CK_INT/2。

【2】CNT计数器:对预分频后的时钟进行计数,计时时钟每来一个上升沿,计数器+1,直到达到最大计数值,即65535。最大值后再增加则会回到0,从头开始。

【3】自动重装寄存器:存储了CNT计数器需要计数的值,当计数器计数值等于自动重装值,则会产生中断信号,并重置计数器。

【4】更新中断:即为【3】所产生的中断信号(图3蓝色框选部分)。更新中断之后就会通往NMIC,再配置好NMIC的定时器通道,更新中断就可得到CPU相应

【5】更新事件:同为【3】所产生的信号,但器不会触发中断,而是触发内部其他电路工作。

1.2、通用定时器结构介绍

【1】图四中,红色框选部分与基本定时器结构相同。但是基本定时器仅有向上计数模式,而通用定时器与高级定时器可支持向下计数与中央对齐计数。

  • 向下计数:从自动重装寄存器设定值向下递减1,直到为0,,并产生中断信号从而触发更新中断或更新时间。
  • 中央对齐计数:CNT从0开始计数,计数值与自动重装值相等时,申请中断,随后又向下自减至0,再次申请中断,以此循环。
  • 【2】 输入时钟:绿色框选部分为输入时钟,对于基本定时器而言,只有内部时钟可选择。对于通用定时器,可以选择外部时钟。

    1.3、定时器定时配置流程

    1.4、时序描述

    1.4.1、预分频器时序

    【1】预分频控制寄存器:仅供读写作用,并不直接决定分频系数。

    【2】缓冲寄存器:真正决定分频系数的寄存器。

    当预分频控制寄存器的分频系数由0>1时,预分频缓冲器并未立即更新分频系数,而是等待本次计数周期结束时,产生更新事件后,预分频控制寄存器的分频系数的值才会被传递到预分频缓存寄存器,此时分频系数才真正变为1。

    计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)。

    1.4.2、计数器时序

     分频系数为2,CK_CNT=72MHz。CNT_EN为时钟使能,高电平启动。

    【1】CK_CNT:由于分频系数为2,因此CK_CNT=CK_INT/2=36MHz。

    【2】计数器寄存器:上升沿自增,增加到0036时,发生溢出,尝产生更新事件,更短中断标志位置1,申请中断。中断响应后需手动清零标志位UIF

    计数器溢出频率CK_CNT_OV = CK_CNT / (ARR + 1)= CK_PSC / (PSC + 1) / (ARR + 1)。

    例如,需要定时1s。

    定时  f=1s=1Hz=72MHz  /  7200  /  10000.

    此时预分频值 (PSC + 1)为720,自动重装值 (ARR + 1)为1000.

    二、现象代码(定时器定时中断)

    以上为该例程硬件接线图

    2.1、Timer.c文件

    以下代码为该文件全部代码(不包含头文件)

    extern int16_t NUM;
    
    void Timer_Init(void)
    {
    	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);  //开启时钟,TIM2是APB1总线的外设
    	
    	TIM_InternalClockConfig(TIM2);
    	
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;		//指定参数分频值(选择1分频),DIV22分频 DIV4 44分频 
    	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择技术模式;
    	TIM_TimeBaseInitStructure.TIM_Period=10000-1;
    	TIM_TimeBaseInitStructure.TIM_Prescaler=720-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_InitStruct;
    	NVIC_InitStruct.NVIC_IRQChannel=TIM2_IRQn;
    	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
    	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;
    	NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
    	NVIC_Init(&NVIC_InitStruct);
    	
    	TIM_Cmd(TIM2,ENABLE);
    }
    
    void TIM2_IRQHandler(void)
    {
    	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
    	{
    	
    		NUM++;
    		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
    	}
    }

    2.1.1、TIM部分

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);  //开启时钟,TIM2是APB1总线的外设
    	
    	TIM_InternalClockConfig(TIM2);
    	
    	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
    	TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;	//指定参数分频值(选择1分频),DIV22分频 DIV4 44分频 
    	TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;//选择计数模式;
    	TIM_TimeBaseInitStructure.TIM_Period=10000-1;      //ARR
    	TIM_TimeBaseInitStructure.TIM_Prescaler=720-1;      //PSC
    	TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
    	TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);			//配置时基单元
    	
    	TIM_ClearFlag(TIM2,TIM_FLAG_Update);
    	
    	TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);

    【1】TIM_InternalClockConfig():使用内部时钟函数。单片机上电默认使用内部时钟,因此可以不写改代码。

    【2】TIM_ClockDivision:指定时钟分频,DIV2 2分频 DIV4 4分频 。本例中选择不分频。

    滤波器主要依靠采用频率以及采用点数工作,其信号来源可以是内部时钟分频而来,而分频值即由该参数决定。此处可任意分配值。

    【3】TIM_CounterMode:计数模式,继续向上、向下或对齐计数模式。本例选择向上计数模式。

    【4】TIM_Period: ARR自动重装寄存器值

    【5】TIM_Prescaler:        PSC预分频计数器置

    【6】TIM_TimeBaseInitStructure:高级定时器才拥有,因此此处给0.

    【7】TIM_ClearFlag():使能中断函数。

    【8】TIM_ClearFlag();引用该函数是为了解决复位立刻进中断的现象。

    2.1.2、NVIC部分

        NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
    	
    	NVIC_InitTypeDef NVIC_InitStruct;
    	NVIC_InitStruct.NVIC_IRQChannel=TIM2_IRQn;
    	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
    	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=2;
    	NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
    	NVIC_Init(&NVIC_InitStruct);
    	
    	TIM_Cmd(TIM2,ENABLE);

    【1】NVIC_PriorityGroupConfig():中断分组,配置优先级分组:抢占优先级和次优先级。

    【2】NVIC_IRQChannel:指定要启用或禁用的IRQ通道。

    【3】NVIC_IRQChannelCmd:使能

    【4】NVIC_IRQChannelPreemptionPriority:抢占优先级

    【5】NVIC_IRQChannelSubPriority:响应优先级

    2.1.3、中断函数

    void TIM2_IRQHandler(void)
    {
    	if(TIM_GetITStatus(TIM2,TIM_IT_Update)==SET)
    	{
    		NUM++;
    		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
    	}
    }

    【1】TIM_GetITStatus():查看TIMx的更新事件中断标志位

    3.1main.c 主程序代码

    三、函数简介

    3.1、时钟源选择(部分函数)

     

    3.1.1、选择内部时钟

    //定时器默认使用内部定时器,可以不用写这个函数

    TIM_InternalClockConfig(TIM_TypeDef* TIMx)

    效果如下图

    3.1.2、选择ITRx其他定时器时钟

    //参数1:选择定时器

    //参数2:选择要接入那个其他定时器

    TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource)

    效果如下图

    3.1.3、选择Tlx捕获通道时钟

    参数1:选择定时器

    参数2:选择Tlx某个引脚

    参数3:输入极性

    参数4:滤波器

    TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,

    uint16_t TIM_ICPolarity, uint16_t ICFilter);

    3.1.4、选择ETR外部时钟模式1的输入时钟

    //参数1:选择定时器

    //参数2:外部触发预分频器—对ETR外部时钟提前做一次预分频

    //参数3:极性

    //参数4:滤波器

    TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter)

    3.1.5、选择ETR外部时钟模式2的输入时钟

    //参数1:选择定时器

    //参数2:外部触发预分频器—对ETR外部时钟提前做一次预分频

    //参数3:极性

    //参数4:滤波器

    TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, 
     uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter

    3.1.6、单独配置ETR预分频器、极性。滤波器等参数

    TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter)

    3.2、 时基单元

    3.2.1、时基单元初始化

    参数1-TIMx:选择某个定时器

    参数2-TIM_TimeBaseInitStruct:结构体,内部包含了配置时基单元的基本信息

    TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)

    3.2.2、将结构体初始化为默认值

    TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)

    结构体内含信息:

    TIM_Prescaler;

    3.2.3、运行控制

    //参数1:选择定时器

    //参数2:使能or失能

    TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState) 

    3.3、中断输出控制

    //参数1:选择定时器

    //参数2:选择配置那个中断输出

    //参数3;使能or失能

    TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState)

    3.4、NVIC配置 

    详见前文

    3.5、补充函数

    3.5.1、单独写预分频值

    //参数1:选择定时器

    //参数2、写入预分频的值

    //参数3、写入模式(用到再详查资料)

    TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode)

    3.5.2、改变计数器的计数模式

    //参数2:选择新的计数器模式

    TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode)

    3.5.3、自动重装器预装功能配置

    TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState)

    3.5.4、计数器写入一个值

    TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter)

    3.5.5、自动重装器写入一个值

    TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload)

    3.5.6、获取当前计数器的值

     TIM_GetCounter(TIM_TypeDef* TIMx)

    3.5.7、获取当前预分频的值

    TIM_GetPrescaler(TIM_TypeDef* TIMx)

    问题解决

    一上电就出现中断在  TIM_TimeBaseInit();后加上函数

    TIM_ClearFlag(TIM2,TIM_FLAG_Update).

    本文以重新优化内容排布,以后将会不定期优化更新。

    物联沃分享整理
    物联沃-IOTWORD物联网 » STM32定时器(TIM)基本定时功能详解

    发表回复