STM32标准库定时器输入捕获
1.输入捕获介绍
输入捕获为STM32定时器的一个功能,可以用来测量输入信号的频率和占空比。
具体原理:当输入信号经过比较捕获通道时,STM32会依据通道的极性设置决定是否触发捕获中断TIM_IT_CCx。此时定时器会将当前计数值TIMx->CNT的值保存在TIMx->CCRx中,通过计算两次捕获中断的时间差便可计算出捕获的电平时长,由此可计算出输入信号的频率、周期、占空比等信息。
在本文中,使用野火指南者开发板,配置TIM2定时器的通道4为输入通道,TIM3定时器的通道1为输出通道。
2. 输入捕获通道与定时器初始化
需要引用头文件
#include "stm32f10x_gpio.h"
#include "stm32f10x_rcc.h"
#include "stm32f10x_tim.h"
void TIM2_Init() // 定时器2初始化
{
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2,ENABLE); // 使能定时器2的时钟
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE); // 使能GPIOA的时钟
GPIO_InitTypeDef GPIO_InitStructure; // 定义GPIO_InitTypeDef类型的结构体
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure; // 定义TIM_TimeBaseInitTypeDef类型的结构体
TIM_ICInitTypeDef TIM_IC_nitStructure; // 定义TIM_ICInitTypeDef类型的结构体
GPIO_InitStructure.GPIO_Pin = GPIO_Pin_3 ; // 选择通道4的引脚
GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING; // 设置通道4为浮空输入
GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; // 设置引脚速度为50MHz
GPIO_Init(GPIOA,&GPIO_InitStructure); // 初始化GPIOA
TIM_TimeBaseInitStructure.TIM_Period = 1000-1; // 设置定时器2的自动重装值,计数到1000-1
TIM_TimeBaseInitStructure.TIM_Prescaler = 720-1; // 设置定时器2的预分频值,分频720-1
TIM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // 设置时钟分割
TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 设置计数器模式为向上计数
TIM_TimeBaseInit(TIM2,&TIM_TimeBaseInitStructure); // 初始化定时器2
TIM_IC_nitStructure.TIM_Channel = TIM_Channel_4; // 选择通道4
TIM_IC_nitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising; // 设置通道4的上升沿触发
TIM_IC_nitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1; // 设置通道4的输入分频器
TIM_IC_nitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI; // 设置通道4映射到TI4
TIM_IC_nitStructure.TIM_ICFilter = 0x00; // 设置通道4的滤波器
TIM_ICInit(TIM2,&TIM_IC_nitStructure); // 初始化定时器2的通道4
NVIC_InitTypeDef NVIC_InitStructure; // 定义NVIC_InitTypeDef结构体变量
NVIC_InitStructure.NVIC_IRQChannel = TIM2_IRQn; // 选择定时器2的中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1; // 设置中断优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1; // 设置中断子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; // 使能中断通道
NVIC_Init(&NVIC_InitStructure); // 初始化NVIC_InitTypeDef结构体变量
TIM_ITConfig(TIM2,TIM_IT_CC4 | TIM_IT_Update ,ENABLE); // 使能定时器2的通道4的中断和更新中断
TIM_Cmd(TIM2,ENABLE); // 使能定时器2
}
需要注意输入通道引脚为GPIO_Mode_IN_FLOATING模式,TIM_Period为定时器溢出值。
-
TIM_ICInitTypeDef:输入捕获通道配置结构体。
-
TIM_Channel:输入通道,可选参数为TIM_Channel_x。
-
TIM_ICPolarity:输入通道极性设置,可选参数为TIM_ICSelection_DirectTI、TIM_ICSelection_IndirectTI、TIM_ICSelection_TRC。
-
TIM_ICSelection_DirectTI:将定时器输入通道1、2、3、4依次映射到IC1、IC2、IC3、IC4。
-
TIM_ICSelection_IndirectTI:将定时器输入通道1、2、3、4依次映射到IC2、IC1、IC4、IC3。
-
TIM_ICSelection_TRC:将定时器输入通道1、2、3、4连接至TRC
我暂时也不知道这个TRC是啥。
-
-
TIM_ICFilter:输入通道滤波器设置,可选参数为0x0~0xF。决定了多少次边沿变换会触发一次输入捕获。
-
3. 中断函数编写
输入捕获中断与定时器中断共用一个中断NVIC。
uint16_t Up_Capture_Cnt,Down_Capture_Cnt,Up_Capture,Up_Capture_Cnt_Temp,Down_Capture;
uint16_t timer_cnt2,timer_cnt1 = 0;
uint16_t Get_State = 0,Get_State1 = 0;
void TIM2_IRQHandler() // 定时器2中断函数
{
if(TIM_GetITStatus(TIM2,TIM_IT_Update) == SET) // 定时器2更新中断
{
timer_cnt1++; // 定时器计数标志量1每溢出一次加一
timer_cnt2++; // 定时器计数标志量2每溢出一次加一
if(timer_cnt1 == 10000) // 定时器计数标志量1溢出时清零
{
timer_cnt1 = 0; // 定时器计数标志量1清零
}
if(timer_cnt2 == 10000) // 定时器计数标志量2溢出时清零
{
timer_cnt2 = 0; // 定时器计数标志量2清零
}
}
if(TIM_GetITStatus(TIM2,TIM_IT_CC4) == SET) // 定时器2输入捕获中断
{
switch(Get_State) // 判断输入捕获状态
{
case 0 :
Up_Capture_Cnt_Temp = Up_Capture_Cnt; // 保存上一次输入捕获通道的值
Down_Capture_Cnt = TIM_GetCapture4(TIM2); // 获取当前输入捕获通道的值
Down_Capture = Down_Capture_Cnt + (timer_cnt2 * 1000) - Up_Capture_Cnt_Temp; // 计算脉冲宽度
timer_cnt1 = 0; // 定时器计数标志量1清零
timer_cnt2 = 0; // 定时器计数标志量2清零
TIM_ClearITPendingBit(TIM2,TIM_IT_CC4); // 清除输入捕获通道的中断标志位
TIM_OC4PolarityConfig(TIM2,TIM_ICPolarity_Falling); // 设置输入捕获通道的极性为下降沿
Get_State = 1; // 设置输入捕获通道的状态为1
break; // 跳出switch语句
case 1:
Up_Capture_Cnt = TIM_GetCapture4(TIM2); // 获取当前输入捕获通道的值
Up_Capture = Up_Capture_Cnt + (timer_cnt1 * 1000) - Down_Capture_Cnt; // 计算脉冲宽度
timer_cnt1 = 0; // 定时器计数标志量1清零
timer_cnt2 = 0; // 定时器计数标志量2清零
TIM_ClearITPendingBit(TIM2,TIM_IT_CC4); // 清除输入捕获通道的中断标志位
TIM_OC4PolarityConfig(TIM2,TIM_ICPolarity_Rising); // 设置输入捕获通道的极性为上升沿
Get_State = 0; // 设置输入捕获通道的状态为0
break; // 跳出switch语句
}
}
TIM_ClearITPendingBit(TIM2,TIM_IT_Update); // 清除定时器溢出中断标志位
}
4. 中断函数代码具体逻辑解释
光看代码可能捋不清先后关系,来看下图就知道了,如图1所示:
图1.采集输入捕获信号矢量图
在图中可以看到,当输入捕获通道的信号周期要长于输入捕获的通道时钟周期时,会导致第二次读取的值比第一次读取的值小,如果不使用定时器溢出次数进行辅助运算会导致算出来的是负数。之后第一次读取的值+溢出时间-第二次读取的值,得到的结果就是脉冲宽度,第二次读取的值+溢出时间-第一次读取的值,得到的就是周期中另一部分的宽度。有了这些信息,就可以得到频率、周期和占空比了。