基于STM32G431嵌入式学习笔记——三、KEY按键入门

本文介绍如何在STM32开发板上配置按键并实现与LED灯的联动效果,包括按键去抖、状态读取及LED控制等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、按键在CubeXM里的配置

可以打开先前配置过LED的CubeMX,在其基础上进行按键的配置。
个人建议在这里先将原环境拷贝
在这里插入图片描述
更改副本文件夹名为LED_KEY_LCD
在这里插入图片描述
打开文件夹中的.ioc文件进行配置
在这里插入图片描述
在配置之前,我们先查阅产品手册了解按键的电路图,以便确定我们引脚的选择。
在这里插入图片描述因此我们需要将这四个引脚进行配置,模式为输入:
在这里插入图片描述
其他位置无需更改,配置完毕后单击右上角生成代码

二、配置按键的文件环境
1.打开keil环境,进行初始化编译

打开LED_KEY_LCD文件夹里的keil环境
在这里插入图片描述
在这里若感觉文件名不适,可以将其修改为与文件夹相同的名字。
在这里插入图片描述
打开环境,进行初始化编译
在这里插入图片描述

2.添加按键相关的.c .h文件
(1)新建文件

在这里插入图片描述命名为key.c,保存路径如下
在这里插入图片描述新建key.h头文件,保存路径如下
在这里插入图片描述

(2)将两文件与环境关联起来。

添加key.c文件进入环境,具体操作可参考基于STM32G431嵌入式学习笔记——二、LCD模块入门中有关于lcd.c文件的导入
在这里插入图片描述
可以发现key.c已经进入环境
在这里插入图片描述在该文件里输入#include “key.h”,保存后编译,实现.h文件与环境的关联
在这里插入图片描述

3.编写按键函数
(1)在key.h中

因为我们需要读取按键的状态,而按键的本质还是引脚数据的读取,因此我们需要用到读引脚信息的函数
GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef GPIOx, uint16_t GPIO_Pin)
函数定义位于
stm32g4xx_hal_gpio.c的370行左右
在这里插入图片描述调用该函数需要使用的头文件也在
stm32g4xx_hal_gpio.c*中给了我们提示:
在这里插入图片描述因此我们key.h里一定要加上该头文件
在这里插入图片描述我们曾了解过,KEY按键与PA0 PB0 PB1 PB2这四个引脚相关(如下)
在这里插入图片描述因此我们要想读取这四个按键的状态,结合上面的读引脚函数应以如下方式调用:

HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0);//key4
HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2);//key3
HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1);//key2
HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0);//key1

借助宏定义表示四个按键的状态,提高代码可读性

#define KB1 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_0)
#define KB2 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_1)
#define KB3 HAL_GPIO_ReadPin(GPIOB,GPIO_PIN_2)
#define KB4 HAL_GPIO_ReadPin(GPIOA,GPIO_PIN_0)

在这里插入图片描述头文件中还要包含我们想要书写的函数的声明,因此再添加相关函数的声明:
在这里插入图片描述

(2)在key.c中

第一个是普通按下的函数

#include "key.h"

char Key_Scan(void)
{
    static int count1,count2,count3,count4;//四个按键
    count1 = count2 = count3 = count4 = 0;
    char key_value = 0;//键值
    /* key1 */
    if(KB1 == 0)
    {
        HAL_Delay(10);//去抖
        if(KB1 == 0)
        {
            count1++;
            if(count1 == 1)
            {
                key_value = 1;//键值为1
                while(KB1 == GPIO_PIN_RESET);//按键松开的效果
            }
        }
    }
    else
    {
        count1 = 0;//置零
    }
    /* key2 */
    if(KB2 == 0)
    {
        HAL_Delay(10);//去抖
        if(KB2 == 0)
        {
            count2++;
            if(count2 == 1)
            {
                key_value = 2;//键值为2
                while(KB2 == GPIO_PIN_RESET);//按键松开的效果
            }
        }
    }
    else
    {
        count2 = 0;//置零
    }
    /* key3 */
    if(KB3 == 0)
    {
        HAL_Delay(10);//去抖
        if(KB3 == 0)
        {
            count3++;
            if(count3 == 1)
            {
                key_value = 3;//键值为3
                while(KB3 == GPIO_PIN_RESET);//按键松开的效果
            }
        }
    }
    else
    {
        count3 = 0;//置零
    }
    /* key4 */
    if(KB4 == 0)
    {
        HAL_Delay(10);//去抖
        if(KB4 == 0)
        {
            count4++;
            if(count4 == 1)
            {
                key_value = 4;//键值为4
                while(KB4 == GPIO_PIN_RESET);
            }
        }
    }
    else
    {
        count4 = 0;
    }
    return key_value;
}

第二个是要求既可以长按,又可以短按的按键函数。

char Key_Scan_CD(void)
{
    static int count1,count2,count3,count4;
    count1 = count2 = count3 = count4 = 0;
    char key_value = 0;
    /* key1 */
    if(KB1 == 0)
    {
        HAL_Delay(10);//去抖
        if(KB1 == 0)
        {
            count1++;
            if(count1 > 70)//长按
            {
                key_value = 11;
            }
        }
    }
    else
    {
        if(count1 >=1 && count1<=50)//短按
            key_value = 1;
        count1 = 0;//恢复置零
    }
    /* key2 */
    if(KB2 == 0)
    {
        HAL_Delay(10);//去抖
        if(KB2 == 0)
        {
            count2++;
            if(count2 > 70)//长按
            {
                key_value = 22;
            }
        }
    }
    else
    {
        if(count2 >=1 && count2<=50)//短按
            key_value = 2;
        count2 = 0;//恢复置零
    }
    /* key3 */
    if(KB3 == 0)
    {
        HAL_Delay(10);//去抖
        if(KB3 == 0)
        {
            count3++;
            if(count3 > 70)//长按
            {
                key_value = 33;
            }
        }
    }
    else
    {
        if(count3 >=1 && count3<=50)//短按
            key_value = 3;
        count3 = 0;//恢复置零
    }
    /* key4 */
    if(KB4 == 0)
    {
        HAL_Delay(10);//去抖
        if(KB4 == 0)
        {
            count4++;
            if(count4 > 70)//长按
            {
                key_value = 44;
            }
        }
    }
    else
    {
        if(count4 >=1 && count4<=50)//短按
            key_value = 4;
        count4 = 0;//恢复置零
    }
    return key_value;
}
三、按键与LED的联动

编写程序实现以下功能:
短按key1——只有奇数灯全亮
短按key2——只有偶数灯全亮
短按key3——全亮
短按key4——全灭

1.编写灯光控制函数
(1)编写led.h
#include "stm32g4xx_hal.h"

/*灯光的亮/灭*/
#define ON	1
#define OFF	0
/*宏定义表示灯光编号*/
#define LED1		GPIO_PIN_8
#define LED2    	GPIO_PIN_9
#define LED3    	GPIO_PIN_10
#define LED4    	GPIO_PIN_11
#define LED5    	GPIO_PIN_12
#define LED6    	GPIO_PIN_13
#define LED7    	GPIO_PIN_14
#define LED8    	GPIO_PIN_15
#define LEDALL		GPIO_PIN_All
/*控制亮灯函数的声明*/
void Control_LED(uint16_t LED, uint8_t LED_Status);

(2)编写亮灯函数

led.c文件内容如下:

#include "led.h"

void Control_LED(uint16_t LED, uint8_t LED_Status)
{
    if(LED_Status == OFF)//灭灯
    {
        HAL_GPIO_WritePin(GPIOC,LED,GPIO_PIN_SET);//设置端口引脚,标明已被占用
        HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
    }
    else //亮灯
    {
        HAL_GPIO_WritePin(GPIOC,LED,GPIO_PIN_RESET);//清除端口引脚,则LED可以使用
        HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_SET);
        HAL_GPIO_WritePin(GPIOD,GPIO_PIN_2,GPIO_PIN_RESET);
    }
}

2.编写主函数

①记得添加#include" led.h"头文件
在这里插入图片描述②主函数中书写switch逻辑语句

int main(void)
{
  /* USER CODE BEGIN 1 */

  /* USER CODE END 1 */

  /* MCU Configuration--------------------------------------------------------*/

  /* Reset of all peripherals, Initializes the Flash interface and the Systick. */
  HAL_Init();

  /* USER CODE BEGIN Init */

  /* USER CODE END Init */

  /* Configure the system clock */
  SystemClock_Config();

  /* USER CODE BEGIN SysInit */

  /* USER CODE END SysInit */

  /* Initialize all configured peripherals */
  MX_GPIO_Init();
  /* USER CODE BEGIN 2 */
  LCD_Init();//LCD初始�?
  /* USER CODE END 2 */

  /* Infinite loop */
  /* USER CODE BEGIN WHILE */
	Control_LED(LEDALL,0);
  while (1)
  {
		value = 0;
    value = Key_Scan();
    switch (value)
    {
      case 1://只有奇数灯亮
        Control_LED(LED1,1);
        Control_LED(LED3,1);
        Control_LED(LED5,1);
        Control_LED(LED7,1);
		Control_LED(LED2,0);
        Control_LED(LED4,0);
        Control_LED(LED6,0);
        Control_LED(LED8,0);
        break;
      case 2://只有偶数灯亮
        Control_LED(LED2,1);
        Control_LED(LED4,1);
        Control_LED(LED6,1);
        Control_LED(LED8,1);
        Control_LED(LED1,0);
        Control_LED(LED3,0);
        Control_LED(LED5,0);
        Control_LED(LED7,0);
        break;
      case 3://灯光全亮
        Control_LED(LEDALL,1);
        break;
      case 4://灯光全灭
        Control_LED(LEDALL,0);
        break;
    }
    // lcd_test();
  }
  /* USER CODE END 3 */
}
3.实验结果

在这里插入图片描述在这里插入图片描述在这里插入图片描述

### 蓝桥杯嵌入式竞赛中按键操作的学习笔记 #### 按键硬件连接与初始化 STM32单片机上的四个按键分别连接至PB0~PB2以及PA0引脚。这些按键的一端通过外部上拉电阻连接到电源,另一端接地。当按键被按下时,对应的GPIO引脚电平会被拉低[^1]。 ```c // 初始化GPIO引脚用于按键输入 void MX_GPIO_Init(void) { GPIO_InitTypeDef GPIO_InitStruct = {0}; __HAL_RCC_GPIOA_CLK_ENABLE(); __HAL_RCC_GPIOB_CLK_ENABLE(); /* Configure PB0-PB2 and PA0 as input with pull-up */ GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2; GPIO_InitStruct.Mode = GPIO_MODE_INPUT; GPIO_InitStruct.Pull = GPIO_PULLUP; HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); GPIO_InitStruct.Pin = GPIO_PIN_0; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); } ``` #### 中断优先级处理 为了防止在按键消抖过程中受到其他中断的影响,特别是SysTick定时器的中断,应当调整其相对于外部中断线(EXTI)的优先级。确保SysTick中断具有更高的抢占优先级可以有效避免因毛刺引起的误触发问题[^2]。 ```c // 配置SysTick中断优先级高于按键中断 void SystemClock_Config(void) { ... // 设置SysTick中断优先级为最高 NVIC_SetPriority(SysTick_IRQn, 0); ... } ``` #### 定时扫描机制实现消抖及长短按识别 采用滴答计数器(Tick Timer),即每过一定周期(如10毫秒),执行一次按键状态检查。这种方法不仅能够完成基本的去抖动工作,还可以区分短按和长按事件。对于长时间保持按下的情况,可以通过记录连续几次扫描期间按键的状态来判定具体的动作类型[^3]。 ```c volatile uint8_t key_status = KEY_RELEASED; // 当前按键状态,默认释放态 volatile uint16_t press_duration = 0; // 按下持续时间计数值 /* 私有化的按键检测函数 */ static void KeyScan(void) { static uint8_t last_key_state = KEY_RELEASED; if (HAL_GPIO_ReadPin(KEY_BUTTON_PORT, KEY_BUTTON_PIN) == GPIO_PIN_RESET) { if (last_key_state != KEY_PRESSED) press_duration = 0; // 清零计数器 ++press_duration; // 更新计数器 last_key_state = KEY_PRESSED; if(press_duration >= SHORT_PRESS_THRESHOLD && press_duration < LONG_PRESS_THRESHOLD) { key_status = KEY_SHORT_PRESS; } else if(press_duration >= LONG_PRESS_THRESHOLD) { key_status = KEY_LONG_PRESS; } }else{ last_key_state = KEY_RELEASED; key_status = KEY_RELEASED; } } /* 主循环内调用 */ while (1) { if(key_status == KEY_SHORT_PRESS){ // 处理短按逻辑... key_status = KEY_RELEASED; } if(key_status == KEY_LONG_PRESS){ // 处理长按逻辑... key_status = KEY_RELEASED; } } ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值