前言

本篇文章将带大家来学习按键的使用,按键其实也就是GPIO的控制,只不过按键的使用需要将GPIO配置为输入模式。

一、按键电路图分析及控制原理

在单片机中,按键通常用于实现用户交互和控制功能。

按键元件:

按键通常由一个机械开关组成,当按键被按下时,开关闭合,允许电流流过。

在板子上有一个用户按键,可供用户使用。

根据原理图可知这个按键接到了PA0引脚,当按键被按下时WK_UP接到了VCC,所以当按键被按下时表现为高电平,但是按键没有被按下时电平状态是不确定的。

所以这里需要接入下拉电阻,下面是接下拉电阻的原因:

稳定的逻辑状态:

当按键未被按下时,输入端口需要保持一个确定的逻辑状态,以确保系统正确地读取输入信号。接下拉电阻将输入端口连接到地(GND),使得在按键未被按下时,输入端口被拉低到逻辑低电平(0),从而保持稳定的逻辑状态。
防止悬空状态:

如果没有接下拉电阻,当按键未被按下时,输入端口将处于悬空状态,容易受到外部环境的干扰而导致不确定的电平状态。这可能导致系统误判按键状态,引发错误的操作或行为。

GD32零基础教程第四节(按键控制LED灯)-LMLPHP
GD32零基础教程第四节(按键控制LED灯)-LMLPHP

二、按键控制LED灯代码编写

1.使能对应的GPIO引脚:

rcu_periph_clock_enable(RCU_GPIOA);//使能GPIOA时钟

2.将GPIO引脚设置为输入模式,并且设置一个下拉电阻:

gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);

3.读取GPIO引脚的状态:

FlagStatus gpio_input_bit_get(uint32_t gpio_periph, uint32_t pin)

函数参数:
gpio_periph:要操作的GPIO外设,通常是使用宏定义指定的GPIOx(x表示具体的GPIO端口,如GPIOA、GPIOB等)。
pin:要获取状态的GPIO引脚,通常是使用宏定义指定的引脚编号(如GPIO_PIN_0、GPIO_PIN_1等)。

返回值:
FlagStatus类型,是一个枚举类型,可能的取值有:
RESET:表示引脚未置位,即为低电平状态。
SET:表示引脚被置位,即为高电平状态。

具体代码:

#include "gd32f4xx.h"//包含了该系列芯片的寄存器定义、常量定义和函数声明等信息
#include "gd32f4xx_libopt.h"//GD32F4 系列芯片的外设库选项配置文件
#include "systick.h"//配置和使用 SysTick 定时器的功能文件

#include "led.h"


static int flag = 0;//定义一个变量来表示LED灯当前的状态

int main(void)
{
    systick_config();//配置系统主频168M,外部8M晶振,配置在#define __SYSTEM_CLOCK_168M_PLL_8M_HXTAL        (uint32_t)(168000000)
			
		LED_Init();//初始化LED灯模块
	
		LED_OFF();//关闭LED灯
	
		rcu_periph_clock_enable(RCU_GPIOA);//使能GPIOA时钟
		gpio_mode_set(GPIOA, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, GPIO_PIN_0);//PA0配置成下拉输入	
		
    while(1)
		{
				if(1 == gpio_input_bit_get(GPIOA, GPIO_PIN_0))
				{
					delay_1ms(10);//延时消抖
					if(1 == gpio_input_bit_get(GPIOA, GPIO_PIN_0))
					{
							flag = !flag;//翻转LED灯电平状态
							if(flag == 1)
							{
								LED_ON();//点亮LED灯
							}
							else if(flag == 0)
							{
								LED_OFF();//关闭LED灯
							}
					}
				}
    }
}

这里主要需要讲解的就是这个延时消抖部分:

按键需要延时消抖是因为机械按键在按下或释放的过程中会产生短暂的电气接触和断开,导致在按键信号上出现不稳定的波动。这种波动称为按键抖动(Bouncing),可能会导致系统误判按键操作,认为按键被按下了多次或产生了错误的按键事件。因此,需要延时消抖来确保稳定地检测按键状态。

按键被按下的理想状态:

GD32零基础教程第四节(按键控制LED灯)-LMLPHP

按键被按下的实际状态:

GD32零基础教程第四节(按键控制LED灯)-LMLPHP

三、模块化封装按键代码

在HardWare文件夹中创建key.c和key.h两个文件来保存按键的代码:

key.c:

/**
 * @file    key.c
 * @brief   KEY功能函数实现文件
 * @author  花落已飘
 * @date    2024-04-08
 */

#include "key.h"
#include "systick.h" // 包含用于延时的头文件

/**
 * @brief 初始化按键
 * @note  使能按键对应 GPIO 端口的时钟,并设置按键引脚为输入模式,并启用下拉电阻
 */
void KEY_Init(void)
{
    KEY_PORT_CLK_EN(); // 使能按键对应 GPIO 端口的时钟
    gpio_mode_set(KEY_PORT, GPIO_MODE_INPUT, GPIO_PUPD_PULLDOWN, KEY_PIN); // 设置按键引脚为输入模式,并启用下拉电阻
}

/**
 * @brief 获取按键状态
 * @note  如果按键被按下,进行延时消抖后再次检测按键状态
 * @return 按键状态,KEY_PRESS 表示按键被按下,KEY_RELEASE 表示按键未被按下
 */
int GetKeyStatus(void)
{
    KEY_STATUS key_status = KEY_RELEASE; // 初始化按键状态为释放状态

    if (1 == gpio_input_bit_get(GPIOA, GPIO_PIN_0)) // 如果检测到按键被按下
    {
        delay_1ms(10); // 延时消抖,等待按键稳定
        if (1 == gpio_input_bit_get(GPIOA, GPIO_PIN_0)) // 再次检测按键状态
        {
            key_status = KEY_PRESS; // 如果按键仍然处于按下状态,则更新按键状态为按下
        }
        else
        {
            key_status = KEY_RELEASE; // 如果按键已经释放,则更新按键状态为释放
        }
    }
    
    return key_status; // 返回按键状态
}

key.h:

#ifndef KEY_H
#define KEY_H

#include "gd32f4xx.h"        // 包含了该系列芯片的寄存器定义、常量定义和函数声明等信息
#include "gd32f4xx_libopt.h" // GD32F4 系列芯片的外设库选项配置文件

#define KEY_PORT     GPIOA     // 按键所在的 GPIO 端口
#define KEY_PIN      GPIO_PIN_0 // 按键所在的引脚编号
#define KEY_CLOCK    RCU_GPIOA // 按键对应的时钟使能

#define KEY_PORT_CLK_EN()  rcu_periph_clock_enable(KEY_CLOCK) // 使能按键对应 GPIO 端口的时钟

typedef enum
{
    KEY_PRESS = 0,   // 按键被按下
    KEY_RELEASE      // 按键未被按下
} KEY_STATUS;         // 定义按键状态枚举类型

/**
 * @brief 初始化按键 GPIO 端口
 * @note  初始化按键所在的 GPIO 端口为输入模式,并设置上下拉电阻为下拉
 */
void KEY_Init(void);

/**
 * @brief 获取按键状态
 * @note  如果按键被按下,进行延时消抖后再次检测按键状态
 * @return 按键状态,KEY_PRESS 表示按键被按下,KEY_RELEASE 表示按键未被按下
 */
int GetKeyStatus(void);

#endif

main.c中调用:

#include "gd32f4xx.h"//包含了该系列芯片的寄存器定义、常量定义和函数声明等信息
#include "gd32f4xx_libopt.h"//GD32F4 系列芯片的外设库选项配置文件
#include "systick.h"//配置和使用 SysTick 定时器的功能文件

//硬件初始化代码
#include "led.h"
#include "key.h"

static int flag = 0;//定义一个变量来表示LED灯当前的状态

int main(void)
{
    systick_config();//配置系统主频168M,外部8M晶振,配置在#define __SYSTEM_CLOCK_168M_PLL_8M_HXTAL        (uint32_t)(168000000)
			
		LED_Init();//初始化LED灯模块
		KEY_Init();//初始化KEY按键模块
		LED_OFF();//关闭LED灯
		
    while(1)
		{
				if(KEY_PRESS == GetKeyStatus())
				{
						flag = !flag;
						if(flag == 1)
						{
								LED_ON();
						}
						else if(flag == 0)
						{
								LED_OFF();
						}
				}
    }
}

总结

本篇文章就讲解到这里,主要是按键的输入模式的使用。

但是在这里代码还是有非常多不足的地方,比如延时会阻塞程序的运行,降低CPU的执行效率。

以及没有及时的读取按键的数据导致数据的丢失等问题,后面的文章也会讲解到解决这些问题的方法。

链接:https://pan.baidu.com/s/10J6bdHrFWlSY-SFLW8hlNw
提取码:abvi

04-09 08:37