定时器 

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

16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时 不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能

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

定时器的本质就是计数器,当这个计数器输入的时钟源是一个准确可靠的基准时钟时,它在对这个时钟进行计数的过程,就是计时的过程

预分频器,可以对计数器的时钟进行分频,让这个计数更加灵活,分频系数=预分频器的值加1,预分频器为1时,对时钟源频率进行二分频,将72M变为36M,这时每两个脉冲经过预分频器变为一个脉冲

自动重装寄存器就是计数的目标值,当计数器的值达到目标值时就可以申请中断

定时器时钟模式

在STM32中,TIM2可以配置为使用内部时钟或外部时钟,两者的主要区别如下:

  1. 内部时钟模式

  2. 内部时钟模式是指定时器的时钟直接来源于STM32的内部时钟系统,如APB1或APB2总线时钟。
  3. 在这种模式下,定时器的时钟是固定的,由系统时钟经过预分频器(Prescaler)和自动重装载寄存器(ARR)来确定定时器的计数频率。
  4. 配置内部时钟模式时,通常使用TIM_InternalClockConfig()函数来设置时基单元的时钟源为内部时钟。
  5. 外部时钟模式(ETR模式)

  6. 外部时钟模式是指定时器的时钟来源于外部信号,如外部事件或另一个外设的输出。
  7. 外部时钟模式允许定时器根据外部事件来触发计数,这在需要与外部事件同步时非常有用。
  8. 在这种模式下,可以使用TIM_ETRClockMode2Config()函数来配置外部触发预分频器、极性选择和滤波器等参数。
  9. 外部时钟模式允许定时器与外部信号同步,例如,可以使用对射式红外传感器模拟外部时钟,当遮挡红外光线再移开后会产生电平变化,从而触发定时器

定时器类型

基本定时器只能选择内部时钟

通用定时器和高级定时器支持向上计数、向下计数和中央对齐三种模式

级联

示例:初始化TIM3,使用主模式将它的更新事件映射到TRGO上,接着再初始化TIM2,选择ITR2,再选择时钟为外部时钟模式1,这样TIM3的更新事件就可以驱动TIM2的时基单元,也就实现了定时器的级联

更新中断和更新事件

当计数器的值达到目标值时,可以选择产生中断信号或更新事件(更新事件不会触发中断,但可以触发内部其他电路的工作),并且清零计数器

主从触发模式示例 :单片机可以将更新事件映射到TRGO(触发输出)的位置,然后将TRGO直接接到DAC的触发转换引脚,从而通过硬件直接输出DAC

外部时钟复用

可以看到PA0的默认复用功能上有TIM2_CH1_ETR,说明TIM2的CH1和ETR都复用在这个端口

计数器各个参数说明

直接控制时钟分频的是预分频缓冲器,在预分频控制器写入值后,要等到本次计数周期结束后,预分频寄存器的值才会被传递到缓冲寄存器里面去

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

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

                                                   = CK_PSC / (PSC + 1) / (ARR + 1)

有无预装时序,就是有无缓冲寄存器的情况

时钟树

在SystemInit函数里,ST是这样来配置时钟的,选择8MHZ为系统时钟,暂时以内部8MHZ的时钟运行,然后再启动外部时钟,配置外部时钟进入PLL锁相环进行倍频,8MHZ倍频9倍得到72MHZ,等到输出稳定后,选择锁相环输出为系统时钟

定时器中断代码

1.先RCC开启时钟

2.选择时基单元的时钟源,对于定时中断,选择内部时钟源

3.配置时基单元

4.配置中断输出控制,允许更新中断输出到NVIC

5.配置NVIC,在NVIC中打开定时器中断的通道,并分配一个优先级

6.配置完整个模块后,还要使能一下计数器

7.写中断函数

/**
  * 函    数:定时中断初始化
  * 参    数:无
  * 返 回 值:无
  */
void Timer_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	
	/*配置时钟源*/
	TIM_InternalClockConfig(TIM2);		//选择TIM2为内部时钟,若不调用此函数,TIM默认也为内部时钟
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 10000 - 1;				//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 7200 - 1;				//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				//将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元	
	
	/*中断输出配置*/
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位
																//TIM_TimeBaseInit函数末尾,手动产生了更新事件
																//若不清除此标志位,则开启中断后,会立刻进入一次中断
																//如果不介意此问题,则不清除此标志位也可
	
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//指定NVIC线路的抢占优先级为2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

/* 定时器中断函数,可以复制到使用它的地方
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}
*/

TIM_TimeBaseInit配置时基单元

TIM_Cmd使能计数器

TIM_ITConfig使能中断输出信号

TIM_InternalClockConfig选择内部时钟
TIM_ITRxExternalClockConfig选择ITRX其他定时器的时钟

TIM_TIxExternalClockConfig选择TIX捕获通道的时钟

TIM_ETRClockMode1Config选择ETR通过外部时钟模式1输入的时钟

TIM_ETRClockMode2Config选择ETR通过外部时钟模式2输入的时钟

TIM_ETRConfig配置ETR引脚的参数
因为在初始化结构体里有很多关键的参数,比如自动重装值和预分频值等等,可能会在初始化之后还需要更改,所以有一些单独的函数,可以方便地更改这些关键参数,

TIM_PrescalerConfig用来单独写预分频值

TIM_CounterModeConfig用来改变计数器的计数模式

TIM_ARRPreloadConfig自动重装器预装功能配置

TIM_SetCounter给计数器手动写入一个值

uint16_t TIM_GetCounter获取当前计数器的值

选择外部时钟源

选择TIM2_ETR所对应的GPIO口,通过外部接入信号给TIM2提供时钟

/**
  * 函    数:定时中断初始化
  * 参    数:无
  * 返 回 值:无
  * 注意事项:此函数配置为外部时钟,定时器相当于计数器
  */
void Timer_Init(void)
{
	/*开启时钟*/
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);			//开启TIM2的时钟
	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);			//开启GPIOA的时钟
	
	/*GPIO初始化*/
	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引脚初始化为上拉输入
	
	/*外部时钟配置*/
	TIM_ETRClockMode2Config(TIM2, TIM_ExtTRGPSC_OFF, TIM_ExtTRGPolarity_NonInverted, 0x0F);
																//选择外部时钟模式2,时钟从TIM_ETR引脚输入
																//注意TIM2的ETR引脚固定为PA0,无法随意更改
																//最后一个滤波器参数加到最大0x0F,可滤除时钟信号抖动
	
	/*时基单元初始化*/
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;				//定义结构体变量
	TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1;		//时钟分频,选择不分频,此参数用于配置滤波器时钟,不影响时基单元功能
	TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;	//计数器模式,选择向上计数
	TIM_TimeBaseInitStructure.TIM_Period = 10 - 1;					//计数周期,即ARR的值
	TIM_TimeBaseInitStructure.TIM_Prescaler = 1 - 1;				//预分频器,即PSC的值
	TIM_TimeBaseInitStructure.TIM_RepetitionCounter = 0;			//重复计数器,高级定时器才会用到
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);				//将结构体变量交给TIM_TimeBaseInit,配置TIM2的时基单元	
	
	/*中断输出配置*/
	TIM_ClearFlag(TIM2, TIM_FLAG_Update);						//清除定时器更新标志位
																//TIM_TimeBaseInit函数末尾,手动产生了更新事件
																//若不清除此标志位,则开启中断后,会立刻进入一次中断
																//如果不介意此问题,则不清除此标志位也可
																
	TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);					//开启TIM2的更新中断
	
	/*NVIC中断分组*/
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);				//配置NVIC为分组2
																//即抢占优先级范围:0~3,响应优先级范围:0~3
																//此分组配置在整个工程中仅需调用一次
																//若有多个中断,可以把此代码放在main函数内,while循环之前
																//若调用多次配置分组的代码,则后执行的配置会覆盖先执行的配置
	
	/*NVIC配置*/
	NVIC_InitTypeDef NVIC_InitStructure;						//定义结构体变量
	NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn;				//选择配置NVIC的TIM2线
	NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;				//指定NVIC线路使能
	NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 2;	//指定NVIC线路的抢占优先级为2
	NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;			//指定NVIC线路的响应优先级为1
	NVIC_Init(&NVIC_InitStructure);								//将结构体变量交给NVIC_Init,配置NVIC外设
	
	/*TIM使能*/
	TIM_Cmd(TIM2, ENABLE);			//使能TIM2,定时器开始运行
}

/**
  * 函    数:返回定时器CNT的值
  * 参    数:无
  * 返 回 值:定时器CNT的值,范围:0~65535
  */
uint16_t Timer_GetCounter(void)
{
	return TIM_GetCounter(TIM2);	//返回定时器TIM2的CNT
}

/* 定时器中断函数,可以复制到使用它的地方
void TIM2_IRQHandler(void)
{
	if (TIM_GetITStatus(TIM2, TIM_IT_Update) == SET)
	{
		
		TIM_ClearITPendingBit(TIM2, TIM_IT_Update);
	}
}
*/

作者:狐璃同学

物联沃分享整理
物联沃-IOTWORD物联网 » STM32(3)定时器

发表回复