一.高级定时器
高级定时器包含4个寄存器:
1)16位的自动重载寄存器ARR。
2)16位的计数器CNT,可向上/向下计数。
3)16位的可编程预分频器PSC,时钟源一般选择内部时钟CK_INT,即来自于芯片内部,等于72MHZ,一般情况下,我们都是使用内部时钟。
4)8位的重复计数器RCR(高级定时器独有,通用定时器没有)。
高级定时器在基本定时器的基础上引入了外部引脚,可以实现输入捕获和输出比较功能。
STM32F103ZET6的高级/通用定时器的通道、引脚分布:
二.时基单元
1.预分频器PSC
CK_PSC:输入时钟,CK_CNT:输出时钟,CK_CNT用于驱动计数器CNT计数。
PSC可以实现1~65536分频:
f(CK_CNT)=CK_PSC/(PSC+1)
2.计数器CNT
高级控制定时器的计数器有三种计数模式:递增计数模式、递减计数模式、中心对齐计数模式。
- 递增计数模式:计数器从0开始–>自动重载寄存器ARR的值;计数器生成上溢事件。若禁用重复计数器,马上生成更新事件;若使能重复计数器,每生成一次上溢事件,重复计数器内容减1,直到重复计数器内容为0,生成更新事件。
- 递减计数模式:计数器从自动重载寄存器ARR值开始->0;计数器生成下溢事件。若禁用重复计数器,马上生成更新事件;若使能重复计数器,计数器每生成一次下溢事件,重复计数器内容减1,直到重复计数器内容为0,生成更新事件。
- 中心对齐模式:计数器从0开始->ARR-1,生成计数器上溢事件;然后从ARR值开始->1,生成计数器下溢事件。每次发生计数器上溢和下溢事件都会产生更新事件。
3.自动重载寄存器ARR
自动重载寄存器 ARR 用来存放与计数器 CNT 比较的值,如果发生上溢或者下溢事件,则递减重复计数器的值。
4.重复计数器RCR
- 在基本/通用定时器发生上/下溢事件时直接生成更新事件(无重复计数器RCR功能);
- 高级定时器有重复计数器,在定时器发生上/下溢事件时,递减重复计数器的值;只有当重复计数器为0时才会生成更新事件。发生N+1个上溢或下溢事件(N为RCR的值)时 ,产生更新事件。
三.输入捕获原理
输入捕获:对输入信号的上升沿、下降沿或者双边沿进行捕获。
当捕获到输入信号的跳变沿,把计数器CNT的值锁存到捕获寄存器CCR中,将前后两次捕获得到的CCR寄存器的值相减,得到脉宽。
特别注意:脉宽的时长超过定时器的周期时,需要记录定时器溢出的所有时间。
1.输入通道:
需要被测量的信号从定时器的外部引脚 TIMx_CH1/2/3/4 进入,通常叫 TI1/2/3/4。
2.输入滤波器和边沿检测器
- 当输入的信号存在高频干扰的时候,我们需要对输入信号进行滤波,即进行重新采样。
- 边沿检测器用来设置信号在捕获的时候是什么边沿有效,可以是上升沿,下降沿,或者是双边沿。
3.捕获通道
捕获通道就是图中的 IC1/2/3/4,每个捕获通道都有相对应的捕获寄存器 CCR1/2/3/4,当发生捕获的时候,计数器 CNT 的值就会被锁存到捕获寄存器中。
4.预分频器
ICx 的输出信号会经过一个预分频器,用于决定发生多少个事件时进行一次捕获。如果希望捕获信号的每一个边沿,则不分频。
5.捕获寄存器
当发生捕获时(第一次),计数器 CNT 的值会被锁存到捕获寄存器 CCR 中,还会产生 CCxI 中断,相应的中断位 CCxIF(在 SR 寄存器中)会被置位。
四.输入捕获应用-测量脉宽或者频率
1.测量脉宽:
1)捕获通道ICx出现上升沿,发生第一次捕获,计数器CNT的值锁存到捕获寄存器CCR中,并进入捕获中断。
2)中断服务程序中记录一次捕获,并把捕获寄存器的值保存到value1中,设置捕获边沿为下降沿捕获(捕获后面的下降沿)。
3)下降沿到来,发生第二次捕获,计数器CNT的值锁存到捕获寄存器CCR中,并进入捕获中断。
4)中断服务程序中,把捕获寄存器的值保存到value2中。把捕获边沿设置为上升沿捕获。
利用 value2和 value1 的差值可以算出信号的脉宽。
注:如果测量的脉宽时间比较长(超过定时周期),定时器就会发生溢出,溢出的时候会产生更新中断,我们可以在中断里面记录定时器溢出的所有时间。
2.测量频率:
1)捕获通道ICx出现上升沿,发生第一次捕获,计数器CNT的值锁存到捕获寄存器CCR中,并进入捕获中断。
2)中断服务程序中记录一次捕获,并把捕获寄存器的值保存到value1中。
3)当出现第二次上升沿时,发生第二次捕获,计数器 CNT的值会再次被锁存到捕获寄存器 CCR 中,并再次进入捕获中断。
4)在捕获中断中,把捕获寄存器的值读取到 value3 中,并清除捕获记录标志。
利用 value3 和 value1 的差值可以算出信号的周期(频率)。
五.定时器初始化结构体
1.输入捕获结构体
typedef struct {
uint16_t TIM_Channel; // 输入通道选择
uint16_t TIM_ICPolarity; // 输入捕获触发选择
uint16_t TIM_ICSelection; // 输入捕获选择
uint16_t TIM_ICPrescaler; // 输入捕获预分频器
uint16_t TIM_ICFilter; // 输入捕获滤波器
} TIM_ICInitTypeDef;
2.TIM_Channel
捕获通道 ICx 选择,可选 TIM_Channel_1、TIM_Channel_2、TIM_Channel_3 或
TIM_Channel_4 四个通道。
3.TIM_ICPolarity
输入捕获边沿触发选择,可选上升沿触发、下降沿触发或边沿跳变触发.
4.TIM_ICSelection
输入通道选择,捕获通道 ICx 的信号可来自三个输入通道,分别为
TIM_ICSelection_DirectTI、TIM_ICSelection_IndirectTI 或 TIM_ICSelection_TRC
5.TIM_ICPrescaler
输入捕获通道预分频器,如果需要捕获输入信号的每个有效边沿,则设置 1 分频即可。
6.TIM_ICFilter
输入捕获滤波器设置,可选设置 0x0 至 0x0F。一般不使用滤波器,即设置为0。
六.脉宽测量输入捕获
1.源代码:
- 主函数main.c:
#include "bsp_usartlx.h"
#include "bsp_generaltimer.h"
int main(void)
{
//分频因子为72-1 计数器时钟
uint32_t CNT_CLOCK = 72000000/((72-1)+1);
//TIM5 初始化
INIT_NVIC_CONFIG();
INIT_GPIO_KEY_CONFIG();
INIT_GENERAL_TIMER_CONFIG();
//串口初始化
INIT_GPIO_CONFIG();
INIT_USART_CONFIG();
printf("\r\nTIM5 通用定时器捕获实验\r\n");
printf("\r\n捕获K1按键按下时高电平脉宽时间\r\n");
while(1)
{
if(CAPTURE_STRUCTURE.FLAG_FINISH == 1)
{
//获取总计数 重载寄存器的值为0xFFFF
uint32_t time_count = CAPTURE_STRUCTURE.CAPTURE_COUNT * 0xFFFF + CAPTURE_STRUCTURE.CAPTURE_VALUE;
uint32_t time_value1 = time_count/CNT_CLOCK;
uint32_t time_value2 = time_count%CNT_CLOCK;
printf("\r\n高电平脉宽时间:%d.%d s\r\n",time_value1,time_value2);
CAPTURE_STRUCTURE.FLAG_FINISH = 0;
}
}
}
- 中断函数:
void TIM5_IRQHandler(void)
{
if(TIM_GetITStatus(TIM5,TIM_IT_Update) == SET)
{
//定时器溢出中断
TIM_ClearITPendingBit(TIM5,TIM_IT_Update);
CAPTURE_STRUCTURE.CAPTURE_COUNT++;
}
if(TIM_GetITStatus(TIM5,TIM_IT_CC1) == SET)
{
//捕获中断
if(CAPTURE_STRUCTURE.FLAG_START == 0)
{
CAPTURE_STRUCTURE.CAPTURE_COUNT = 0;
CAPTURE_STRUCTURE.CAPTURE_VALUE = 0;
CAPTURE_STRUCTURE.FLAG_START = 1;
CAPTURE_STRUCTURE.FLAG_FINISH = 0;
//设置计数寄存器的值为0
TIM_SetCounter(TIM5,0);
//设置下降沿捕获
TIM_OC1PolarityConfig(TIM5,TIM_OCPolarity_Low);
}
else
{
CAPTURE_STRUCTURE.FLAG_START = 0;
CAPTURE_STRUCTURE.FLAG_FINISH = 1;
CAPTURE_STRUCTURE.CAPTURE_VALUE = TIM_GetCapture1(TIM1);//获取捕获寄存器的值
//设置上升沿捕获
TIM_OC1PolarityConfig(TIM5,TIM_OCPolarity_High);
}
TIM_ClearITPendingBit(TIM5,TIM_IT_CC1);
}
}
- 定时器和中断设置
#include "bsp_generaltimer.h"
struct CAPTURE CAPTURE_STRUCTURE = {0,0,0,0};
void INIT_NVIC_CONFIG()
{
NVIC_InitTypeDef NVIC_InitStructure;
//设置中断组
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_1);
//设置中断源
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;
//设置主优先级
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;
//设置抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;
NVIC_Init(&NVIC_InitStructure);
}
void INIT_GPIO_KEY_CONFIG()
{
GPIO_InitTypeDef GPIO_InitStructure;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA,&GPIO_InitStructure);
}
void INIT_GENERAL_TIMER_CONFIG()
{
//时基结构体初始化
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;
//开启定时器时钟,即内部时钟为72MHZ
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
TIM_TimeBaseInitStructure.TIM_Prescaler = 72 -1;//分频因子
TIM_TimeBaseInitStructure.TIM_Period = 0xFFFF;//16位自动重载寄存器的值 累计TIM_Period+1个频率 产生一个中断
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up;//向上计数
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure);
//输入捕获结构体初始化
TIM_ICInitTypeDef TIM_ICInitStructure;
TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;//通道1
TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;//设置捕获极性,即上升沿捕获
TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;//输入通道和捕获通道的映射关系-直连
TIM_ICInit(TIM5,&TIM_ICInitStructure);
//清除定时器更新和捕获状态标志位
TIM_ClearFlag(TIM5,TIM_FLAG_CC1|TIM_FLAG_Update);
//开启更新和捕获中断
TIM_ITConfig(TIM5,TIM_IT_CC1|TIM_IT_Update,ENABLE);
//使能定时器
TIM_Cmd(TIM5,ENABLE);
}
2.实验现象
通过串口调试助手,显示按键按下时高电平的脉宽时间。