简介
定时器也是计数器,可以对输入的时钟进行计数,在计数值达到设定值时触发中断
16位计数器、预分频器、自动重装寄存器的时基单元,在72MHz计数时钟下可以实现最大59.65s的定时
不仅具备基本的定时中断功能,而且还包含内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等多种功能
根据复杂度和应用场景分为了高级定时器、通用定时器、基本定时器三种类型
定时器类型
类型 | 编号 | 总线 | 功能 |
---|---|---|---|
高级定时器 | TIM1、TIM8 | APB2 | 有通用定时器的全部功能,并且额外有重复定时器、死区生成、互补输出、刹车输入等功能 |
通用定时器 | TIM2、TIM3、TIM4、TIM5 | APB1 | 用于基本定时器全部功能,并额外具有内外时钟源选择、输入捕获、输出比较、编码器接口、主从触发模式等功能 |
基本定时器 | TIM6、TIM7 | APB1 | 定时中断、主模式触发ADC的功能 |
STM32F103C8T6的定时器资源:TIM1、TIM2、TIM3、TIM4(一个高级,三个通用)
基本定时器
名词:
UI:更新中断,更新中断之后通往NVIC,再配置好NVIC的定时器通道,定时器的更新中断就可以得到CPU的响应
U:更新事件,不会触发中断,但是可以触发内部其他电路的工作
计数时间到,就会触发更新中断和更新事件
主从触发DAC模式
可以让内部的硬件再不受程序控制下实现自动运行,可以减轻CPU的负担
用途:在使用DAC的时候,可能要用DAC输出一段波形,就需要每隔一段时间来触发一次ADC,让它输出下一个电压点,使用主模式,可以把定时器的更新事件映射到出发输出TRGO的位置,这样,定时器的更新就不需要用中断来触发DAC转换了
通用定时器
总结:外部时钟模式1的输入可以是ETR引脚、其他定时器、CH1引脚的边沿、CH1引脚和CH2引脚
定时器中断基本图
定时中断和内外时钟源选择
涉及的结构:时基单元、RCC内部时钟、ETR外部时钟、内部时钟模式、外部时钟模式2
时序图
预分频器时序
计数器计数频率:CK_CNT = CK_PSC / (PSC + 1)
计数器时序图
记到0036发生溢出,再来一个上升沿,计数器清零
计数器溢出频率:CK_CNT_OV = CK_CNT / (ARR + 1) = CK_PSC / (PSC + 1) / (ARR + 1)
用72MHz/ (PSC + 1) / (ARR + 1)就能得到溢出频率,如果想算溢出事件,只需要取倒数
1.1 定时器基本定时功能
实现功能:定时中断和内外时钟源选择
定一个时间,然后让定时器每隔这个时间产生一次中断,来实现每隔一定时间就实现特定程序的目的
步骤:
第一步:RCC开启时钟,打开时钟后,定时器的基准时钟和整个外设的工作时钟就会同时打开
第二步:选择时基单元的时钟源,对于定时中断,选择内部时钟源
第三步:配置时基单元,包括预分频器、自动重装器、计数模式等
第四步:配置输出中断控制,允许更新中断输出到NVIC
第五步:配置NVIC,再NVIC中打开定时器中断的通道,并分配一个优先级
第六步:运行控制,使能计数器,否则不会运行
当定时器使能后,计数器就会开始计数,当计数器更新时,触发中断,最后再写一个定时器的中断函数,这样中断函数每隔一段事件就会中断一次
相关定时器库函数
void TIM_DeInit(TIM_TypeDef* TIMx);
恢复缺省配置
时基单元选择
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
时基单元初始化,用来配置时基单元,第一个参数TIMx选择某个定时器,第二个结构体TIM_TimeBaseInitStruct,包含配置时基单元的一些参数
void TIM_TimeBaseStructInit(TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
可以把结构体变量赋一个默认值
运行控制
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
用来使能计数器(对应运行控制),两个参数,第一个TIMx选择寄存器,第二个参数选择状态,使能还是失能
中断输出控制
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
用来使能中断输出信号,对应中断输出控制(第四步),第一个参数TIMx选择定时器,第二个参数TIM_IT选择要配置哪个中断输出,第三个参数新的状态
时钟源选择:对应时基单元的时钟选择部分的函数,可以选择RCC内部时钟、ETR外部时钟,ITRx其他定时器、TIX捕获通道(第二步)
void TIM_InternalClockConfig(TIM_TypeDef* TIMx);
选择内部时钟
void TIM_ITRxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_InputTriggerSource);
选择ITRX其他定时器的时钟,第一个参数选择要配置的定时器,第二个参数选择要接入哪个其他定时器
void TIM_TIxExternalClockConfig(TIM_TypeDef* TIMx, uint16_t TIM_TIxExternalCLKSource,uint16_t TIM_ICPolarity, uint16_t ICFilter);
选择TIx捕获通道的时钟,第一个参数TIMx选择定时器,第二个参数TIM_TIxExternalCLKSource选择TIx具体的某个引脚,剩下两个参数分别是输入的极性和滤波器
void TIM_ETRClockMode1Config(TIM_TypeDef* TIMx, uint16_t TIM_ExtTRGPrescaler, uint16_t TIM_ExtTRGPolarity,
uint16_t ExtTRGFilter);
选择ETR通过外部时钟模式1输入的时钟,第二个参数TIM_ExtTRGPrescaler为外部触发预分频器,可以对ETR的外部时钟再提前做一个分频
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);
用来单独写预分频值的, Prescaler参数就是要写入的预分频值,TIM_PSCReloadMode参数就是要写入的模式,可以选择是听从安排,在更新事件后生效,或者是在写入后,手动产生一个更新事件,让这个值立刻生效
void TIM_CounterModeConfig(TIM_TypeDef* TIMx, uint16_t TIM_CounterMode);
用来改变计数器的计数模式,参数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);
用来获取标志位和清除标志位
注:
如果想跨文件使用变量:
(1)可以在使用变量的那个文件上面,用extern声明一下要用的变量,比如在Timer.c文件上面写上extern uint16_t Num;这样就可以在Timer.c文件中使用主函数的Num变量
(2)直接把中断函数复制到主函数后面,就不需要extern函数
配置代码示例
以下文件均为Timer.c文件
内部时钟配置
#include "stm32f10x.h" // Device header
//初始化TIM2,通用定时器,定时中断的初始化函数
void Timer_Init(void)
{
//第一步:开启时钟
//RCC_APB1Periph_TIM2表示RCC_APB1外设TIM2
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
//第二步:选择时基单元的时钟
TIM_InternalClockConfig(TIM2);
//第三步:配置时基单元
//结构体类型名 结构体变量名
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
//Specifies the clock division.指定时钟分频,随便配一个就可以
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
//ARR自动重装器的值
TIM_TimeBaseInitStructure.TIM_Period=10000-1;
//PSC预分频器的值
TIM_TimeBaseInitStructure.TIM_Prescaler=7200-1;
//重复计数器的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
//1s时间:定时1s也就是定时频率为1Hz,预分频是对72M进行7200分频,得到10K的计数频率
//在10K的频率下,记10000个数字就是1s的时间
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
//第四步:使能更新中断
//手动清除更新中断标志位,避免按下复位键刚初始化完就立刻进中断
TIM_ClearFlag(TIM2,TIM_IT_Update);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
//第五步:配置NVIC
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);
// }
//}
外部时钟配置
#include "stm32f10x.h" // Device header
//初始化TIM2,通用定时器,定时中断的初始化函数
void Timer_Init(void)
{
//第一步:开启时钟
//RCC_APB1Periph_TIM2表示RCC_APB1外设TIM2
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
RCC_APB1PeriphClockCmd(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);
//第二步:选择时基单元的时钟
TIM_ETRClockMode2Config(TIM2,TIM_ExtTRGPSC_OFF,TIM_ExtTRGPolarity_NonInverted,0x00);
//第三步:配置时基单元
//结构体类型名 结构体变量名
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
//Specifies the clock division.指定时钟分频,随便配一个就可以
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1;
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up;
//ARR自动重装器的值
TIM_TimeBaseInitStructure.TIM_Period=10-1;
//PSC预分频器的值
TIM_TimeBaseInitStructure.TIM_Prescaler=1-1;
//重复计数器的值
TIM_TimeBaseInitStructure.TIM_RepetitionCounter=0;
//1s时间:定时1s也就是定时频率为1Hz,预分频是对72M进行7200分频,得到10K的计数频率
//在10K的频率下,记10000个数字就是1s的时间
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure);
//第四步:使能更新中断
//手动清除更新中断标志位,避免按下复位键刚初始化完就立刻进中断
TIM_ClearFlag(TIM2,TIM_IT_Update);
TIM_ITConfig(TIM2,TIM_IT_Update,ENABLE);
//第五步:配置NVIC
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)
// {
//
// //清除标志位
// TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
// }
//}