输入捕获简介:
STM32F4除了基本定时器TIM6和TIM7,其他定时器都具有输入捕获功能。输入捕获可以对输入的信号的上升沿,下降沿或者双边沿进行捕获,通常用于测量输入信号的脉宽、测量 PWM 输入信号的频率及占空比。输入捕获的工作原理比较简单,在输入捕获模式下,当相应的 ICx 信号检测到跳变沿后,将使用捕获/比较寄存器(TIMx_CCRx)来锁存计数器的值。简单的说就是通过检测TIMx_CHx上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的通道的捕获/比较寄存(TIMx_CCRx)里面,完成一次捕获。同时还可以配置捕获时是否触发中断/DMA 等。
最开始设置的捕获极性如果是上升沿的话,当捕获到上升沿的时候将计数器cnt置0,计数器开始计数,再设置捕获为下降沿捕获,当捕获到下降沿的时候将cnt的值给CCRx2中,在整个高电平的过程中cnt会产生溢出,溢出后又从0开始计数,所以需要几率溢出次数。
CNT计数的次数等于:N*ARR+CCRx2,有了这个计数次数,再乘以 CNT 的计数周期,即可得到 t2-t1 的时间长度,即高电平持续时间。
输入捕获配置步骤:
(1)使能定时器及端口时钟,并设置引脚复用器映射和引脚模式等
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);
GPIO_PinAFConfig(GPIOA,GPIO_PinSource0,GPIO_AF_TIM5);
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF; //复用输出模式
(2)初始化定时器参数,包含自动重装值,分频系数,计数方式等
voidTIM_TimeBaseInit(TIM_TypeDef*TIMx,TIM_TimeBaseInitTypeDef* TIM_TimeBaseInitStruct);
(3)设置通用定时器的输入捕获参数,开启输入捕获功能
void TIM_ICInit(TIM_TypeDef* TIMx, TIM_ICInitTypeDef*
TIM_ICInitStruct);
typedef struct
{
uint16_t TIM_Channel; //通道
uint16_t TIM_ICPolarity; //捕获极性
uint16_t TIM_ICSelection;//映射
uint16_t TIM_ICPrescaler;//分频系数
uint16_t TIM_ICFilter; //滤波器长度
} TIM_ICInitTypeDef;
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);
(4)开启捕获和定时器溢出(更新)中断
void TIM_ITConfig(TIM_TypeDef* TIMx, uint16_t TIM_IT, FunctionalState NewState);
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);
(5)设置定时器中断优先级,使能定时器中断通道
NVIC初始化库函数是NVIC_Init()
(6)编写定时器中断服务函数
TIM5_IRQHandler
ITStatus TIM_GetITStatus(TIM_TypeDef* TIMx, uint16_t TIM_IT);
void TIM_ClearITPendingBit(TIM_TypeDef* TIMx, uint16_t TIM_IT);
TIM_SetCounter(TIM5,0); //定时器初值为0
(7)使能定时器
void TIM_Cmd(TIM_TypeDef* TIMx, FunctionalState NewState);
使用TIM5的CH1检测输入信号高电平脉宽,将检测的高电平脉宽时间通过printf函数打印出来,同时让D1指示灯不
断闪烁表示系统正常运行。由于TIM5的CH1与KEY_UP共用一个管脚,所以可以使用KEY_UP来产生输入信号。
input.c
#include "input.h"
u8 TIM5_CH1_CAPTURE_STA=0; //输入捕获的状态 在源文件中声明过的全局变量
u32 TIM5_CH1_CAPTURE_VAL=0;//输入捕获值(TIM2/TIM5是32位)
void TIM5_CH1_Input_Init(u32 arr,u16 psc)
{
TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;//定时器参数结构体
TIM_ICInitTypeDef TIM_ICInitStructure;//输入捕获结构体
NVIC_InitTypeDef NVIC_InitStructure;//NVIC初始化结构体
GPIO_InitTypeDef GPIO_InitStructure;//端口结构体
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA,ENABLE);//使能端口A时钟
RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);//使能TIM5时钟
GPIO_PinAFConfig(GPIOA,GPIO_PinSource0,GPIO_AF_TIM5);//管脚复用,将PA0映射到TIM5上
//初始化PA0端口
GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;//管脚设置
GPIO_InitStructure.GPIO_Mode=GPIO_Mode_AF; //复用输出模式
GPIO_InitStructure.GPIO_Speed=GPIO_Speed_100MHz;//速度为100M
GPIO_InitStructure.GPIO_OType=GPIO_OType_PP;//推挽输出
GPIO_InitStructure.GPIO_PuPd=GPIO_PuPd_DOWN;//下拉
GPIO_Init(GPIOA,&GPIO_InitStructure); //初始化结构体
//初始化定时器参数
TIM_TimeBaseInitStructure.TIM_Period=arr; //自动装载值,定时器5是32位的注意形参大小
TIM_TimeBaseInitStructure.TIM_Prescaler=psc; //分频系数
TIM_TimeBaseInitStructure.TIM_ClockDivision=TIM_CKD_DIV1; //时钟分割,时钟分频 通常不需要修改
TIM_TimeBaseInitStructure.TIM_CounterMode=TIM_CounterMode_Up; //设置向上计数模式
TIM_TimeBaseInit(TIM5,&TIM_TimeBaseInitStructure); //初始化定时器5
//设置通用定时器的输入捕获参数,开启输入捕获功能
TIM_ICInitStructure.TIM_Channel=TIM_Channel_1; PA0是定时器5通道1
TIM_ICInitStructure.TIM_ICFilter=0x00; //不使用,直接设置成0
TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//设置为上升沿捕获
TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1; //不分频,设置成1分频
TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;//直接映射到TI1
TIM_ICInit(TIM5,&TIM_ICInitStructure);//初始化输入捕获结构体
//配置溢出中断
TIM_ITConfig(TIM5,TIM_IT_Update|TIM_IT_CC1,ENABLE);//开启更新和通道1捕获中断
//配置中断通道
NVIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;//中断通道
NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;//抢占优先级
NVIC_InitStructure.NVIC_IRQChannelSubPriority =0; //子优先级
NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE; //IRQ通道使能
NVIC_Init(&NVIC_InitStructure);//根据指定的参数初始化VIC寄存器
TIM_Cmd(TIM5,ENABLE); //使能定时器
}
//中断服务函数
void TIM5_IRQHandler(void)
{
if((TIM5_CH1_CAPTURE_STA&0x80)==0) //最高位代表上升沿捕获完成,但是最开始肯定没有上升沿,所以为0
{
if(TIM_GetITStatus(TIM5,TIM_IT_Update)) //读取更新中断状态标志,读取溢出次数
{
if(TIM5_CH1_CAPTURE_STA&0X40)//捕获到了高电平
{
if((TIM5_CH1_CAPTURE_STA&0x3f)==0x3f) //高电平时间太长,说明后6位全部为1,累计到最大值
{
TIM5_CH1_CAPTURE_STA|=0x80; //标志一次捕获成功,时间过长强制让其捕获完成
TIM5_CH1_CAPTURE_VAL=0xffffffff;
}
else
{
TIM5_CH1_CAPTURE_STA++;//没有累计到最大值,让其累加1
}
}
}
if(TIM_GetITStatus(TIM5,TIM_IT_CC1)) //发生捕获中断
{
if(TIM5_CH1_CAPTURE_STA&0X40)//捕获到了低电平,//次高位为表示已经捕获
{
TIM5_CH1_CAPTURE_STA|=0x80; //成功捕获一次高电平
TIM5_CH1_CAPTURE_VAL=TIM_GetCapture1(TIM5); //读取通道1ccrx的值
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising); //设置上升沿捕获,//最开始肯定没有被捕获TIM5_CH1_CAPTURE_STA次高位肯定为0,进入else,进入上升沿捕获
}
else
{
TIM5_CH1_CAPTURE_STA=0;//置0防止干扰
TIM5_CH1_CAPTURE_VAL=0;
TIM5_CH1_CAPTURE_STA|=0x40; //捕获到高电平 标志 //设置次高位为1,让其为已经捕获的状态
TIM_Cmd(TIM5,DISABLE); //关闭定时器
TIM_SetCounter(TIM5,0); //定时器初值置0
TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling); //设置下降沿捕获
TIM_Cmd(TIM5,ENABLE); //开启定时器
}
}
}
TIM_ClearITPendingBit(TIM5,TIM_IT_CC1|TIM_IT_Update);//清空中断标志位
}
input.h
#ifndef __input_H
#define __input_H
#include "system.h"
extern u8 TIM5_CH1_CAPTURE_STA;//保存输入捕获状态的标志,最高是否捕获成功,次高位是否捕获到上升沿,低六位累计溢出次数
extern u32 TIM5_CH1_CAPTURE_VAL; //保存输入捕获的值CCRx
void TIM5_CH1_Input_Init(u32 arr,u16 psc);
void TIM5IRQHandler(void);
#endif
main
#include "system.h"
#include "SysTick.h"
#include "led.h"
#include "usart.h"
#include "input.h"
int main()
{
u8 i=0;
long long indata=0;
SysTick_Init(168);
NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //中断优先级分组 分2组
LED_Init();
USART1_Init(9600);
TIM5_CH1_Input_Init(0xffffffff,83); //以1M频率计数
while(1)
{
if(TIM5_CH1_CAPTURE_STA&0x80) //成功捕获
{
indata=TIM5_CH1_CAPTURE_STA&0x3f;
indata*=0xffffffff; //溢出次数乘以一次的计数次数时间 us
indata+=TIM5_CH1_CAPTURE_VAL;//加上高电平捕获的时间
printf("高电平持续时间:%lld us\r\n",indata); //总的高电平时间
TIM5_CH1_CAPTURE_STA=0; //开始下一次捕获
}
i++;
if(i%20==0)
{
led1=!led1;
}
myDelay_ms(10);
}
}