目录
[外部时钟模式 1( TI1、 TI2)](#外部时钟模式 1( TI1、 TI2))
如果来自外部的时钟信号的频率过高或者混杂有高频干扰信号的话,我们就需要使用滤波器对信号重新采样,来达到降频或者去除高频干扰的目的,也可以设置不使用滤波器
[外部时钟模式 2( ETR)](#外部时钟模式 2( ETR))
[预分频器 PSC](#预分频器 PSC)
[自动重载寄存器 ARR](#自动重载寄存器 ARR)
[计数器 CNT](#计数器 CNT)
定时器分类
STM32F1系列最多有8个常规定时器, 2个基本定时器( TIM6、 TIM7)、 4个通用定时器( TIM2、 TIM3、 TIM4、TIM5)、 2个高级定时器( TIM1、 TIM8)
STM32F103C8T6定时器资源:TIM1、TIM2、TIM3、TIM4
图1
图2
通用定时器框图
时钟源
内部时钟(CK_INT)
STM32F1 系列的定时器 TIM2-7都是挂载在 APB1 总线上,时钟不是直接由 APB1 总线直接提供,而是先经过一个倍频器。当 APB1 的预分频器系数为 1 时,这个倍频器系数为 1,即定时器的时钟频率等于 APB1 总线时钟频率;当 APB1 的预分频器系数≥2 分频时,这个倍频 器系 数就 为 2, 即定 时器的 时钟 频率 等于 APB1 总 线时 钟频 率的 两倍,一般设置 APB1 总线时钟频率为 36M, APB1 总线的预分频器分频系数是 2,所以挂载在 APB1 总线的定时器时钟频率为 72Mhz。一般情况下都是使用内部时钟。当从模式控制寄存器 TIMx_SMCR 的 SMS 位等于 000 时,则使用内部时钟。
高级定时器 TIM1 和 TIM8 是挂载在 APB2 总线上的,如果 APB2 预分频系数为 1,挂载在该总线的定时器时钟频率不变,否则频率是该总线时钟频率的 2 倍。一般设置 APB2 总线时钟频率为 72MHz, APB2 预分频器的预分频系数为1,所以 TIM1 和 TIM8 时钟源频率为 72MHz。
外部时钟模式 1( TI1、 TI2)
顾名思义时钟信号来自芯片外部。时钟源进入定时器的流程如下:外部时钟源信号→IO→TIMx_CH1(或者 TIMx_CH2),这里需要注意的是:从 IO到 TIMx_CH1(或者 TIMx_CH2),就需要配置 IO 的复用功能,才能使 IO 和定时器通道相连通。
时钟信号输入引脚
当使用外部时钟模式 1 的时候,时钟信号来自于定时器的输入通道,总共有 4 个,分别为 TI1/2/3/4,即 TIMx_CH1/2/3/4。具体使用哪一路信号,由 TIM_CCMRx 的位 CCxS[1:0] 配置,其中 CCMR1控制 TI1/2, CCMR2 控制 TI3/4。外部时钟模式 1 下,时钟源信号只能从 CH1 或者 CH2 输入到定时器, CH3 和 CH4 都是不可以的。这以 CH2(通道 2)为例的,时钟源信号到达 CH2 后,那么这里这个时钟源信号用 TI2 表示
滤波器
如果来自外部的时钟信号的频率过高或者混杂有高频干扰信号的话,我们就需要使用滤波器对信号重新采样,来达到降频或者去除高频干扰的目的,也可以设置不使用滤波器
边沿检测
边沿检测的信号来自于滤波器的输出,在成为触发信号之前,需要进行边沿检测,决定是上升沿有效还是下降沿有效
触发输入选择器:触发选择
图中框出了 TI1F_ED、 TI1FP1 和 TI2FP2 三个触发输入信号(TRGI)。 TI1F_ED 表示来自于 CH1,并且没有经过边沿检测器过滤的信号,所以它是 CH1 的双边沿信号,即上升沿或者下降沿都是有效的。 TI1FP1 表示来自 CH1 并经过边沿检测器后的信号,可以是上升沿或者下降沿。 TI2FP2 表示来自 CH2 并经过边沿检测器后的信号,可以是上升沿或者下降沿。这里以CH2 为例,那只能选择 TI2FP2。如果是 CH1 为例,那就可以选择 TI1F_ED 或者 TI1FP1
从模式选择
选定了触发源信号后,最后需把信号连接到 TRGI 引脚,让触发信号成为外部时钟模式 1 的输入,最终等于 CK_PSC,然后驱动计数器 CNT 计数。由 ECE 位和 SMS[2:0]位来选择定时器的时钟源,外部时钟模式 1,所以 ECE 位置 0, SMS[2:0] = 111 即可。
使能计数器
最后只需使能计数器开始计数,外部时钟模式 1 的配置就算完成。使能计数器由 TIMx_CR1 的位 CEN 配置
外部时钟模式 2( ETR)
在外部时钟模式 2 下,定时器时钟信号首先从 ETR 引脚进来,信号只有 1 个;接着经过外部触发极性选择器,由 ETP 位来设置上升沿有效还是下降沿有效;然后经过外部触发预分频器,由 ETPS[1:0]位来设置预分频系数,频率不能超过 TIMx_CLK(72M)的 1/4;紧接着经过滤波器器,由 ETF[3:0]位来设置滤波方式,也可以设置不使用滤波器;最后经过从模式选择器,由 ECE 位和 SMS[2:0]位来选择定时器的时钟源,体的配置 TIMx_SMCR 的位 ECE 为 1 即可选择外部时
钟模式 2;最后只需使能计数器开始计数,外部时钟模式 2 的配置就完成。使能计数器由 TIMx_CR1 的位 CEN 配置
内部触发输入(ITRx)
内部触发输入是使用一个定时器作为另一个定时器的预分频器。在硬件上高级控制定时器和通用定时器在内部连接在一起,可以实现定时器同步或级联。主模式的定时器可以对从模式定时器执行复位、启动、停止或提供时钟
假如TIM1 作为 TIM2 的预分频器:
TIM1_CR2 寄存器的 MMS[2:0]位设置为 010,即 TIM1 的主模式选择为更新(选择更新
事件作为触发输出 (TRGO));
TIM2_SMCR 寄存器的 TS[2:0]位设置为 000,即使用 ITR1 作为内部触发,如下图
外部时钟模式 1 的时候 TI1F_ED、TI1FP1 和 TI2FP2,以及外部时钟模式 2 的ETRF,它们都是属于外部的,其余的都是内部触发了。TS[2:0]位设置为 000,使用 ITR0 作为内部触发,这个 ITR0 什么意思?看下图
当从模式定时器为 TIM2 时, ITR1 表示主模式定时器就是 TIM2,TIM1 和 TIM2 的 CEN 位都要置 1,即启动计数器
控制器
控制器包括:从模式控制器、编码器接口和触发控制器(TRGO)。从模式控制器可以控制
计数器复位、启动、递增/递减、计数。编码器接口针对编码器计数。触发控制器用来提供触发信号给别的外设,比如为其它定时器提供时钟或者为 DAC/ADC 的触发转换提供信号。
时基单元
预分频器 PSC
预分频器 PSC,有一个输入时钟 CK_PSC 和一个输出时钟 CK_CNT。输入时钟 CK_PSC 就是上面时钟源的输出,输出 CK_CNT 则用来驱动计数器 CNT 计数。通过设置预分频器 PSC 的值可以得到不同的 CK_CNT,PSC预分频的范围为0~65535
自动重载寄存器 ARR
自动重载寄存器 ARR 用来存放与计数器 CNT 比较的值,可以向上计数、向下计数或向上/向上双向计数。当计数值达到设定值时,会产生溢出事件,可以发出中断或DMA请求,然后再由自动装载寄存器进行重新加载或更新。通过 TIMx_CR1 寄存器的 ARPE 位控制自动重载影子寄存器功能,如果 ARPE 位置 1,自动重载影子寄存器有效,只有在事件更新时才把 TIMx_ARR 值赋给影子寄存器。如果 ARPE 位为 0,则修改 TIMx_ARR 值马上有效
计数器 CNT
高级控制定时器的计数器有三种计数模式,分别为递增计数模式、递减计数模式和递增/递减 (中心对齐) 计数模式,CNT计数器的范围为0~65535。
纵轴表示计数器的计数值,横轴表示时间, ARR 表示自动重载寄存器的值,小红点就是更新事件发生的时间点。例如:递增计数模式下,当计数值等于 ARR 时,计数器的值被复位为 0,定时器溢出,并伴随着更新事件的发生,后面继续递增计数
定时器的定时时间主要取决于预分频系数和定时周期,计算公式为:
当前系统时钟频率为72MHz, APB1二分频为36MHz, TIMxCLK则为72MHz。预分频系数任意取一值,假设为PSC=10000-1,自动装载器值假设为ARR=7200-1,则此时定时器定时为:
即,定时器每间隔1s,将产生一次溢出事件,产生中断
hal库代码
/*1毫秒后会触发中断,然后执行中断服务函数操作。*/
TIM_HandleTypeDef g_timer;
/*定时器中断初始化函数*/
void general_timer_init()
{
TIM_ClockConfigTypeDef tim_config = {0};
g_timer.Instance = TIM2;/*选择定时器*/
/*72MHz经过72分频后,定时器时钟为1MHz,即定时器计数1次的时间,刚好为1us;*/
g_timer.Init.Prescaler = 72-1;/*设置预分频系数*/
g_timer.Init.CounterMode = TIM_COUNTERMODE_UP;/*递增计数模式*/
g_timer.Init.Period = 0;/*自动重载值ARR,任意,后面代码再修改*/
g_timer.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1; /*时钟分频*/
g_timer.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE; /*不自动重载*/
HAL_TIM_Base_Init(&g_timer);
tim_config.ClockSource = TIM_CLOCKSOURCE_INTERNAL;/*用内部时钟作为定时器时钟源*/
HAL_TIM_ConfigClockSource(&g_timer , &tim_config);
HAL_TIM_Base_Start_IT(&g_timer);/*使能定时器中断*/
}
/*定时器MSP初始化函数*/
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
if(htim->Instance == TIM2)
{
__HAL_RCC_TIM2_CLK_ENABLE();/*使能时钟*/
HAL_NVIC_SetPriority(TIM2_IRQn, 1, 1);/*设置中断抢占和响应优先级*/
HAL_NVIC_EnableIRQ(TIM2_IRQn);/*使能中断*/
}
}
/*定时器实现的延时函数,延时时间为 t us , 最多能延时65535us*/
void timer_us_delay(uint16_t t)
{
uint16_t counter = 0;
__HAL_TIM_SET_AUTORELOAD(&g_timer, t);/*直接设置ARR寄存器的值*/
__HAL_TIM_SET_COUNTER(&g_timer, counter);/*直接设置CNT计数器的值,保证定时器从0开始计数;*/
HAL_TIM_Base_Start(&g_timer);/*启动定时器*/
while( counter != t)
{
counter = __HAL_TIM_GET_COUNTER(&g_timer);/*获取定时器当前计数*/
}/*直到定时器计数从 0 计数到 t 结束循环,刚好 t us*/
HAL_TIM_Base_Stop(&g_timer);/*停止定时器*/
}
/*定时器实现的延时函数,延时时间为 t ms, 最多65ms多点*/
void timer_ms_delay(uint16_t t)
{
int i = 0;
for(; i<t; i++)
timer_us_delay(1000);/*设置定时器的自动重载值(ARR)为1000*/
}
#if 1
/*中断服务函数:配置了使能中断,当计数器的值达到1000时,会触发中断*/
void TIM2_IRQHandler(void)
{
HAL_TIM_IRQHandler(&g_timer);
}
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if(htim ->Instance == TIM2)
{
/*中断触发*/
}
}
#endif
标准库代码
void tim_init(void)
{
/*开启时钟*/
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE);
/*使用内部时钟*/
TIM_InternalClockConfig(TIM2);
/*初始化定时器*/
/*计数器溢出频率= 72Mhz/(TIM_Prescaler+1)/(TIM_Period+1),这里定1秒 */
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;/*设置时钟分频因子为1,即没有分频*/
TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;/*向上计数*/
TIM_TimeBaseInitStruct.TIM_Period = 10000-1;/*自动重装值*/
TIM_TimeBaseInitStruct.TIM_Prescaler = 7200-1;/*预分频值*/
TIM_TimeBaseInitStruct.TIM_RepetitionCounter = 0;/*重复计数器,高级定时器才有,这里给0*/
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStruct);
/*清除更新中断标志位,避免系统初始化(或复位)后立刻更新进入中断*/
TIM_ClearFlag(TIM2,TIM_IT_Update);
/*使能中断*/
/*TIM_IT_Update表示更新中断,也就是定时器计数溢出时产生的中断*/
TIM_ITConfig(TIM2, TIM_IT_Update, ENABLE);
/*配置NVIC*/
/*分组*/
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);
NVIC_InitTypeDef NVIC_InitStruct;
NVIC_InitStruct.NVIC_IRQChannel = TIM2_IRQn;/*中断通道*/
NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;/* 使能中断 */
NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 2; /* 抢断优先级*/
NVIC_InitStruct.NVIC_IRQChannelSubPriority = 2; /* 子优先级 */
NVIC_Init(&NVIC_InitStruct);
/*开启Tim*/
TIM_Cmd(TIM2,ENABLE);
}
/*中断服务函数*/
void TIM2_IRQHandler(void)
{
/*检查中断标志*/
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET)
{
/*中断触发*/
/*清除标志位*/
TIM_ClearITPendingBit(TIM2,TIM_IT_Update);
}
}