第3章 按键输入实验

第三章 按键输入实验

1. STM32F4 IO口简介

STM32F4 的 IO 口在上两章已经有了比较详细的介绍,这里我们不再多说。 STM32F4 的 IO口做输入使用的时候,是通过调用函数 GPIO_ReadInputDataBit() 来读取 IO 口的状态的。了解了这点,就可以开始我们的代码编写了。

一章,我们将通过 ALIENTEK 探索者 STM32F4 开发板上载有的 4 个按钮(KEY_UP、KEY0、 KEY1 和 KEY2),来控制板上的 2 个 LED(DS0 和 DS1) 和蜂鸣器,其中 KEY_UP 控制蜂鸣器,按一次叫,再按一次停; KEY2 控制 DS0, 按一次亮,再按一次灭; KEY1 控制 DS1,效果同 KEY2; KEY0 则同时控制 DS0 和 DS1,按一次,他们的状态就翻转一次。

2. 硬件设计

本实验用的的硬件资源有:

  1. 指示灯DS0、DS1

  2. 蜂鸣器

  3. 4个按键:KEY0、KEY1、KEY2和KEY_UP

DS0、 DS1 以及蜂鸣器和 STM32F4 的连接在上两章都已经分别介绍了,在探索者 STM32F4开发板上的按键 KEY0 连接在 PE4 上、 KEY1 连接在 PE3 上、 KEY2 连接在 PE2 上、 KEY_UP连接在 PA0 上。

屏幕截图 2024 09 08 174410

这里需要注意的是: KEY0、 KEY1 和 KEY2 是低电平有效的,而 KEY_UP 是高电平有效的, 并且外部都没有上下拉电阻,所以,需要在 STM32F4 内部设置上下拉。

3. 软件设计

led方面的我们就不用解释了,重点是按键扫描配置

  • key宏定义
// 下面的方式是通过直接操作HAL库函数方式读取IO
#define KEY0  HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_4)  // KEY0按键PE4
#define KEY1  HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_3)  // KEY1按键PE3
#define KEY2  HAL_GPIO_ReadPin(GPIOE, GPIO_PIN_2)  // KEY2按键PE2
#define WK_UP HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0)  // WKUP按键PA0

#define KEY0_PRES   1
#define KEY1_PRES   2
#define KEY2_PRES   3
#define WKUP_PRES   4
  • 这些宏定义定义了各个按键的引脚和端口。

    • KEY0_PINKEY1_PINKEY2_PIN 和 WKUP_PIN 指定了按键连接到的具体引脚。
    • KEY0_PORTKEY1_PORTKEY2_PORT 和 WKUP_PORT 指定了这些引脚所在的GPIO端口。
    • KEY0_PRESKEY1_PRESKEY2_PRES 和 WKUP_PRES 是每个按键的状态标志,用于在程序中标识按键的状态。
  • key初始化函数

// 按键初始化函数
void KEY_Init(void)
{
    GPIO_InitTypeDef GPIO_Initure;
    __HAL_RCC_GPIOA_CLK_ENABLE(); // 开启GPIOA时钟
    __HAL_RCC_GPIOE_CLK_ENABLE(); // 开启GPIOE时钟
    GPIO_Initure.Pin = GPIO_PIN_0;       // PA0
    GPIO_Initure.Mode = GPIO_MODE_INPUT; // 输入
    GPIO_Initure.Pull = GPIO_PULLDOWN;   // 下拉
    GPIO_Initure.Speed = GPIO_SPEED_HIGH;// 高速
    HAL_GPIO_Init(GPIOA, &GPIO_Initure);
    GPIO_Initure.Pin = GPIO_PIN_2|GPIO_PIN_3|GPIO_PIN_4; // PE2,3,4
    GPIO_Initure.Mode = GPIO_MODE_INPUT;  // 输入
    GPIO_Initure.Pull = GPIO_PULLUP;      // 上拉
    GPIO_Initure.Speed = GPIO_SPEED_HIGH; // 高速
    HAL_GPIO_Init(GPIOE, &GPIO_Initure);
}
  • 按键扫描函数
// 按键处理函数
// 返回按键值
// mode:0,不支持连续按;1,支持连续按;
// 0,没有任何按键按下
// 1,WKUP按下 WK_UP
// 注意此函数有响应优先级,KEY0>KEY1>KEY2>WK_UP!!
u8 KEY_Scan(u8 mode)
{
    static u8 key_up = 1; // 按键松开标志
    if(mode == 1)
        key_up = 1; // 支持连按
    if(key_up && (KEY0==0||KEY1==0||KEY2==0||WK_UP==1)) // 按键按下
    {
        delay_ms(10);
        key_up = 0; // 按键按下标志
        if(KEY0 == 0)     return KEY0_PRES; // KEY0按下
        else if(KEY1 == 0)  return KEY1_PRES; // KEY1按下
        else if(KEY2 == 0)  return KEY2_PRES; // KEY2按下
        else if(WK_UP == 1) return WKUP_PRES; // WKUP按下
    }
    else if(KEY0==1&&KEY1==1&&KEY2==1&&WK_UP==0) // 按键松开
    {
        key_up = 1; // 按键松开
        return 0;   // 无按键按下
    }
    return 0; // 无按键按下
}
  1. 功能:检测按键状态并返回按键值。
  2. 模式
  • mode = 0:不支持连续按(只在按键释放后才响应下一次按键)。

  • mode = 1:支持连续按(在按键按下时可以持续响应)。

  • 主函数

int main(void)
{
    u8 key; // 按键扫描值
    HAL_Init();                   // 初始化HAL库    
    Stm32_Clock_Init(336,8,2,7);  // 设置时钟,168Mhz
    delay_init(168);              // 初始化延时函数
    LED_Init();                      // 初始化LED    
    KEY_Init();                   // 初始化按键
    while(1)
    {
        key = KEY_Scan(0); // 按键扫描
        switch(key)
        {                 
            case WKUP_PRES:    // 控制LED0,LED1互斥点亮
                LED1 = !LED1;
                LED0 = !LED1;
                break;
            case KEY2_PRES: // 控制LED0翻转
                LED0 = !LED0;
                break;
            case KEY1_PRES: // 控制LED1翻转     
                LED1 = !LED1;
                break;
            case KEY0_PRES: // 同时控制LED0,LED1翻转 
                LED0 = !LED0;
                LED1 = !LED1;
                break;
        }
        delay_ms(10);
    }
}

根据不同的按键值产生不同的效果

4. 小结

前面我们都是将GPIO的基本输出,这一章本章其实就是检测GPIO输入,怎么输入的?通过按键,不同的按键值产生不同的效果,一个新的hal库函数我们需要掌握:

HAL_GPIO_ReadPin() 是 STM32 HAL 库中的一个函数,用于读取某个 GPIO(通用输入输出)引脚的状态。这个函数属于 STM32 的硬件抽象层(HAL),它简化了对 GPIO 引脚状态的读取操作,避免了直接操作寄存器的复杂性。

函数原型

GPIO_PinState HAL_GPIO_ReadPin(GPIO_TypeDef* GPIOx, uint16_t GPIO_Pin);

参数

  1. GPIOx: 指向 GPIO 外设的基地址。例如,GPIOAGPIOBGPIOC 等。
  2. GPIO_Pin: 需要读取状态的 GPIO 引脚的编号。例如,GPIO_PIN_0GPIO_PIN_1 等。

返回值

  • GPIO_PIN_RESET: 引脚的状态为低电平(逻辑 0)。
  • GPIO_PIN_SET: 引脚的状态为高电平(逻辑 1)。

示例代码

下面是一个使用 HAL_GPIO_ReadPin() 函数读取 GPIO 引脚状态的示例。假设我们要读取 GPIOA 端口上的 GPIO_PIN_0 引脚的状态。

#include "stm32f4xx_hal.h"

// 假设已经初始化了 HAL 库和 GPIO

int main(void)
{
    // HAL 库初始化
    HAL_Init();

    // GPIO 初始化配置
    __HAL_RCC_GPIOA_CLK_ENABLE(); // 启用 GPIOA 时钟

    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // 配置 GPIOA 的引脚 0 作为输入模式
    GPIO_InitStruct.Pin = GPIO_PIN_0;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_NOPULL; // 不使用上拉或下拉电阻
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);

    while (1)
    {
        // 读取 GPIOA 的引脚 0 的状态
        GPIO_PinState pinState = HAL_GPIO_ReadPin(GPIOA, GPIO_PIN_0);

        if (pinState == GPIO_PIN_SET)
        {
            // 引脚状态为高电平
            // 执行相应的操作
        }
        else
        {
            // 引脚状态为低电平
            // 执行相应的操作
        }

        // 延时
        HAL_Delay(100); // 延时 100ms
    }
}

2024.9.27 第一次修订,后期不再维护

posted @ 2024-09-27 15:36  hazy1k  阅读(15)  评论(0编辑  收藏  举报