STM32输入捕获实验示例详解

发布者:reaper2009最新更新时间:2022-04-20 来源: eefocus关键字:stm32  输入捕获  寄存器 手机看文章 扫描二维码
随时随地手机看文章

STM32输入捕获实验

寄存器部分讲解(以TIM5_CH1为例)

TIMx_CCMR1.ICF[3:0]的作用

滤波器的作用就是“采集取样以便于确定准确的电平状态”。我们以ICIF[3:0] = 0010为例:

 

实例应用:假设输入信号在最多5个内部时钟周期的时间内抖动,我们须配置滤波器的带宽长于5个时钟周期。因此我们可以(以fDTS频率)连续采样8次,以确认在TI1上一次真实的边沿变换,即在TIMx_CCMR1寄存器中写入IC1F=0011。


TIMx_CCER.CC1P的作用

这个寄存器很重要,它决定了“上升沿/下降沿触发输入捕获 “,而且最重要的是,它是用来配置极性的唯一寄存器,这说明输入极性与输出极性都要经过它进行配置,因此我们在下面调用函数时,你会发现无论是PWM输出还是输入捕获,改变极性都是配置的相同函数。

TIMx_CCMR1.CC1S的作用

以上是通道配置,CC1S[1:0]的存在是为了配置“触发事件/中断的信号种类“,例如CC1S[1:0] = 01,那么在TIM5_CH1中触发中断/事件的信号是来自TI1FP1的。

TIMx_CCMR1.ICPS的作用

预分频器的作用就在于配置“我们需要几个有效的电平变化才触发一次中断?“。

TIMx_CCER.CC1E的作用

使能相应的输入捕获通道,使得当IC1PS端口输出有效的电平变化,能立刻被捕获触发相应的事件/中断。


编写程序的技巧

如何判断是上升沿还是下降沿?

脉冲沿+前一时刻的电平状态=电平变化。例如:前一时刻的电平为高电平而且脉冲沿被捕获,那么我们就可以知道“此时的电平为低电平而且脉冲沿为下降沿脉冲“。


如何进行脉冲宽度测量?

我们知道“输入捕获的原理就是:当有效的脉冲变化被捕获那么计数器的值就会自动被捕获/比较寄存器捕获“,我们可以这样做:如果我们想要测量高电平的持续时间,我们可以当上升沿出现时我们将计数器的初始计数值置0,下降沿出现时我们将此时的计数器的值加载到捕获/比较计数器中进行捕获读取。最后”(捕获的值-计数器初始值)*计数器的单位计数时长=高电平持续时间“。


如果高电平持续时间过长定时器发生溢出该怎么办?

我们知道每个定时器都有一个对应的中断服务函数,我们可以使能两个中断:计数器溢出中断+输入捕获中断。我们可以记录当输入电平为高电平时,定时器溢出的次数。最终计算时间时,“高电平持续时间 =(溢出次数*(计数器的MAX值-计数器的初值)+(捕获值-计数器初值))*计数器单位计数时间“。


如果定时器溢出次数过多,该怎么办?

当高电平持续时间过长导致计数器溢出次数过多,我们就没有必要在等待“下降沿脉冲“,直接置位”捕获完成标志位“,并且将捕获值置为其数据类型所能表示的MAX值。


库函数配置原理

总框图

复用引脚以及GPIO的配置

使能相应的总线时钟与GPIO初始化:

我将PA0引脚复用为TIM5_CH1用于进行输入捕获。

我们看KEY_UP的状态就可以得知,GPIOA.0必须进行下拉输入,只有这样按键按下才可以触发输入电平的变化。


GPIO_InitTypeDef GPIO_InitStructure;  

      

RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE); // 使能GPIOA与TIM5的时钟进行引脚的复用

RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);  

      

GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;  

GPIO_InitStructure.GPIO_Pin = GPIO_Pin_0;  

GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;  

GPIO_Init(GPIOA, &GPIO_InitStructure); // 配置PA0为下拉输入

 


计数器的配置

计数器是定时器的灵魂,我们无论配置PWM输出还是输入捕获都要先配置定时器:

上图就是“IM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // AHB->APB1不分频”的作用。


TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStructure;    

IM_TimeBaseInitStructure.TIM_ClockDivision = TIM_CKD_DIV1; // AHB->APB1不分频  

TIM_TimeBaseInitStructure.TIM_CounterMode = TIM_CounterMode_Up; // 向上计数模式  

TIM_TimeBaseInitStructure.TIM_Period = ARR;  

TIM_TimeBaseInitStructure.TIM_Prescaler = PR;  

TIM_TimeBaseInit(TIM3, &TIM_TimeBaseInitStructure); // 初始化计数器  

输入捕获通道属性的配置

按照输入/输出两种不同的模式来对TIM5_CH1进行进一步配置:


TIM_ICInitTypeDef TIM_ICInitStructure;    

TIM_ICInitStructure.TIM_Channel = TIM_Channel_1;   // 使用TIM5_CH1进行输入捕获实验

TIM_ICInitStructure.TIM_ICFilter = 0;   // 不进行采集滤波

TIM_ICInitStructure.TIM_ICPolarity = TIM_ICPolarity_Rising;  // 上升沿触发

TIM_ICInitStructure.TIM_ICPrescaler = TIM_ICPSC_DIV1;  // 一个有效上升沿触发一次输入捕获

TIM_ICInitStructure.TIM_ICSelection = TIM_ICSelection_DirectTI;  // 选择通道1的信号作为触发输入捕获的信号

TIM_ICInit(TIM5, &TIM_ICInitStructure); // 配置TIM5_CH1的输入捕获属性  

 


中断优先级配置

配置NVIC中断向量优先级:


NVIC_InitTypeDef NVIC_InitStructure;      

VIC_InitStructure.NVIC_IRQChannel = TIM5_IRQn;   // TIM5中断使能

NVIC_InitStructure.NVIC_IRQChannelCmd = ENABLE;  

NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 1;   // 抢占优先级 = 1

NVIC_InitStructure.NVIC_IRQChannelSubPriority = 1;  // 响应优先级 = 1

NVIC_Init(&NVIC_InitStructure); // 配置NVIC嵌入式中断向量优先级  

 


TIM5_CH1中断类型选择

进一步配置TIM5_CH1中断触发种类:


TIM_ITConfig(TIM5, TIM_IT_CC1|TIM_IT_Update, ENABLE); // 使能TIM5_CH1的中断  

 


定时器的使能

定时器TIM5使能:


TIM_Cmd(TIM5, ENABLE); // TIM5定时器使能  

 


测量有效脉冲的中断服务函数

在这里我们想要测量按键按下的时长,也就是计算“高电平的持续时间“:


u8 TIM_CAP_STA = 0;  

u16 TIM_CAP_VAL = 0;  

  

void TIM5_IRQHandler()  

{  

    if((TIM_CAP_STA&0x80) == 0) // 捕获未完成  

    {  

        if(TIM_GetITStatus(TIM5, TIM_IT_CC1) == SET)  

        {  

            if((TIM_CAP_STA&0x40) == 0) // 上升沿  

            {  

                TIM_SetCounter(TIM5, 0); // 开始计数  

                TIM_CAP_VAL = 0;  

                TIM_CAP_STA = 0;  

                TIM_CAP_STA |= 0x40; // 高电平  

                TIM_OC1PolarityConfig(TIM5, TIM_OCPolarity_Low); // 捕获下降沿  

            }  

            else // 下降沿  

            {  

                TIM_CAP_VAL = TIM_GetCapture1(TIM5); // 捕获当前值  

                TIM_CAP_STA |= 0x80; // 完成捕获  

                TIM_OC1PolarityConfig(TIM5, TIM_OCPolarity_High); // 捕获上升沿  

            }  

        }  

        if(TIM_GetITStatus(TIM5, TIM_IT_Update) == SET)  

        {  

            if(TIM_CAP_STA&0x40)  

            {  

                if((TIM_CAP_STA&0x3F) == 0x3F)  

                {  

                    TIM_CAP_VAL = 0xFFFF;  

                    TIM_CAP_STA |= 0x80;  

                }  

                else  

                {  

                    TIM_CAP_STA++;  

                }  

            }  

        }  

    }  

    TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update);  

}  

 


重点:中断服务函数的编写逻辑

 

u8 TIM_CAP_STA = 0;  

u16 TIM_CAP_VAL = 0;  

  

void TIM5_IRQHandler()  

{  

    if((TIM_CAP_STA&0x80) == 0) // 捕获未完成  

    {  

        if(TIM_GetITStatus(TIM5, TIM_IT_CC1) == SET)  // TIM5_CH1捕获有效的脉冲沿

        {  

            if((TIM_CAP_STA&0x40) == 0) // 前一刻为低电平——脉冲沿为上升沿  

            {  

                TIM_SetCounter(TIM5, 0); // 开始计数  

                TIM_CAP_VAL = 0;  

                TIM_CAP_STA = 0;  

                TIM_CAP_STA |= 0x40; // 更新电平状态  

                TIM_OC1PolarityConfig(TIM5, TIM_OCPolarity_Low); // 捕获下降沿  

            }  

            else // 前一刻为高电平——脉冲沿为下降沿  

            {  

                TIM_CAP_VAL = TIM_GetCapture1(TIM5); // 捕获当前值  

                TIM_CAP_STA |= 0x80; // 完成捕获  

                TIM_OC1PolarityConfig(TIM5, TIM_OCPolarity_High); // 捕获上升沿  

            }  

        }  

        if(TIM_GetITStatus(TIM5, TIM_IT_Update) == SET)  // 计数器溢出

        {  

            if(TIM_CAP_STA&0x40)  // 如果为高电平,计数器溢出则会被记录

            {  

                if((TIM_CAP_STA&0x3F) == 0x3F)  // 如果计数器溢出次数过多

                {  

                    TIM_CAP_VAL = 0xFFFF;  // 此时的捕获值设为MAX

                    TIM_CAP_STA |= 0x80;  // 已经完成高电平的捕获

                }  

                else  

                {  

                    TIM_CAP_STA++;  // 计数器溢出次数+1

                }  

            }  

        }  

    }  

    TIM_ClearITPendingBit(TIM5, TIM_IT_CC1|TIM_IT_Update);  // 清除中断标志

}  

经过如下程序我们已经可以判断:此时的电平是否有效?


if(TIM_GetITStatus(TIM5, TIM_IT_CC1) == SET)  // TIM5_CH1捕获有效的脉冲沿

        {  

            if((TIM_CAP_STA&0x40) == 0) // 前一刻为低电平——脉冲沿为上升沿  

            {  

                TIM_SetCounter(TIM5, 0); // 开始计数  

                TIM_CAP_VAL = 0;  

                TIM_CAP_STA = 0;  

                TIM_CAP_STA |= 0x40; // 更新电平状态  

                TIM_OC1PolarityConfig(TIM5, TIM_OCPolarity_Low); // 捕获下降沿  

[1] [2] [3]
关键字:stm32  输入捕获  寄存器 引用地址:STM32输入捕获实验示例详解

上一篇:PWM输出实验详细示例
下一篇:“电容触摸按键实验”实例解析

推荐阅读最新更新时间:2024-11-10 10:25

stm32关闭中断
我只试了一下 NVIC_SETFAULTMASK(); //关闭总中断 NVIC_RESETFAULTMASK();//开放总中断 可以开、关中断 但是汇编 asm( CPSID I ); //关中断 asm( CPSIE I ); //开中断 编译过不了 我使用的是KEIL 3.50 今天找答案的时候找到这里的,发现还有自己的回复,还有自己的一个问题,下面把答案给附上: 项目中包含 STM32F10x_StdPeriph_Lib_V3.1.2\Libraries\CMSIS\Core\CM3\core_cm3.h STM32F10x_StdPeriph_Lib_V3.1.2\Libraries\CMSI
[单片机]
STM32使用过程中的踩坑记录
1. 中断函数不要随意使用prinf()函数 记调试步进电机加速减速过程的一次大坑。 2. 使用HAL库的时候不要在中断中使用HAL_Delay()函数 HAL库的HAL_Delay()函数是通过Systick定时器的1ms中断实现的,一般情况下Systick定时器的优先级设置为最低,因此在更高优先级的中断触发后导致HAL_Delay()函数uwtick值无法更新,因此程序会卡死在HAL_Delay()函数中。 3.注意STM32库在配置串口字长时是包含校验位的字长,而一般上位机配置的串口字长是不包含校验位的。 这点在配置使用校验时是非常重要的,如果配置出错会导致通讯不正常。 4.使用不同的开发板的时候一定要注意不同板
[单片机]
<font color='red'>STM32</font>使用过程中的踩坑记录
S3C2440裸机------异常与中断__CPU的工作模式和状态以及寄存器
1.CPU工作模式(Mode) ARM CPU有七种模式,各种模式如下图所示。注意用户模式下不可进入其他模式,用户模式是在有操作系统的时候给应用程序使用的,写应用程序的人水平千差万别,不能保证写的程序是好是坏,所以让应用程序运行在用户模式,限制应用程序的权限,防止破坏整个系统, 2.状态(State) ARM架构的CPU有ARM state和Thumb state, ARM State:用的是ARM指令集,每个指令占据4 byte, Thumb State:用的是Thumb指令集,每个指令2 byte. 比如对于同样的一条指令,MOV R0,R1 对于ARM指令集就要占据四个字节,对于Thumb指令集占据两个字节
[单片机]
S3C2440裸机------异常与中断__CPU的工作模式和状态以及<font color='red'>寄存器</font>
STM32开发笔记46:STM32F0低功耗设计
项目中需要使单片机STM32F070F6P6进入低功耗模式,本文记录整个过程。 1、STM32F070的低功耗模式: 2、低功耗模式描述,在项目中希望外部中断予以唤醒,所以三种低功耗模式,都可以使用。 3、进入Stop模式的代码如下,实际测得进入Stop模式后,8.0uA。在这里需要注意的是,我选用的单片机STM32F070F6P6,仅有端口A、端口B和端口F。我使用参考例程,没有注意里面的具体写法,增加了端口C和端口D,则一直进入不了Stop模式,网上说需要停止SysTick,这些都是不正确的。只有进入Sleep模式,才需停止SysTick,在Stop模式中,不需事先关闭SysTick。程序开始将所有的引
[单片机]
<font color='red'>STM32</font>开发笔记46:STM32F0低功耗设计
STM32入门系列-库目录及文件介绍
已经介绍了过了CMSIS标准,ST公司按照这个标准设计了一套基于STM32F10x的固件库,我们可以直接在ST公司的官网进行下载,现在给大家STM32最新固件库v3.5,在网盘上给大家提供了下载包,链接及提取码如下。 链接:http://pan.baidu.com/s/1nuXXLt3 密码:wztk 文件夹介绍 下面就来介绍下库文件的目录及文件。打开下载好的固件库包如下图所示。 下面简单介绍各个文件件及文件的作用。 _htmresc 文件夹:存放ST公司的LOGO图标,这个文件夹不用管。 Libraries 文件夹:在这个文件夹内有两个子目录,CMSIS文件夹用于存放符合CMSIS标准的文件,包括STM32启动文
[单片机]
<font color='red'>STM32</font>入门系列-库目录及文件介绍
STM32实例之LED灯闪烁控制以及相关注意事项
在本实例中,主要是为了实现LED灯的闪烁。首先分析LED的驱动方式,本实验中使用的是OpenM3V,内置8个LED均采用灌流方式驱动(低电平亮)。如果想要实现其闪烁,则需要给相应端口持续不断的高低交替电平。 在软件结构设计中,加入使用LED8,则需要在PD7口不断的输出高电平和低电平。首先需要初始化系统时钟,然后再开始配置PD7作为输出使用,打开外设时钟最后控制PD7输出持续的高低轮流。 开始 - 配置系统时钟 - 配置PD7作为输出在打开PD外设时钟 - 置位PD7,熄灭LED8 - 延时程序 - 清PD7,点亮LED8 - 延时 - 置位PD7,以此开始循环闪烁。 以下给出具体的代码(代码运行在KEIL5上)。 在软件代码编写
[单片机]
串口通讯(DMA模式)
在高级语言中,I/O 流输入(input)操作一般都要求指定要读取的数据的最大长度(字节数)。当接收到至少1字节、最多所指定的字节数时,函数返回。 STM32 串口接收数据时,HAL API 要求指定数据长度。但无论轮询、中断或是DMA方式,都必须完整地接收到这么多字节,程序流程才继续。如何接收变长消息,我想不到特别好的实现方式。一种方式是,轮询加超时。另一种方式是,设计消息协议,使消息头为定长,且消息头内包含消息体的长度。但是,如果通讯异常,导致消息数据错误或丢失,那么,还是缺少“提前返回”的机制。 相对来说,轮询加超时的方式似乎更好些。效率低,但是是可靠的。我也不确定。 DMA是STM32内的一个硬件模块,它独立于
[单片机]
STM32 ADC的采样周期确定
一 STM32 ADC 采样 频率的确定   先看一些资料,确定一下STM32 ADC 的时钟:   (1),由时钟控制器提供的ADCCLK 时钟和PCLK2(APB2 时钟)同步。CLK 控制器为ADC 时钟提供一个专用的可编程预分频器。   (2)一般情况下在程序 中将 PCLK2 时钟设为 与系统时钟 相同   RCC_HCLKConfig(RCC_SYSCLK_Div1);   RCC_PCLK2Config(RCC_HCLK_Div1);   RCC_PCLK1Config(RCC_HCLK_Div2);   (3)在时钟配置寄存器(RCC_CFGR) 中 有 为ADC 时钟提供一个专用的可编程预分器   位15:14 A
[单片机]

推荐帖子

电源转换模块220V转18V
使用220V转18V变压器,变压器输出端(12,14,17,19脚)电压输出为12V,经过MB6S整流器之后电SP1的3脚输出电压为27V,SP2输出的4脚输出电压为-27V。然后就是通过LM7818和MC7918,但是7818和7918的输出端电压不是+18V和-18V,而是+27V和-27V,为什么稳压管(7818,7918)没起作用?电源转换模块220V转18V已在另帖中回复。不必重复发那么多内容相同的帖子。
jiguangshen PCB设计
請問沒有參加譯碼的地扯如何處理?
若低位地址(A0-A11)接在内存芯片地址引脚上,高位地址(A12-A19)进行片选译码(其中,A14和A16没有参加译码),且片选信号低电平有效,则对下图所示的译码器,不属于此译码空间的地址为(36)。(36)A.AB000H~ABFFFHB.BB000H~BBFFFHC.EF000H~EFFFFHD.FE000H~FEFFFH請問沒有參加譯碼的地扯如何處理?
8573420 嵌入式系统
求一个用5529写的IC卡的程序
就是往IC卡里面读入读出数据的,向往大神顺便介绍一下原理求一个用5529写的IC卡的程序
FLY--小强 微控制器 MCU
STM32以太网数据接收中断函数是那个?
我正在使用stm32_eth_lib固件库,我希望能将以太网接收到的数据通过USB转发到上位机(我只需要将以太网物理层接收到的一帧数据经过USB转发到上位机,不需要经过协议栈)。但是找不到以太网接收到数据后的中断处理函数,以太网是数据是通过DMA通道传输数据的,因此我在程序中找到DMA初始化的结构体,找到以下提示信息:ETH_InitStruct-ETH_DMAArbitration=ETH_DMAArbitration_RoundRobin_RxTx_1_1;为了验证这个是否是将
vikione stm32/stm8
EEWORLD大学堂----高性能DCDC设计的关键之电源热设计
电源热设计需要相关的导热界面材料可以合作一下哦
hi5 电源技术
功率电子
问一下,功率电子仿真中,如何控制全桥或者半桥MOS管的开通与关断,怎么加PWM波信号呀,比如半桥中,我先让MOS管1先导通,然后经过一段死区之后,MOS管2再导通,怎么生成这两个PWM波,这部分涉及什么知识模块。(主要用simulink仿真)。功率电子【功率电子仿真中,如何控制全桥或者半桥MOS管的开通与关断,怎么加PWM波信号呀,比如半桥中,我先让MOS管1先导通,然后经过一段死区之后,MOS管2再导通,怎么生成这两个PWM波】通常生成PWM信号包括死区是使用专门的芯片。这类芯
爱干饭 汽车电子
小广播
设计资源 培训 开发板 精华推荐

最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

换一换 更多 相关热搜器件

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved