一、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,输入的方波可以经过极性选择,边沿检测,预分频器,输入滤波等将波形进行整形
- 外部时钟可以来自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