【STM32】学习笔记:定时器

一、定时器(Timer)简介

1.1 定时器定义

  • TIM (Timer)定时器

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

  • 16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时

  • 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能

  • 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型

类型编号总线功能
高级定时器TIM1、TIM8APB2拥有通用定时器全部功能,并额外具有重复计数器、死区生成、互补输出、刹车输入等功能
通用定时器TIM2、TIM3、TIM4、TIM5APB1拥有基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能
基本定时器TIM6、TIM7APB1拥有定时中断、主模式触发DAC的功能
  • STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4

1.2 定时器工作原理

内部时钟通过触发控制器传至预分频器,将分频后的信号传递到计数器,计数器每得到一个信号就执行向上计数、向下计数或中央对齐操作,触发中断并自动重装载计数

  • 向上计数:计数器从零开始,得到信号执行+1操作,当计数器增至重装值,清零并触发中断

  • 向下计数:计数器从重装值开始,得到信号执行-1操作,当计数器减至零,重装并触发中断

  • 中央对齐:计数器从零开始,得到信号执行+1操作,当计数器增至重装值,触发中断,在下一个周期得到信号执行-1操作,当计数器减至零,触发中断

1.3 定时器结构框图

1.4 定时器计算公式

  • CK_CNT=CK_PSC/(PSC+1)

  • CK_CNT_OV=CK_CNT/(ARR+1)=CK_PSC/(PSC+1)/(ARR+1)

CK_CNT为计数器时钟频率,CK_PSC为预分频时钟频率,PSC为预分频数,CK_CNT_OV为中断触发频率,ARR为重载计数值

使用自动加载影子寄存器会在触发中断后重载计数器,若没有使用自动加载影子寄存器会立即修改自动加载寄存器:

  • 当计数寄存器小于修改后的自动加载寄存器,将在达到新的自动加载寄存器时触发中断(自加)

  • 当计数寄存器大于修改后的自动加载寄存器,先自加到最大值(65536)时归零再递增到自动加载寄存器触发中断

二、定时器函数

2.1 定时器配置初始化

2.1.1 恢复定时器的缺省配置
void TIM_DeInit(TIM_TypeDef* TIMx);

清除TIMx定时器原有配置,将定时器配置恢复成缺省状态

2.1.2 时基单元初始化
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);

根据时基单元初始化结构体变量来初始化定时器TIMx

2.1.3 赋予时基单元结构变量默认值
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
2.1.4 使能定时器
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);

用于使能或失能定时器,TIMx为指定的定时器,NewState可以选择为DISABLE或ENABLE

2.1.5 使能中断输出信号
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);

用于使能或失能中断输出信号,TIMx为指定的定时器,TIM_IT为指定的中断输出,NewState可以选择为DISABLE或ENABLE

2.1.6 选择内部时钟
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);

指定的定时器TIMx选择内部时钟作为时钟源

2.1.7 选择其他定时器时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);

选择ITRx其他定时器时钟作为指定的定时器的时钟源,TIM_InputTriggerSource为选择的其他定时器源

2.1.8 选择捕获通道时钟
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,uint16_t TIM_ICPolarity, uint16_t ICFilter);

选择TIx的某个引脚捕获的信号作为指定的定时器TIMx的时钟源,TIM_TIxExternalCLKSource为指定TIx的某个具体引脚,TIM_ICPolarity为选择的极性,ICFilter为滤波器

2.1.9 选择外部时钟模式1时钟
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);

选择ETR外部时钟模式1的时钟作为指定定时器TIMx的时钟源,TIMx为指定的定时器,TIM_ExtTRGPrescaler为外部触发预分频器,TIM_ExtTRGPolarity为外部定时器极性,ExtTRGFilter为外部触发滤波器

外部触发预分频器参数作用
TIM_ExtTRGPSC_OFF不分频
TIM_ExtTRGPSC_DIV22分频
TIM_ExtTRGPSC_DIV44分频
TIM_ExtTRGPSC_DIV88分频
外部定时器极性作用
TIM_ExtTRGPolarity_Inverted极性反向,低电平或下降沿有效
TIM_ExtTRGPolarity_NonInverted极性不反向,高电平或上升沿有效

2.1.10 选择外部时钟模式2时钟
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);

与2.9相似

2.1.11 配置ETR预分频器、极性和滤波器
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);

2.2 定时器配置操作

2.2.1 修改预分频器配置
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);

TIMx为定时器,Prescaler为预分频值,TIM_PSCReloadMode为预分频写入模式

2.2.2 修改计数器模式
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);

修改TIMx的计数器计数模式,TIM_CounterMode为计数器计数模式

2.2.3 计数器预装使能
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);

使TIMx的重载计数器的预装模式(重载影子计数器)使能或失能

2.2.4 给计数器写入值
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);
2.2.5 给自动重载计数器写入值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);
2.2.6 获取当前计数器的值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);
2.2.7 获取当前预分频器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);

2.3 定时器标志位管理

2.3.1 获取定时器标志位
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
2.3.2 清除定时器标志位
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
2.3.3 获取定时器中断标志位
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
2.3.4 清除定时器中断挂起标志位
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

三、定时器程序

3.1 定时器初始化

3.1.1 开启定时器时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); //开启TIM2外设时钟
TIM_InternalClockConfig(TIM2);  //使用内部时钟
3.1.2 配置定时器
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;

定义定时器结构体变量,其中

typedef struct
{
  uint16_t TIM_Prescaler;         /*!< Specifies the prescaler value used to divide the TIM clock.
                                       This parameter can be a number between 0x0000 and 0xFFFF */
​
  uint16_t TIM_CounterMode;       /*!< Specifies the counter mode.
                                       This parameter can be a value of @ref TIM_Counter_Mode */
​
  uint16_t TIM_Period;            /*!< Specifies the period value to be loaded into the active
                                       Auto-Reload Register at the next update event.
                                       This parameter must be a number between 0x0000 and 0xFFFF.  */ 
​
  uint16_t TIM_ClockDivision;     /*!< Specifies the clock division.
                                      This parameter can be a value of @ref TIM_Clock_Division_CKD */
​
  uint8_t TIM_RepetitionCounter;  /*!< Specifies the repetition counter value. Each time the RCR downcounter
                                       reaches zero, an update event is generated and counting restarts
                                       from the RCR value (N).
                                       This means in PWM mode that (N+1) corresponds to:
                                          - the number of PWM periods in edge-aligned mode
                                          - the number of half PWM period in center-aligned mode
                                       This parameter must be a number between 0x00 and 0xFF. 
                                       @note This parameter is valid only for TIM1 and TIM8. */
} TIM_TimeBaseInitTypeDef;   
  • TIM_Prescaler:预分频器数值,最大65535(填入)+1(实际+1)

  • TIM_CounterMode:计数器计数模式

  • TIM_Period:计数周期,也叫自动重载计数器(ARR),最大65535+1

  • TIM_ClockDivision:滤波器分频数(和时基单元关系并不大)

  • TIM_RepetitionCounter:重复计数器,高级定时器才有,类似中断对计数器的预分频,最大65535+1

TIM_Prescaler函数参数作用
TIM_CounterMode_Up向上计数
TIM_CounterMode_Down向下计数
TIM_CounterMode_CenterAlignedx中央对齐计数模式x
//TIM_CounterMode计数模式
#define TIM_CounterMode_Up  ((uint16_t)0x0000)
#define TIM_CounterMode_Down    ((uint16_t)0x0010)
#define TIM_CounterMode_CenterAligned1  ((uint16_t)0x0020)
#define TIM_CounterMode_CenterAligned2  ((uint16_t)0x0040)
#define TIM_CounterMode_CenterAligned3  ((uint16_t)0x0060)
//TIM_ClockDivision滤波器分频
#define TIM_CKD_DIV1    ((uint16_t)0x0000)
#define TIM_CKD_DIV2    ((uint16_t)0x0100)
#define TIM_CKD_DIV4    ((uint16_t)0x0200)

例程,采用向上计数法,计数器以10k频率计数,每10k次触发一次中断

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_ITConfig(TIM2,TIM_IT_Update,ENABLE);    //定时器中断使能

ps:在TIM初始化后会立即挂起中断标志位用于将预分频数送入预分频寄存器,在后续开启TIM中断时会立即进入一次中断,所以可以在TIM_TimeBaseInit后面添加中断标志清除函数

TIM_ClearFlag(TIM2, TIM_IT_Update);

3.2 NVIC初始化

NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置中断优先级分组
NVIC_InitTypeDef NVIC_InitStructure;    //定义NVIC结构体变量
NVIC_InitStructure.NVIC_IRQChannel=TIM2_IRQn;   //TIM2定时器中断
NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;   //使能
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2; //抢占优先级(先占优先级)
NVIC_InitStructure.NVIC_IRQChannelSubPriority=1;    //响应优先级(从占优先级)
NVIC_Init(&NVIC_InitStructure); //NVIC初始化

3.3 中断函数

void TIM2_IRQHandler(void){
    if (TIM_GetITStatus(TIM2,TIM_IT_Update)==SET){  //判断中断挂起标志位
    
        TIM_ClearITPendingBit(TIM2,TIM_IT_Update);  //清除中断挂起标志位
    }
}

3.4 例程

#include "stm32f10x.h"
​
void Timer_Init(void){
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
    
    TIM_InternalClockConfig(TIM2);
    
    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_ClearFlag(TIM2, TIM_IT_Update);
    TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
    
    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);
    
    TIM_Cmd(TIM2,ENABLE);
}
​
void TIM2_IRQHandler(void){
    if (TIM_GetITStatus(TIM2,TIM_IT_Update)==SET){
    
        TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
    }
}

3.5 使用外部引脚做时钟源

将TIM_InternalClockConfig(TIM2)改为

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

同时需要配置GPIO引脚,根据功能表来确定TIMx的外部触发引脚

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    
GPIO_InitTypeDef GPIO_InitStructure;
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);

官方推荐使用浮空输入模式,但是浮空输入会导致输入抖动,可以使用滤波器,在输入功率较大的情况下可以改为上拉输入

  • 26
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值