栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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

【STM32】标准库与HAL库对照学习教程十--输入捕获实验
  • 一、前言
  • 二、准备工作
  • 三、输入捕获介绍
    • 1、简介
    • 2、原理
  • 四、输入捕获结构
    • 1、通道映射表
    • 2、结构图
  • 五、输入捕获寄存器位
    • (1)ICF[3:0]
    • (2)CC1S[1:0]
    • (3)ICPS[1:0]
    • (4)CC1P
    • (5)CC1E
  • 六、硬件电路
  • 七、标准库配置输入捕获
    • 1、配置步骤
    • 2、配置工程
    • 3、实验程序
    • 4、实验效果
  • 八、HAL库配置输入捕获
    • 1、使用cubemx生成工程
    • 2、相关函数
    • 3、实验程序
    • 4、实验效果

一、前言
本篇文章是对定时器功能中的输入捕获功能的讲解与配置,本篇从输入捕获原理讲解开始,一步一步让您学会输入捕获的知识与配置,您可以点击目录跳转您想要看的内容。
二、准备工作
  • STM32F103开发板(我用的是普中的STM32F103ZE开发板)
  • cubemx软件、keil 5(MDK)
  • 开发板原理图

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

三、输入捕获介绍 1、简介

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

2、原理

在输入捕获模式下,捕获中断与定时器中断是打开的。

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


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使能
}


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"



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 msrn",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


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 msrn", 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、实验效果


到这里就结束啦!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/587476.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号