完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
1)实验平台:ALIENTEK NANO STM32F411 V1开发板
2)摘自《正点原子STM32F4 开发指南(HAL 库版》关注官方微信号公众号,获取更多资料:正点原子 第十五章输入捕获实验 上一章,我们介绍了 STM32F4 的通用定时器作为 PWM 输出的使用方法,这一章,我们将向大家介绍通用定时器作为输入捕获的使用。在本章中,我们将用 tiM2 的通道 1(PA0)来做输入捕获,捕获 PA0 上高电平的脉宽(用 KEY_UP 按键输入高电平),通过串口打印高电平脉宽时间,从本章分为如下几个部分: 15.1 输入捕获简介 15.2 硬件设计 15.3 软件设计 15.4 下载验证 15.5 STM32CubeMX 配置定时器输入捕获功能 15.1 输入捕获简介 输入捕获模式可以用来测量脉冲宽度或者测量频率。我们与测量脉宽为例,用一个简图来说明输入捕获的原理,如图 15.1.1 所示: 图 15.1.1 输入捕获脉宽测量原理 如图 15.1.1 所示,就是输入捕获测量高电平脉宽的原理,假定定时器工作在向上计数模式, 图中 t1~t2 时间,就是我们需要测量的高电平时间。测量方法如下:首先设置定时器通道 x 为上升沿捕获,这样,t1 时刻,就会捕获到当前的 CNT 值,然后立即清零 CNT,并设置通道 x为下降沿捕获,这样到 t2 时刻,又会发生捕获事件,得到此时的 CNT 值,记为 CCRx2。这样, 根据定时器的计数频率,我们就可以算出 t1~t2 的时间,从而得到高电平脉宽。 在 t1~t2 之间,可能产生 N 次定时器溢出,这就要求我们对定时器溢出,做处理,防止高电平太长,导致数据不准确。如图15.1.1所示, t1~t2之间,CNT计数的次数等于:N*ARR+CCRx2, 有了这个计数次数,再乘以 CNT 的计数周期,即可得到 t2-t1 的时间长度,即高电平持续时间。输入捕获的原理,我们就介绍到这。 在 STM32F411 的定时器都有输入捕获功能。STM32F4 的输入捕获,简单的说就是通过检测 TIMx_CHx 上的边沿信号,在边沿信号发生跳变(比如上升沿/下降沿)的时候,将当前定时器的值(TIMx_CNT)存放到对应的通道的捕获/比较寄存器(TIMx_CCRx)里面,完成一次捕 获。同时还可以配置捕获时是否触发中断/DMA 等。 本章我们用到 TIM2_CH1 来捕获高电平脉宽,捕获原理如图 15.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,这个寄存器在输入捕获的时候,非常有 用,有必要重新介绍,该寄存器的各位描述如图 15.1.2 所示: 图 15.1.2 TIMx_CCMR1 寄存器各位描述 当在输入捕获模式下使用的时候,对应图 15.1.2 的第二行描述,从图中可以看出, TIMx_CCMR1 明显是针对 2 个通道的配置,低八位[7:0]用于捕获/比较通道 1 的控制,而高八 位[15:8]则用于捕获/比较通道 2 的控制,因为 TIMx 还有 CCMR2 这个寄存器,所以可以知道 CCMR2 是用来控制通道 3 和通道 4(详见《STM32F411xC/E 参考手册》364 页,13.4.8 节)。 这里我们用到的是 TIM2 的捕获/比较通道 1,我们重点介绍 TIMx_CCMR1 的[7:0]位(其 高 8 位配置类似),TIMx_CCMR1 的[7:0]位详细描述见图 15.1.3 所示: 图 15.1.3 TIMx_CCMR1 [7:0]位详细描述 其中 CC1S[1:0],这两个位用于 CCR1 的通道配置,这里我们设置 IC1S[1:0]=01,也就是配 置 IC1 映射在 TI1 上(关于 IC1,TI1 不明白的,可以看《STM32F411xC/E 参考手册》316 页 的图 87-通用定时器框图),即 CC1 对应 TIMx_CH1。 输入捕获 1 预分频器 IC1PSC[1:0],这个比较好理解。我们是 1 次边沿就触发 1 次捕获,所 以选择 00 就是了。 输入捕获 1 滤波器 IC1F[3:0],这个用来设置输入采样频率和数字滤波器长度。其中, fCK_INT 是定时器的输入频率(TIMxCLK),一般为 96Mhz,而 fDTS 则是根据 TIMx_CR1 的 CKD[1:0] 的设置来确定的,如果 CKD[1:0]设置为 00,那么 fDTS = fCK_INT。N 值就是滤波长度,举个简 单的例子:假设 IC1F[3:0]=0011,并设置 IC1 映射到通道 1 上,且为上升沿触发,那么在捕获 到上升沿的时候,再以 fCK_INT 的频率,连续采样到 8 次通道 1 的电平,如果都是高电平,则 说明却是一个有效的触发,就会触发输入捕获中断(如果开启了的话)。这样可以滤除那些高 电平脉宽低于 8 个采样周期的脉冲信号,从而达到滤波的效果。这里,我们不做滤波处理,所 以设置 IC1F[3:0]=0000,只要采集到上升沿,就触发捕获。 再来看看捕获/比较使能寄存器:TIMx_CCER,该寄存器的各位描述见图 14.1.2(在第 14 章)。本章我们要用到这个寄存器的最低 2 位,CC1E 和 CC1P 位。这两个位的描述如图 15.1.3 所示: 图 15.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 值,通过两次捕获(一 次上升沿捕获,一次下降沿捕获)的差值,就可以计算出高电平脉冲的宽度。 至此,我们把本章要用的几个相关寄存器都介绍完了,本章要实现通过输入捕获,来获取 TIM2_CH1(PA0)上面的高电平脉冲宽度,并从串口打印捕获结果。下面我们介绍输入捕获的配 置步骤: 1)开启 TIM2 时钟,配置 PA0 为复用功能(AF1),并开启下拉电阻。 要使用 TIM2,我们必须先开启 TIM2 的时钟。同时我们要捕获 TIM2_CH1 上面的高电平 脉宽,所以先配置 PA0 为带下拉的复用功能,同时,为了让 PA0 的复用功能选择连接到 TIM2, 所以设置 PA0 的复用功能为 AF1,即连接到 TIM2 上面。 开启定时器和 GPIO 时钟的方法和上一章是一样的,这里我们就不做过多讲解。 配置PA0为复用功能,并开启下拉功能也和上一章一样是通过函数HAL_GPIO_Init来实现。 由于这一步配置过程和上一章几乎没有区别,所以这里我们直接列出配置代码: __HAL_RCC_TIM2_CLK_ENABLE(); //使能 TIM2 时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); //开启 GPIOA 时钟 GPIO_Initure.Pin=GPIO_PIN_0; //PA0 GPIO_Initure.Mode=GPIO_MODE_AF_INPUT; //复用输入 GPIO_Initure.Pull=GPIO_PULLDOWN; //下拉 GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速 GPIO_Initure.Alternate=GPIO_AF1_TIM2; //PA0 复用为 TIM2 通道 1 HAL_GPIO_Init(GPIOA,&GPIO_Initure); 跟上一讲 PWM 输出类似,这里我们使用的是定时器 2 的通道 1。 2)初始化 TIM2,设置 TIM2 的 ARR 和 PSC。 当上一讲 PWM 输出实验一样,当使用定时器做输入捕获功能时,在 HAL 库中并不使用定 时器初始化函数 HAL_TIM_Base_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 TIM2_Handler; TIM2_Handler.Instance=TIM2; //通用定时器 2 TIM2_Handler.Init.Prescaler=psc; //分频系数 TIM2_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; //向上计数器 TIM2_Handler.Init.Period=arr; //自动装载值 TIM2_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频银子 HAL_TIM_IC_Init(&TIM2_Handler);//初始化输入捕获时基参数 3)设置 TIM2 的输入比较参数,开启输入捕获 TIM2_CCMR1 寄存器控制着输入捕获 1 和 2 的模式,包括映射方式,滤波和分频等。这里 我们需要设置通道 1 为输入模式,且 IC1 映射到 TI1(通道 1)上面,并且不使用滤波(提高响 应速度)器。HAL 库是通过 HAL_TIM_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(&TIM2_Handler,TIM_CHANNEL_1); //清除极性设置 TIM_SET_CAPTUREPOLARITY(&TIM2_Handler,TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING);//定时器 2 通道 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 TIM2_CH1Config; TIM2_CH1Config.ICPolarity=TIM_ICPOLARITY_RISING; //上升沿捕获 TIM2_CH1Config.ICSelection=TIM_ICSELECTION_DIRECTTI;//映射到 TI1 上 TIM2_CH1Config.ICPrescaler=TIM_ICPSC_DIV1; //配置输入分频,不分频 TIM2_CH1Config.ICFilter=0; //配置输入滤波器,不滤波 HAL_TIM_IC_ConfigChannel(&TIM2_Handler,&TIM2_CH1Config,TIM_CHANNEL_1); //配置 TIM2 通道 1 4)使能捕获和更新中断(设置 TIM2 的 DIER 寄存器) 因为我们要捕获的是高电平信号的脉宽,所以,第一次捕获是上升沿,第二次捕获时下降 沿,必须在捕获上升沿之后,设置捕获边沿为下降沿,同时,如果脉宽比较长,那么定时器就 会溢出,对溢出必须做处理,否则结果就不准了。这两件事,我们都在中断里面做,所以必须 开启捕获中断和更新中断。 HAL 库中开启定时器中断方法在定时器中断实验已经讲解,方法为: __HAL_TIM_ENABLE_IT(&TIM2_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)使能定时器(设置 TIM2 的 CR1 寄存器) 在步骤 4 中,如果我们调用了函数 HAL_TIM_IC_Start_IT 来开启输入捕获通道以及输入捕 获中断,实际上它同时也开启了相应的定时器。单独的开启定时器的方法为: __HAL_TIM_ENABLE();//开启定时器方法 6)设置 NVIC 中断优先级 因为我们要使用到中断,所以我们在系统初始化之后,需要先设置中断优先级,这里方法 跟前面讲解一致,这里我们就不累赘了。 这里大家要注意,一般清除下 NVIC 配置我们都会放在 MSP 回调函数中。对于输入捕获功 能,回调函数是我们步骤 2 讲解的函数 HAL_TIM_IC_MspInit。 7)编写中断服务函数 最后编写中断服务函数,定时器 2 中断服务函数为: void TIM2_IRQHandler(void); 和定时器中实验一样,一帮情况下,我们都不把中断控制逻辑直接编写在中断服务函数中, 因为 HAL 库提供了一个共用的中断处理入口参数 HAL_TIM_IRQHandler,该函数中会对中断 来源进行判断然后调用相应的中断处理回调函数,HAL 库提供了多个中断处理回调函数,本章 实验,我们要使用到更新中断和捕获中断,所以我们要使用的回调函数为: void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)//更新中断(溢出)中断 void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//捕获中断 我们只需要在我们工程中,重新定义这两个函数,编写中断处理控制逻辑即可。 15.2 硬件设计 本实验用到的硬件资源有: 1) 指示灯 DS0 2) KEY_UP 按键 3) 串口 4) 定时器 TIM2 前面 3 个,在之前的章节均有介绍。本节,我们将捕获 TIM2_CH1(PA0)上的高电平脉 宽,通过 KEY_UP 按键输入高电平,并从串口打印高电平脉宽。 15.3 软件设计 打开光盘的输入捕获实验,可以看到,我们的工程和上一个实验没有什么改动。因为我们 的输入捕获代码是直接添加在 timer.c 和 timer.h 中。同时输入捕获相关的库函数还是在 stm32f4xx_hal_tim.c 和 stm32f4xx_hal_tim.h 文件中。 我们在 timer.c 里面加入如下代码: TIM_HandleTypeDef TIM2_Handler; //定时器 2 句柄 //定时器 2 通道 1 输入捕获配置 //arr:自动重装值(TIM2 是 16 位的!!) //psc:时钟预分频数 void TIM2_CH1_Cap_Init(u32 arr,u16 psc) { TIM_IC_InitTypeDef TIM2_CH1Config; TIM2_Handler.Instance=TIM2; //通用定时器 2 TIM2_Handler.Init.Prescaler=psc; //分频系数 TIM2_Handler.Init.CounterMode=TIM_COUNTERMODE_UP; //向上计数器 TIM2_Handler.Init.Period=arr; //自动装载值 TIM2_Handler.Init.ClockDivision=TIM_CLOCKDIVISION_DIV1;//时钟分频因子 TIM2_Handler.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_ENABLE; //使能自动重载 HAL_TIM_IC_Init(&TIM2_Handler); //初始化输入捕获时基参数 TIM2_CH1Config.ICPolarity=TIM_ICPOLARITY_RISING; //上升沿捕获 TIM2_CH1Config.ICSelection=TIM_ICSELECTION_DIRECTTI;//映射到 TI1 上 TIM2_CH1Config.ICPrescaler=TIM_ICPSC_DIV1; //配置输入分频,不分频 TIM2_CH1Config.ICFilter=0; //配置输入滤波器,不滤波 HAL_TIM_IC_ConfigChannel(&TIM2_Handler,&TIM2_CH1Config, TIM_CHANNEL_1); //配置 TIM2 通道 1 HAL_TIM_IC_Start_IT(&TIM2_Handler,TIM_CHANNEL_1); //开启 TIM2 的捕获通道 1,并且开启捕获中断 __HAL_TIM_ENABLE_IT(&TIM2_Handler,TIM_IT_UPDATE); //使能更新中断 HAL_NVIC_SetPriority(TIM2_IRQn,2,0); //设置中断优先级,抢占优先级 2,子优先级 0 HAL_NVIC_EnableIRQ(TIM2_IRQn); //开启 ITM2 中断通道 } //定时器 2 底层驱动,时钟使能,引脚配置 //此函数会被 HAL_TIM_IC_Init()调用 //htim:定时器 2 句柄 void HAL_TIM_IC_MspInit(TIM_HandleTypeDef *htim) { GPIO_InitTypeDef GPIO_Initure; __HAL_RCC_TIM2_CLK_ENABLE(); //使能 TIM2 时钟 __HAL_RCC_GPIOA_CLK_ENABLE(); //开启 GPIOA 时钟 GPIO_Initure.Pin=GPIO_PIN_0; //PA0 GPIO_Initure.Mode=GPIO_MODE_AF_INPUT; //复用输入 GPIO_Initure.Pull=GPIO_PULLDOWN; //下拉 GPIO_Initure.Speed=GPIO_SPEED_HIGH; //高速 GPIO_Initure.Alternate=GPIO_AF1_TIM2; //PA0 复用为 TIM2 通道 1 HAL_GPIO_Init(GPIOA,&GPIO_Initure); HAL_NVIC_SetPriority(TIM2_IRQn,2,0);//设置中断优先级,抢占优先级 2,子优先级 0 HAL_NVIC_EnableIRQ(TIM2_IRQn); //开启 TIM2 中断通道 } //捕获状态 //[7]:0,没有成功的捕获;1,成功捕获到一次. //[6]:0,还没捕获到低电平;1,已经捕获到低电平了. //[5:0]:捕获低电平后溢出的次数 u8 TIM2CH1_CAPTURE_STA=0; //输入捕获状态 u32 TIM2CH1_CAPTURE_VAL; //输入捕获值 //定时器 2 中断服务函数 void TIM2_IRQHandler(void) { HAL_TIM_IRQHandler(&TIM2_Handler); //定时器共用处理函数 } //定时器更新中断(计数溢出)中断处理回调函数, 该函数在 HAL_TIM_IRQHandler 中会 被调用 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) //更新中断(溢出)发生时执行 { if((TIM2CH1_CAPTURE_STA&0X80)==0)//还未成功捕获 { if(TIM2CH1_CAPTURE_STA&0X40)//已经捕获到高电平了 { if((TIM2CH1_CAPTURE_STA&0X3F)==0X3F)//高电平太长了 { TIM2CH1_CAPTURE_STA|=0X80;//标记成功捕获了一次 TIM2CH1_CAPTURE_VAL=0XFFFFFFFF; }else TIM2CH1_CAPTURE_STA++; } } } //定时器输入捕获中断处理回调函数,该函数在 HAL_TIM_IRQHandler 中会被调用 void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)//捕获中断发生时执行 { if((TIM2CH1_CAPTURE_STA&0X80)==0)//还未成功捕获 { if(TIM2CH1_CAPTURE_STA&0X40)//捕获到一个下降沿 { TIM2CH1_CAPTURE_STA|=0X80;//标记成功捕获到一次高电平脉宽 TIM2CH1_CAPTURE_VAL=HAL_TIM_ReadCapturedValue(&TIM2_Handler, TIM_CHANNEL_1);//获取当前的捕获值. TIM_RESET_CAPTUREPOLARITY(&TIM2_Handler,TIM_CHANNEL_1); //一定要先清除原来的设置!! TIM_SET_CAPTUREPOLARITY(&TIM2_Handler,TIM_CHANNEL_1, TIM_ICPOLARITY_RISING);//配置 TIM2 通道 1 上升沿捕获 }else //还未开始,第一次捕获上升沿 { TIM2CH1_CAPTURE_STA=0; //清空 TIM2CH1_CAPTURE_VAL=0; TIM2CH1_CAPTURE_STA|=0X40; //标记捕获到了上升沿 __HAL_TIM_DISABLE(&TIM2_Handler); //关闭定时器 2 __HAL_TIM_SET_COUNTER(&TIM2_Handler,0); TIM_RESET_CAPTUREPOLARITY(&TIM2_Handler,TIM_CHANNEL_1); //一定要先清除原来的设置!! TIM_SET_CAPTUREPOLARITY(&TIM2_Handler,TIM_CHANNEL_1, TIM_ICPOLARITY_FALLING);//定时器 2 通道 1 设置为下降沿捕获 __HAL_TIM_ENABLE(&TIM2_Handler);//使能定时器 2 } } } 此部分代码包含5个函数,其中TIM2_CH1_Cap_Init函数和回调函数HAL_TIM_IC_MspInit 共同用来实现 15.1 小节讲解的步骤 1~7。TIM2_IRQHandler 是 TIM2 的中断服务函数,该函数 内部和定时器中断实验一样,只有一行代码就是直接调用函数 HAL_TIM_IRQHandler。根据我 们前面的讲解,函数 HAL_TIM_IRQHandler 内部会对中断来源进行判断(中断标志位),然后 分别调用对应的中断处理回调函数,最后还会自动清除相应的中断标志位。函数 HAL_TIM_PeriodElapsedCallback 和 HAL_TIM_IC_CaptureCallback 就是我们要着重讲解的定时 器更新中断(溢出)以及输入捕获中断回调函数。 同时,在该文件中我们还定义了两个全局变量,用于辅助实现高电平捕获。其中 TIM2CH1_CAPTURE_STA,是用来记录捕获状态,该变量类似我们在 usart.c 里面自行定义的 USART_RX_STA 寄存器(其实就是个变量,只是我们把它当成一个寄存器那样来使用)。 TIM2CH1_CAPTURE_STA 各位描述如表 15.3.1 所示: 表 15.3.1 TIM2CH1_CAPTURE_STA 各位描述 另外一个变量 TIM2CH1_CAPTURE_VAL,则用来记录捕获到下降沿的时候,TIM2_CNT 的值。 现在我们来介绍一下,捕获高电平脉宽的思路:首先,设置 TIM2_CH1 捕获上升沿,这在 TIM2_Cap_Init 函数执行的时候就设置好了,然后等待上升沿中断到来,当捕获到上升沿中断, 此时如果 TIM2CH1_CAPTURE_STA 的第 6 位为 0,则表示还没有捕获到新的上升沿,就先把 TIM2CH1_CAPTURE_STA、TIM2CH1_CAPTURE_VAL 和 TIM2->CNT 等清零,然后再设置 TIM2CH1_CAPTURE_STA 的第 6 位为 1,标记捕获到高电平,最后设置为下降沿捕获,等待 下降沿到来。如果等待下降沿到来期间,定时器发生了溢出,就在 TIM2CH1_CAPTURE_STA 里面对溢出次数进行计数,当最大溢出次数来到的时候,就强制标记捕获完成(虽然此时还没 有捕获到下降沿)。当下降沿到来的时候,先设置 TIM2CH1_CAPTURE_STA 的第 7 位为 1, 标记成功捕获一次高电平,然后读取此时的定时器值到 TIM2CH1_CAPTURE_VAL 里面,最后 设置为上升沿捕获,回到初始状态。 这样,我们就完成一次高电平捕获了,只要 TIM2CH1_CAPTURE_STA 的第 7 位一直为 1, 那么就不会进行第二次捕获,我们在main函数处理完捕获数据后,将TIM2CH1_CAPTURE_STA 置零,就可以开启第二次捕获。 timer.h 头文件内容比较简单,主要是函数申明,这里我们不做过多讲解。 接下来我们看看 main 函数内容: extern u8 TIM2CH1_CAPTURE_STA; //输入捕获状态 extern u32 TIM2CH1_CAPTURE_VAL; //输入捕获值 int main(void) { u16 t=0; long long temp=0; HAL_Init(); //初始化 HAL 库 Stm32_Clock_Init(96,4,2,4); //设置时钟,96Mhz delay_init(96); //初始化延时函数 uart_init(115200); //初始化串口 LED_Init(); //初始化 LED TIM2_CH1_Cap_Init(0XFFFFFFFF,96-1);//以 1Mhz 的频率计数 while(1) { delay_ms(10); if(TIM2CH1_CAPTURE_STA&0X80) //成功捕获到了一次高电平 { temp=TIM2CH1_CAPTURE_STA&0X3F; temp*=0XFFFFFFFF; //溢出时间总和 temp+=TIM2CH1_CAPTURE_VAL; //得到总的高电平时间 printf("HIGH:%lld usrn",temp);//打印总的高点平时间 TIM2CH1_CAPTURE_STA=0; //开启下一次捕获 } t++; if(t==20) { t=0; LED0=!LED0; } } } 在 main 函数中,通过设置 TIM2_Cap_Init(0XFFFFFFFF,96-1),将 TIM2_CH1 的捕获计数 器设计为 1us 计数一次,并设置重装载值为最大,所以我们的捕获时间精度为 1us。 主函数通过 TIM2CH1_CAPTURE_STA 的第 7 位,来判断有没有成功捕获到一次高电平, 如果成功捕获,则将高电平时间通过串口输出到电脑。 至此,我们的软件设计就完成了。 15.4 下载验证 在完成软件设计之后,将我们将编译好的文件下载到 NANO STM32F4 开发板上,可以看 到 DS0 在闪烁,证明程序已经正常在跑了,我们再打开串口调试助手,选择对应的串口,然后 按 KEY_UP 按键,可以看到串口打印的高电平持续时间,如图 15.4.1 所示: 图 15.4.1 采样高电平的时间 15.5 STM32CubeMX 配置定时器输入捕获功能 使用 STM32CubeMX 配置输入捕获功能初始化代码步骤如下: ① 在 TIMERS->TIM2 配置项中,配置 Channel1 的值为 Input Capture direct mode,然后选 中Internal Clock。操作过程如下图 15.5.1 所示: 图 15.5.1 TIM2 配置 ② 进入 Configuration->Parameter Setting 配置项,Counter Settings 配置栏下面的五个选项 就是用来配置定时器的预分频系数,自动装载值,计数模式,时钟分频因子。在界面的 Input Capture Channel 1 配置栏配置输入捕获通道 1 的捕获极性,分频系数,映射,滤波器等参 数,操作方法如下图 15.5.2 所示: 图 15.5.2 TIM2 参数设置 3 进入 System Core->NVIC 配置页中,在弹出的界面中点击 NVIC 选项卡,配置 Intertupe Table 中的 TIM2 global interrupt,使能中断,配置抢占优先级和相应优先级。 配置完上面步骤后,生成代码。在生成的代码中,并没有使能相应中断的代码,也没有改 写中断处理回调函数,这些都是用户根据自己需要来编写的,这也说明 STM32CubeMX 不知万 能得,大家还得认真学习 STM32 基础知识。 |
|
相关推荐
|
|
hal库中i2c卡死在HAL_I2C_Master_Transmit
881 浏览 1 评论
LL库F030进行3个串口收发,2个串口为232,一个为485,长时间后,会出现串口1停止运行,另外两个正常,只有重启复原
1402 浏览 1 评论
516 浏览 0 评论
1020 浏览 0 评论
1149 浏览 1 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-3 08:21 , Processed in 0.548358 second(s), Total 66, Slave 48 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号