一、简介
定时器分为基本定时器,通用计时器和高级计时器。如下图。
STM32F1系列除互联型产品,共8个定时器。
关于时钟源
时钟选择计数器时钟可由下列时钟源提供:
- 内部时钟(CK_INT)
- 外部时钟模式1:外部输入脚(TIx)
- 外部时钟模式2:外部触发输入(ETR)
- 内部触发输入(ITRx):使用一个定时器作为另一个定时器的预分频器
内部时钟
来自RCC的TIMx_CLK,等于72M。
当禁止从模式控制器(TIMx_SMCR寄存器的SMS=000),则预分频的时钟源CK_PSC由内部时钟源(CK_INT)驱动。
外部时钟模式1
来自定时器自身输入通道1或通道2的输入信号,经过极性选择和滤波以后生成的触发信号,连接到从模式控制器,进而控制计数器的工作。
外部时钟模式2
来自于外部触发脚[ETR脚]经过极性选择、分频、滤波以后的信号,经过触发输入选择器,连接到从模式控制器(分频和滤波非必需)。可以根据外来信号频率高低及信号干净度来决定。
内部触发输入(ITRx)
内部触发输入是使用一个定时器作为另一个定时器的预分频器。硬件上高级控制定时器和通用定时器在内部连接在一起,可以实现定时器同步或级联。主模式的定时器可以对从模式定时器执行复位、启动、停止或提供时钟。
二、定时器计算公式
- T:定时器计时周期
- CNT:定时器计数值
- PSC:定时器分频数
- Tick:晶振频率
三、基本定时器
- 位于APB1总线上
- 基本定时器时钟的时钟源只能来自内部时钟。
- 基本定时器没有TIM_ClockDivision功能。
- 基本定时器只能是向上计数,即TIMx_CNT只能从0开始递增,并且无需初始化。
四、通用定时器
位于APB1总线上。
通用定时器的时钟源可来自内部时钟,也可以选择外部时钟源或者直接来自其他定时器等待模式。
内部时钟
来源于APB1总线时钟,是根据APB1总线时钟是否分频来决定的:
- 如果APB1总线时钟预分频系数为1,则通用定时器的内部时钟就是APB1总线时钟;
- 如果APB1总线时钟的分频系数为2,则通用定时器的内部时钟就是APB1总线时钟的2倍。
其他时钟
- TIMx_ETR(外部时钟输入)
- ITRx(来自其余定时器用于定时器的级联)
- TIMx_CHx(外部通道)
16位向上、向下、向上/下计数模式,自动装载计数器(TIMx_CNT)
①向上计数模式:计数器从0计数到自动加载值(TIMx_ARR),然后重新开始从0开始计数并且产生一个计数器溢出事件。
②向下计数模式:计数器从自动加载值(TIMx_ARR)的值开始,然后计数到0并且产生一个计数器溢出事件。
③向上/向下计数模式(中央对齐模式):计数器从0开始计数到自动加载(TIMx_ARR)-1的值,产生一个计数器溢出事件,然后向下计数到1并且产生一个计数器溢出事件;然后再从0开始重新计数。
五、高级定时器
位于APB2总线上。
高级定时器的时钟源可来自内部时钟,也可以选择外部时钟源或者直接来自其他定时器等待模式。
- 1、时钟源
- 2、控制器
- 3、时基单元
- 4、输入捕获
- 5、输出比较
- 6、断路功能
输入通道和捕获通道的关系映射如下图。
输入捕获—测量脉宽或者频率原理:输入引脚检测到电平跳变时(上升沿或下降沿),把计数器CNT的值锁存到捕获寄存器CCRx中,把前后两次捕获到的CCRx寄存器中的值相减,就可以算出脉宽或者频率。
其中
- 测量频率时捕获边沿一直为上升沿
- 测量脉宽时捕获边沿为:上升沿—>下降沿—>上升沿(来回切换捕获边沿的极性)
六、代码段
//定时器基本初始化结构体
typedef struct
{
uint16_t TIM_Prescaler; //预分频器,设定TIMx_PSC 寄存器的值。设置范围为:0~65535
uint16_t TIM_CounterMode; //计数模式,可为向上计数、向下计数以及三种中心对齐模式
uint16_t TIM_Period; //定时器周期,设定自动重载寄存器的值。设置范围为:0~65535
uint16_t TIM_ClockDivision; //时钟分频,设置定时器时钟CK_INT频率与数字滤波器采样时钟频率分频比
uint8_t TIM_RepetitionCounter; //重复计算器
} TIM_TimeBaseInitTypeDef;
//配置定时中断的步骤
1、使能定时器时钟 RCC_APB1PeriphClockCmd();
2、初始化定时器,配置ARR,PSC TIM_TimeBaseInit();
3、开启定时器中断,配置NVIC void TIM_ITConfig();
NVIC_Init();
4、使能定时器 TIM_Cmd();
5、编写中断服务函数 TIMx_IRQHandler();
七、HAL定时器中断
CubeMX这里可以选择配置GPIO(灯),也可以生成代码后在MX_GPIO_Init()里面添加。
生成代码之后在main.c函数里添加计时的Tim_cnt 和回调函数HAL_TIM_PeriodElapsedCallback()
uint16_t Tim_cnt = 0;
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
if (htim->Instance == htim6.Instance)
{
Tim_cnt++;
if(Tim_cnt==500)
{
Tim_cnt=0;
HAL_GPIO_TogglePin(GPIOB, GPIO_PIN_5);
}
}
}
同时还有重要的一步:在主函数里面添加HAL_TIM_Base_Start_IT(&htim6)
来开始中断(与之对应:HAL_TIM_Base_Stop)。
while(1)里面无需添加其他东西。
相关函数
- 定时器中断处理函数:
HAL_TIM_IRQHandler(&htim6)
判断中断是否正常,是哪一类定时器中断(溢出中断/捕获中断/PWM中断…),然后进入相应的中断回调函数 - 定时器溢出中断回调函数:
HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
定时器中断时,每进行完一个中断,不会立刻退出,而是会进入到中断回调函数中
以下工程仅供参考:基于STM32VET6的TIM6定时实验
参考自:
1、STM32定时器功能概括
2、STM32F103定时器