1)实验平台:alientek 阿波罗 STM32F767 开发板2)摘自《STM32F7 开发指南(HAL 库版)》关注官方微信号公众号,获取更多资料:正点原子

第十四章 输入捕获实验

上一章,我们介绍了 STM32F7 的通用定时器作为 PWM 输出的使用方法,这一章,我们将

向大家介绍通用定时器作为输入捕获的使用。在本章中,我们将用 TIM5 的通道 1(PA0)来做

输入捕获,捕获 PA0 上高电平的脉宽(用 KEY_UP 按键输入高电平),通过串口打印高电平脉

宽时间,从本章分为如下几个部分:

14.1 输入捕获简介

14.2 硬件设计

14.3 软件设计

14.4 下载验证

14.5 STM32CubeMX 配置定时器输入捕获功能

14.1 输入捕获简介

输入捕获模式可以用来测量脉冲宽度或者测量频率。我们以测量脉宽为例,用一个简图来

说明输入捕获的原理,如图 14.1.1 所示:

图 14.1.1 输入捕获脉宽测量原理

如图 14.1.1 所示,就是输入捕获测量高电平脉宽的原理,假定定时器工作在向上计数模式,

图中 t1~t2 时间,就是我们需要测量的高电平时间。测量方法如下:首先设置定时器通道 x 为

上升沿捕获,这样,t1 时刻,就会捕获到当前的 CNT 值,然后立即清零 CNT,并设置通道 x

为下降沿捕获,这样到 t2 时刻,又会发生捕获事件,得到此时的 CNT 值,记为 CCRx2。这样,

根据定时器的计数频率,我们就可以算出 t1~t2 的时间,从而得到高电平脉宽。

在 t1~t2 之间,可能产生 N 次定时器溢出,这就要求我们对定时器溢出,做处理,防止高

电平太长,导致数据不准确。如图14.1.1所示,t1~t2之间,CNT计数的次数等于:N*ARR+CCRx2,

有了这个计数次数,再乘以 CNT 的计数周期,即可得到 t2-t1 的时间长度,即高电平持续时间。

输入捕获的原理,我们就介绍到这。

STM32F767 的定时器,除了 TIM6 和 TIM7,其他定时器都有输入捕获功能。STM32F767

的输入捕获,简单的说就是通过检测 TIMx_CHx 上的边沿信号,在边沿信号发生跳变(比如上

升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的通道的捕获/比较寄存器

(TIMx_CCRx)里面,完成一次捕获。同时还可以配置捕获时是否触发中断/DMA 等。

本章我们用到 TIM5_CH1 来捕获高电平脉宽,捕获原理如图 14.1.1 所示,这里我们就不再

多说了。接下来,我们介绍我们本章需要用到的一些寄存器配置,需要用到的寄存器有:TIMx_ARR、

TIMx_PSC、TIMx_CCMR1、TIMx_CCER、TIMx_DIER、TIMx_CR1、TIMx_CCR1 这些寄存

器在前面 2 章全部都有提到(这里的 x=5),我们这里就不再全部罗列了,我们这里针对性的介绍

这几个寄存器的配置。

首先 TIMx_ARR 和 TIMx_PSC,这两个寄存器用来设自动重装载值和 TIMx 的时钟分频,

用法同前面介绍的,我们这里不再介绍。

再来看看捕获/比较模式寄存器 1:TIMx_CCMR1,这个寄存器在输入捕获的时候,非常有

用,有必要重新介绍,该寄存器的各位描述如图 14.1.2 所示:

图 14.1.2 TIMx_CCMR1 寄存器各位描述

当在输入捕获模式下使用的时候,对应图 14.1.2 的第二行描述,从图中可以看出,

TIMx_CCMR1 明显是针对 2 个通道的配置,低八位[7:0]用于捕获/比较通道 1 的控制,而高八

位[15:8]则用于捕获/比较通道 2 的控制,因为 TIMx 还有 CCMR2 这个寄存器,所以可以知道

CCMR2 是用来控制通道 3 和通道 4(详见《STM32F7 中文参考手册》705 页,23.4.8 节)。

这里我们用到的是 TIM5 的捕获/比较通道 1,我们重点介绍 TIMx_CCMR1 的[7:0]位(其

高 8 位配置类似),TIMx_CCMR1 的[7:0]位详细描述见图 14.1.3 所示:

图 14.1.3 TIMx_CCMR1 [7:0]位详细描述

其中 CC1S[1:0],这两个位用于 CCR1 的通道配置,这里我们设置 IC1S[1:0]=01,也就是配

置 IC1 映射在 TI1 上(关于 IC1,TI1 不明白的,可以看《STM32F7 中文参考手册》651 页的图

19.1 通用定时器框图),即 CC1 对应 TIMx_CH1。

输入捕获 1 预分频器 IC1PSC[1:0],这个比较好理解。我们是 1 次边沿就触发 1 次捕获,所

以选择 00 就是了。

输入捕获 1 滤波器 IC1F[3:0],这个用来设置输入采样频率和数字滤波器长度。其中,

是定时器的输入频率(TIMxCLK),一般为 54Mhz/108Mhz(看该定时器在那个总线上),而

则是根据TIMx_CR1的CKD[1:0]的设置来确定的,如果CKD[1:0]设置为00,那么

=

N 值就是滤波长度,举个简单的例子:假设 IC1F[3:0]=0011,并设置 IC1 映射到通道 1 上,且

为上升沿触发,那么在捕获到上升沿的时候,再以

的频率,连续采样到 8 次通道 1 的电

平,如果都是高电平,则说明却是一个有效的触发,就会触发输入捕获中断(如果开启了的话)。

这样可以滤除那些高电平脉宽低于 8 个采样周期的脉冲信号,从而达到滤波的效果。这里,我

们不做滤波处理,所以设置 IC1F[3:0]=0000,只要采集到上升沿,就触发捕获。

再来看看捕获/比较使能寄存器:TIMx_CCER,该寄存器的各位描述见图 14.1.3(在第 14

章)。本章我们要用到这个寄存器的最低 2 位,CC1E 和 CC1P 位。这两个位的描述如图 14.1.4

所示:

图 14.1.4 TIMx_CCER 最低 2 位描述

所以,要使能输入捕获,必须设置 CC1E=1,而 CC1P 则根据自己的需要来配置。

接下来我们再看看 DMA/中断使能寄存器:TIMx_DIER,该寄存器的各位描述见图 13.1.2

(在第 13 章),本章,我们需要用到中断来处理捕获数据,所以必须开启通道 1 的捕获比较中

断,即 CC1IE 设置为 1。

控制寄存器:TIMx_CR1,我们只用到了它的最低位,也就是用来使能定时器的,这里前

面两章都有介绍,请大家参考前面的章节。

最后再来看看捕获/比较寄存器 1:TIMx_CCR1,该寄存器用来存储捕获发生时,TIMx_CNT

的值,我们从 TIMx_CCR1 就可以读出通道 1 捕获发生时刻的 TIMx_CNT 值,通过两次捕获(一

次上升沿捕获,一次下降沿捕获)的差值,就可以计算出高电平脉冲的宽度(注意,对于脉宽

太长的情况,还要计算定时器溢出的次数)。

至此,我们把本章要用的几个相关寄存器都介绍完了,本章要实现通过输入捕获,来获取

TIM5_CH1(PA0)上面的高电平脉冲宽度,并从串口打印捕获结果。下面我们介绍库函数配置上

述功能输入捕获的步骤:

1)开启 TIM5 和 GPIOA 时钟,配置 PA0 为复用功能(AF2),并开启下拉电阻。

要使用 TIM5,我们必须先开启 TIM5 的时钟。同时我们要捕获 TIM5_CH1 上面的高电平

脉宽,所以先配置 PA0 为带下拉的复用功能,同时,为了让 PA0 的复用功能选择连接到 TIM5,

所以设置 PA0 的复用功能为 AF2,即连接到 TIM5 上面。

开启定时器和 GPIO 时钟的方法和上一章是一样的,这里我们就不做过多讲解。

配置 PA0 为复用功能(AF2)并开启下拉功能也和上一章一样是通过函数 HAL_GPIO_Init

来实现。由于这一步配置过程和上一章几乎没有区别,所以这里我们直接列出配置代码:

__HAL_RCC_TIM5_CLK_ENABLE(); //使能 TIM5 时钟

__HAL_RCC_GPIOA_CLK_ENABLE(); //开启 GPIOA 时钟

GPIO_Initure.Pin=GPIO_PIN_0; //PA0

GPIO_Initure.Mode=GPIO_MODE_AF_PP;

//复用推挽输出

GPIO_Initure.Pull=GPIO_PULLDOWN;

//下拉

GPIO_Initure.Speed=GPIO_SPEED_HIGH

//高速

GPIO_Initure.Alternate=GPIO_AF2_TIM5; //PA0 复用为 TIM5 通道 1

HAL_GPIO_Init(GPIOA,&GPIO_Initure);

跟上一讲 PWM 输出类似,这里我们使用的是定时器 5 的通道 1,所以我们从 STM32F7 对

应的数据手册可以查看到对应的 IO 口为 PA0:

2)初始化 TIM5,设置 TIM5 的 ARR 和 PSC。

和上一讲 PWM 输出实验一样,当使用定时器做输入捕获功能时,在 HAL 库中并不使用定

时器初始化函数 HAL_TIM_Base_Init 来实现,而是使用输入捕获特定的定时器初始化函数

HAL_TIM_IC_Init。当我们使用函数 HAL_TIM_IC_Init 来初始化定时器的输入捕获功能时,该

函数内部会调用输入捕获初始化回调函数HAL_TIM_IC_MspInit 来初始化与 MCU 无关的步骤。

函数 HAL_TIM_IC_Init 声明如下:

HAL_StatusTypeDef HAL_TIM_IC_Init(TIM_HandleTypeDef *htim);

该函数非常简单,和 HAL_TIM_Base_Init 函数以及函数 HAL_TIM_PWM_Init 使用方法是

一模一样的,这里我们就不累赘。

回调函数 HAL_TIM_IC_MspInit 声明如下:

void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim);

该函数使用方法和 PWM 初始化回调函数 HAL_TIM_PWM_MspInit 使用方法一致。一般情

况下,输入捕获初始化回调函数中会编写步骤 1 内容,以及后面讲解的 NVIC 配置。

有了 PWM 实验基础知识,这两个函数的使用就非常简单,这里我们列出该步骤程序如下:

TIM_HandleTypeDef TIM5_Handler;

TIM5_Handler.Instance=TIM5; //通用定时器 5

TIM5_Handler.Init.Prescaler=89; //分频系数

TIM5_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;

//向上计数器

TIM5_Handler.Init.Period= 0XFFFFFFFF;

//自动装载值

TIM5_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; //时钟分频银子

HAL_TIM_IC_Init(&TIM5_Handler);//初始化输入捕获时基参数

3)设置 TIM5 的输入捕获参数,开启输入捕获。

TIM5_CCMR1 寄存器控制着输入捕获 1 和 2 的模式,包括映射关系,滤波和分频等。这里

我们需要设置通道 1 为输入模式,且 IC1 映射到 TI1(通道 1)上面,并且不使用滤波(提高响应

速度)器。HAL 库是通过 HAL_TIM_IC_ConfigChannel 函数来初始化输入比较参数的:

HAL_StatusTypeDef HAL_TIM_IC_ConfigChannel(TIM_HandleTypeDef *htim,

TIM_IC_InitTypeDef* sConfig, uint32_t Channel);

该函数有三个参数,第一个参数是定时器初始化结构体指针类型,该参数很好理解。第二

个参数是设置要初始化的定时器通道值,取值范围为 TIM_CHANNEL_1~ TIM_CHANNEL_4。

接下来我们着重讲解第二个入口参数 sConfig,该参数是 TIM_IC_InitTypeDef 结构体指针类型,

它是真正用来初始化定时器通道的捕获参数的。该结构体类型定义为:

typedef struct

{

uint32_t ICPolarity;

uint32_t ICSelection;

uint32_t ICPrescaler;

uint32_t ICFilter;

} TIM_IC_InitTypeDef;

成员变量 ICPolarity 用来设置输入信号的有效捕获极性 , 取 值 范 围 为 :

TIM_ICPOLARITY_RISING(上升沿捕获),TIM_ICPOLARITY_FALLING(下降沿捕获)和

TIM_ICPOLARITY_BOTHEDGE(双边沿)捕获。实际上,HAL 还提供了设置输入捕获极性以

及清除输入捕获极性设置方法。如下:

TIM_RESET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1);//清除极性设置

TIM_SET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1,

TIM_ICPOLARITY_FALLING);//定时器 5 通道 1 设置为下降沿捕获

成员变量 ICSelection 用来设置映射关系,我们配置 IC1 直接映射在 TI1 上,选择

TIM_ICSELECTION_DIRECTTI。

成员变量ICPrescaler用来设置输入捕获分频系数,可以设置为TIM_ICPSC_DIV1(不分频),

TIM_ICPSC_DIV2(2 分频),TIM_ICPSC_DIV4(4 分频)以及 TIM_ICPSC_DIV8(8 分频),

本实验需要设置为不分频,所以选值为 TIM_ICPSC_DIV1。

成员变量 ICFilter 用来设置滤波器长度,这里我们不使用滤波器,所以设置为 0。

本实验,我们要设置输入捕获参数为:上升沿捕获,不分频,不滤波,同时 IC1 映射到 TI1(通

道 1)上,实例代码如下:

TIM_IC_InitTypeDef TIM5_CH1Config;

TIM5_CH1Config.ICPolarity=TIM_ICPOLARITY_RISING; //上升沿捕获

TIM5_CH1Config.ICSelection=TIM_ICSELECTION_DIRECTTI;//IC1 映射到 TI1 上

TIM5_CH1Config.ICPrescaler=TIM_ICPSC_DIV1; //配置输入分频,不分频

TIM5_CH1Config.ICFilter=0; //配置输入滤波器,不滤波

HAL_TIM_IC_ConfigChannel(&TIM5_Handler,&TIM5_CH1Config,TIM_CHANNEL_1);

4)使能捕获和更新中断(设置 TIM5 的 DIER 寄存器)

因为我们要捕获的是高电平信号的脉宽,所以,第一次捕获是上升沿,第二次捕获时下降

沿,必须在捕获上升沿之后,设置捕获边沿为下降沿,同时,如果脉宽比较长,那么定时器就

会溢出,对溢出必须做处理,否则结果就不准了,不过,由于 STM32F7 的 TIM5 是 32 位定时

器,假设计数周期为 1us,那么需要 4294 秒才会溢出一次,这基本上是不可能的。这两件事,

我们都在中断里面做,所以必须开启捕获中断和更新中断。

HAL 库中开启定时器中断方法在定时器中断实验已经讲解,方法为:

__HAL_TIM_ENABLE_IT(&TIM5_Handler,TIM_IT_UPDATE); //使能更新中断

实际上,由于本章使用的是定时器的输入捕获功能,HAL 还提供了一个函数同时用来开启

定时器的输入捕获通道和使能捕获中断,该函数为:

HAL_StatusTypeDef HAL_TIM_IC_Start_IT (TIM_HandleTypeDef *htim, uint32_t Channel);

实际上该函数同时还使能了定时器,一个函数具备三个功能。

如果我们不需要开启捕获中断,只是开启输入捕获功能,HAL 库函数为:

HAL_StatusTypeDef HAL_TIM_IC_Start (TIM_HandleTypeDef *htim, uint32_t Channel);

5)使能定时器(设置 TIM5 的 CR1 寄存器)

在步骤 4 中,如果我们调用了函数 HAL_TIM_IC_Start_IT 来开启输入捕获通道以及输入捕

获中断,实际上它同时也开启了相应的定时器。单独的开启定时器的方法为:

__HAL_TIM_ENABLE(); //开启定时器方法

6)设置 NVIC 中断优先级

因为我们要使用到中断,所以我们在系统初始化之后,需要先设置中断优先级,这里方法

跟我们前面讲解一致,这里我们就不累赘了。、

这里大家要注意,一般情况下 NVIC 配置我们都会放在 MSP 回调函数中。对于输入捕获功

能,回调函数是我们步骤 2 讲解的函数 HAL_TIM_IC_MspInit。

7) 编写中断服务函数

最后编写中断服务函数。定时器 5 中断服务函数为:

void TIM5_IRQHandler(void);

和定时器中断实验一样,一般情况下,我们都不把中断控制逻辑直接编写在中断服务函数

中,因为 HAL 库提供了一个共用的中断处理入口函数 HAL_TIM_IRQHandler,该函数中会对

中断来源进行判断然后调用相应的中断处理回调函数。HAL 库提供了多个中断处理回调函数,

本章实验,我们要使用到更新中断和捕获中断,所以我们要使用的回调函数为:

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim);//更新(溢出)中断

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim);//捕获中断

我们只需要在我们工程中,重新定义这两个函数,编写中断处理控制逻辑即可。

14.2 硬件设计

本实验用到的硬件资源有:

1) 指示灯 DS0

2) KEY_UP 按键

3) 串口

4) 定时器 TIM3

5) 定时器 TIM5

前面 4 个,在之前的章节均有介绍。本节,我们将捕获 TIM5_CH1(PA0)上的高电平脉

宽,通过 KEY_UP 按键输入高电平,并从串口打印高电平脉宽。同时我们保留上节的 PWM 输

出,大家也可以通过用杜邦线连接 PF9 和 PA0,来测量 PWM 输出的高电平脉宽。

14.3 软件设计

相比上一章讲解的 PWM 实验,我们直接在 timer.c 和 timer.h 中直接添加了输入捕获相关程

序。对于输入捕获,我们也同样使用的定时器相关操作,所以相比上一实验我们并没有添加其

他任何 HAL 库文件。

接下来我们来看看 timer.c 文件中新增的内容如下:

TIM_HandleTypeDef TIM5_Handler; //定时器 5 句柄

//定时器 5 通道 1 输入捕获配置

//arr:自动重装值(TIM2,TIM5 是 32 位的!!) psc:时钟预分频数

void TIM5_CH1_Cap_Init(u32 arr,u16 psc)

{

TIM_IC_InitTypeDef TIM5_CH1Config;

TIM5_Handler.Instance=TIM5;

//通用定时器 5

TIM5_Handler.Init.Prescaler=psc;

//分频系数

TIM5_Handler.Init.CounterMode=TIM_COUNTERMODE_UP;

//向上计数器

TIM5_Handler.Init.Period=arr;

//自动装载值

TIM5_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1; //时钟分频银子

HAL_TIM_IC_Init(&TIM5_Handler); //初始化输入捕获时基参数

TIM5_CH1Config.ICPolarity=TIM_ICPOLARITY_RISING;

//上升沿捕获

TIM5_CH1Config.ICSelection=TIM_ICSELECTION_DIRECTTI; //映射到 TI1 上

TIM5_CH1Config.ICPrescaler=TIM_ICPSC_DIV1;

//配置输入分频,不分频

TIM5_CH1Config.ICFilter=0;

//配置输入滤波器,不滤波

HAL_TIM_IC_ConfigChannel(&TIM5_Handler,&TIM5_CH1Config,TIM_CHANNEL_1);

//配置 TIM5 通道 1

HAL_TIM_IC_Start_IT(&TIM5_Handler,TIM_CHANNEL_1);

//开启 TIM5 的捕获通道 1,并且开启捕获中断

__HAL_TIM_ENABLE_IT(&TIM5_Handler,TIM_IT_UPDATE); //使能更新中断

}

//定时器 5 底层驱动,时钟使能,引脚配置

//此函数会被 HAL_TIM_IC_Init()调用

//htim:定时器 5 句柄

void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim)

{

GPIO_InitTypeDef GPIO_Initure;

__HAL_RCC_TIM5_CLK_ENABLE();

//使能 TIM5 时钟

__HAL_RCC_GPIOA_CLK_ENABLE();

//开启 GPIOA 时钟

GPIO_Initure.Pin=GPIO_PIN_0;

//PA0

GPIO_Initure.Mode=GPIO_MODE_AF_PP;

//复用推挽输出

GPIO_Initure.Pull=GPIO_PULLDOWN;

//下拉

GPIO_Initure.Speed=GPIO_SPEED_HIGH;

//高速

GPIO_Initure.Alternate=GPIO_AF2_TIM5;

//PA0 复用为 TIM5 通道 1

HAL_GPIO_Init(GPIOA,&GPIO_Initure);

//初始化 PA0

HAL_NVIC_SetPriority(TIM5_IRQn,2,0); //设置中断优先级,抢占 2,子优先级 0

HAL_NVIC_EnableIRQ(TIM5_IRQn); //开启 ITM5 中断通道

}

//捕获状态

//[7]:0,没有成功的捕获;1,成功捕获到一次.

//[6]:0,还没捕获到低电平;1,已经捕获到低电平了.

//[5:0]:捕获低电平后溢出的次数(对于 32 位定时器来说,1us 计数器加 1,溢出时间:4294 秒)

u8 TIM5CH1_CAPTURE_STA=0; //输入捕获状态

u32 TIM5CH1_CAPTURE_VAL;//输入捕获值(TIM2/TIM5 是 32 位)

//定时器 5 中断服务函数

void TIM5_IRQHandler(void)

{

HAL_TIM_IRQHandler(&TIM5_Handler);//定时器共用处理函数

}

//定时器更新中断(溢出)中断处理回调函数, 在 HAL_TIM_IRQHandler 中会被调用

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//更新中断发生时执行

{

if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获

{

if(TIM5CH1_CAPTURE_STA&0X40)//已经捕获到高电平了

{

if((TIM5CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了

{

TIM5CH1_CAPTURE_STA|=0X80;

//标记成功捕获了一次

TIM5CH1_CAPTURE_VAL=0XFFFFFFFF;

}else TIM5CH1_CAPTURE_STA++;

}

}

}

//定时器输入捕获中断处理回调函数,该函数在 HAL_TIM_IRQHandler 中会被调用

void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//捕获中断发生时执行

{

if((TIM5CH1_CAPTURE_STA&0X80)==0)//还未成功捕获

{

if(TIM5CH1_CAPTURE_STA&0X40)

//捕获到一个下降沿

{

TIM5CH1_CAPTURE_STA|=0X80; //标记成功捕获到一次高电平脉宽

TIM5CH1_CAPTURE_VAL=

HAL_TIM_ReadCapturedValue(&TIM5_Handler,TIM_CHANNEL_1);

//获取当前的捕获值.

TIM_RESET_CAPTUREPOLARITY(&TIM5_Handler,

TIM_CHANNEL_1); //清除设置

TIM_SET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1,

TIM_ICPOLARITY_RISING);//上升沿捕获

}else //还未开始,第一次捕获上升沿

{

TIM5CH1_CAPTURE_STA=0;

//清空

TIM5CH1_CAPTURE_VAL=0;

TIM5CH1_CAPTURE_STA|=0X40;

//标记捕获到了上升沿

__HAL_TIM_DISABLE(&TIM5_Handler); //关闭定时器 5

__HAL_TIM_SET_COUNTER(&TIM5_Handler,0);

TIM_RESET_CAPTUREPOLARITY(&TIM5_Handler,

TIM_CHANNEL_1); //清除原来设置

TIM_SET_CAPTUREPOLARITY(&TIM5_Handler,TIM_CHANNEL_1,

TIM_ICPOLARITY_FALLING);//下降沿捕获

__HAL_TIM_ENABLE(&TIM5_Handler);//使能定时器 5

}

}

}

此部分代码包含 5 个函数。函数 TIM5_CH1_Cap_Init 和回调函数 HAL_TIM_IC_MspInit

共同用来实现 14.1 小节讲解的步骤 1~6。TIM5_IRQHandler 是 TIM5 的中断服务函数,该函数

内部和定时器中断实验一样,只有一行代码就是直接调用函数 HAL_TIM_IRQHandler。根据我

们前面的讲解,函数 HAL_TIM_IRQHandler 内部会对中断来源进行判断(中断标志位),然后

分别调用对应的中断处理回调函数 ,最后还会自动清除相应的中断标志位 。函数

HAL_TIM_PeriodElapsedCallback 和 HAL_TIM_IC_CaptureCallback 就是我们要着重讲解的定时

器更新中断(溢出)以及输入捕获中断处理回调函数。

同时,在该文件中我们还定义了两个全局变量,用于辅助实现高电平捕获。其中

TIM5CH1_CAPTURE_STA,是用来记录捕获状态,该变量类似我们在 usart.c 里面自行定义的

USART_RX_STA 寄存器(其实就是个变量,只是我们把它当成一个寄存器那样来使用)。

TIM5CH1_CAPTURE_STA 各位描述如表 14.3.1 所示:

表 14.3.1 TIM5CH1_CAPTURE_STA 各位描述

另外一个变量 TIM5CH1_CAPTURE_VAL,则用来记录捕获到下降沿的时候,TIM5_CNT

的值。

现在我们来介绍一下,捕获高电平脉宽的思路:首先,设置 TIM5_CH1 捕获上升沿,这在

TIM5_Cap_Init 函数执行的时候就设置好了,然后等待上升沿中断到来,当捕获到上升沿中断

(执行中断处理回调函数HAL_TIM_IC_CaptureCallback),此时如果TIM5CH1_CAPTURE_STA

的第 6 位为 0,则表示还没有捕获到新的上升沿,就先把

TIM5CH1_CAPTURE_STA、TIM5CH1_CAPTURE_VAL 和计数器值 TIM5->CNT 等清零,然后

再设置 TIM5CH1_CAPTURE_STA 的第 6 位为 1,标记捕获到高电平,最后设置为下降沿捕获,

等待下降沿到来。如果等待下降沿到来期间,定时器发生了溢出(执行溢出中断处理回调函数

HAL_TIM_PeriodElapsedCallback),就在 TIM5CH1_CAPTURE_STA 里面对溢出次数进行计数,

当最大溢出次数来到的时候,就强制标记捕获完成(虽然此时还没有捕获到下降沿)。当下降沿

到来的时候,先设置 TIM5CH1_CAPTURE_STA 的第 7 位为 1,标记成功捕获一次高电平,然

后读取此时的定时器值到 TIM5CH1_CAPTURE_VAL 里面,最后设置为上升沿捕获,回到初始

状态。

这样,我们就完成一次高电平捕获了,只要 TIM5CH1_CAPTURE_STA 的第 7 位一直为 1,

那么就不会进行第二次捕获,我们在main函数处理完捕获数据后,将TIM5CH1_CAPTURE_STA

置零,就可以开启第二次捕获。

timer.h 头文件内容比较简单,主要是函数申明,这里我们不做过多讲解。

接下来,我们看看 main 函数内容:

extern u8 TIM5CH1_CAPTURE_STA;

//输入捕获状态

extern u32

TIM5CH1_CAPTURE_VAL;//输入捕获值

int main(void)

{

long long temp=0;

Cache_Enable();

//打开 L1-Cache

HAL_Init();

//初始化 HAL 库

Stm32_Clock_Init(432,25,2,9); //设置时钟,216Mhz

delay_init(216);

//延时初始化

uart_init(115200);

//串口初始化

LED_Init();

//初始化 LED

TIM3_PWM_Init(500-1,108-1); //108M/108=1M 的计数频率,自动重装载为 500,

//那么 PWM 频率为 1M/500=2kHZ

TIM5_CH1_Cap_Init(0XFFFFFFFF,108-1); //以 1MHZ 的频率计数

while(1)

{

delay_ms(10);

TIM_SetTIM3Compare4(TIM_GetTIM3Capture4()+1);

if(TIM_GetTIM3Capture4()==300)TIM_SetTIM3Compare4(0);

if(TIM5CH1_CAPTURE_STA&0X80)

//成功捕获到了一次高电平

{

temp=TIM5CH1_CAPTURE_STA&0X3F;

temp*=0XFFFFFFFF;

//溢出时间总和

temp+=TIM5CH1_CAPTURE_VAL; //得到总的高电平时间

printf("HIGH:%lld us",temp);//打印总的高点平时间

TIM5CH1_CAPTURE_STA=0; //开启下一次捕获

}

}

}

该 main 函数是在 PWM 实验的基础上修改来的,我们保留了 PWM 输出,同时通过设置

TIM5_Cap_Init(0XFFFFFFFF,108-1),将 TIM5_CH1 的捕获计数器设计为 1us 计数一次,并设置

重装载值为最大,所以我们的捕获时间精度为 1us。

主函数通过 TIM5CH1_CAPTURE_STA 的第 7 位,来判断有没有成功捕获到一次高电平,

如果成功捕获,则将高电平时间通过串口输出到电脑。

至此,我们的软件设计就完成了。

14.4 下载验证

在完成软件设计之后,将我们将编译好的文件下载到阿波罗 STM32 开发板上,可以看到

DS0 的状态和上一章差不多,由暗亮的循环。说明程序已经正常在跑了,我们再打开串口调

试助手,选择对应的串口,然后按 KEY_UP 按键,可以看到串口打印的高电平持续时间,如图

14.4.1 所示:

图 14.4.1 PWM 控制 DS0 亮度

从上图可以看出,其中有 2 次高电平在 100us 以内的,这种就是按键按下时发生的抖动。

这就是为什么我们按键输入的时候,一般都需要做防抖处理,防止类似的情况干扰正常输入。

大家还可以用杜邦线连接 PA0 和 PB1,看看上一节中我们设置的 PWM 输出的高电平是如何变

化的。

14.5 STM32CubeMX 配置定时器输入捕获功能

使用 STM32CubeMX 配置输入捕获功能初始化代码步骤如下:

① 在 Pinout->TIM5 配置项中,配置 Channel1 的值为 Input Capture direct mode,然后选中

Internal Clock。操作过程如下图 14.5.1 所示:

图 14.5.1 TIM3 配置

② 进入 Configuration->TIM5 配置页,在弹出的界面中点击 Parameter Settings 选项卡,

Counter Settings 配置栏下面的四个选项就是用来配置定时器的预分频系数,自动装载

值,计数模式以及时钟分频因子。在界面的 Input Capture Channel 1 配置栏配置输入捕

获通道 1 的捕获极性,分频系数,映射,滤波器等参数,操作方法如下图 14.5.2 所示:

③ 进入 Configuration->NVIC 配置页,在弹出的界面中点击 NVIC 选项卡,配置 Interrupt

Table 中的 TIM5 global interrupt,使能中断,配置抢占优先级和响应优先级。

配置完上面步骤后,生成代码。在生成的代码中,并没有使能相应中断的代码,也没有改

写中断处理回调函数,这些都是用户根据自己需要来编写的,这也说明 STM32CubeMX 不是万

能的,大家还得认真学习 STM32 基础知识。

stm32f767串口下载软件_ALIENTEK 阿波罗 STM32F767 开发板资料连载十四章 输入捕获实验...相关推荐

  1. cc2530定时器和捕获比较_ALIENTEK 阿波罗 STM32F767 开发板资料连载十四章 输入捕获实验...

    1)实验平台:alientek 阿波罗 STM32F767 开发板2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 http://weixi ...

  2. fastreport masterdata每页都显示_ALIENTEK 阿波罗 STM32F767 开发板资料连载十六章 OLED 显示实验...

    1)实验平台:alientek 阿波罗 STM32F767 开发板2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 http://weixi ...

  3. 判断按键值_ALIENTEK 阿波罗 STM32F767 开发板资料连载第七章 按键输入实验

    1)实验平台:alientek 阿波罗 STM32F767 开发板2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 第七章 按键输入实验 上 ...

  4. c语言头文件格式图片_阿波罗 STM32F767 开发板资料连载第四十九章 图片显示实验...

    1)实验平台:alientek 阿波罗 STM32F767 开发板2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 第四十九章 图片显示实验 ...

  5. stm32f767中文手册_ALIENTEK 阿波罗 STM32F767 开发板资料连载第五章 SYSTEM 文件夹

    1)实验平台:alientek 阿波罗 STM32F767 开发板2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 第五章 SYSTEM 文 ...

  6. gd32f303 设计中断优先级_ALIENTEK 阿波罗 STM32F767 开发板资料连载第九章 外部中断实验...

    1)实验平台:alientek 阿波罗 STM32F767 开发板2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 第九章 外部中断实验 这 ...

  7. 正点原子阿波罗STM32F429开发板资料发布,采用底板+核心板

    正点原子阿波罗STM32F429开发板资料发布,采用底板+核心板.... https://www.amobbs.com/thread-5654611-1-1.html 正点原子STM32开发板 htt ...

  8. application.properties 不识别_阿波罗 STM32F767 开发板资料连载第五十八章 手写识别实验

    1)实验平台:alientek 阿波罗 STM32F767 开发板2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 第五十八章 手写识别实验 ...

  9. 485串口测试工具软件_探索者 STM32F407 开发板资料连载第三十一章 485 实验

    1)实验平台:alientek 阿波罗 STM32F767 开发板2)摘自<STM32F7 开发指南(HAL 库版)>关注官方微信号公众号,获取更多资料:正点原子 第三十一章 485 实验 ...

最新文章

  1. Springmvc配置定时任务注解开发
  2. vue 引用 vue-resource步骤 (遇错排解)
  3. boost::math::bernoulli_b2n用法的测试程序
  4. oracle的安装与plsql的环境配置
  5. opengl实现经纹理映射的旋转立方体_《图形编程技术学习》(五十三)环境映射...
  6. 黑匣子_NOI导刊2010提高 (对顶堆)
  7. CSDN中Markdown格式(编辑器)语法及其使用
  8. 中兴iptv机顶盒破解教程图文:亲测中兴B760EV3、B860A、B860AV1.1完美安装应用!非ttl破解![转]
  9. 五邑大学、广东工业大学教务系统一键评教代码分享及技术简易剖析
  10. 【初识Unity】UI游戏:看看你能答对几题?(游戏可下载)
  11. Pytorch使用CPU
  12. 计算机速算训练,世界上最快的速算法,背熟就是速算神童,大脑堪比计算机!仅发一次...
  13. Android通知不显示横幅的一个坑
  14. 大数据治理工程师_大数据治理遇到的问题有哪些?大数据工程师必须认真应对...
  15. 多重剪贴板工具 CLCL
  16. 受到 1 万点暴击,二狗子被 DDoS 攻击的惨痛经历
  17. 将指定的正整数n分解成若干个互不相同的自然数的和,且使这些自然数的乘积最大
  18. No such operator image::read_file问题解决
  19. 恶搞!关闭程序进程!!嘿嘿嘿~
  20. 计算机硬件软件分析图,中国工业软件产业链全景图布局及企业分析

热门文章

  1. Tekla自定义组件类型的区别
  2. USBWriter之后恢复磁盘大小
  3. 用C/C++编程实现挖金子游戏「含项目源码」
  4. 监控录像服务器性能要求,如何打造高性能的视频监控存储系统?(《中国安防》)...
  5. altium designer多通道设计技巧
  6. Unpacker ExeCryptor 2.x.x. version 1.0 RC1 [Public Build]
  7. 2020-07-11
  8. C++课程设计--学生考试质量分析系统设计(MFC)
  9. linux 查看网卡损坏,Linux服务器网卡驱动安装及故障排除
  10. 局域网内mysql互联