飞道的博客

STM32——TIM简介与TIM中断

403人阅读  评论(0)

一、TIM简介

  • TIM(Timer)定时器
  • 定时器可以对输入的时钟(方波)进行计数,并在计数值达到设定值触发中断
    • 输入时钟: 内部时钟,外部时钟
    • 对输入的时钟进行计数就是计时
  • 每个定时器都具备的3个核心:
    • 16位计数器【寄存器】
    • 16位预分频器【对计数器时钟分频】
    • 16位自动重装寄存器【计数的目标值】
    • 3个核心组成时基单元
    • 在72MHz时钟下可以实现最大59.65s(65536*65536/72MHZ)的定时(中断)
      • 72MHZ/65536=最小时钟分频(最大的周期)
      • 时钟分频倒数是周期,周期表示一个方波的时长,乘以重装值(方波的个数)就是定时(中断)的时长
  • 定时器支持级联,级联一个定时器:59.65* 65536* 65536
  • 不仅具备基本的定时中断功能,定时器还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
  • 根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型

二、定时器类型


注意:STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4

基本定时器

  • 只能连接内部时钟(72MHZ)
  • 预分频的值+1=实际的分频系数,例如预分频的值为1则表示分频系数为2,是2分频,则输出36MHZ
  • 自动重装寄存器满了可以触发更新中断(通往NVIC)和更新事件(不会触发中断,触发其他外设电路)
  • 计数器:对预分频后的时钟(方波)进行计数
  • 主模式触发DAC:防止主程序频繁被中断,通过主模式,将定时器的更新事件映射到触发输出,定时器的更新就无须通过更新中断来实现【类似于DMA控制器,实现硬件自动化】

通用定时器

  • 通用定时器包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能

  • 通用定时器和高级定时器支持向上计数,向下技术,中央对齐模式

  • 编码器接口:读取正交编码器的输出波形

  • 该框图可以分为三个部分

    • 上面部分可以完成定时中断功能,内外时钟源选择和主从触发模式选择

    • 下面由输入捕获单元和输出比较电路组成

      • IC输入比较 ;CC捕获比较寄存器 ;OC输出比较
  • 外部时钟模式2:通过TIMx_ETR——触发控制器——时基单元

    • 外部时钟可以来自TIMx_ETR,如图所示PA0可以输入外部时钟给stm32,输入的方波可以经过极性选择,边沿检测,预分频器,输入滤波等将波形进行整形
  • 外部时钟模式1(用作触发输入):通过TRGI——触发控制器——时基单元

    • TRGI当做外部时钟,时钟源来自多个地方
    • TRGI的输入可以是ETR(即TIMx_ETR)或ITR信号(即其他定时器)或TI1F_ED信号(输入捕获单元的CH1、CH2、CH3引脚)或TI1FP1和TI1FP2信号(CH1、CH2引脚的时钟)
      • 关于ITR信号:TRGO输出可以接到其他定时器,所以可以级联,而其他定时器的接入是设计在ITR信号部分
      • ITRX可以实现定时器级联功能
      • 关于TI1F_ED信号:ED表示上升沿和下降沿都有效
  • 各种定时器时钟源的GPIO配置

高级定时器

  • DTG死区生成电路:可以驱动三相无刷电机,输出一对互补的的PWM波
  • BRK完成 刹车输入功能
  • 重复计数器:每隔几个计数周期才发送一次更新事件和更新中断(即对输出的信号再进行分频,定时时间更长)

三、定时中断基本结构

  • RCC使能定时器,此时定时器的基准时钟和外设的工作时钟都设定好了

中断源选择:

  • 外部中断:有中断引脚选择AFIO,例如配置GPIO_EXTILineConfig(GPIO_PortSourceGPIOB,GPIO_PinSource14);
  • 定时器中断:有中断输出控制,例如配置TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
    • 如通用定时器框图所示,一个定时器中包含多个中断源如TGI,计数器值等于重装值,输入捕获与输出比较匹配时,参数可以选择如下

      void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState)
      @param  TIM_IT: specifies the TIM interrupts sources to be enabled or disabled.
      *   This parameter can be any combination of the following values:
      *     @arg TIM_IT_Update: TIM update Interrupt source
      *     @arg TIM_IT_CC1: TIM Capture Compare 1 Interrupt source
      *     @arg TIM_IT_CC2: TIM Capture Compare 2 Interrupt source
      *     @arg TIM_IT_CC3: TIM Capture Compare 3 Interrupt source
      *     @arg TIM_IT_CC4: TIM Capture Compare 4 Interrupt source
      *     @arg TIM_IT_COM: TIM Commutation Interrupt source
      *     @arg TIM_IT_Trigger: TIM Trigger Interrupt source
      *     @arg TIM_IT_Break: TIM Break Interrupt source
      

四、时序图

预分频器时序

  • 计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)【预分频的值+1=实际的分频系数】

  • 当计数到一半改变分频值,不会从那个时刻修改,而是等本次计数周期结束后才开始【结束会产生更新事件,预分频寄存器的值才会传递到预分频缓冲器,下一个周期才会起作用】

  • CK_PSC与CK_CNT的关系

  • 预分频寄存器实际上有两个,一个是供用户读写使用的预分频控制寄存器,该寄存器不会决定分配系数,另一个是缓冲寄存器(影子寄存器),真正起作用

计数器时序


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

例如:定时1s时间计算psc和arr:

  • 定时1s表示计数溢出频率是1HZ
  • 而计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1)=CK_PSC / (PSC + 1) / (ARR + 1)
  • 所以只要满足(PSC + 1) / (ARR + 1) = 72000000即可
  • psc=7200-1
  • arr = 10000-1

自动重装寄存器有缓冲寄存器可以使用,使用则是有预装,不使用则是无预装,通过设置ARPE位进行设置

计数器无预装时序

  • 更改自动加载寄存器时为36,此时计数器计数到36会触发中断更新

计数器有预装时序

  • 更改自动加载寄存器时为36,由于设置了缓冲寄存器,此时由自动加载影子寄存器起作用,到F5时才重新计数
  • 引入缓冲寄存器的目的是为了让值的变化与更新事件同步,防止运行过程中造成错误

RCC时钟树

  • 任何一个时钟源都可以开启或关闭,任何一个外设的时钟也可以被开启或关闭,将系统功耗降到最低
  • SystemInit()就是用来配置时钟树
    • 首先启动内部8MHZ高速RC振荡器作为系统时钟
    • 然后启动外部4-16MHZ高速石英晶体振荡器,进入PLL锁相环倍频到72MHZ,选择锁相环输出为系统时钟
  • 中间的SYSCLK就是系统时钟72MHZ
    • 进入AHB预分频器,当SystemInit()设置为1的时候就是不分频,还是72MHZ
    • APB1总线配置的系数是2,分频后为36MHZ,由于下面的支路会将频率扩大2倍,所以stm32中所有的定时器的预分频还是72MHZ。
    • 关于与门控制,就是通过该函数(RCC_APB1/2PeriphClockCmd())进行控制,
  • 时钟产生电路有四个震荡源:
    • 内部8MHZ高速RC振荡器
    • 外部4-16MHZ高速石英晶体振荡器(晶振),比内部8MHZ高速RC振荡器稳定
    • 外部32.768MHZ低速晶振,给RTC提供时钟
    • 内部40KHZ低速RC振荡器,给看门狗提供时钟
  • CSS:时钟安全系统:检测外部时钟运行状态,一旦外部时钟失效就会切换到内部时钟
  • 关系图:

五、定时器定时中断

Timer.c

#include "stm32f10x.h"                  // Device header

uint16_t Num;

void Timer_Init(void)
{
   
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	
	TIM_InternalClockConfig(TIM2);//选择内部时钟源,如果是使用内部时钟可以不写
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;//时钟分频,有1分频2分频和4分频
	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_FLAG_Update);//在非中断函数中清除定时中断标志位
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);//使能该定时器外设的中断输出控制(常见)
	
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
	
	NVIC_InitTypeDef NVIC_InitStructure;
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;//stm32f10x.h中对应型号的参数
	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)
	{
   
		Num ++;
		//中断函数中清除标志位
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}

 
  • .h头文件的声明里面省略了extern

六、定时器外部时钟

Timer.c

#include "stm32f10x.h"                  // Device header

uint16_t Num;

void Timer_Init(void)
{
   
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
	
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//PA0可以作为TIMx_ETR信号输入端
	
	TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);//第二个参数是预分频器,第三个参数是外部触发的极性,第一是反向,低电平或下降沿有效,第二个是不反向,高电平或上升沿有效,最后一个参数是外部触发滤波器,通过设置16进制来设定采样频率和采样个数
	
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;
	TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;//重装值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 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_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);
}

uint16_t Timer_GetCounter(void)
{
   
	return TIM_GetCounter(TIM2);//获取计数器的值
}


void TIM2_IRQHandler(void)
{
   
	//中断函数中查看标志位
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
   
		Num ++;
		//中断函数中清除标志位
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}



 

七、定时器库函数(tim.h)

void TIM_DeInit(TIM_TypeDef* TIMx);//恢复缺省配置
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);//定时器时基单元配置
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);//使能运行控制
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);//使能中断输出信号
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);//内部时钟模式
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);//ITx其他定时器模式
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,uint16_t TIM_ICPolarity, uint16_t ICFilter);//TIx捕获通道
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);//ETR外部时钟模式1
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler,uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);//ETR外部时钟模式2
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,uint16_t ExtTRGFilter);//不是配置时钟,而是设置ETR引脚的预分频器,极性,滤波器扥
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode);//单独设置预分频值
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);//改变计数器计数模式
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState);//自动重装器是否设置预装功能
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter);//给计数器写入一个值
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload);//给自动重装器写入一个值
uint16_t TIM_GetCounter(TIM_TypeDef* TIMx);//获取当前计数器的值
uint16_t TIM_GetPrescaler(TIM_TypeDef* TIMx);//获取当前预分频的值
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
void TIM_ClearFlag(TIM_TypeDef* TIMx, uint16_t TIM_FLAG);
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);

 

转载:https://blog.csdn.net/NRWHF/article/details/128529354
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场