1、TIM (Timer) 定时器

定时器可以对输入的时钟进行计数,并在计数值达到设定值时触发中断,其含有16位计数器、预分频器、自动重装寄存器时基单元,在72MHz计数时钟下可以实现最大59.65s的定时;定时器不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能根据复杂度和应用场景;分为高级定时器、通用定时器、基本定时器三种类型:

1.1 基本定时器

        时基单元:PSC预分频器、自动重装在寄存器、CNT计数器构成的最基本的计时计数电路了

        基本定时器只能连接内部时钟,频率一般为系统主频72MHz;

        定时中断:进入到16位的PSC预分频器后进行预分频,n分频:输出=输入/n+1,最大可进行65536分频;预分频后的计数时钟进入到CNT计数器进行计数:将目标值存储到16位的自动重装载寄存器中,CNT计数器不断自加(基本定时器只支持向上计数),当当前值与自动重装载寄存器中的值相等时,触发定时中断并清零计数器,开始下一次计数

        主模式触发DAC:将定时器的更新事件映射到触发控制器的TRGO(Trigger Out)上,再直接接到DAC的转换引脚上,避免了通过中断反复触发DAC转换,以及避免反复打断CPU执行非中断程序,提高了运行效率

1.2 通用定时器

1.2.1 结构框图

        1、通用定时器支持72Mhz内部时钟和外部时钟:

        TIMx_ETR(ETR:External)引脚为外部时钟。在TIMx_ETR外接一个方波时钟,经过配置极性选择,边沿检测和预分频器,再配置输入滤波电路(对外部时钟进行整形、滤波),接着分为两路外部时钟模式2:外部触发输入(ETR)(图中【输入滤波】方框过后上方的箭头)和外部时钟模式1:外部输入脚(TIx)(图中【输入滤波】方框过后下方的箭头)

        内部触发输入(ITRx):通过TRTO,使用一个定时器作为另一个定时器的预分频器,如可以配置一个定时 器Timer1而作为另一个定时器Timer2的预分频器,可实现定时器级联的功能

        2、编码器接口:可读取正交编码器的输出波形

1.2.2 基本功能

        定时中断:同上,但是通用定时器和高级定时器拥有向上计数、向下计数和中央对齐三种模式

        主模式输出:把内部一些事件映射到TRGO引脚上,如:其他定时器、DAC、ADC,触发输出范围比基本定时器广

        输出比较:共四个通道,对应CH1~CH4引脚,可输出PWM波形

        输入捕获电路:共四个通道,对应CH1~CH4引脚,可测量输入方波的频率

捕获/比较寄存器:输入捕获和输出比较电路共用的,故输入捕获和输出比较不能同时使用

1.3 高级定时器

与通用定时器不同的:

申请中断的地方增加了一个重复次数计数器,可实现每隔几个计数周期再触发中断

死区生成电路(DTG):右侧输出变成了两个互补的输出,可以输出一堆互补的PWM,一般用于控制三相电机

刹车输入功能:若外部时钟TIMx_BKIN产生了刹车信号或内部时钟失效产生了故障,则控制电路会自动切断电机输出,防止意外发生

2、定时中断基本结构

粉色块为时基单元,橙色块为运行控制,操作这些寄存器即可控制时基单元的运行;左侧模块为时钟选择部分;右侧为产生中断后的信号去向,中断信号会在状态寄存器里设置一个中断标志位,并通过中断输出控制(中断允许位)到NVIC申请中断。

2.1 预分频器时序

        CK_PSC:时钟信号,一般为72MHz

        CNT_EN:计数器使能,高电平运行,低电平停止

        定时器时钟 = CK_CNT:预分频器的时钟输出,计数器的时钟输入

        计数器寄存器:“FC”为ARR自动重装载寄存器中的值,计数值与重装值相等且下一个时钟来临时,计数器清零

        更新事件(UEV):计数器清零时,产生一个更新事件

        预分频控制寄存器:可供写入以改变预分系数的寄存器

        预分频缓冲器:也叫影子寄存器(通过ARPE位是否置1选择是否使用),当计数到一半是更改了预分频控制寄存器中的值时,变化不会立即生效,而是会等到本次计数周期结束,产生更新事件后再生效

        预分频计数器:根据计数来分频,预分频值为0时,计数器一直为0,输出原频率;预分频值为1时,计数器隔一帧计数一次,翻转为0时输出脉冲,即为二分频。预分频器的值与实际的分频系数之间有一帧的偏移

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

2.2 计数器时序

        CK_INT:内部时钟72Mhz

        CNT_EN:时钟使能

        定时器时钟 = CK_CNT:分频系数为2,故频率为CK_INT/2

        计数器寄存器:计数器寄存器的值在 CK_CNT 的上升沿自增

        计数器溢出:计数器寄存器到0036的下一个CK_CNT上升沿时计数器溢出

        更新事件(UEV):计数器溢出时产生一个更新事件脉冲

        更新中断标志位(UIF):计数器溢出同时产生一个更新中断标志位

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

2.3 RCC时钟树

从AHB预分频器划分,左侧为时钟发生电路,右侧为时钟分配电路

2.3.1时钟发生电路:四个震荡源

        内部8MHz高速RC振荡器;外部4~16MHz高速晶振,一般为8MHz;外部的32.768KHz低速晶振,一般用于给RTC提供时钟;内部40KHz低速RC振荡器,一般用于给看门狗提供时钟

        高速晶振用于提供系统时钟,包括APB1、APB2、AHB,外部晶振较内部RC振荡器更为稳定,故一般使用外部晶振,外部时钟使用时会进入PLLMUL锁相环进行倍频,得到72MHz

        CSS(Clock Security System)为时钟安全系统,负责检测外部时钟运行状态,一旦外部时钟失效,则自动切换为内部时钟,保证系统时钟运行

2.3.2 时钟分配电路

        时钟信号经过AHB总线的预分频器进行预分频,后进入APB1总线的预分频器进行二分频最大可设置为72Mhz/2 = 36Mhz 至APB1外设;向下进入定时器2~7时,由于APB1预分频系数不为1.故频率需*2,得到72MHz进入定时器2~7;

        APB2总线与APB1类似,但实际进入APB2外设的时钟频率最大可为72Mhz

3、代码

根据上图让定时器工作需要的步骤为:

        1、RCC开启时钟        2、选择时基单元的时钟源       

        3、配置时基单元,包括预分频器、自动重装器、计数模式等

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

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

        6、运行控制        7、使能定时器

首先,认识一下STM32的TIM库函数

3.1 STM32 TIM库函数

void TIM_DeInit(TIM_TypeDef* TIMx);
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
void TIM_OC1Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC2Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC3Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_OC4Init(TIM_TypeDef* TIMx, TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
void TIM_PWMIConfig(TIM_TypeDef* TIMx, TIM_ICInitTypeDef* TIM_ICInitStruct);
void TIM_BDTRConfig(TIM_TypeDef* TIMx, TIM_BDTRInitTypeDef *TIM_BDTRInitStruct);
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
void TIM_OCStructInit(TIM_OCInitTypeDef* TIM_OCInitStruct);
void TIM_ICStructInit(TIM_ICInitTypeDef* TIM_ICInitStruct);
void TIM_BDTRStructInit(TIM_BDTRInitTypeDef* TIM_BDTRInitStruct);
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_CtrlPWMOutputs(TIM_TypeDef* TIMx, FunctionalState NewState);
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
void TIM_GenerateEvent(TIM_TypeDef* TIMx, uint16_t TIM_EventSource);
void TIM_DMAConfig(TIM_TypeDef* TIMx, uint16_t TIM_DMABase, uint16_t TIM_DMABurstLength);
void TIM_DMACmd(TIM_TypeDef* TIMx, uint16_t TIM_DMASource, FunctionalState NewState);
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,
                                uint16_t TIM_ICPolarity, uint16_t ICFilter);
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
                             uint16_t ExtTRGFilter);
void TIM_ETRClockMode2Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, 
                             uint16_t TIM_ExtTRGPolarity, uint16_t ExtTRGFilter);
void TIM_ETRConfig(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
                   uint16_t ExtTRGFilter);

本节使用为:

时基单元:

        TIM_DeInit :恢复默认设置;

        TIM_TimeBaseInit :时基单元初始化,用于配置时基单元,TIMx选择某个定时器,TIM_TimeBaseInitStruct为结构体

        TIM_TimeBaseStructInit :把结构体变量赋默认值

        TIM_Cmd :使能计数器

        TIM_ITConfig :使能中断输出信号,TIMx选择某个定时器,TIM_IT配置哪个终端输出,NewState配置状态(使能or失能)

时钟源选择:

        TIM_InternalClockConfig :选择内部时钟;

        TIM_ITRxExternalClockConfig :选择ITRx其他定时器的时钟;

        TIM_TIxExternalClockConfig :选择TIx捕获通道的时钟,TIM_TIxExternalCLKSource选择具体引脚,TIM_ICPolarity输入极性,ICFilter滤波器;

        TIM_ETRClockMode1Config :外部触发预分频器;TIM_ExtTRGPrescaler分频,TIM_ExtTRGPolarity极性,ExtTRGFilter滤波器;

        TIM_ETRClockMode2Config :选择ETR外部时钟模式2输入的时钟;参数同上;

        TIM_ETRConfig :用于配置ETR引脚的预分频器、极性、滤波器等参数;

更改关键参数的函数:

        TIM_PrescalerConfig :单独设置预分频系数;

        TIM_CounterModeConfig :改变计数器计数模式;

        TIM_ARRPreloadConfig:自动重装在寄存器功能配置;

        TIM_SetCounter :给计数器写入值;

        TIM_SetAutoreload :给自动重装在寄存器写入值;

        TIM_GetCounter :获取当前计数器的值;

        TIM_GetPrescaler :获取当前预分频器的值;

3.2 定时中断初始化代码实现

void Timer_Init(void)//TIM2 -> APB1总线
{
	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;//重复计数器,高级定时器才可使用,置0
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseInitStructure);
	
	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);
	}
}

作者:陽临

物联沃分享整理
物联沃-IOTWORD物联网 » STM32 TIM_定时中断篇

发表回复