GD32 MCU机械按键状态的识别

发布者:轻松自在最新更新时间:2024-11-05 来源: elecfans关键字:GD32  MCU  识别 手机看文章 扫描二维码
随时随地手机看文章

GPIO口的输入功能-机械按键状态的识别

硬件: 深圳标航科技有限公司 暴风 开发板


处理器:GD32F103VET6

开发环境:MDK(keil 5) + STM32CubeMX

1.1 GPIO口的输入的作用

输入,其意是指将处理器外部的逻辑信号0或者1输入到处理器的内部。输入是每一个处理器的IO引脚的基本功能。利用处理器的输入功能我们可以获取外部电路的状态,进而做出进一步的判断。GPIO的输入功能的典型应用是获取机械按键的状态—判断按键是按下还是弹起。

1.2 机械按键状态的识别

1.2.1 机械按键电路的设计

按键有两个状态,一个是按下一个是弹起。通过巧妙的电路设计,会使得按键的按下与弹起时IO引脚的逻辑电平不一样。通过GPIO引脚的输入功能将这些逻辑电平输入到内部供处理器识别,由此可知按键是按下还是弹起,并做出进一步的判断。

下面我们先来讨论按键电路的设计。常用的按键电路设计如图1的(a)和(b)所示。

图片

(a)

图片

(b)

图1 按键电路设计

先来看图1的(a)图,在(a)图中,按键的一端接地,另一端接IO引脚,接IO引脚这一端通过一个电阻连接到高电平VCC(这种电阻叫上拉电阻)。在没有按键按下时,由于处理器吸取的电流非常非常小,R1两端可以认为没有电流流动,所以它们两边电位一样,也即IO引脚的电平跟VCC基本一样,此时IO引脚端为高电平。当按键按下后,IO引脚和地端相连,IO引脚直接变为了低电平。通过这个分析,我们得出图1(a)按键电路的特点如下:

(1)没有按键按下,IO引脚为高电平;

(2)有按键按下,IO引脚为低电平。

所以,如果处理器某个时候读到这个引脚信号为0,说明此时按键按下了,如果读到为1,说明按键没有按下。

再来看图1的(b)图,图1(b)中,按键一端接高电平,另一端接IO引脚,其中接IO引脚这一端通过一个电阻接到地(这种电阻叫下拉电阻)。图1(b)按键电路的特点如下:

(1)没有按键按下时,IO引脚为低电平;

(2)有按键按下时,IO引脚为高电平。

所以,如果处理器某个时候读到这个引脚信号为1,说明此时按键按下了,如果读到为0,说明按键没有按下。

暴风开发板的按键电路如图2所示,可以看到,在标航的暴风开发板中特意将两个按键分别按图1的(a)和(b)来连接,以便读者学习这两种按键电路按键状态的识别,这个设计比较人性化,可以照顾不同开发者的不同应用场合。

图片

图2 暴风开发板按键电路图

图2中要注意,此时电路设计没有在PE2和PA0两个引脚分别连一个电阻到VCC和地,所以我们得在GD32的内部这两个引脚这里分别使能这两个电阻(GD32和STM32的每个IO引脚内部都配有一对受控的上下拉电阻)。

1.2.2 机械按键状态识别的思路设计

通常,我们都是设计一个函数来单独判断按键是否按下,这个按键函数的思路设计如下:

uint8_t Key_Scan(void)

{

   if(KEY0_Status == 0) || (WK_UP1_Status == 1)  //说明有按键按下了

   {

      if(KEY0_Status == 0)     return KEY0_Value;

      if(WK_UP1_Status == 1)  return WK_UP1_Value;

}

return KEY0_NO;

}


在函数Key_Scan中,我们先判断KEY0的状态是不是0或者WK_UP1的状态是不是为1,如果KEY0的状态是0或者WK_UP1的状态是1,说明按键按下了,接下来进行细分,看看是KEY0按下还是WK_UP1按下,并返回对应的按键值。

对于Key_Scan函数的调用,我们可以在主函数中这样调用

int main(void)

{

      uint8  keyvalue = 0;

    系统初始化;

      while(1)

      {

         keyvalue = Key_Scan();

         if(keyvalue == KEY0_Value) LED0 = ~LED0; // LED0状态反转

         if(keyvalue == WK_UP1_Value) LED1 = ~LED1;// LED1状态反转

    }

}


在主函数中,我们循环执行按键扫描,如果发现按键扫描函数返回的是KEY0_Value,则将LED0的状态反转,如果返回的是WK_UP1_Value,则将LED1的状态反转。总的来说,我们是希望按下一次按键,对应的LED的状态就反转。

上面这两个函数的配合是否有问题呢?表面看来好像没有问题,但是当你用这个思路去完善程序并下载到开发板执行的时候,你会发现按键按下时,灯的状态是不受控的,这个不受控的原因是什么呢?我们看一下整个执行过程。

假设有按键KEY0按下,则整个过程为

①执行语句“keyvalue = Key_Scan();”此时返回KEY0_Value,接着执行判断并使得LED0状态反转一次,这个过程持续时间非常短,1ms内估计就能执行完。

②又回来执行语句“keyvalue = Key_Scan();”,此时由于按键仍然处于按下状态(人为按下时,按键的按下状态通常会超过100ms,典型的是600ms左右),所以又会返回KEY0_Value,接着执行判断并使得LED0状态又反转一次。

注意,此时按键的状态已经变化两次了,但是我们只是执行一次按下而已!!!!!

继续往下分析,你会发现按键按下一次时,这个判断系统会执行多次返回,这是错误的。错误的原因在哪里呢?在Key_Scan这个函数中,这个函数里面只要KEY0_Status等于0,它就会返回一次KEY0_Value,所以我们需要加一个变量,用于描述按键的当前状态,如果当前按键已经按下了,则这里就不需要再次判断了。由于这个变量描述按键的按下与弹起状态,在Key_Scan执行完后也不能释放它的存储空间,所以我们需要用static修饰它,此时的Key_Scan函数需要修改如下:

uint8_t Key_Scan(void)

{

   static  uint8_t  flag = 0; //flag =0说明当前是弹起,=1说明是按下

   /*如果刚才是弹起但现在有按键按下则判断是那个按键按下,同时将按键状态置为1*/

   if((flag == 0)&&((KEY0_Status == 0) || (WK_UP1_Status == 1)))     {

      flag = 1;

      if(KEY0_Status == 0)     return KEY0_Value;

      if(WK_UP1_Status == 1)  return WK_UP1_Value;

}

return KEY0_NO;

}

将Key_Scan函数修改为上面的样子后,解决了按下一次就执行一次返回,避免了按下一次则返回多次的问题。但是它仍然是有重大缺陷的,因为我们按下一次按键后,flag被设置为1了,当按键再次被按下时里面的按下判断再也得不到执行,也即刚刚修改后的函数只能判断一次按键按下。要想将flag恢复为0,我们要在Key_Scan中增加弹起的语句,如果弹起了,将flag设置为0,则就可以解决多次按下后都能触发判断的问题了。增加判断后的Key_Scan函数如下:


uint8_t Key_Scan(void)

{

   static  uint8_t  flag = 0; //flag =0说明当前是弹起,=1说明是按下

   /*如果刚才是弹起但现在有按键按下则判断是那个按键按下,同时将按键状态置为1*/

   if((flag == 0)&&((KEY0_Status == 0) || (WK_UP1_Status == 1)))  

   {

      flag = 1;

      if(KEY0_Status == 0)     return KEY0_Value;


      if(WK_UP1_Status == 1)  return WK_UP1_Value;

}

/*如果刚才按键按下,现在弹起了,则设置flag=0*/

   if((flag == 1)&&((KEY0_Status == 1) && (WK_UP1_Status == 0)))     {

      flag = 0;

  }

  return KEY0_NO;

}


注意,按键弹起指的是所有按键的弹起,所以(KEY0_Status == 1) && (WK_UP1_Status == 0)这里要用逻辑与,体现出“而且”之意。

至此,机械按键状态识别的关键问题就解决了。

1.2.3 机械按键的抖动及其消除

虽然关键问题解决了,但还有一些细节要注意,这个细节就是按键的抖动。

以图3的按键电路为例,当按键按下时,PE2引脚的电平状态如图4所示。

图片

图3 按键电路图

图片

图4 按键按下过程

由图4可见,按键按下时,PE2并不是马上变为低电平,而是有一个渐变过程,而在弹起时也不是马上变为高电平,它也有一个渐变过程。这些渐变过程我们叫做抖动。在进行按键的按下与弹起时,我们都要进行抖动的消除。消除的方法非常简单,就是延时。通常,抖动持续的时间在10ms之内,所有,我们只要进行10ms的延时可以解决掉绝大部分机械按键的抖动—如果解决不了,此时就要用示波器测一下你的按键按下与放开时的信号,看看具体的抖动是多少,然后增加延时消除它,笔者就曾遇到过需要延时20ms的情况……

这个消抖的过程如下:

1、按下的消抖

if(按键按下)
{   Delay(10ms);   if(按键按下)
   {
      按键是真的按下,执行相应的动作;
    }
}

2、弹起的消抖

if(按键弹起)
{Delay(10ms);if(按键弹起)
{
按键是真的弹起了,执行相应的动作;
}
}

1.2.4 完整的按键判断程序

加入消抖后,整个按键判断的函数可以修改如下

uint8_t KEY_Scan(void)

{

      static  uint8_t  flag=0; //按键弹起为0,按下为1

      if(( flag == 0) && ((KEY0_Status == 0)||(WK_UP1_Status == 1)))

      {  

         /*按键刚刚处于弹起状态,但现在有按下*/

         HAL_Delay(10);    //延时10ms,消除抖动

         if((KEY0_Status == 0)||(WK_UP1_Status == 1))

         {  

            /*确实有按键按下*/

            flag = 1;   //按键为按下状态

            if(KEY0_Status == 0)  return KEY0_Value;

            if(WK_UP1_Status == 1) return WK_UP1_Value;

         }  

      }

     if((flag == 1) && ((KEY0_Status == 1) && (WK_UP1_Status == 0)))

      {  

         /*按键处于弹起状态而且刚才是按下状态*/

         HAL_Delay(10);    //消除弹起抖动

         if((KEY0_Status == 1) && (WK_UP1_Status == 0))


         {

            flag = 0;   //按键弹起了

         }  

      }

      return  KEY_NO;   //没有按键按下返回KEY_NO

}


1.3 按键状态判断实验

下面我们通过一个例子来验证按键状态的识别。

【例1】已知按键电路和LED电路如图5所示,编写程序实现以下功能:

按下按键KEY0, LED0的状态反转;按下按键WK_UP1,LED1的状态反转。

图片

图5 按键电路和LED电路示意图

【实现过程】

1.配置RCC的高速时钟来自于外部晶体陶瓷晶振,并且设置HCLK的频率为72Mhz。

2.设置调式方式为Serial Wire。

以上两步不懂如何设置的可以回头看一下【模块一 GPIO口的输出功能-LED的闪烁实验】这一部分的步骤介绍。

3.设置PE12、PE13引脚的工作模式为输出,PE12的User Label选项为LED0,PE13的User Label设置为LED1,以使能更加直观方便。另外,一开始我们将这两盏LED灯都点亮,以方便观察结果。PE12和PE13的设置结果如图6所示。

图片

图6 PE12和PE13的设置过程

4.设置PE2和PA0为输入。PE2引脚上拉电阻使能,PA0引脚下拉电阻使能。同时设置PE2的用户标号为KEY0,设置PA0引脚的标号为WK_UP1,。PE2引脚的设置如图7所示,PA0引脚的设置如图8所示。

图片

图7 PE2引脚的设置结果示意图

图片

图8 PA0引脚的设置

5.设置好后,给工程取名,同时选择IDE,并生成工程代码。

6.添加代码。

①编写按键识别的C语言文件,其内容如图9所示。

图片

图9 key.c中内容示意图

②在key.h中定义KEY0_Value等宏名,如图10所示。

图片

图10 KEY0_Value等的定义示意图

③修改主函数,其内容如图11所示。

图片

图11 主函数的内容示意图

在主函数中,注意要将头文件key.h包含进工程,如图12所示。

图片

至此,工程的代码添加完毕,编译后下载到开发板,按复位键,然后按KEY0或者WK_UP0,可以看到对应的LED灯的状态反转,任务目标完成。

1.4 按键识别实验用到的HAL库的函数解读

1.引脚电平反转函数 HAL_GPIO_TogglePin()

在主函数main的while循环中,我们使用到了函数HAL_GPIO_TogglePin,这个函数的相关信息为

●作用:将某个IO引脚的输出电平反转。比如要反转PE12引脚的电平,我们可以采用如下的方式来调用该函数:

HAL_GPIO_TogglePin(GPIOE, GPIO_PIN_12);

●函数参数,有两个,第一个用于指明要反转信号的引脚位于第几组GPIO口,第二个用于指明要反转的是哪一个引脚的信号。

是不是很方便呢?

最后要注意,函数HAL_GPIO_TogglePin要使用于将IO引脚已经配置为输出的场合。

2.读引脚信号函数HAL_GPIO_ReadPin()

在key.c函数中,有一个宏定义

#define KEY0_Status HAL_GPIO_ReadPin(KEY0_GPIO_Port, KEY0_Pin)

里面用到了一个函数HAL_GPIO_ReadPin,这个函数相关的信息如下:

●作用:读取某个引脚的状态。比如要读取PA0的状态,我们可以采用如下的方式来调用该函数

HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);

●函数参数,有两个,第一个用于指明要读取信号的引脚位于那组GPIO口,第二个参数用于指明是那个引脚。

注意,函数HAL_GPIO_ReadPin使用于IO引脚已经设置为输入的场合

1.5 GPIO输入功能总结

在使用GD32/STM32的IO引脚时要注意以下两点:

1.如果引脚外部没有上拉电阻或者下拉电阻,则可能需要在引脚内部使能上拉电阻或者下拉电阻。

2.对按键状态进行识别时,一定要注意防止按下一次时有多次返回值。


关键字:GD32  MCU  识别 引用地址:GD32 MCU机械按键状态的识别

上一篇:GD32开发实战指南(基础篇) 第14章 内部温度传感器
下一篇:GD32 Azure IoT解决方案

推荐阅读最新更新时间:2026-03-24 01:11

GD32 MCU机械按键状态识别
GPIO口的输入功能-机械按键状态的识别 硬件: 深圳标航科技有限公司 暴风 开发板 处理器:GD32F103VET6 开发环境:MDK(keil 5) + STM32CubeMX 1.1 GPIO口的输入的作用 输入,其意是指将处理器外部的逻辑信号0或者1输入到处理器的内部。输入是每一个处理器的IO引脚的基本功能。利用处理器的输入功能我们可以获取外部电路的状态,进而做出进一步的判断。GPIO的输入功能的典型应用是获取机械按键的状态—判断按键是按下还是弹起。 1.2 机械按键状态的识别 1.2.1 机械按键电路的设计 按键有两个状态,一个是按下一个是弹起。通过巧妙的电路设计,会使得按键的按下与弹起时IO引脚的逻辑电平不一样。通
[单片机]
<font color='red'>GD32</font> <font color='red'>MCU</font><font color='red'>机械</font><font color='red'>按键</font><font color='red'>状态</font>的<font color='red'>识别</font>
STM32与GD32单片机
一、相同点 都是基于Arm Cortex-M3/M4内核的32位通用微控制器,广泛应用于各种嵌入式系统和物联网领域。 二、不同点 1.1 内核和主频 GD32单片机采用的是二代的M3/M4内核;根据ARM公司的M3内核勘误表,GD32使用的内核只有一个BUG。 STM32单片机主要采用的是一代的M3/M4内核;STM32使用的内核有多个BUG1。 所以GD32的内核更稳定和可靠! 高速外部时钟(HSE)时,GD32的主频最大可以达到108MHz,STM32的主频最大只能达到72MHz; 高速内部时钟(HSI)时,GD32的主频最大可以达到108MHz,STM32的主频最大只能达到64MHz2; 主频越高,意味着单片机代码运行的速度
[单片机]
GD32 MCU 移植教程】10、从STM32F030系列移植到GD32E230系列
1. 前言 GD32E230 对比 STM32F030 有着很好的兼容性和更高的性价比,内核和外设都有所增强。本人曾做过产品的 MCU 替换,将基于 STM32F0xx 1.5.0 固件库的应用程序移植到 GD32E230 上,大体上来说工作量不大,移植后的效果也不错,GD32E230 相比 STM32F030 有不少功能的升级,主频也更高,能感觉到国产 MCU 一直在进步。本人将此前的移植经验进行了整理,可帮助有需要的朋友快速将应用程序从 STM32F030 移植到GD32E230 上(基于STM32F0xx 标准库 V3.5.0 和 STM32F10x 标准库 V3.5.0)。本移植工作除基于STM32F0xx 1.5.0固件
[单片机]
【<font color='red'>GD32</font> <font color='red'>MCU</font> 移植教程】10、从STM32F030系列移植到GD32E230系列
GD32单片机STM32远程下载手机程序升级固件下载局域网网页升级工具
GD32、STM32单片机,是我们最常见的一种MCU。通常我们在使用STM32单片机都会遇到程序在线升级下载的问题。 GD32/STM32单片机的在线下载通常需要以下几种方式完成: 1、使用ST/GD提供的串口下载工具,本地完成固件的升级下载。 2、自行完成系统BootLoader的编写,将系统程序分为BootLoader和APP两个部分,BootLoader完成固件升级。 3、使用STM32/GD固件服务器,完成固件的升级,固件服务器https://simplewifi.taobao.com/ 几种方式各有优缺点: 使用ST提供的方法进行固件升级,方法简单,不需要额外的开发。但是,只能本地完成STM32单片机的升级。
[单片机]
<font color='red'>GD32</font><font color='red'>单片机</font>STM32远程下载手机程序升级固件下载局域网网页升级工具
GD32 MCU上电跌落导致启动异常如何解决
大家是否碰到过MCU上电过程中存在电源波动或者电压跌落导致MCU启动异常的问题?本视频将会为大家讲解可能的原因以及解决方法: GD32 MCU上下电复位波形如下图所示,上电过程中如果存在吃电的模块,比如wifi模块/4G模块/开启某块电路等,可能存在电源电压跌落的情况,此时若MCU在启动过程中可能会造成MCU加载代码异常,进而导致启动异常。电压跌落到POR和PDR之间会有可能出现启动异常,因为该段电压区间为非正常工作电压且不会发生PDR复位。 解决方法上可以有以下两种:1、硬件整改上电波形,在VDD端增加电容,提升VDD的供电能力,进而降低电源波动;2、修改NRST引脚对应的RC阻容,调整MCU启动时间,避开电源电压跌落的
[单片机]
GD32 MCU 入门教程】GD32 MCU 常见外设介绍(3)NVIC 介绍
NVIC(Nested vectored interrupt controller,嵌套向量中断控制器)是Cortex-M处理器的一部分,它是可编程的,且寄存器位于存储器映射的系统控制空间(SCS)。NVIC与内核相辅相成,共同完成对中断的响应。本章将介绍中断的优先级设置、如何定义中断函数名称、中断向量如何偏移。有关NVIC的更多知识,请见《ARM Coretex-M3权威指南》。 3.1.优先级的设置 在Cortex-M中,优先级对于异常来说很关键的,它会影响一个异常是否能被响应,以及何时可以响应。优先级的数值越小,则优先级越高。Cortex-M支持中断嵌套,使得高优先级异常会抢占低优先级异常。有3个系统异常:复位,NMI以
[单片机]
【<font color='red'>GD32</font> <font color='red'>MCU</font> 入门教程】<font color='red'>GD32</font> <font color='red'>MCU</font> 常见外设介绍(3)NVIC 介绍
你了解GD32 MCU上下电要求吗
你了解GD32 MCU的上下电要求吗?MCU的上下电对于系统的稳定运行非常重要。 以GD32F30X为例,上电/掉电复位波形如如下图所示。 上电过程中,VDD/VDDA电压上电爬坡,当电压高于VPOR(上电复位电压)MCU开始启动,之后内部逻辑电路延迟2ms后NRST引脚拉高,MCU正式启动,此为上电过程。上电过程中,对于上电爬坡斜率没有要求,对于电源稳定性有要求,上电过程中尽量避免电源波动以及突然的跌落,比如上电到2.6V以后再次跌落到POR附近,但没有跌破PDR,则有可能造成MCU加载代码异常进而导致启动失败。 掉电过程中,当电压低于VPDR后,NRST拉低,MCU完成掉电。掉电电压需要跌落到VPDR以下,尽量跌落到0V
[单片机]
你了解<font color='red'>GD32</font> <font color='red'>MCU</font>上下电要求吗
如何排查GD32 MCU复位是由哪个复位源导致的?
上期为大家讲解了GD32 MCU复位包括电源复位和系统复位,其中系统复位还包括独立看门狗复位、内核软复位、窗口看门狗复位等,在一个GD32系统中,如果莫名其妙产生了MCU复位,如何排查具体是由哪个复位源导致的呢? GD32 MCU贴心的为大家提供了一个查看复位源的寄存器,如下图所示,该寄存器的bit26-bit31显示各种复位状态,其中LPRSTF表示发生过低功耗复位、WWDGTRSTF表示发上过窗口看门狗复位、FWDGTRSTF表示发生过独立看门狗复位、SWRSTF表示发生过系统软复位、PORRSTF表示发生过POR电源复位、EPRSTF表示发生过NRST引脚复位,这几个状态标志位为只读标志位,如果希望清除复位标志,可以通过
[单片机]
如何排查<font color='red'>GD32</font> <font color='red'>MCU</font>复位是由哪个复位源导致的?
小广播
最新单片机文章
何立民专栏 单片机及嵌入式宝典

北京航空航天大学教授,20余年来致力于单片机与嵌入式系统推广工作。

厂商技术中心

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

 
机器人开发圈

电子工程世界版权所有 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2026 EEWORLD.com.cn, Inc. All rights reserved