在嵌入式系统开发里,按键是最基础且常用的人机交互外设。通过按键,我们能实现设备的状态切换、功能触发等操作。本文基于 STM32,详细拆解 WKUP、KEY0、KEY1 三个按键的驱动配置,以及它们各自的控制逻辑,帮助大家理解如何在 STM32 中实现按键功能。

一、按键硬件电路分析

从原理图可知:

  • WKUP 按键:连接 PA0 引脚,一端接 3V3 电源,按下时 PA0 引脚会检测到高电平,未按下时由电路配置决定电平(后续代码会配置为下拉输入,初始为低电平 )。
  • KEY0 按键:连接 PE4 引脚,一端接地,按下时 PE4 引脚检测到低电平,未按下时由电路配置为上拉输入,保持高电平 。
  • KEY1 按键:连接 PE3 引脚,一端接地,按下时 PE3 引脚检测到低电平,未按下时由电路配置为上拉输入,保持高电平 。

这些按键可用于实现不同功能,比如控制 LED 灯状态、触发蜂鸣器播放音乐、系统复位等。

二、按键初始化配置

(一)WKUP 按键初始化(wkup_config函数)

void wkup_config(void){
#ifdef c
    // 库函数方式:使能GPIOA时钟,配置PA0为下拉输入
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA,ENABLE);
    GPIO_InitTypeDef key_wkup;
    key_wkup.GPIO_Pin = GPIO_Pin_0;
    key_wkup.GPIO_Mode = GPIO_Mode_IPD; // 下拉输入
    GPIO_Init(GPIOA,&key_wkup);
#else
    // 寄存器方式:使能GPIOA时钟,配置PA0为上拉/下拉输入模式并设置为下拉
    RCC->APB2ENR |= (0X1 << 2);
    GPIOA->CRL &= ~(0XF); 
    GPIOA->CRL |= (0X8);  // 上拉下拉输入模式
    GPIOA->ODR &= ~(0x1);  // 设置为下拉模式,初始低电平
#endif
}
  • 库函数方式通过RCC_APB2PeriphClockCmd使能 GPIOA 时钟,利用GPIO_InitTypeDef结构体配置 PA0 为下拉输入模式(GPIO_Mode_IPD )。
  • 寄存器方式直接操作RCC->APB2ENR使能时钟,通过配置GPIOA->CRL寄存器设置引脚模式,再用GPIOA->ODR设置初始电平为下拉。

(二)KEY0 和 KEY1 按键初始化(库函数方式)

void key0_config(void){
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
    GPIO_InitTypeDef key_key0;
    key_key0.GPIO_Pin = GPIO_Pin_4;
    key_key0.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
    GPIO_Init(GPIOE,&key_key0);
    GPIO_SetBits(GPIOE,GPIO_Pin_4); // 设置初始高电平
}

void key1_config(void){
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE,ENABLE);
    GPIO_InitTypeDef key_key1;
    key_key1.GPIO_Pin = GPIO_Pin_3;
    key_key1.GPIO_Mode = GPIO_Mode_IPU; // 上拉输入
    GPIO_Init(GPIOE,&key_key1);
    GPIO_SetBits(GPIOE,GPIO_Pin_3); // 设置初始高电平
}

        对于 KEY0 和 KEY1,因为它们一端接地,所以配置为上拉输入模式(GPIO_Mode_IPU ),这样未按下时引脚保持高电平,按下时变为低电平。通过GPIO_SetBits设置初始高电平,确保初始化状态正确。

三、按键状态获取与功能实现

(一)WKUP 按键:控制 LED 灯状态(get_wkup_state函数)

// 按键状态定义
#define KEY_UP   0
#define KEY_DOWN 1

uint8_t get_wkup_state(uint8_t *key_state) {
    uint8_t result = 255;
    uint8_t key_pressed = GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0);  // 高电平表示按下
    
    // 检测按键状态变化
    if (key_pressed && (*key_state == KEY_UP)) {
        // 按键按下处理,消抖
        delay_ms(10);  
        if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)) {
            // 等待释放
            while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)) {};
            
            *key_state = KEY_DOWN;
            // 切换LED(PB5)状态
            //这个代码的逻辑就是翻转PB5的电平
            GPIO_WriteBit(GPIOB, GPIO_Pin_5, 
                         (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_5)));
            result = 0;
        }
    }
    else if (key_pressed && (*key_state == KEY_DOWN)) {
        // 按键释放处理,消抖
        delay_ms(10);  
        if (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)) {
            // 等待按下
            while (GPIO_ReadInputDataBit(GPIOA, GPIO_Pin_0)) {};
            
            *key_state = KEY_UP;
            // 切换LED(PB5)状态
            GPIO_WriteBit(GPIOB, GPIO_Pin_5, 
                         (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOB, GPIO_Pin_5)));
            result = 1;
        }
    }
    
    return result;
}
  • 定义KEY_UPKEY_DOWN表示按键的两种状态。
  • 通过GPIO_ReadInputDataBit读取 PA0 引脚电平,判断按键是否按下。
  • 加入消抖处理(delay_ms(10) ),避免按键机械抖动导致误判。
  • 当按键状态变化时,切换 PB5 引脚控制的 LED 灯状态,实现按下或释放按键时 LED 状态翻转。

(二)KEY0 按键:控制另一路 LED 灯状态(get_key0_state函数)

uint8_t get_key0_state(uint8_t *key_state) {
    uint8_t result = 255;
    uint8_t key_pressed = !GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4);  // 低电平表示按下
    
    // 检测按键状态变化
    if (key_pressed && (*key_state == KEY_UP)) {
        // 按键按下处理,消抖
        delay_ms(10);  
        if (!GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4)) {
            // 等待释放
            while (!GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4)) {};
            
            *key_state = KEY_DOWN;
            // 切换LED(PE5)状态
            GPIO_WriteBit(GPIOE, GPIO_Pin_5, 
                         (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOE, GPIO_Pin_5)));
            result = 0;
        }
    }
    else if (key_pressed && (*key_state == KEY_DOWN)) {
        // 按键释放处理,消抖
        delay_ms(10);  
        if (!GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4)) {
            // 等待按下
            while (!GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_4)) {};
            
            *key_state = KEY_UP;
            // 切换LED(PE5)状态
            GPIO_WriteBit(GPIOE, GPIO_Pin_5, 
                         (BitAction)(1 - GPIO_ReadOutputDataBit(GPIOE, GPIO_Pin_5)));
            result = 1;
        }
    }
    
    return result;
}
  • KEY0 连接 PE4 引脚,低电平表示按下,所以用!GPIO_ReadInputDataBit判断。
  • 功能与 WKUP 按键类似,不过是控制 PE5 引脚的 LED 灯状态,实现按下或释放时 LED 状态翻转。

(三)KEY1按键:控制蜂鸣器播放音乐,见蜂鸣器博客

 按键KEY1控制蜂鸣器/复位
uint8_t get_key1_state(uint8_t *key_state) {
    uint8_t result = 255;
    uint8_t key_pressed = !GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3);  // 低电平表示按下
    
    // 检测按键状态变化
    if (key_pressed && (*key_state == KEY_UP)) {
        // 按键按下处理
        delay_ms(10);  // 消抖
        if (!GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3)) {
            // 等待释放
            while (!GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3)) {};
            
            *key_state = KEY_DOWN;
            // 播放音乐
            play_two_tigers();
            result = 0;
        }
    }
    else if (key_pressed && (*key_state == KEY_DOWN)) {
        // 按键长按处理(触发复位)
        delay_ms(10);  // 长按确认
        if (!GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3)) {
            // 等待释放
            while (!GPIO_ReadInputDataBit(GPIOE, GPIO_Pin_3)) {};
            
            *key_state = KEY_UP;
            // 触发系统复位
            system_reset();
            result = 1;
        }
    }
    
    return result;
}

四、按键功能整合与应用

        在主程序或其他逻辑中,可通过不断调用这些get_xxx_state函数来检测按键状态,执行相应操作,示例如下:

int main(void)
{
    uint8_t wkup_state = KEY_UP;
    uint8_t key0_state = KEY_UP;
    uint8_t key1_state = KEY_UP;
    
    // 按键初始化
    wkup_config();
    key0_config();
    key1_config();
    
    // 其他初始化(如LED、蜂鸣器等)
    
    while(1)
    {
        get_wkup_state(&wkup_state);
        get_key0_state(&key0_state);
        get_key1_state(&key1_state);
        
        // 可在此根据按键状态执行更多逻辑
    }
}

        通过循环检测按键状态,能实时响应按键操作,实现 LED 控制、音乐播放、系统复位等功能,构建完整的人机交互逻辑。

####当然目前是轮询方式检测按键,在后续按键中断控制灯的博客可以看到中断的方式,提高系统效率,让 CPU 在按键未操作时处理其他任务,有按键事件时再响应。

0voice · GitHub

Logo

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

更多推荐