一、引言

上篇文章中,介绍了GPIO的输出模式,使用推挽输出,点亮LED灯,并让蜂鸣器发声。本文介绍GPIO作为输入的情况。

二、基于标准固件库配置GPIO输入

1. 看原理图

本次实验使用到按键,我的开发板一共有4个按键:

KEY0 -- PE4;   KEY1 -- PE3;   KEY2 -- PE2;   WK_UP -- PA0;

  每个按键的具体连接方式如下:

KEY_UP外接高,所以PA0应配置为下拉,按键没按下时为常低,按键按下,电平由低变高;

KEY0/KEY1/KEY2外界地,所以PE4/PE3/PE2应配置为上拉,按键没按下时为常高,按键按下,电平由高变低

2. 添加工程文件

与GPIO输出相比,FWLIB文件夹中并无变化

Hardware文件夹中添加Key.c文件

同时,添加头文件引用路径

3. 编写代码

LED的相关配置代码参考前一篇文章,本文代码实现的内容为:通过按键控制LED灯的亮灭

Key.c文件中代码如下:

#include "Key.h"
static GPIO_InitTypeDef KEY_UP_init_str =
{
    GPIO_Pin_0,
    GPIO_Mode_IN,
    GPIO_Fast_Speed,
    GPIO_OType_PP,
    GPIO_PuPd_DOWN,
};
static GPIO_InitTypeDef KEY_012_init_str =
{
    GPIO_Pin_4 | GPIO_Pin_3 | GPIO_Pin_2,
    GPIO_Mode_IN,
    GPIO_Fast_Speed,
    GPIO_OType_PP,
    GPIO_PuPd_UP,
};

void KEY_init(void)
{
    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA | RCC_AHB1Periph_GPIOE,ENABLE);

    GPIO_Init(GPIOA,&KEY_UP_init_str);
    GPIO_Init(GPIOE,&KEY_012_init_str);

}

//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,KEY0按下
//2,KEY1按下
//3,KEY2按下 
//4,WKUP按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY2>WK_UP!!
u8 KEY_Scan(u8 mode)
{	 
	static u8 key_up=1;//按键按松开标志
	if(mode)key_up=1;  //支持连按		  
	if(key_up&&(KEY0_Value==0||KEY1_Value==0||KEY2_Value==0||KEY_WAKE_UP==1))
	{
		delay_ms(10);//去抖动 
		key_up=0;
		if(KEY0_Value==0)return 1;
		else if(KEY1_Value==0)return 2;
		else if(KEY2_Value==0)return 3;
		else if(KEY_WAKE_UP==1)return 4;
	}else if(KEY0_Value==1&&KEY1_Value==1&&KEY2_Value==1&&KEY_WAKE_UP==0)key_up=1; 	    
 	return 0;// 无按键按下
}

其中Key_Scan函数为正点原子参考函数。delay 10ms的目的是为了消抖,即检测到按键按下,等待10ms之后,再检测一次,若按键确实按下,则返回相应的按键值;

Key.h文件代码如下:

#ifndef _KEY_H_
#define _KEY_H_
#include "stm32f4xx_rcc.h"
#include "stm32f4xx_gpio.h"
#include "delay.h"

#define KEY_WAKE_UP    GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_0) //PAin(0) //both is ok
#define KEY0_Value           PEin(4)
#define KEY1_Value           PEin(3)
#define KEY2_Value           PEin(2)


extern void KEY_init(void);
extern u8 KEY_Scan(u8);
#endif

main.c中的代码如下:

#include "LED.h"
#include "Key.h"

int main(void)
{
    {
        // Initinal Functions
        delay_init(168);
        LED_init();
        KEY_init();
        
        delay_ms(500);
    }

    while (1)
    {

        {
             //Key Test
             switch(KEY_Scan(0)):
             {
              case 1://按键Key0按下
                   LED0 = !LED0;
                   break;

              case 2://按键Key1按下
                   LED1 = !LED1;
                   break;

              case 3://按键Key2按下
                   LED0 = !LED0;
                   LED1 = !LED1;
                   break;

              case 4://按键WK_UP按下
                   LED0 = !LED0;
                   break;

              default://无按键按下
                   break;
              }
        }
        
    }
    
}

以上便是基于固件库实现GPIO输入的配置步骤。

三、基于CubeMx实现GPIO输入

CubeMx新工程的建立以及生成代码已在前文提过具体步骤,本文重点介绍GPIO输入的配置

PA0以及PE4/PE3/PE2需要全部配置为GPIO_Input的模式

             

    

配置引脚上拉/下拉

而后生成代码

新建key.h文件,内容如下:

#ifndef _KEY_H_
#define _KEY_H_
#include "main.h"
#include "stm32f4xx_hal_gpio.h"
uint8_t KEY_Scan(uint8_t mode);

#define KEY_WAKE_UP    HAL_GPIO_ReadPin(KEY_WK_UP_GPIO_Port,KEY_WK_UP_Pin)
#define KEY0_Value     HAL_GPIO_ReadPin(KEY0_GPIO_Port,KEY0_Pin)
#define KEY1_Value     HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)
#define KEY2_Value     HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)

typedef enum
{
    KEY0 = 1,
    KEY1,
    KEY2,
    KEY_WKUP,

    KEY_max
}KEY_all;

#endif

新建key.c文件,内容如下:


#include "key.h"
//按键处理函数
//返回按键值
//mode:0,不支持连续按;1,支持连续按;
//0,没有任何按键按下
//1,KEY0按下
//2,KEY1按下
//3,KEY2按下 
//4,WKUP按下 WK_UP
//注意此函数有响应优先级,KEY0>KEY1>KEY2>WK_UP!!
uint8_t KEY_Scan(uint8_t mode)
{	 
	static uint8_t key_up=1;//按键按松开标志
	if(mode)key_up=1;  //支持连按		  
	if(key_up&&(KEY0_Value==0||KEY1_Value==0||KEY2_Value==0||KEY_WAKE_UP==1))
	{
		HAL_Delay(10);//去抖动 
		key_up=0;
		if(KEY0_Value==0)return KEY0;
		else if(KEY1_Value==0)return KEY1;
		else if(KEY2_Value==0)return KEY2;
		else if(KEY_WAKE_UP==1)return KEY_WKUP;
	}else if(KEY0_Value==1&&KEY1_Value==1&&KEY2_Value==1&&KEY_WAKE_UP==0)key_up=1; 	    
 	return 0;// 无按键按下
}


在main.c文件的while(1)循环中,添加如下代码

uint8_t KeyPress  = 0;

    KeyPress = KEY_Scan(0);
    switch (KeyPress)
    {
    case KEY0:
      HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
      break;

    case KEY1:
      HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
      break;

    case KEY2:
      HAL_GPIO_TogglePin(Beep_GPIO_Port,Beep_Pin);
      break;
      
    case KEY_WKUP:
      HAL_GPIO_TogglePin(LED0_GPIO_Port,LED0_Pin);
      HAL_GPIO_TogglePin(LED1_GPIO_Port,LED1_Pin);
      HAL_GPIO_TogglePin(Beep_GPIO_Port,Beep_Pin);
      break;
    
    default:
      break;
    }

而后便可编译运行

四、结语

本文介绍了GPIO作为按键输入时候的配置,分别基于标准固件库和CubeMx+HAL库实现功能。需要注意的关键点有:

  • 结合外部按键电路判断GPIO输入上拉/下拉;
  • delay 10ms用以消抖;
  • Key_Scan()函数中对于支持连按和不支持连按的实现
Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐