STM32的HAL库开发—TIMER(定时器) —通用定时器-定时功能
一、通用定时器简介
1、通用定时器有TIM2~TIM5
2、主要特性:
二、通用定时器框图
通用定时器具备基本定时器的所有功能,从框图就可以看出来。
1、时钟源部分:通用定时器有4种时钟源。
2、控制器部分:
TRGO可以触发ADC、DAC还有其他定时器,具体连接其他定时器就是将这个TRGO信号连接到其他定时器的IRT0~IRT3上面的内部触发信号,称为定时器的级联。
控制部分还包括从模式控制器和编码器接口。
3、时基单元部分:与基本定时器一样,包括预分频器PSC和计数器CNT,还有自动重装载寄存器。
4、输入捕获部分:以通道1为例子
信号来自外部IO口,将IO口复用为定时器的通道,信号首先经过一个异或XOR门,这个异或门一般是编码器接口用的,基本用不到。然后来到TI1,这个TI1信号会来到输入滤波器和边缘检测器,由于外部的信号可能电平是不稳定的,可能有毛刺,通过这个滤波器可以将这些毛刺过滤掉,保存的就是完美的波形。边沿检测器是看是上升沿还是下降沿的。
经过输入滤波器和边沿检测器之后,得到两个信号,一个是TI1FP1。一个是TI1FP2,这两个信号是根据配置来的,看这个TI1配置映射到IC1还是IC2,也就是看输入信号是配置到输入捕获通道1还是输入捕获通道2。一般情况是通道1就映射到输入捕获通道1(IC1),特殊情况下可以将通道1映射到输入捕获通道2(IC2)。
IC1经过一个预分频器,如果预分频器为1,就是不分频,就会产生IC1PS信号,如果来一个上升沿,就会捕获一个上升沿,同时会产生捕获事件。产生捕获事件之后,会将计数器CNT的值转移到捕获/比较1寄存器。得到计数值,就可以得到输入捕获事件,一般用于测量脉冲时间。
除了产生捕获事件以外,还可以产生捕获中断。中断开启需要用户自行配置。
5、捕获/比较部分:这部分是输入捕获和输出比较的公共部分。
6、输出比较:程序员往第五部分的捕获/比较部分的捕获/比较寄存器写入比较值,当计数器的值与比较寄存器的值相同时,实际与比较寄存器的影子寄存器比较,也是具有缓冲的作用,产生更新事件时候值会转移到影子寄存器中。两个值相同的时候,会改变OC1REF信号,这个信号成为输出参考信号,高电平有效。还会产生比较事件。
OC1REF信号会来到输出控制,然后控制OC1信号,最终输出到外部通道,通过IO口输出到外面。
三、通用定时器时钟源
1、时钟源
- 内部时钟(CK_INT),来自外设总线APB提供的时钟,乘不乘1,就是看APB的分频系数,为1就不乘2,不为1,就乘2。
- 外部时钟模式1:外部输入引脚(TIx),来自定时器通道1或者通道2引脚的信号,其中TI1F_ED是双边沿检测,最终计数器是上升沿加1,下降沿也加1。而TI1FP1和TI2FP2只能计一个数,要不上升沿,要不下降沿。
- 外部时钟模式2:外部触发输入(ETR),来自可以复用为TIMXETR的IO引脚。
- 内部触发输入(ITRx),用于与芯片内部其它通用/高级定时器级联。
2、计数器时钟源寄存器设置方法
如果设置内部时钟除了设置SMS=000外,还需要保证ECE = 0,因为ECE为是单独设置外部时钟模式2的,如果ECE = 1,那么配置的是外部时钟模式2。
设置外部时钟模式2,除了设置ECE位 =1 ,还有一种方式就是"设置ECE位与选择外部时钟模式1并将TRGI连到ETRF(SMS=111和TS=111)具有相同功效"
3、外部时钟模式1
设置 TIMx_SMCR的SMS[2:0]设置为111,外部时钟模式1被选中。ECE位是控制外部时钟模式2的,要设置成0;设置成外部时钟模式1,则连接到TRGI上面。
再设置TS[2:0]位,外部时钟模式1有三种触发方式,TI1_ED是双边沿触发,只有通道1可以配置。
TI1FP1和TI2FP2可以配置成上升沿,也可配置成下降沿。
以通道2为例子,TI2先经过一个滤波器,通过设置TIMX_CCMR1的IC2F[3:0]位,配置输入滤波。然后经过边沿检测滤波器,通过设置TIMX_CCER的CC2P位,即可配置是上升沿还是下降沿。如果是通道1,就是设置CC1P位。
TIMX_CCMR1的IC2F[3:0]位:
设置成0000,表示无滤波器。则按fDTS频率采样,其中fDTS再TIMx_CR1寄存器里边设置,设置位9:8的CKD[1:0],如果这两位设置成00,则tDTS = tCK_INT,即fDTS = fCK_INT,这里的CK_INT是定时器时钟频率,为72MHz,不是CK_CNT的定时器工作频率。
设置成0011,采样频率为fCK_INT,N = 8意思为一个时钟周期内采样8次,如果8次内电平没有发生跳变,则就这采样电平。如果8次内电平发生跳变,则按第一次电平输出。
TIMX_CCER的CC2P位:设置成0表示不反向,上升沿发生捕获。设置成1反向,下降沿发生捕获。这里的反向由于TRGI在上升沿触发,如果是下降沿则需要反向。
4、外部时钟模式2
第一种方式就是直接将ECE位设置成1,就是设置成外部时钟模式2。第二种方式是将SMS[2:0]设置成111,就是外部时钟模式1,然后将TS[2:0]设置成111,也就是将TRGI连接到ETRF上面。
ETRF信号也是上升沿触发,信号最终是来自ETR引脚。首先经过一个边沿检测器,通过设置TIMX_SMCR寄存器的ETP位,设置上升沿还是下降沿有效。然后经过分频器,然后再经过滤波器,通过设置TIMX_SMCR的ETF[3:0]位,这里的滤波方法跟前面说的外部时钟模式1的滤波器一样。fDTS也是跟前面说的一样,都是在TIMx_CR1寄存器里边设置。
四、使用一个定时器作为另一个定时器的预分频器
将定时器1设置为主模式,就是设置TIMX_CR2寄存器的MMS = 010,主模式选择为更新。将定时器2设置成从模式,设置TS = 000,将定时器1连接到定时器2上边。同时SMS要设置成111,也就是前面讲的外部时钟模式1。同时设置CEN = 1,启动定时器。
MMS设置成010,就是在产生更新事件的时候,触发输出。
五、通用定时器中断实验
gtim.h头文件
#ifndef __GTIM_H
#define __GTIM_H
#include "stm32f1xx.h"
void GTIM_Init(uint16_t psc,uint16_t arr);
#endif
gtim.c源文件
#include "./BSP/GTIM/gtim.h"
#include "./BSP/LED/led.h"
TIM_HandleTypeDef htim;
void GTIM_Init(uint16_t psc,uint16_t arr)
{
htim.Instance = TIM2;
//设置ARR预装载寄存器是否有缓冲功能
htim.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE;
//设置ARR寄存器的值
htim.Init.Period = arr;
//设置预分频系数
htim.Init.Prescaler = psc;
//设置计数模式 这里设置成递增 递减都可以
htim.Init.CounterMode = TIM_COUNTERMODE_DOWN;
HAL_TIM_Base_Init(&htim);
//启动定时器及中断 这里只使用了计数器溢出更新事件中断
HAL_TIM_Base_Start_IT(&htim);
}
//重新定义MSP初始化函数 HAL_TIM_Base_Init这个函数里边自动调用
void HAL_TIM_Base_MspInit(TIM_HandleTypeDef *htim)
{
//由于这个函数是定时器初始化公用的 所以需要判断一下是定时器几。
if(htim ->Instance == TIM2)
{
//开启定时器2时钟 因为定时器2的内部时钟是来自APB1上边的 因为APB1的分频系数为2,所以乘2,就是72HZz
__HAL_RCC_TIM2_CLK_ENABLE();
//设置定时器2的中断优先级
HAL_NVIC_SetPriority(TIM2_IRQn, 2, 2);
//开启定时器2中断
HAL_NVIC_EnableIRQ(TIM2_IRQn);
}
}
//定时器2的中断服务函数
void TIM2_IRQHandler(void)
{
/* 方式1 :使用HAL库的公共中断处理和KAL库回调函数的方式*/
//这个函数是HAL库的定时器公共中断处理函数 里边主要是清空标志位和调用回调函数
//HAL_TIM_IRQHandler(&htim);
/* 方式2 :直接使用寄存器的方式*/
if(__HAL_TIM_GET_FLAG(&htim, TIM_FLAG_UPDATE) == SET )
{
LED0_TOGGLE();
__HAL_TIM_CLEAR_FLAG(&htim, TIM_FLAG_UPDATE);
}
}
//回调函数 由HAL_TIM_IRQHandler函数自动调用
void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
//由于这个函数是定时器初始化公用的 所以需要判断一下是定时器几。
if(htim ->Instance == TIM2)
{
LED0_TOGGLE();
}
}
这里在通用定时器的中断服务函数里,使用了两种方式,第一种是使用HAL库的中断公共处理函数,然后自动调用回调函数,中断标志位有中断公共处理函数里边清除。 第二种是直接通过寄存器的方式,直接判断事件更新中断标志位,然后清空这个标志位。
main.c主函数程序
#include "./SYSTEM/sys/sys.h"
#include "./SYSTEM/usart/usart.h"
#include "./SYSTEM/delay/delay.h"
#include "./BSP/LED/led.h"
#include "./BSP/GTIM/gtim.h"
int main(void)
{
HAL_Init(); /* 初始化HAL库 */
sys_stm32_clock_init(RCC_PLL_MUL9); /* 设置时钟, 72Mhz */
delay_init(72); /* 延时初始化 */
led_Init(); /* LED初始化 */
GTIM_Init(7199,4999);
while(1)
{
}
}
作者:猿~~~