今天来简单介绍一下STM32的输入捕获功能。输入捕获说的通俗一点就是用计数器(定时器)来记录某一个脉冲高电平的时间,或者我们只捕获脉冲的上升沿或者下降沿,这具体要根据具体事例进行分析。
它的基本工作过程就是先捕捉一次脉冲上升沿,然后计数器开始计时,等待着捕捉到脉冲下降沿,等到捕捉到下降沿的时候,计数器停止计数,计算计数器中的数值,这个数值就是高电平所持续的时间,然后再重现开始下一轮的捕捉。
今天我们就用输入捕获来实现计算某一个脉冲的高电平持续时间。
我们所用到的是STM32F103ZET6的定时器5的通道一,然后是通过按键按下去来产生一个一定宽度的矩形脉冲,其中按键是接在PA0引脚上,并且按键按下去,引脚会被拉为高电平,以此我们通过输入捕获来计算按键按下去的时间。
首先用到定时器,还是按照老套路进行相关的配置,代码如下

void time_cap_init(u16 arr,u16 psc)
{//定义结构体TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_ICInitTypeDef TIM_ICInitStructure;NVIC_InitTypeDef NVIC_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;//使能时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//配置TIM5TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;TIM_TimeBaseStructure.TIM_Period=arr;TIM_TimeBaseStructure.TIM_Prescaler=psc;TIM_TimeBaseInit(TIM5,&TIM_TimeBaseStructure);//配置TIM5 Channel1输入捕获TIM_ICInitStructure.TIM_Channel=TIM_Channel_1;TIM_ICInitStructure.TIM_ICFilter=0x00;TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//上升沿捕获TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;TIM_ICInit(TIM5,&TIM_ICInitStructure);//配置GPIO口GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;//下拉输入模式GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;GPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_ResetBits(GPIOA,GPIO_Pin_0);//配置中断分组NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitStructure.NVIC_IRQChannel=TIM5_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;NVIC_Init(&NVIC_InitStructure);TIM_Cmd(TIM5,ENABLE);//使能TIM5TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE);//允许溢出中断TIM_ITConfig(TIM5,TIM_IT_CC1,ENABLE);//允许捕获中断
}

以上就是我们对定时器和GPIO进行的相关初始化,里面很多和之前博客中介绍的配置方法基本一样,我就不再多说,我们只来看一下配置定时器输入捕获功能的相关代码,如下

    //定义结构体TIM_ICInitTypeDef TIM_ICInitStructure;//配置TIM5 Channel1输入捕获TIM_ICInitStructure.TIM_Channel=TIM_Channel_1;TIM_ICInitStructure.TIM_ICFilter=0x00;TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//上升沿捕获TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;TIM_ICInit(TIM5,&TIM_ICInitStructure);

这段代码就是配置定时器的输入捕获功能的代码,它也是用一个结构体来实现配置的。首先做的就是先定义一个结构体,然后结构体的第一个变量就是配置定时器的几通道来进行捕获,这里我们选用定时器5的通道1;第二个成员变量配置的是关于捕获滤波的,关于滤波,他其实就像我们对按键进行消抖一样,这里我不做过多说明,大家可以查阅百度,我们这里选择不进行滤波,所以第二个成员配置成为0x00;第三个成员配置的是捕获的极性,也就是我们要让单片机捕获上升沿还是下降沿,因为我们今天要测的是高电平的时间,所以初始化的时候,我们要设置成上升沿捕获;第四个成员配置的是我们检测了几个上升沿(或者下降沿)才产生一次中断,这里我们要让他每次检测到上升沿(或者下降沿)就产生中断,所以参数配置成TIM_ICPSC_DIV1,这个名字在32的库里面已经定义好了,它代表的就是0x00,如下

#define TIM_ICPSC_DIV1                     ((uint16_t)0x0000) /*!< Capture performed each time an edge is detected on the capture input. */
#define TIM_ICPSC_DIV2                     ((uint16_t)0x0004) /*!< Capture performed once every 2 events. */
#define TIM_ICPSC_DIV4                     ((uint16_t)0x0008) /*!< Capture performed once every 4 events. */
#define TIM_ICPSC_DIV8                     ((uint16_t)0x000C) /*!< Capture performed once every 8 events. */
#define IS_TIM_IC_PRESCALER(PRESCALER) (((PRESCALER) == TIM_ICPSC_DIV1) || \((PRESCALER) == TIM_ICPSC_DIV2) || \((PRESCALER) == TIM_ICPSC_DIV4) || \((PRESCALER) == TIM_ICPSC_DIV8))

然后第五个成员变量我就不过多说了,因为我也说不太清楚。
然后就是调用函数TIM_ICInit(TIM5,&TIM_ICInitStructure);把各个成员变量导入到相应的结构体中,从而控制相应的寄存器。
以上是关于几个模块的初始化程序,下面介绍今天的重点,那就是定时器5的中断服务程序,如下

void TIM5_IRQHandler(void)//中断处理函数  (尤其要注意中断服务函数名字的写法)
{if((time5_capture_sta&0x80)==0)//标志位最高位为0,说明还没有成功捕获到高电平,此时捕获中断和溢出中断可被响应{if(TIM_GetITStatus(TIM5,TIM_IT_CC1)!=RESET)//发生捕获中断{if((time5_capture_sta&0x40)==0)//标志位的次高位为0,说明还没有捕获到一次上升沿,所以这此发生的捕获中断,是捕捉到了一次上升沿{time5_capture_sta=0;time5_capture_val=0;TIM_SetCounter(TIM5,0);//设置计数器中的值,在这里将计数器的值设为0time5_capture_sta|=0x40;//把标志位的次高位置1,标记为已经捕获到了一次上升沿,这里必须要用或运算,因为time5_capture_sta这个变量的第六位保存着计数器溢出的次数,我们在改变这个变量的最高位和次高位的时候,不能改动后面的第六位数据TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);//设置为下降沿捕获}else if(time5_capture_sta&0x40)//标志位次高位为1,说明上次已经捕获到了一次上升沿,那么这次发生的捕获中断就是捕获到的下降沿{time5_capture_sta|=0x80;//把标志位的最高位置1,标记已经成功捕获到了一次高电平time5_capture_val=TIM_GetCapture1(TIM5);//获取当前计数器中的数值TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);//设置为上升沿捕获,为下一次捕获做准备}}if(TIM_GetITStatus(TIM5,TIM_IT_Update)!=RESET)//发生了溢出中断{if(time5_capture_sta&0x40)//判断是否在溢出之前已经捕获到了高电平,其实是肯定已经捕捉到了一次高电平{if((time5_capture_sta&0x3f)==0x3f)//高电平时间太长了,已经溢出了2的6次方-1=63次{time5_capture_sta|=0x80;//把标志位的最高位置1,标记已经成功捕获到了一次高电平,此时为强制结束高电平检测time5_capture_val=0xffff;//强制结束后,将计数器的值设为最大,其实程序运行到这里,它已经是最大了}else{time5_capture_sta++;//如果溢出次数没有超出标志位的低六位所能表示的范围,那么就让标志位直接加1,以此来记录溢出的次数,溢出次数一直加下去,啥时候超出了标志位的低六位所能表示的范围,那么就强制退出检测,直接认为此次检测高电平已经结束,然后直接开启下一次检测}}}}//清除中断标志位TIM_ClearITPendingBit(TIM5,TIM_IT_CC1);TIM_ClearITPendingBit(TIM5,TIM_IT_Update);
}

这个程序也比较多,细说也比较麻烦,我已经基本上把每一句程序都给写了注释,我就大概讲一下整体的思路。
这个中断中我们使用到了两个变量:time5_capture_sta和time5_capture_val,这两个变量都是我们自己定义的,来帮助我们进行输入捕获功能的实现。其中time5_capture_val就是用来记录当前计数器中的数值,这个比较好理解,主要就是另一个变量,time5_capture_sta,我们来看一下它的各数据位所代表的的含义

从图中我们可以看到,time5_capture_sta的最高位是捕获完成的标志位,如果我们目前已经完成了一次高电平的捕获(即捕捉到了一次上升沿和一次下降沿),那么该位置1,否则置0;time5_capture_sta的次高位是捕获到高电平标志位,如果我们现在已经捕获到了一次上升沿,那么就把该位置1,然后再把捕获的极性改为下降沿,然后就等待着下一个中断的发生(每捕捉到一次边沿就发生一次中断),我们就这样一直依靠这两个标志位来进行上升沿和下降沿的捕获。然后还有一个问题就是,如果某一个脉冲高电平的时间太长了,已经超出了我们定时器(计数器)所能记录的范围,那么计数器就会产生溢出,而这个溢出的次数我们要记录下来,我们一会计算高电平时间要用到这个溢出次数,所以我们就用time5_capture_sta的后六位来进行记录计数器溢出的次数,如果高电平持续时间仍然提高,导致溢出的次数也超过了time5_capture_sta后六位所能表示的数值范围,那么这个时候,就让程序强制结束此次高电平的捕获,即认为这个脉冲的高电平时间是计数器的最大值。
这个中断服务函数里面包含了两个中断,一个是捕获中断,一个是溢出中断,在进入到这个中断服务函数里面,我们首先进行判断是发生了哪种中断,然后再进行相应的操作。
以下是整个time.c文件的程序

#include "time.h"
u8 time5_capture_sta=0;
u16 time5_capture_val;void time_cap_init(u16 arr,u16 psc)
{//定义结构体TIM_TimeBaseInitTypeDef TIM_TimeBaseStructure;TIM_ICInitTypeDef TIM_ICInitStructure;NVIC_InitTypeDef NVIC_InitStructure;GPIO_InitTypeDef GPIO_InitStructure;//使能时钟RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5,ENABLE);RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);//配置TIM5TIM_TimeBaseStructure.TIM_ClockDivision=TIM_CKD_DIV1;TIM_TimeBaseStructure.TIM_CounterMode=TIM_CounterMode_Up;TIM_TimeBaseStructure.TIM_Period=arr;TIM_TimeBaseStructure.TIM_Prescaler=psc;TIM_TimeBaseInit(TIM5,&TIM_TimeBaseStructure);//配置TIM5 Channel1输入捕获TIM_ICInitStructure.TIM_Channel=TIM_Channel_1;TIM_ICInitStructure.TIM_ICFilter=0x00;TIM_ICInitStructure.TIM_ICPolarity=TIM_ICPolarity_Rising;//上升沿捕获TIM_ICInitStructure.TIM_ICPrescaler=TIM_ICPSC_DIV1;TIM_ICInitStructure.TIM_ICSelection=TIM_ICSelection_DirectTI;TIM_ICInit(TIM5,&TIM_ICInitStructure);//配置GPIO口GPIO_InitStructure.GPIO_Mode=GPIO_Mode_IPD;//下拉输入模式GPIO_InitStructure.GPIO_Pin=GPIO_Pin_0;GPIO_Init(GPIOA,&GPIO_InitStructure);GPIO_ResetBits(GPIOA,GPIO_Pin_0);//配置中断分组NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);NVIC_InitStructure.NVIC_IRQChannel=TIM5_IRQn;NVIC_InitStructure.NVIC_IRQChannelCmd=ENABLE;NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority=2;NVIC_InitStructure.NVIC_IRQChannelSubPriority=0;NVIC_Init(&NVIC_InitStructure);TIM_Cmd(TIM5,ENABLE);//使能TIM5TIM_ITConfig(TIM5,TIM_IT_Update,ENABLE);//允许溢出中断TIM_ITConfig(TIM5,TIM_IT_CC1,ENABLE);//允许捕获中断
}void TIM5_IRQHandler(void)//中断处理函数  (尤其要注意中断服务函数名字的写法)
{if((time5_capture_sta&0x80)==0)//标志位最高位为0,说明还没有成功捕获到高电平,此时捕获中断和溢出中断可被响应{if(TIM_GetITStatus(TIM5,TIM_IT_CC1)!=RESET)//发生捕获中断{if((time5_capture_sta&0x40)==0)//标志位的次高位为0,说明还没有捕获到一次上升沿,所以这此发生的捕获中断,是捕捉到了一次上升沿{time5_capture_sta=0;time5_capture_val=0;TIM_SetCounter(TIM5,0);//设置计数器中的值,在这里将计数器的值设为0time5_capture_sta|=0x40;//把标志位的次高位置1,标记为已经捕获到了一次上升沿,这里必须要用或运算,因为time5_capture_sta这个变量的第六位保存着计数器溢出的次数,我们在改变这个变量的最高位和次高位的时候,不能改动后面的第六位数据TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Falling);//设置为下降沿捕获}else if(time5_capture_sta&0x40)//标志位次高位为1,说明上次已经捕获到了一次上升沿,那么这次发生的捕获中断就是捕获到的下降沿{time5_capture_sta|=0x80;//把标志位的最高位置1,标记已经成功捕获到了一次高电平time5_capture_val=TIM_GetCapture1(TIM5);//获取当前计数器中的数值TIM_OC1PolarityConfig(TIM5,TIM_ICPolarity_Rising);//设置为上升沿捕获,为下一次捕获做准备}}if(TIM_GetITStatus(TIM5,TIM_IT_Update)!=RESET)//发生了溢出中断{if(time5_capture_sta&0x40)//判断是否在溢出之前已经捕获到了高电平,其实是肯定已经捕捉到了一次高电平{if((time5_capture_sta&0x3f)==0x3f)//高电平时间太长了,已经溢出了2的6次方-1=63次{time5_capture_sta|=0x80;//把标志位的最高位置1,标记已经成功捕获到了一次高电平,此时为强制结束高电平检测time5_capture_val=0xffff;//强制结束后,将计数器的值设为最大,其实程序运行到这里,它已经是最大了}else{time5_capture_sta++;//如果溢出次数没有超出标志位的低六位所能表示的范围,那么就让标志位直接加1,以此来记录溢出的次数,溢出次数一直加下去,啥时候超出了标志位的低六位所能表示的范围,那么就强制退出检测,直接认为此次检测高电平已经结束,然后直接开启下一次检测}}}}//清除中断标志位TIM_ClearITPendingBit(TIM5,TIM_IT_CC1);TIM_ClearITPendingBit(TIM5,TIM_IT_Update);
}

以下是time.h文件中的代码

#ifndef __TIME_H_
#define __TIME_H_
#include "sys.h"void time_cap_init(u16 arr,u16 psc);#endif

接下来看一下main.c文件的代码

#include "sys.h"
#include "usart.h"
#include "delay.h"
#include "time.h"extern u8 time5_capture_sta;
extern u16 time5_capture_val;
int main(void)
{u32 temp=0;time_cap_init(0xffff,71);while(1){if(time5_capture_sta&0x80)//标志位的最高位为1,说明已经完成了一次高电平的捕获{temp=time5_capture_sta&0x3f;//取出溢出的次数temp*=65536;//溢出的时间总和temp+=time5_capture_val;printf("HIGH: %d us\r\n",temp);time5_capture_sta=0;//清除标志位的最高位,以便能够响应接下来的捕获中断和溢出中断,开启下一次的捕获}}
}

在main.c文件中我们主要就是进行相关初始化函数的调用以及进行高电平时间的计算。
其中主要部分就是高电平时间的计算,他就是计数器溢出次数乘以计数器的最大值,然后再加上当前计数器中的数值,然后再把标志位给清零。
以上就是输入捕获的所有内容,第一次接触可能不太好理解这个过程,其实仔细慢下来思考一下这个过程,也就慢慢接受它了。

STM32之输入捕获相关推荐

  1. STM32定时器输入捕获

    5.STM32定时器输入捕获 前言: STM32定时器输入捕获简介 STM32的输入捕获可以用于捕获脉宽, 测量时间 . 例如超声波测距模块就是需要用输入捕获功能, 通过测量输入脉冲的高电平脉宽 , ...

  2. 【STM32】输入捕获程序

    00. 目录 文章目录 00. 目录 01. 概述 02. 硬件设计 03. 寄存器概述 04. 配置步骤 05. 程序示例 06. 附录 07. 声明 01. 概述 输入捕获模式可以用来测量脉冲宽度 ...

  3. stm32捕获占空比_「话说定时器系列」之六:STM32定时器输入捕获话题

    STM32定时器是 ST MCU 内部最基础且常用的外设,实际应用尤为普遍.去年,电堂推出了<STM32 TIMER基础及常规应用介绍>,为大家梳理了 STM32 TIMER 的庞大内容, ...

  4. STM32处理器输入捕获分析

    前言: 1.博文基于ARM Cortex-M3内核的STM32F103ZET6芯片和标准3.5.0库: 2.如有不足之处,还请多多指教: ** 一 基本知识 ** 输入捕获的功能:用来测量脉宽或者测量 ...

  5. 【STM32】输入捕获实验代码详解

    文章目录 main.c timer.c timer.h main.c #include "led.h" #include "delay.h" #include ...

  6. stm32 PWM输入捕获

    普通的输入捕获,可使用定时器的四个通道,一路捕获占用一个捕获寄存器. PWM输入,只能使用两个通道,通道1和通道2. 一路PWM输入占用两个捕获寄存器,一个捕获周期,一个捕获占空比. 这里,用通用定时 ...

  7. STM32定时器输入捕获,脉宽测量知识点

    定时器特性 时基初始化结构体:TIM_TimeBaseInitTypeDef typedef struct {   uint16_t TIM_Prescaler; TIM_Prescaler:定时器预 ...

  8. STM32 TIM1输入捕获测试输入信号的频率

    测试芯片STM32F103C8T6,外部时钟 1.输入捕获通道测试 TIM1的CH1N,无法进行输入捕获(估计是没找到其他原因,测试就也没有测试出来效果),直接改用TIM1的CH1通道进行测试,可以进 ...

  9. STM32——TIM输入捕获

    上一节已经学习了定时器的输出比较,那输入捕获又是什么呢? 输入捕获就是能够检测外部的方波,其实之前也学过一个可以检测外部引脚电平变化的,就是外部中断,但是呢,外部中断挺有限的,它只能检测一个上升沿或者 ...

最新文章

  1. 2015计算机二级java真题_2015年计算机二级《JAVA》章节习题及答案(9)
  2. 给大家介绍一个相当好的播放器 J River Media Center 15
  3. Python文件与目录操作
  4. 游戏设计模式实操经验:游戏结算功能实现的两个要点
  5. wordpress.org手动安装主题
  6. 编制一个c语言成绩记录簿_C语言基础知识点模拟试题
  7. 前端页面-不可编辑控制
  8. 子程序与中断程序的异同_西门子200samrt高速计数器指令向导及程序
  9. 如何构建自己的笔记系统?
  10. android开发 修改标题栏背景_Android哆啦A梦调试工具体验
  11. 损失函数——交叉熵损失函数(引子)
  12. C++实现 逆波兰表达式计算问题
  13. 计算机菜单界面无法缩小怎么办,如何解决Win10个性化设置最小化任务栏后不能弹出的问题?...
  14. Oracle安装教程
  15. uu云验证码识别平台,验证码,验证码识别,全自动验证码识别技术,优优云全自动打码,代答题系统,优优云远程打码平台,uu云打码...
  16. Flex Builder 3.0正式版+破解补丁
  17. python量化交易是什么意思_量化交易通俗解释是什么?
  18. 【转】局域网速度测试 三款软件轻松搞定
  19. 工商总局:将对网店卖家身份进行全面普查
  20. python因数之和等于数字本身,完全数,盈数,亏数到底是什么鬼?python实现给你看...

热门文章

  1. Moss 2007 入门(1) - 功能概述【转】
  2. 苹果手机与安坐手机input输入框ios 会有灰色背景解决方法
  3. jeecg3.8popup弹出窗口触发失去焦点事件,引发验证弹窗,影响体验问题的解决办法
  4. 2021年企业直播营销研究报告
  5. 2015-2020年各类国际会议与期刊基于图像的三维对象重建论文综述(3)——Volumetric decoding
  6. 剑指offer 面试题03. 数组中重复的数字
  7. 7723java梦游游戏,渠道SDK登录
  8. python3安装包是说解压数据出错怎么办_无法修复“zipimport.zipimporter错误:无法解压缩数据;键入python3.6时zlib不可用获取pip.py...
  9. 软件oem要注意什么_租房软件有哪些 租房有什么需要注意的地方
  10. 时隔25年重访 Linus:流量时代的“技术大师”