GPIO复用功能1——定时器输入捕获

news2024/12/27 14:19:57

文章目录

  • 前言
  • 定时器输入捕获
    • 红外接收的数据分析
    • 捕获思路
    • 编程实践
      • 1.初始化时钟
      • 2.初始化GPIO
      • 3.配置定时器的时钟基准
      • 4.配置输入捕获参数
      • 5.中断配置
      • 6.中断服务函数
      • 7.处理数据帧
    • 实现效果
  • 总结

前言

在这里插入图片描述
前面介绍了GPIO的通用输入输出功能,以及其模拟时序的功能,可以发现,使用模拟时序来驱动外设,代码部分会有些许的麻烦,而且有些时序和功能是用模拟时序无法实现的。这时候就需要使用到外设了,这里的外设不仅仅是指芯片外片外外设,还有一部分是芯片内部的片内外设。
这些片上外设大多数是需要与芯片外进行数据交换的,如定时器输入捕获、定时器比较输出、SPI控制器、I2C控制器。
这些需要数据交换的片上外设都需要使用到前面的GPIO来作为中介。因为GPIO是单片机内部与外部的唯一沟通桥梁,使用这些片上外设时,GPIO就需要根据需要配置为特殊模式,也叫做复用模式。
![在这里插入图片描述](https://img-blog.csdnimg.cn/e7ff1952b78443c09008e92099784d9b.png#pic_center =600*200)
通过芯片手册可以看出STM32F103C8T6的片上外设数量。笔者这个板子使用了四个定时器、一个SPI、三个USART、一个ADC,具体的使用会在后面一一介绍。
在这里插入图片描述

定时器输入捕获

为了衔接上一篇的内容,本文先介绍利用定时器的输入捕获来解析红外接收管的数据,并以此来作为一种输入信号进行控制。

红外接收的数据分析

上一篇中我们介绍了HS0038接收的NEC格式的数据帧,截个图过来,如下图所示:
在这里插入图片描述
根据这个帧格式,可以找到如下的特征:
在这里插入图片描述

1.一帧数据的起始调节有一个同步头,这个同步头有9ms的低电平和4.5ms的高电平组成;
2.逻辑“1”与逻辑“0”的前半段信号都是560us的低电平,只有后半段的高电平时间不同,逻辑“1”的高电平持续时间是1680us,逻辑“0”的高电平持续时间是560us;
根据这些特征,如果想解析出数据,就需要定位到每一个上升沿并且获取到各个部分的高电平持续时间就可以了。根据高电平时间的不同就可以知道此时的信号时同步头还是逻辑“0”、逻辑“1”。
既要准确定位边沿,并且记录下电平持续时间,这个过程是不是瞅着有一些眼熟,在前一个系列介绍输入捕获的时候,我们曾经使用了输入捕获来获取按键按下的时间长度,以及通过超声波模块获取了物体距离,这都是检测边沿加计时。
这里如果不了解输入捕获功能的小伙伴们可以去查看笔者的这篇介绍,在此处不再赘述。
嵌入式学习笔记——输入捕获

捕获思路

根据帧格式的特征,可以捋一下获取红外遥控信号的编程思路:
1.首先需要根据硬件连接初始化对应的定时通道以及GPIO;
2.配置好对应的时基,这里为了便于更加准确的计数,最好是将定时器的计数周期设置为1us一次,这样也便于数据处理。具体的设置方式在后面编程时介绍;
3.设置输入捕获的触发方式以及是否分频的参数,
4.配置捕获中断,进行捕获,
这里由于要获取高电平的持续时间:
首先肯定是需要上升沿触发捕获中断的,上升沿触发后先清空计数值,从0开始计数,且切换触发模式为下降沿触发,当下降沿触发时返回计数值,并再次切换回上升沿触发,判断对比计数值的范围与各个信号的特征就可以知道此时的数据值了。
在这里插入图片描述
5.除了捕获中断,还可以开启更新中断作为接收完成的标志,为了进行区分,这里更新中断的时间选择10ms,整个过程中,只有接收完32位信号后,计数值才有可能溢出从而产生更新中断,更新中断一产生就代表可以进行数据处理了。
在这里插入图片描述

编程实践

首先根据思路查找对应的GPIO,通过原理图和数据手册可以知道,使用的是GPIOA1,复用的是TIM2的通道2。
在这里插入图片描述

1.初始化时钟

再使用任何一个片上外设时,第一件事就是要开启其对应的时钟,这里我们需要开启GPIOA的时钟、TIM2的时钟,由于使用到了复用功能,所以还需要开启复用的时钟。
根据之前的经验的,时钟相关先看数据手册找到片上外设的挂接总线。
在这里插入图片描述
可以发现,这俩片上外设分别挂载在APB2与APB1上,所以需要代码如下:

    RCC_APB1PeriphClockCmd(RCC_APB1Periph_TIM2, ENABLE);	//使能定时器2时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA , ENABLE);  //使能GPIOA外设能模块时钟
 	RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);  //使能GPIOA AFIO复用功能模块时钟

可以看到上代码中还有一个是开启了AFIO的时钟,开启他的原因是需要使用到GPIO的复用功能,所以必须开启这个时钟。
在这里插入图片描述

2.初始化GPIO

这里需要注意一点,对于想C8T6这种GPIO数量较少的MCU会有一个重映射的设置,重映射的作用就在于可以切换片上外设的复用GPIO端口,但是不是随意更改的,而是根据类似下图的映射表来实现的。此处的TIM2CH2默认就是PA1不需要进行重映射。

void GPIO_PinRemapConfig(uint32_t GPIO_Remap, FunctionalState NewState)这个函数是专门用来实现重映射设置的。这里暂时用不上,后面用到了再说。
在这里插入图片描述

那么问题来了,GPIO的初始化要怎么写呢?
作为输入捕获,模式自然是输入;但是在M3的GPIO输入模式中,没有复用输入模式,这里我们不需要上下拉,也不是模拟输入,所以,直接选择浮空输入即可。
在这里插入图片描述

 //设置该引脚为浮空输入功能,
	GPIO_InitTypeDef GPIO_InitStructure;
	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1; //TIM2_CH2
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;  //浮空输入
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_2MHz;
	GPIO_Init(GPIOA, &GPIO_InitStructure);//初始化GPIO

3.配置定时器的时钟基准

定时器的时钟基准就是设置定时的预分频数、重装载值、计数模式这些定时器的基本参数,关于这些东西的详细介绍,可以参考笔者前一个系列的嵌入式学习笔记——通用定时器。
这里使用库函数的思路与之前的GPIO其实是差不多的,也是先在库函数中找到stm32f10x_tim.h,然后找到对应的初始化结构,根据描述以及后面的参数宏或者参数枚举,依次进行结构体成员的赋值。
在这里插入图片描述
参数赋值完成后,也是需要调用对应的结构体初始化函数将这些参数写入对应的寄存器中。参数分别是定时器编号,以及上面的结构体的地址。
在这里插入图片描述

//初始化TIM2
	TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;//定义定时器结构体
	TIM_TimeBaseStructure.TIM_Period = arr-1; //设置在下一个更新事件装入活动的自动重装载寄存器周期的值
	TIM_TimeBaseStructure.TIM_Prescaler =psc-1; //设置用来作为TIMx时钟频率除数的预分频值 
	TIM_TimeBaseStructure.TIM_ClockDivision = TIM_CKD_DIV1; //设置时钟分割:TDTS = Tck_tim
	TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;  //TIM向上计数模式
	TIM_TimeBaseInit(TIM2, &TIM_TimeBaseStructure); //根据TIM_TimeBaseInitStruct中指定的参数初始化TIMx的时间基数单位

4.配置输入捕获参数

这里我们需要使用到定时器的输入捕获功能,因此还需要再次基础上进一步进行配置,流程与上面一样,也是找到对应的结构体变量,然后参考描述以及参数宏雨参数枚举进行赋值以及初始化。这里需要配置物理通道、触发信号种类、输入捕获与映射通道。
在这里插入图片描述
同样的配置完相关参数后,也需要调用对应的初始化函数进行写入寄存器。
在这里插入图片描述


	//输入捕获
	TIM_ICInitTypeDef TIM_ICInitStruct;//定时器输入捕获控制寄存器
	TIM_ICInitStruct.TIM_Channel=TIM_Channel_2;//通道2
	TIM_ICInitStruct.TIM_ICFilter=0x00;//不使用滤波器
	TIM_ICInitStruct.TIM_ICPolarity=TIM_ICPolarity_Rising;//上升沿捕获
	TIM_ICInitStruct.TIM_ICPrescaler=TIM_ICPSC_DIV1;//不分频
	TIM_ICInitStruct.TIM_ICSelection=TIM_ICSelection_DirectTI;// TI2FP2 输入捕获通道选择
	TIM_ICInit(TIM2,&TIM_ICInitStruct);

5.中断配置

然后就是中断的配置了,首先需要调用定时器的函数实现中断信号的配置,这里需要使用到更新中断与捕获中断两种。
在这里插入图片描述
上面只是指定了中断信号,还没对中断进行配置,因此还需要找到
misc.h,在这里面也有对应的参数配置结构体。
在这里插入图片描述
同样也有对应的初始化函数
在这里插入图片描述
于是代码如下:

//中断配置
	TIM_ITConfig(TIM2,TIM_IT_CC2|TIM_IT_Update,ENABLE);//更新中断和捕获中断

	NVIC_InitTypeDef NVIC_InitStruct;
	NVIC_InitStruct.NVIC_IRQChannel=TIM2_IRQn;
	NVIC_InitStruct.NVIC_IRQChannelCmd=ENABLE;
	NVIC_InitStruct.NVIC_IRQChannelPreemptionPriority=1;
	NVIC_InitStruct.NVIC_IRQChannelSubPriority=1;
	NVIC_Init(&NVIC_InitStruct);
	 //使能放到最后,避免写入失败。
	TIM_Cmd(TIM2, ENABLE);  //使能TIM2

6.中断服务函数

上面的都只是初始化的配置函数,真正的功能实现,还需要使用中断服务函数。首先需要去到启动文件拿到对应的中断服务函数名称。
整段代码的思路就是,
1.利用上升沿触发清空计数器,并将触发模式切换为下降沿触发,计数周期是1us一次;
2.下降沿触发时,判断计数值,并做比较,根据三类特征,判断是同步头、逻辑1还是逻辑0;
3.根据低位在前的顺序,定义一个32位的数据,按位接收数据;
4.当计数器溢出时,也就是计数值到了10000 代表联系10ms的高电平,此时判断为接收完成。

//定时器2中断服务程序	 
void TIM2_IRQHandler(void)
{ 	
	if(TIM_GetITStatus(TIM2,TIM_IT_Update)!=RESET)
	{//10MS产生一次中断,整个传输中最长的高电平时间为同步码 的 4.5ms ;只有释放信号才可能进入更新中断	
		TIM_ClearITPendingBit(TIM2,TIM_IT_Update);	 
		if(Infrared_Receive.start)//接收到过同步头且产生了更新中断说明接收到完毕了
		{
			Infrared_Receive.start=0;
			Infrared_Receive.end =1;//接收完成的标志位
			
		}
	}
	if(TIM_GetITStatus(TIM2,TIM_IT_CC2)!=RESET)
	{	  
		TIM_ClearITPendingBit(TIM2,TIM_IT_CC2);	 
		//进入捕获中断,由于需要捕获比较的是高电平的时间,所以第一次捕获为上升沿,
		//检测是否是上升沿使用IO的输入电平来判断
		if(REMOTE_DATA)					//此时是高电平,说明是上升沿检测,
		{
			//为了统计高电平的时间,进入后需要将计数器的值清零计数一次是1US
			TIM_SetCounter(TIM2,0);  //表示计数值清零
			//切换为下降沿捕获
			TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Falling);	
		}
		else       //说明是下降沿的捕获中断,
		{
			//此时需要判断是否为起始(同步头)信号
			//获取此时计数器的值,也就是高电平的持续时间
			Infrared_Receive.ccr = TIM_GetCapture2(TIM2);//获取当前计数值
			TIM_OC2PolarityConfig(TIM2,TIM_ICPolarity_Rising);//切换为上升沿捕获
			
			if(Infrared_Receive.start == 1)     //如果已经接收过同步头
			{
				if(Infrared_Receive.ccr>300 && Infrared_Receive.ccr<800)//560us高电平说明是数据0
				{
						Infrared_Receive.data=Infrared_Receive.data>>1;//低位在前接收后的格式为:8位控制码反码+8位控制码+8位地址码反码+8位地址码
				}
				else if(Infrared_Receive.ccr>1300 && Infrared_Receive.ccr<1900)//1680us说明是数据1
				{
						Infrared_Receive.data=Infrared_Receive.data>>1;
						Infrared_Receive.data |=0x80000000;//接收数据为1时,给对应位补1
				}
			}
			else                           //没有接收到同步头,先判断是否有同步头 
			{
					if(Infrared_Receive.ccr>4000 && Infrared_Receive.ccr<5000)//检测到同步头信号
					{
						Infrared_Receive.start=1;
					}
			}
		}
	}    
}

7.处理数据帧

接收到32bit数据后,根据其帧格式,可以提取出我们需要的数据

/************************************************
函数功能:红外获取控制码
函数名:Infrared_Get_Data
函数形参:None
函数返回值:无控制-1  有控制码则返回控制码
备注:  
**************************************************/
s8 Infrared_Get_Data(void)
{
	u8 data;  //控制码
	
	if(Infrared_Receive.end==1)//检测到数据接收完毕
	{
		Infrared_Receive.end=0;//清除标志位,方便下次接收
		
		data = (Infrared_Receive.data & 0x00ff0000) >> 16;  //获取控制码
		
		return data;	
	}
	return -1;
}

然后就可以调用这个函数根据不同的输入值来执行对应的功能了。

实现效果

在这里插入图片描述

总结

关于定时器的输入捕获实现红外输入信号的处理就介绍到这里,文中如有不足之处欢迎批评指正。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/711056.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

软件模拟实现SPI通信-GD32

软件模拟实现SPI通信-GD32 设计流程 #ifndef _SOFT_SPI_H #define _SOFT_SPI_H /** filename: soft_spi.h**/ #include "gd32f10x.h" #include "systick.h"//定义表示具体IO口的资源宏 #define SPI_PORT GPIOA #define SPI_MOSI GPIO_PIN_7 #define SP…

【华为OD机试】翻转骰子(python, java, c++, js)

翻转骰子 前言:本专栏将持续更新华为OD机试题目,并进行详细的分析与解答,包含完整的代码实现,希望可以帮助到正在努力的你。关于OD机试流程、面经、面试指导等,如有任何疑问,欢迎联系我,wechat:steven_moda;email:nansun0903@163.com;备注:CSDN。 题目描述 骰子是…

java中的knife4j、Swagger配置过程详解

knife4j的配置过程如下&#xff1a; 1、首先引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency><groupId>com.github…

CentOS Linux的替代品(四)_openEuler 22.03-LTS基础安装教程

文章目录 1 openEuler 22.03-LTS简介1.1 系统安装1.1.1 发布件1.1.2 最小硬件要求1.1.3 硬件兼容性 1.2 关键特性1.2.1 openEuler 22.03-LTS基于 Linux Kernel 5.10内核构建, 在进程调度、内存管理等方面带来10余处创新1.2.2 新介质文件系统1.2.3 内存分级扩展1.2.4 用户态协议…

Clouder Manager日常管理及使用

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…

低代码:解放生产力的利器还是一场空洞的炒作?

低代码有人说是毒瘤&#xff0c;也有人说是银弹。到底应该怎么看呢&#xff1f;存在即合理。 “存在”包括两个角度&#xff1a; 银弹论毒瘤论 无论从哪个角度看&#xff0c;既然存在这样的论调&#xff0c;就有它们的合理性。 先把关注点移到低代码本身&#xff0c;低代码…

关联查询和子查询

关联查询和子查询&#xff1a; 二、关联查询&#xff1a; 1、全连接&#xff1a;两个表作笛卡尔积 2、内连接&#xff1a;找到两个表中的关联数据 3、外连接&#xff1a; 左外连接&#xff1a;grade中12无学生&#xff0c;但是还是会显示出来&#xff08;以左边的表为基准&am…

javascript 触发事件

在 JavaScript 中&#xff0c;原始方法 initEvent() 用于创建新事件。 最新更新添加了用于构建自定义事件的新关键字。 此外&#xff0c;在为 JavaScript 构建的清单中还有大量事件。 以下部分将预览预定义事件之一的示例及其触发方式。 还有一个定制的事件&#xff0c;将细节…

Redis 高可用【主从复制 哨兵 集群】

主从复制&#xff1a;主从复制是高可用Redis的基础&#xff0c;哨兵和集群都是在主从复制基础上实现高可用的。主从复制主要实现了数据的多机备份&#xff0c;以及对于读操作的负载均衡和简单的故障恢复。缺陷&#xff1a;故障恢复无法自动化&#xff1b;写操作无法负载均衡&am…

使用JMX管理Spring Bean

1.使用JMX管理Spring Bean 2.spring通过annotation注解注册MBean到JMX实现监控java运行状态 3.Spring与JMX集成

LangChain大型语言模型(LLM)应用开发(三):Chains

LangChain是一个基于大语言模型&#xff08;如ChatGPT&#xff09;用于构建端到端语言模型应用的 Python 框架。它提供了一套工具、组件和接口&#xff0c;可简化创建由大型语言模型 (LLM) 和聊天模型提供支持的应用程序的过程。LangChain 可以轻松管理与语言模型的交互&#x…

系列一、RocketMQ入门

一、MQ概述 1.1、MQ简介 MQ&#xff0c;Message Queue&#xff0c;是一种提供消息队列服务的中间件&#xff0c;也称为消息中间件&#xff0c;是一套提供了消息生产、存储、消费全过程的API软件系统。消息&#xff1a;消息即数据&#xff0c;一般消息的体量不会很大。 1.2、M…

redis主从同步对象模型学习笔记

目录 1 淘汰策略1.1 配置1.2 哪些数据将会被淘汰呢&#xff1f;1.2.1 过期key中的1.2.2 所有的key1.2.3 禁止淘汰 2 持久化2.1 背景2.1.1 概念2.1.2 fork进程写时复制机制2.1.3 大key 2.2 redis持久化的方式2.2.1 aof2.2.2 aof-rewrite2.2.3 rdb 2.3 redis持久方案的优缺点2.4 …

Redis跳表

简介 Redis 中的有序集合(Sorted Set)是用跳表&#xff08;Skip list&#xff09;来实现的。这里就需要了解一下跳表的概念。 链表&#xff1a;链表插入&#xff0c;删除某一个节点的时间复杂度是O(1)&#xff0c;但是查询的时间复杂度是O(n)。 跳表就是解决链表在有序节点场…

Jmeter:使用代理录制脚本

目录 前言&#xff1a; 介绍下各设置项&#xff1a; 前言&#xff1a; JMeter 是一个功能强大的性能测试工具&#xff0c;可以用于模拟多种场景下的负载测试和性能测试。其中一个常用的功能是使用代理服务器录制脚本&#xff0c;以便在后续的测试中模拟真实的用户行为。 下…

数据库优化---索引

这里写目录标题 索引简介结构&#xff08;Btree&#xff09;操作总结 索引 简介 优缺点 利远远大于弊 结构&#xff08;Btree&#xff09; 所有的数据都存在叶子节点 操作 具体代码 注意&#xff0c;主键以及唯一约束&#xff0c;都会自动创建一个索引&#xff0c;主键索…

git常用命令之Pull

7. Pull 命令作用延展阅读git pull 1. git fetch2. git merge FETCH_HEAD1. fetch origin的所有分支2. merge 当前分支参考git pull origin master1. git fetch origin master2. git merge FETCH_HEAD 拓展&#xff1a;Git常用命令汇总

时间触发嵌入式系统:各种系统的概念

1.1 引言 一说到软件系统&#xff0c;脑子里面就会闪现很多名词&#xff1a; 信息系统 桌面应用系统 实时系统 嵌入式系统 事件触发系统 时间触发系统 这些名字搞得脑子很乱&#xff0c;感觉都熟悉&#xff0c;又有些陌生&#xff0c;还是需要简单的介绍一下。 1.2 信息系统…

MySQL高可用 MMM

MySQL高可用 MMM 一、MMM1.1 MMM介绍1.2 关于 MMM 高可用架构的说明 二、搭建 MySQL MMM2.1 环境准备2.2 搭建 MySQL 多主多从模式2.3 安装配置 MySQL-MMM 一、MMM 1.1 MMM介绍 Master-Master replication manager for MvSQL&#xff0c;MySQL 主主复制管理器。 1、是一套支持…

vue3如何使用vant中IndexBar索引栏?

目录 1、为什么要使用IndexBar索引栏&#xff1f;2、引入3、基础使用4、处理后端返回的数据5、渲染页面 1、为什么要使用IndexBar索引栏&#xff1f; 在我们开发移动端的时候&#xff0c;有时候会遇到制作通讯录或者城市索引栏&#xff0c;这种时候我们就可以使用vant中快捷方…