定时器是用于测量或产生特定时间间隔的的外设。通过这个特定的时间间隔,我们可以实现1.定时中断,2.测量输入信号脉冲宽度,3.产生精确的输出波形(PWM)等功能。
STM32中拥有三种定时器,基本定时器、通用定时器、高级定时器。
基本定时器(TIM6、TIM7)
基本定时器包含一个16位(0-65535)的自动装载计数器(存储计数的最值)。可以通过内部时钟RCC和预分频器(PSC)提供计次频率。
使用规定的计次频率,通过递增、递减等方式,达到设定的数值后,定时器会产生事件或中断。
时基单元
时基单元用于配置定时器的计次频率、计数值。拥有三个寄存器
-
计数器寄存器(TIMx_CNT):用于存储定时器当前计数。
16位(0-65535)
当控制寄存器(TIMx_CR1)写入使能时,CNT会持续计数。
-
自动重装载寄存器(TIMx_ARR):用于存储设定的定时器计数值。
16位(0-65535)
在自动重装载寄存器之外还有个预加载寄存器,每次读写自动重装载寄存器中的数值时,实际是写入到预加载寄存器,当上次的定时周期结束后,才会在下一次定时周期中,将预加载寄存器的值写入到自动重装载寄存器中(也可设置直接写入,通过自动重装预加载器(ARPE)使能决定)。
-
预分频寄存器(TIMx_PSC):用于将内部时钟进行分频。
16位(0-65535)
可以对内部时钟信号进行分频,一般内部时钟信号位72MHZ,也就是每秒计数72000000次,很显然远大于65535,不够计数的,整个定时器都不能计次1s。因此需要对时钟信号进行分频。如果7200分频,那就是72MHZ/72000=1000次,每秒就会计次1000次,也就是1000HZ。如果希望定时器每一秒产生一次中断,那么只需再令ARR=1000。
时序图
现在来了解下预分频器变化的定时器时序图(以预分频系数从1变道4为例)
寄存器
TIM6和TIM7控制寄存器1(TIMx_CR1)
16位。
两个使能位,ARPE——使能自动重装载(ARR),CEN——使能计数器(CNT)。
OPM——在执行定时器事件时,CNT是否停止计数。
URS——选择更新事件的来源,是否是只设置计数器上溢/下溢可产生更新中断(是否使能 设置UG、从模式控制器 导致的更新中断)
UDIS——控制是否禁止更新事件发生。如果设置为1,则即使触发了更新事件,也不会更新定时器的影子寄存器。
标准库中与CR1寄存器相关的函数有
//ARPE位,自动重装载计数器使能
void TIM_ARRPreloadConfig(TIM_TypeDef* TIMx, FunctionalState NewState){
if (NewState != DISABLE){
TIMx->CR1 |= TIM_CR1_ARPE;//失能
}
else{
TIMx->CR1 &= (uint16_t)~((uint16_t)TIM_CR1_ARPE);//失能
}
}
//URS位,设置更新请求源
void TIM_UpdateRequestConfig(TIM_TypeDef* TIMx, uint16_t TIM_UpdateSource)
{
if (TIM_UpdateSource != TIM_UpdateSource_Global){
TIMx->CR1 |= TIM_CR1_URS;
}
else{
TIMx->CR1 &= (uint16_t)~((uint16_t)TIM_CR1_URS);
}
}
//UDIS位,使能或失能TIMx更新事件
void TIM_UpdateDisableConfig(TIM_TypeDef* TIMx, FunctionalState NewState)
{
if (NewState != DISABLE){
TIMx->CR1 |= TIM_CR1_UDIS;
}
else{
TIMx->CR1 &= (uint16_t)~((uint16_t)TIM_CR1_UDIS);
}
}
//OPM位,设置单脉冲模式
void TIM_SelectOnePulseMode(TIM_TypeDef* TIMx, uint16_t TIM_OPMode)
{
TIMx->CR1 &= (uint16_t)~((uint16_t)TIM_CR1_OPM);
TIMx->CR1 |= TIM_OPMode;
}
都是很简单地直接对寄存器进行配置。但这么多函数,感觉通过标准库配置CR1寄存器整个配置下来还是很麻烦的。
TIM6和TIM7 DMA/中断 使能寄存器(TIMx_DIER)
16位
标准库中的定时器使能函数就是通过直接向DIER寄存器赋值
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState)
{
if (NewState != DISABLE){
TIMx->DIER |= TIM_IT;//失能
}
else{
TIMx->DIER &= (uint16_t)~TIM_IT;//使能
}
}
TIM6和TIM7状态寄存器(TIMx_SR)
16位
当产生更新中断时,UIF(Update Interrupt Flag)位置1,并在执行完事件后,自动清零(不像EXTI一样需要手动清零)
在标准库中的函数:可直接读取SR的值
FlagStatus TIM_GetFlagStatus(TIM_TypeDef* TIMx, uint16_t TIM_FLAG)
{
ITStatus bitstatus = RESET;
if ((TIMx->SR & TIM_FLAG) != (uint16_t)RESET){
bitstatus = SET;
}
else{
bitstatus = RESET;
}
return bitstatus;
}
TIM6和TIM7事件产生寄存器(TIMx_EGR)
更新事件是指,计数器达到了自动重装载值,于是计数器清零(CNT寄存器更新)、预分频器清零(ARR_CNT清零)
//设置 TIMx 事件由软件产生
void TIM_GenerateEvent(TIM_TypeDef* TIMx, uint16_t TIM_EventSource)
{
TIMx->EGR = TIM_EventSource;
}
//在基本定时器中,事件源只有TIM_EventSource_Update,更新事件源
TIM6和TIM7计数器(TIMx_CNT)
标准库函数:
void TIM_SetCounter(TIM_TypeDef* TIMx, uint16_t Counter)
{
TIMx->CNT = Counter;
}
TIM6和TIM7预分频器(TIMx_PSC)
标准库函数:
void TIM_PrescalerConfig(TIM_TypeDef* TIMx, uint16_t Prescaler, uint16_t TIM_PSCReloadMode)
{
TIMx->PSC = Prescaler;
TIMx->EGR = TIM_PSCReloadMode;
}
TIM6和TIM7自动重装载寄存器(TIMx_ARR)
在ARR之外还有个ARR_CNT,ARR是设置的ARR_CNT计数达到的最大值,同样,当ARR_CNT=ARR时,ARR_CNT清零。
标准库函数:
void TIM_SetAutoreload(TIM_TypeDef* TIMx, uint16_t Autoreload)
{
TIMx->ARR = Autoreload;
}
CNT、PSC、ARR都是16位寄存器,也是配置定时器最基本的参数。作为时基单元,它们可以使用一个结构体统一配置:
typedef struct
{
uint16_t TIM_Prescaler;//预分频系数(PSC)
uint16_t TIM_CounterMode;//计数模式(自增/自减等)
uint16_t TIM_Period;//计数周期,也就是自动重装载置(ARR)
uint16_t TIM_ClockDivision;//时钟分频,直接对RCC分频
uint8_t TIM_RepetitionCounter;//重复计数器(仅高级定时器有效)
} TIM_TimeBaseInitTypeDef;
//将结构体值传给寄存器
void TIM_TimeBaseInit(TIM_TypeDef* TIMx, TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct)