【STM32】标准库与HAL库对照学习教程十--输入捕获实验


STM32全部教程【STM32】标准库与HAL库对照学习系列教程大全

一、前言

本篇文章是对定时器功能中的输入捕获功能的讲解与配置,本篇从输入捕获原理讲解开始,一步一步让您学会输入捕获的知识与配置,您可以点击目录跳转您想要看的内容。

二、准备工作

  • STM32F103开发板(我用的是普中的STM32F103ZE开发板)
  • cubemx软件、keil 5(MDK)
  • 开发板原理图

有关于定时器不了解的可以看这篇文章【STM32】标准库与HAL库对照学习教程七–定时器中断

三、输入捕获介绍

1、简介

定时器章节我们了解到通用定时器具有多种功能,输入捕获就是其中一种。
STM32F1除了基本定时器TIM6和TIM7其他定时器都具有输入捕获功能
输入捕获可以对输入的信号上升沿,下降沿或者双边沿进行捕获,通常用于测量输入信号的脉宽、测量 PWM 输入信号的频率及占空比。
与输出比较一样,每个定时器的输入捕获有4个通道,分别映射到四个引脚上

2、原理

在输入捕获模式下,捕获中断定时器中断是打开的。
在这里插入图片描述

  • ①输入捕获初始化
  • ②检测到边沿电平变化,触发捕获中断,在中断函数里,清空计算器TIMx_CNT的值
  • ③计算期间,可能会达到重装载值ARR触发定时器中断,在中断函数里,记录定时器计算溢出的次数为N
  • ④再次检测到边沿电平变化,触发中断,在中断函数里,得到计算器TIMx_CNT的值,记录到一个变量里(CCR)
  • ⑤高电平持续时间(s)=(CCR+N*ARR)/时钟频率,ARR为定时器重装载值。

在拿一个对比图形
在这里插入图片描述

四、输入捕获结构

1、通道映射表

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

图片来源于STM32F1xx中文参考手册 116页

2、结构图


在这里插入图片描述
可以看到,捕获与比较的通道是相同的,所以在捕获与比较只能用一种,并且在图中可以看出通道1与通道2的捕获是可以互通的。

在这里插入图片描述

五、输入捕获寄存器位

由上面的图2,可以知道,控制输入捕获的比较重要的几个位,分别是来自TIMx_CCMR1寄存器的ICF[3:0]、CC1S[1:0]、ICPS[1:0]位TIMx_CCER寄存器的CC1P、CC1E位


(1)ICF[3:0]

在这里插入图片描述
数字滤波器由一个事件计数器组成,假设我们是检测高电平,滤波N次,当连续N次采样检测,如果都是高电平,则说明这是一个有效的电平信号,这样便可以过滤掉那些因为某些而干扰产生的一些信号 。
输入捕获滤波器IC1F[3:0],这个用于设置采样频率和数字滤波器长度


(2)CC1S[1:0]

在这里插入图片描述
因为通道1与通道2是可以互通的,所以要使用这个位选择映射位置


(3)ICPS[1:0]

在这里插入图片描述
捕获预分频器,表示遇到N个边沿时捕获一次


(4)CC1P

在这里插入图片描述
配置通道是输入捕获还是输出比较


(5)CC1E

在这里插入图片描述
捕获使能位。


图片来源于STM32F1xx中文参考手册 282页

六、硬件电路

本篇使用输入捕获检测按键引脚高电平的持续时间
按键电路请参考自己开发板的原理图。
在这里插入图片描述
在这里插入图片描述

七、标准库配置输入捕获

1、配置步骤

  • (1)使能定时器及端口时钟,并设置引脚复用器映射和引脚模式等
  • (2)初始化定时器参数,包含自动重装值,分频系数,计数方式等
  • (3)设置通用定时器的输入捕获参数,开启输入捕获功能
  • (4)开启捕获和定时器溢出(更新)中断
  • (5)设置定时器中断优先级,使能定时器中断通道
  • (6)编写定时器中断服务函数
  • (7)使能定时器

2、配置工程

标准库


(1)复制上一章的工程,并重命名为10、输入捕获实验
在这里插入图片描述


(2)进入工程文件,进入APP文件,新建Input用来存放与输入捕获相关的文件。
在这里插入图片描述


(3)打开工程,新建文件,并命名为input.h与input.c

在这里插入图片描述


在这里插入图片描述


(4)添加文件到目录,并添加头文件路径

在这里插入图片描述

在这里插入图片描述


3、实验程序

input.h

#ifndef INPUT_H_
#define INPUT_H_

#include "stm32f10x.h"

extern volatile u16 TIM_Exceed;   //定时器计数溢出次数
extern volatile u8 TIM_IC_Edge;  //边沿检测计数
extern volatile u16 TIM_IC_VAL;   //存放计数器CNT的值


void TIM5_CH1_Input_Init(u16 Psc,u16 Per);

#endif

input.c

#include "input.h"

volatile u16 TIM_Exceed = 0;   //定时器计数溢出次数
volatile u8 TIM_IC_Edge = 0;  //边沿检测计数
volatile u16 TIM_IC_VAL = 0;   //存放计数器CNT的值

/*************************************************
*函数名:       TIM5_CH1_Input_Init
*函数功能:     定时器5通道1输入捕获初始化函数
*输入:         Psc:定时器分频,Per:自动重装载值
*返回值:       无
**************************************************/
void TIM5_CH1_Input_Init(u16 Psc,u16 Per)
{
	GPIO_InitTypeDef GPIO_InitStruct;
	TIM_TimeBaseInitTypeDef TIM_TimeBaseInitStruct;
	TIM_ICInitTypeDef TIM_ICInitStruct;
	NVIC_InitTypeDef NVIC_InitStruct;
	
	RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM5, ENABLE);  //使能定时器时钟
	RCC_APB1PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); //使能引脚时钟
	
	//引脚配置
	GPIO_InitStruct.GPIO_Pin = GPIO_Pin_0;  //PA0
	GPIO_InitStruct.GPIO_Mode = GPIO_Mode_IPD;  //下拉输入
	GPIO_InitStruct.GPIO_Speed = GPIO_Speed_50MHz;
	GPIO_Init(GPIOA, &GPIO_InitStruct);   ///初始化引脚
	
	//定时器配置
	TIM_TimeBaseInitStruct.TIM_Prescaler = Psc;  //时钟分频
	TIM_TimeBaseInitStruct.TIM_Period = Per;  //重装载数
	TIM_TimeBaseInitStruct.TIM_CounterMode = TIM_CounterMode_Up;  //向上计数模式
	TIM_TimeBaseInitStruct.TIM_ClockDivision = TIM_CKD_DIV1;  //系统1分频
	TIM_TimeBaseInit(TIM5, &TIM_TimeBaseInitStruct);  //初始化时钟
	
	//捕获通道设置
	TIM_ICInitStruct.TIM_Channel = TIM_Channel_1;  //选择通道1
	TIM_ICInitStruct.TIM_ICPrescaler = TIM_ICPSC_DIV1; //预分频器的值设置为1
	TIM_ICInitStruct.TIM_ICPolarity = TIM_ICPolarity_Rising; //设置极性为上升沿捕获
	TIM_ICInitStruct.TIM_ICFilter = 0;   //不进行滤波
	TIM_ICInitStruct.TIM_ICSelection = TIM_ICSelection_DirectTI; //选择直接映射到TI1
	TIM_ICInit(TIM5, &TIM_ICInitStruct); //输入捕获初始化
	
	TIM_ITConfig(TIM5, TIM_IT_Update|TIM_IT_CC1, ENABLE);  //开启捕获中断与定时器中断
	
	NVIC_InitStruct.NVIC_IRQChannel = TIM5_IRQn;  //定时器中断通道
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority = 0;  //抢占式优先级
	NVIC_InitStruct.NVIC_IRQChannelSubPriority = 3;  //响应式优先级
	NVIC_InitStruct.NVIC_IRQChannelCmd = ENABLE;  //中断通道使能
	NVIC_Init(&NVIC_InitStruct);  //中断通道初始化
	
	TIM_Cmd(TIM5, ENABLE);    //定时器5使能
}

/*************************************************
*函数名:      TIM5_IRQHandler
*函数功能:    定时器5的中断函数
*输入:        无
*返回值:      无
**************************************************/
void TIM5_IRQHandler()
{
	if(TIM_GetITStatus(TIM5, TIM_IT_Update))  //计数器溢出产生的中断
	{
		if(TIM_IC_Edge == 1)  //高电平状态
		{
			TIM_Exceed++;  //定时器计数溢出次数加1
		}
	}
	if(TIM_GetITStatus(TIM5, TIM_IT_CC1))   //捕获中断
	{
		if(TIM_IC_Edge == 0)   //捕获的上升沿
		{
			TIM_Cmd(TIM5, DISABLE);    //定时器5失能
			TIM_OC1PolarityConfig(TIM5, TIM_OCPolarity_Low);  //设置极性为下升沿捕获
			TIM_Exceed = 0;   //定时器计数溢出次数清零
			TIM_SetCounter(TIM5, 0);  //定时器5的CNT清零
			TIM_IC_Edge++;  //下个状态为下降沿捕获
			TIM_Cmd(TIM5, ENABLE);    //定时器5使能
		}
		else    //捕获的是下降沿
		{
			TIM_Cmd(TIM5, DISABLE);    //定时器5失能
			TIM_IC_VAL = TIM_GetCapture1(TIM5);  //获取定时器5的计数值
			TIM_OC1PolarityConfig(TIM5, TIM_OCPolarity_High);  //设置极性为上升沿捕获
			TIM_IC_Edge++;  //下个状态到主函数
		}
	}
	TIM_ClearITPendingBit(TIM5, TIM_IT_Update|TIM_IT_CC1); //清空标志位
}

main.c

#include "LED.h"
#include "Delay.h"
#include "System.h"
#include "input.h"
#include "usart.h"
#include "stdio.h"


/*************************************************
*函数名:    main
*函数功能: 主函数
*输入:     无  
*返回值:   无
**************************************************/
int main()
{
	u32 time;
	SysTick_Init(72);
	NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2);  //抢占式优先级与响应式优先级的分组
	LED_Init();
	USART1_Init(9600);
	TIM5_CH1_Input_Init(71,0xffff);   //计一个数1us
	while(1)
	{
		if(TIM_IC_Edge == 2)
		{
			time = TIM_IC_VAL+TIM_Exceed*0xffff; //得到总计数值
			printf("高电平持续时间是 %d ms\r\n",time/1000); //打印高电平持续时间
			TIM_IC_Edge = 0;  //边沿捕获值清零
			TIM_Cmd(TIM5, ENABLE);	 //重新使能定时器
		}
	}
}

4、实验效果

在这里插入图片描述

八、HAL库配置输入捕获

1、使用cubemx生成工程


(1) 打开cubemx,新建工程,选择自己的芯片。
在这里插入图片描述


(2) 配置RCC,选择外部高速时钟
在这里插入图片描述


(3) 配置时钟树
在这里插入图片描述


(4) 配置捕获通道

在这里插入图片描述

在这里插入图片描述

  • 上升沿捕获
  • 直接映射到引脚
  • 不分频
  • 0次滤波

③打开中断
在这里插入图片描述
④设置引脚为下拉
在这里插入图片描述


(5) 配置串口
在这里插入图片描述


(6) 工程文件配置并生成工程

在这里插入图片描述


在这里插入图片描述


2、相关函数

  • HAL_TIM_ReadCapturedValue() ; 获取捕获通道计数器的函数
  • HAL_TIM_IC_Start();开启输入捕获通道
  • HAL_TIM_IC_Stop();关闭输入捕获通道
  • HAL_TIM_IC_Start_IT();开启输入捕获通道与中断
  • HAL_TIM_IC_Stop_IT();关闭输入捕获通道与中断
  • __HAL_TIM_SET_COUNTER(HANDLE, COUNTER)设置计数器CNT的值,(HANDLE:时钟,COUNTER:设置的值)
  • TIM_RESET_CAPTUREPOLARITY(HANDLE, CHANNEL);清除通道极性
  • TIM_SET_CAPTUREPOLARITY(HANDLE, CHANNEL, POLARITY);设置通道极性
  • __HAL_TIM_SET_CAPTUREPOLARITY(HANDLE, CHANNEL, POLARITY);清除通道极性,并设置通道极性
  • HAL_TIM_PeriodElapsedCallback() ;定时器中断回调函数
  • HAL_TIM_IC_CaptureCallback();捕获中断回调函数

Channel

  • TIM_CHANNEL_1
  • TIM_CHANNEL_2
  • TIM_CHANNEL_3
  • TIM_CHANNEL_4
  • TIM_CHANNEL_ALL
    极性
  • TIM_ICPOLARITY_RISING-上升沿
  • TIM_ICPOLARITY_FALLING-下降沿
  • TIM_ICPOLARITY_BOTHEDGE-上升下降沿

3、实验程序



在这里插入图片描述

#include<stdio.h>


在这里插入图片描述

int fputc(int ch, FILE *f)
{
  HAL_UART_Transmit(&huart1, (uint8_t *)&ch, 1, 0xffff);
  return ch;
}
uint8_t TIM5_CH1_Edge=0;  //状态变化时,计数值
uint32_t TIM5_CH1_VAL=0;  //储存计数器的记录值
uint32_t TIM5_CH1_OVER=0; //计数器溢出的个数
uint32_t time;   //高电平持续时间


在这里插入图片描述

HAL_TIM_Base_Start_IT(&htim5);	//打开定时器中断
HAL_TIM_IC_Start_IT(&htim5,TIM_CHANNEL_1);  //打开输入捕获
if(TIM5_CH1_Edge == 2)
		{
			TIM5_CH1_Edge = 0;  //重新开始捕获
			time = TIM5_CH1_VAL + TIM5_CH1_OVER*0xffff;
			printf("高电平持续时间为 %d ms\r\n", time/1000);
			HAL_TIM_IC_Start_IT(&htim5,TIM_CHANNEL_1);  //打开输入捕获
		}


回调函数
在这里插入图片描述

void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim)
{
	if(htim == &htim5)
	{
		if(TIM5_CH1_Edge == 1)
		{
			TIM5_CH1_OVER++;  //定时器溢出值增加
		}
	}
}
void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim)
{
	if(htim == &htim5)
	{
		if(TIM5_CH1_Edge == 0) //捕获到上升沿
		{
			TIM5_CH1_Edge++;  //进入捕获下降沿状态
			TIM5_CH1_OVER = 0;  //定时器溢出值清零
			__HAL_TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_FALLING); //设置捕获极性为下降沿
			__HAL_TIM_SET_COUNTER(&htim5,0);  //设置定时器CNT计数器的值为0
		}
		else //捕获到下升沿
		{
			HAL_TIM_IC_Stop_IT(&htim5,TIM_CHANNEL_1); //关闭定时器5
			TIM5_CH1_Edge++;  //进入到主函数状态
			TIM5_CH1_VAL = HAL_TIM_ReadCapturedValue(&htim5,TIM_CHANNEL_1); //读取捕获通道的值
			__HAL_TIM_SET_CAPTUREPOLARITY(&htim5,TIM_CHANNEL_1,TIM_ICPOLARITY_RISING); //设置捕获极性为上降沿
		}
	}
}

4、实验效果

在这里插入图片描述
到这里就结束啦!
在这里插入图片描述

  • 53
    点赞
  • 190
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 19
    评论
STM32标准库HAL库都是针对STM32系列微控制器的库,但是它们的设计思路和使用方法有所不同。STM32标准库是由ST公司提供的一套基于寄存器操作的低层驱动库,而HAL库则是基于标准库的封装,提供了更加高级的API接口,使得开发者可以更加方便地进行开发。 具体来说,STM32标准库主要包括以下几个部分: 1. CMSIS(Cortex Microcontroller Software Interface Standard):这是一套由ARM公司提供的针对Cortex-M系列微控制器的标准接口,包括了一些通用的API接口和一些与具体芯片相关的头文件和链接脚本等。 2. 寄存器操作库:这是STM32标准库的核心部分,通过对芯片内部寄存器的直接操作,实现了对外设的控制和数据传输等功能。 3. 外设驱动库:这是对一些常用外设(如USART、SPI、I2C等)的封装,提供了更加方便的API接口,使得开发者可以更加方便地进行开发。 相比之下,HAL库则提供了更加高级的API接口,使得开发者可以更加方便地进行开发。HAL库主要包括以下几个部分: 1. HAL库核心:这是HAL库的核心部分,提供了一些通用的API接口,如时钟配置、中断控制等。 2. 外设驱动库:这是对一些常用外设(如USART、SPI、I2C等)的封装,提供了更加方便的API接口,使得开发者可以更加方便地进行开发。 3. 扩展库:这是一些额外的功能库,如USB库、文件系统库等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 19
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

修成真

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值