立创梁山派学习笔记——GPIO输入检测

news2025/1/19 14:31:58

按键检测

  • 前言
  • 按键的硬件电路
    • BOOT选择
    • 复位按键
    • 唤醒按键
      • GPIO输入框图
      • 软件配置
      • 寄存器简介
        • 1.端口控制寄存器(GPIOx_CTL, x=A..I)
        • 2.端口上拉/下拉寄存器(GPIOx_PUD, x=A..I)
        • 3.端口输入状态寄存器(GPIOx_ISTAT, x=A..I)
      • 打开GPIO的时钟
      • 按键初始化
      • 使用Watch观察调试
  • 总结

前言

在上一篇中,记录了关于梁山派的GPIO输出功能,本文继续使用寄存器的开发方式,通过按键来实现GPIO的输入检测的功能。在日常使用电子产品的过程中,最常见的输入方式就是按键,例如电脑的键盘、门锁的触摸按键等等这些的本质都是通过GPIO的输入进行检测的。

按键的硬件电路

在立创梁山派的电路中,预留了一个按键供我们编程使用,也就是下图中的KEY_UP按键。
在这里插入图片描述
三个按键的原理图如下图所示:

在这里插入图片描述

BOOT选择

其中BOOT0按键是用来选择GD32的启动方式的,可以这么理解,这个电路直接决定了单片机从哪儿读取我们编程好的代码。在数据手册的第3.4节有关于BOOT模式的介绍,一共有三种模式,第一种默认模式就是从主Flash中读取代码,模式2是从系统的存储器中,模式三是从SRAM中获取运行的代码。三种模式是通过BOOT1和BOOT0两个脚来控制的,对应下面的表中的拉高拉低状态即可选中对应的内存选择。
在这里插入图片描述
那么,梁山派选用的是哪个内存区间呢,来看原理图,BOOT1接了下拉电阻,而BOOT0默认也是拉低,但是按下按键时为高。也就是说梁山派有两种启动方式可以选择,通过按键进行切换。其中默认情况下使用的是从主Flash进行读取代码的。而按下BOOT0对应的按键后,就变成了从系统存储器读取代码。这样做的目的是什么呢,主要用途就是在后面的开发过程中,有时候会因为我们的代码时序问题或者逻辑问题,会导致单片机运行异常,时序混乱,造成无法烧录代码的问题,也就是常说的芯片锁住了,这时候就需要切换一下启动方式,让单片机重新正常跑起来,然后再次烧录自己的代码就可以了。
在这里插入图片描述
提到这里了,就顺带扒一扒实际烧录代码的地址和内存大小,如下图所示,默认状况时,我们的代码是烧录进了0x0800 0000到 0X0810 0000中,共计1M的存储空间。
在这里插入图片描述
上述两种启动模式:
1.BOOT0=0、BOOT1=0;此时从主闪存存储器启动,代码启动之后就相当于从0x08000000开始。主闪存存储器是GD32内置的Flash,作为芯片内置的Flash,是正常的工作模式。一般我们使用JTAG或者SWD模式下载程序时,就是下载到这个里面,重启后也直接从这启动程序。
2.BOOT0=1、BOOT1=0,此时从系统存储器启动。复位后,GD32与上述两种方式类似,从系统存储器地址0x1FFF F000开始执行代码。系统存储器是芯片内部一块特定的区域,芯片出厂时在这个区域预置了一段Bootloader,就是通常说的ISP程序。这个区域的内容在芯片出厂后没有人能够修改或擦除,即它是一个ROM区。启动的程序功能由厂家设置。系统存储器存储的其实就是STM32自带的bootloader代码。这个区域是只读的,目的是为了保证芯片的正常启动。
原文链接
bootloader也可以叫启动文件,无论性能高下,结构简繁,价格贵贱,每一种微控制器(处理器)都必须有启动文件,启动文件的作用便是负责执行微控制器从“复位”到“开始执行main函数”中间这段时间(称为启动过程)所必须进行的工作。关于单片机的详细启动过程大家可以参考此文——http://t.csdn.cn/Jz2oI。

复位按键

看完了BOOT电路的介绍,接下来来看另外一个功能被固定的按键——复位按键,这个电路的功能有两个,一是实现手动复位,也就是按下按键就进入复位操作,GD32是低电平复位的,当RESET脚被拉低一定时间后就会进入服务模式;除此之外,这个电路还兼顾了上电复位的功能,注意下方的C30这个电容,当整个板子打开电源开关的一瞬间,整个系统的其他地方都得到了所需的电平,但是此时,由于这个C30的存在,上电瞬间会先对其进行充电,这个过程中,RESET的电压会有一个处于低电平的时间,在这个时间内,单片机就会识别为复位信号,对代码进行复位操作。
在这里插入图片描述
既然提到了复位,也来扒一扒GD32的复位吧,首先看数据手册的第3.3节,这里有关于复位电路的介绍,这段话告诉我们GD32F470的复位有系统复位、软件复位和硬件复位。而上电复位和掉电复位的工作电压范围为1.8V到2.4V,当电源电压低于指定的电压时,系统不会开始工作,会一直处于复位状态。
在这里插入图片描述
除此之外,在中文参考手册中对于复位还有更加详细的描述,这里关于复位的分类方式与数据手册的有一些差异,分为了电源复位、系统复位和备份域复位,其中电源复位主要是用来根据电源状态进行复位操作的,而系统复位除了上电复位以外,其他的大部分都是跟软件复位有关的。
在这里插入图片描述

唤醒按键

剩下的最后一个就是需要编程进行控制的了,可以看到PA0默认是被拉低了的,只有按键按下时是高电平,在实际操作过程中只要判断到PA0变成高电平就说明按键被按下了。
在这里插入图片描述
至于为啥要叫唤醒按键,这是因为PA0这个GPIO管脚有一个特有的唤醒功能,当RTC等等功能需要使用到低功耗模式时,就需要用PA0来唤醒功能。
在这里插入图片描述
我们这里暂时用不上唤醒功能,只需要普通按键的功能,所以就不需要对唤醒功能进行使能。
在这里插入图片描述

GPIO输入框图

GPIO口的输入结构框图如下图所示,对比输出框图就简单多了,乍一看,只有一个上下拉寄存器需要配置,然后有一个输入驱动,再之后就可在输入状态寄存器中读取对应的状态值了。
在这里插入图片描述
这里需要注意一点,在上下拉的电阻的配置过程中,如果按键外部带有上拉或者下拉电阻来确立按键空闲状态就不需要使用到这里的弱上下拉配置了。
在这里插入图片描述
而常见的按键输入电路还有另外一种,就是外部硬件电路不带上拉或者下拉电阻,此时就要根据有效电平对上下拉寄存器进行配置。
在这里插入图片描述

软件配置

在弄清楚了按键检测原理以及框图后,接下来就需要进行具体的配置了,在上一篇介绍输出功能的时候,用到了一些专门用于输出功能的寄存器,例如输出模式时推挽还是开漏,输出速度这些寄存器在这里就可以忽略了。
参考输出的配置对输入配置做个大致的流程猜测,1.打开对应端口的时钟;2.将对应的管脚配置为输入模式;3.配置上下拉;4.检测输入端口的高低电平。

寄存器简介

大致知道了流程后接下来就是对对应的寄存器做一个了解,打开中文应用手册,找到GPIO章节,在这个GPIO配置表中,可以看到,配置为输入模式时需要使用到寄存器,分别是CTL、PUDy,这两寄存器在上一篇的输出配置中我们都用过,第一个是模式配置,第二个是上下拉配置。
在这里插入图片描述

1.端口控制寄存器(GPIOx_CTL, x=A…I)

前面提到过,端口值寄存器的作用就是控制对应的管脚是输入模式还是输出模式的,在这里肯定是需要配置为输入模式的,由于这个寄存器是两个二进制位控制一个管脚的,而我们使用到管脚号是0,所以需要将CTL0配置为00。
在这里插入图片描述
根据上一篇的寄存器写法以及位操作的方式,可以将GPIOA0配置为输入模式,具体的写法如下:

	GPIO_CTL(GPIOA) &= ~(3<<0);  //PA0通用输入

2.端口上拉/下拉寄存器(GPIOx_PUD, x=A…I)

按照目录的寄存器,接下来的输出模式、输出速度包括输出控制寄存器,这些都是与输出有关的寄存器,与输入模式无关,所以可以直接不看了。
关于这个上下拉寄存器,在上面的按键硬件电路介绍那里提到过,有外接的上拉电阻或者下拉电阻时,就不需要配置若上下了,所以在此处,我们将GPIOA0的PUD配置为浮空模式。
在这里插入图片描述
根据寄存器介绍可以知道,这里将PUD0配置为00即可。
在这里插入图片描述
具体的写法如下:

//上下拉配置
	GPIO_PUD(GPIOA) &= ~(3<<0);//浮空模式

3.端口输入状态寄存器(GPIOx_ISTAT, x=A…I)

将GPIO配置为输入模式其实就只需要上面的这两个寄存器,但是,配置为输入模式后的目的是获取GPIO管脚的电平是高还是低,以此作为按键是否按下的标志,那么怎么获取呢,既需要使用到这个寄存器,端口输入状态寄存器,当GPIO管脚的输入电平为低电平时,这个寄存器的值是0;当GPIO管脚的输入电平是高电平时,这个寄存器的值是1;使用过程中只需要判断这个寄存器的值就可以了。
在这里插入图片描述
那么怎么写呢,前面有个输出状态寄存器,是通过我们给他赋值0或者1来输出高或者低电平,是个写入的过程;而这里是需要获取外界的0和1,应该是个读取的过程,我们可以设置一个变量来读取这个寄存器的值,但是更直接的方案就是使用宏定义,直接判断即可。

#define KEY1 (GPIO_ISTAT(GPIOA) & (1<<0))

打开GPIO的时钟

搞定了输入配置,切记,在单片机的配置过程中,第一步一定是开启对应的时钟,这里我们需要开启GPIOA的时钟,具体的开启过程在上一篇输出模式中有介绍,这里不赘述了,直接给出配置代码:

  RCU_AHB1EN |=(1<<0);//开启A端口的时钟

按键初始化

到这里有关按键的初始化配置就已经搞定了,接下来就是新建源文件和头文件,并添加进工程,然后开始编程,这个具体的步骤就不做描述了。
在这里插入图片描述
在这里归纳出总的初始化代码:

/***********************************************************
*函数名    :Key_Init
*函数功能  :KEY所用的管脚的初始化配置
*函数参数  :无
*函数返回值:无
*函数描述  :KEY1-----------PA0    检测到高电平表示按键按下
*************************************************************/
void Key_Init(void)
{
	//端口时钟使能
	RCU_AHB1EN |= (1<<0);  //A
	//端口控制寄存器配置
	GPIO_CTL(GPIOA) &= ~(3<<0);  //PA0通用输入
	//上下拉配置
	GPIO_PUD(GPIOA) &= ~(3<<0);  //PA0浮空输入
}

使用Watch观察调试

为了更直观的看见效果,这里我们使用MDK的Watch来辅助调试,主函数的逻辑是按下按键,key_cnt会自增。
在这里插入图片描述

按照我们的思路,应该是按下一次按键这个变量会自增1,而且是每按下一次自增1,但实际上是这样吗,我们来看看实际效果。编译通过后,先将代码下载到单片机,然后点击红框处的Debug按钮,等待进入如下调试界面。
在这里插入图片描述
然后根据下图的操作打开Watch的窗口。
在这里插入图片描述
然后选中想要显示的变量,右键,这里我们选中key_cnt,然后右键就会弹出菜单选项,我们选择Add key_cnt to,再选择Watch1.
在这里插入图片描述
然后就可以看见在Watch中有了key_cnt这个变量,但是此时变量后面是黑灰色的,说明不能观察到这个变量的值, < cannot evaluate > 出现这个问题,一种可能是你的变量是局部变量,局部变量是无法观察的,这里我们是全局变量,但是还是不能观察,此时就是编译器的优化等级问题了,由于优化等级高了,这个变量被优化了导致无法观察,我们返回编程界面。
在这里插入图片描述
点击魔法棒,选择C/C++,然后在Optimzation中,选择-O0,然后重新编译下载。然后再打开debug调试。
在这里插入图片描述
再次打开,就可以看见key_cnt已经有值了,但是是16进制的,为了更方便的观察,可以将它修改为10进制,同样在这里的key_cnt处右键,将Hexadecimal Display给取消勾选即可。
在这里插入图片描述
在这里插入图片描述
然后电机橙色框内的开始运行,运行后按下板子上的KEY_UP键,观察Watch里面key_cnt的值。
在这里插入图片描述
效果:可以看见下图的效果,当按键按下的时候,这里的key_cnt并不是一次自增1,而是一次增加了很多值,这是为什么呢。
在这里插入图片描述
前面提到过GD32F470的主频是240MHZ,也就是说它一秒内可以运行240 000 000调机器指令,而一条C语言语句约等于四个机器指令,我们这代码的while(1)中,满打满算也就3条C语句,不到20个机器指令,也就是说,我们按下按键的那一个过程,对于我们的主观感受来说,已经很快了,但是最少最少也有个10MS以上的时间吧,也就是说,单片机在这十多毫秒内一直在重复运行key_cnt++这个过程,因此,效果就变成了我们看见的这样,按下一次按键key_cnt自增了非常多。
那么怎么解决这个问题呢,一个方案就是设置一个标志,按下按键的时候这个标志置位,直到按键松开,这个标志位才被释放,这样就可以规避上面这个问题了。
于是就有了下面的这个按键扫描函数:key_flag就是用来实现按下自锁,松手释放的功能。

/*******************************************
*函数名    :Key_Scanf
*函数功能  :按键扫描函数
*函数参数  :无
*函数返回值:uint8_t
*函数描述  :
*********************************************/
uint8_t Key_Scanf(void)
{
	uint8_t key_val = 0xff;
	static uint8_t key_falg = 1;                //定义标志位变量       
	if((KEY1&& key_falg)    //按键有按键按下并且标志位允许扫描   
	{
	//delay1ms(10);//延时消抖
		if(KEY1)                             //再次确认是否按键1真的按下
		{
			key_val = 1;                       //赋值键值1
			key_falg = 0;                      //标志位锁定 
		}
	}
	//按键抬起就解锁标志位
	if(!KEY1)      //判断按键是否抬起
	{
		key_falg = 1;                        //标志位解锁
	}

	return  key_val;                       //返回键值
}

效果:
在这里插入图片描述
可以看见,再添加了标志位后,基本上实现了按下一次按键,key_cnt自增1的效果,但是还是会存在有时候一次自增2或者3的情况,这又是按键输入的另外一个要注意的点了,实际的按键按下时,并不是下图中的理想波形,而是有抖动的,所以会造成按一次按键出现连续两次到三次自增的效果,为了规避这个问题,一般会采取一个小的延时来实现消抖功能。
在这里插入图片描述
消抖的简单粗暴方案就是在按键按下后加一个小的延时,规避抖动的那段时间就可以了。将上面的那段代码的delay1ms(10);//延时消抖换成自己的延时函数就可以了。一般延时10ms左右就可以了。
除此之外,像梁山派这种板载按键很少的核心板,如果手头上没有按键模块,还可以通过长按,短按,双击来实现不同的功能,思路是当按下按键时开始计数,当计数值大于一定值时是长按,小于一定值时是短按。这里给大家几篇大佬的状态机实现的参考——
【stm32单片机基础】按键状态机实现长按和短按
【按键】[独立按键] - 1: 单击,双击,三击以及N击
在介绍到定时器中断后,笔者会返回来对这个进行一个补充。有定时器寄存的同学可以直接参考这个链接进行操作即可。

总结

关于GD32的GPIO的输入部分就介绍到此,文中如有不足欢迎批评指正。

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

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

相关文章

cs109-energy+哈佛大学能源探索项目 Part-2.2(Data Wrangling)

博主前期相关的博客见下&#xff1a; cs109-energy哈佛大学能源探索项目 Part-1&#xff08;项目背景&#xff09; cs109-energy哈佛大学能源探索项目 Part-2.1&#xff08;Data Wrangling&#xff09; 这次是将数据整理的部分讲完。 Data Wrangling 数据整理 Weather data 天…

chatgpt赋能Python-python_changeint

Python中changeint函数介绍&#xff1a;将浮点数转换为整数 Python是一种高级编程语言&#xff0c;众所周知&#xff0c;它非常强大且易于学习。在Python中&#xff0c;有一个很有用的函数&#xff0c;名为changeint&#xff0c;它可以将一个浮点数转换为整数。在本文中&#…

进程间通信—进程池设计

进程池设计 文章目录 进程池设计代码目的头文件 对子进程操作建立子进程对象并把子进程对象放进数组里建立子进程需要执行的任务表创建子进程和父进程通信的管道&#xff0c;并且让子进程阻塞读取对父进程操作回收子进程整体代码子进程具有读端未关闭的bug 代码目的 创建一个父…

【libdatachannel】1 :cmake+vs2022 构建

libdatachannel libdatachannel 是基于c++17实现的cmake 链接openssl 可以参考【libcurl 】win32 构建 Release版本 修改cmakelist 链接openssl1.1.*构建 OpenSSL 找不到 Selecting Windows SDK version 10.0.22000.0 to target Windows 10.0.22621. The CXX compiler identifi…

机试打卡 -01 字母异位词(滑动窗口)

算法小白的代码如下↓ class Solution(object):def findAnagrams(self, s, p):""":type s: str:type p: str:rtype: List[int]"""# 输出列表answer_list[]# p的长度p_lenlen(p)# 索引遍历s的子串for i in range(len(s)):# 最后一次循环if ip_le…

Python对大量表格文件加以数据截取、逐行求差、跨文件合并等处理的方法

本文介绍基于Python语言&#xff0c;针对一个文件夹下大量的Excel表格文件&#xff0c;基于其中每一个文件&#xff0c;首先依据某一列数据的特征截取我们需要的数据&#xff0c;随后对截取出来的数据逐行求差&#xff0c;并基于其他多个文件夹中同样大量的Excel表格文件&#…

【NeRF】(一)NeRF论文学习笔记

文章目录 NeRF学习笔记1 实现过程1.1 相机参数&#xff1a;如何通过不同角度的照片得出输入数据1.2 MLP1.3 体积渲染及离散化1.4 优化点 NeRF学习笔记 概述&#xff1a; 重建&#xff1a;根据目前有的不同角度二维图片&#xff0c;重建三维物体。 用 MLP 网络学 Scene Represe…

《WEB安全漏洞30讲》(第5讲)任意文件上传漏洞

1.任意文件上传漏洞原理 文件上传漏洞,指攻击者利用程序缺陷绕过系统对文件的验证与处理策略将恶意程序上传到服务器并获得执行服务器端命令的能力。 这个漏洞其实非常简单,就是攻击者给服务器上传了恶意的木马程序,然后利用此木马程序执行操作系统命令,从而获得服务器权…

汇编实现点灯

循环亮灯 .text .global _start _start: bl LED1_INIT bl LED1_ON bl delay_1s bl LED1_OFF bl LED2_INIT bl LED2_ON bl delay_1s bl LED2_OFF bl LED3_INIT bl LED3_ON bl delay_1s bl LED3_OFF bl _start /**********LED1点灯PE10**************/ LED1_INIT: 1.通过RC…

UE5实现天际线分析效果

文章目录 1.实现目标2.实现过程2.1 后处理材质2.2 验证测试3.参考资料1.实现目标 UE5中使用CesiumForUnreal插件加载在线的地形影像与OSM建筑数据,再基于后处理材质实现天际线分析效果,GIF动图如下所示: 2.实现过程 依旧是通过边缘检测,得到天际线位置,再通过后处理材质将…

PointGPT 论文解读,点云的自回归生成预训练

PointGPT: Auto-regressively Generative Pre-training from Point Clouds 论文&#xff1a;https://arxiv.org/pdf/2305.11487.pdf 一种将GPT概念扩展到点云的方法&#xff0c;在多个3D点云下有任务中&#xff08;点云分类&#xff0c;part分割等&#xff09;上实现了最先进…

C++中set的用法

博主简介&#xff1a;Hello大家好呀&#xff0c;我是陈童学&#xff0c;一个与你一样正在慢慢前行的人。 博主主页&#xff1a;陈童学哦 所属专栏&#xff1a;CSTL 前言&#xff1a;Hello各位小伙伴们好&#xff01;欢迎来到本专栏CSTL的学习&#xff0c;本专栏旨在帮助大家了解…

[SWPUCTF 2021 新生赛] (WEB一)

目录 gift_F12 jicao easy_md5​ caidao include easy_sql easyrce babyrce Do_you_know_http ez_unserialize gift_F12 1.开启环境 2.题目提示f12 ctrlf 搜索flag 得到flag "WLLMCTF{We1c0me_t0_WLLMCTF_Th1s_1s_th3_G1ft} jicao 1.开启环境 2.传参 jso…

MyBatis-Plus中AutoGenerator的详细使用案例

AutoGenerator是什么&#xff1f; AutoGenerator 是 MyBatis-Plus 的代码生成器&#xff0c;通过 AutoGenerator 可以快速生成 Pojo、Mapper、 Mapper XML、Service、Controller 等各个模块的代码 AutoGenerator能干什么&#xff1f; 对于单表而言&#xff0c;几乎是一个全能…

调用百度API实现菜品识别

调用百度API实现菜品识别 1、作者介绍2、百度API菜品识别接口介绍2.1图像识别接口介绍2.2接口使用说明2.2.1请求示例2.2.2请求参数 3、调用百度API流程4、代码实现 1、作者介绍 李延&#xff0c;男&#xff0c;西安工程大学电子信息学院&#xff0c;2022级研究生 研究方向&…

如何禁用网络共享打印?

139端口是为“NetBIOS Session Service”提供的&#xff0c;主要用于提供Windows文件和打印机共享以及Unix中的Samba服务。在Windows中要在局域网中进行文件的共享&#xff0c;必须使用该服务。 开启139端口虽然可以提供共享服务&#xff0c;但是常常被攻击者所利用进行攻击&am…

chatgpt赋能Python-python_chi

Python Chi&#xff1a;掌握Python语言的关键 Python是一种面向对象、直译式计算机程序设计语言&#xff0c;被广泛应用于各种领域。无论是数据分析、人工智能、Web开发、网络爬虫还是科学计算&#xff0c;Python都扮演着重要的角色。而Python Chi则是Python语言中一个重要的方…

HQL函数--打地鼠游戏及WordCount案例分析及实现

1.打地鼠 uid,hit,m 1,1,0 1,2,1 1,3,1 1,4,1 1,5,0 1,6,0 1,7,1 2,1,1 2,2,1 2,3,1 2,4,1 2,5,1 3,1,1 3,2,1 3,3,1 3,4,0 3,5,0 3,6,1 3,7,0 3,8,1 create table tb_ds( uid int , -- 用户名 hit int , -- 第几次打地鼠 m int -- 是否命中 1命中 0 …

Request响应和Response对象

什么是Request响应和Response对象 实际上就是指Request对象和Response对象。 一个get或者post请求实际上就是一些有规律的字符串&#xff0c;Tomcat将这些字符串获取并且封装成一个可操作的对象。 通过Response对象可以设置响应数据&#xff0c;也就是Tomcat想要给浏览器发送…

Redis安全性详解

目录 ​编辑 Redis安全性详解 1.Redis的访问控制措施。 2.Redis的密码认证机制 3.Redis的绑定IP地址 4.Redis的网络ACL 5.Redis的防火墙或网络安全组 Redis安全性详解 Redis是一个快速、开源的内存数据库&#xff0c;被广泛用于缓存、消息传递和数据存储等场景。然而&a…