【星云 Orbit-F4 开发板】03g. 按键玩法七:矩阵键盘单个触发

news/2025/2/27 10:27:12

【星云 Orbit-F4 开发板】03g. 按键玩法七:矩阵键盘单个触发


引言

矩阵键盘是一种常见的输入设备,广泛应用于各种嵌入式系统中。通过矩阵键盘,用户可以通过按键输入字符或控制信号。本文将详细介绍如何使用STM32F407的GPIO引脚实现矩阵键盘的单个触发功能。通过本教程,读者将能够掌握矩阵键盘的基本原理以及如何通过HAL库实现单个触发的检测。


硬件准备

在开始编程之前,确保您已经准备好以下硬件:

  1. STM32F407开发板:板载STM32F407VGT6(Cortex-M4/168MHz)作为主控芯片。
  2. 矩阵键盘模块:4x4矩阵键盘模块。
  3. 连接线:用于连接矩阵键盘和开发板。
  4. 限流电阻:若干个220Ω至330Ω的电阻,用于保护电路。

硬件连接

矩阵键盘连接到STM32F407的GPIO引脚:

  1. 连接矩阵键盘的行引脚

    • 矩阵键盘的行引脚(通常标记为R0-R3)连接到STM32F407的PB0-PB3引脚。
  2. 连接矩阵键盘的列引脚

    • 矩阵键盘的列引脚(通常标记为C0-C3)连接到STM32F407的PC0-PC3引脚。
  3. 连接地线

    • 矩阵键盘的地线连接到STM32F407的GND引脚。

GPIO配置

在STM32F407中,GPIO引脚可以配置为多种模式,以适应不同的应用需求。以下是一些常见的GPIO模式:

  1. 输入模式

    • 浮空输入(Floating Input):引脚处于高阻态,适用于不需要上拉或下拉的情况。
    • 上拉输入(Pull-Up Input):引脚内部连接上拉电阻,适用于外部信号为低电平有效的情况。
    • 下拉输入(Pull-Down Input):引脚内部连接下拉电阻,适用于外部信号为高电平有效的情况。
  2. 输出模式

    • 推挽输出(Push-Pull Output):引脚可以输出高电平或低电平,适用于直接驱动小电流负载,如LED灯。
    • 开漏输出(Open-Drain Output):引脚输出低电平或高阻态,适用于需要外部上拉电阻的场合。

在本教程中,我们将矩阵键盘的行引脚配置为推挽输出模式,列引脚配置为上拉输入模式。

配置GPIO时钟

在使用GPIO引脚之前,需要先使能相应的GPIO时钟。对于PB0-PB3引脚,需要使能GPIOB时钟;对于PC0-PC3引脚,需要使能GPIOC时钟。

// 使能GPIOB时钟
__HAL_RCC_GPIOB_CLK_ENABLE();

// 使能GPIOC时钟
__HAL_RCC_GPIOC_CLK_ENABLE();

配置GPIO引脚

使用GPIO_InitTypeDef结构体配置GPIO引脚的模式、上下拉、速度等参数。

GPIO_InitTypeDef GPIO_InitStruct = {0};

// 配置PB0-PB3为推挽输出模式
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

// 配置PC0-PC3为上拉输入模式
GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;
GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
GPIO_InitStruct.Pull = GPIO_PULLUP;

HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);

按键扫描模块

为了实现模块化设计,我们将按键的扫描功能封装到bsp/KEY目录下的bsp_key.cbsp_key.h文件中。

1. 创建KEY目录

  1. 打开文件管理器

    • 导航到STM32F407项目目录下的Drivers/BSP文件夹。
  2. 创建新目录

    • 右键点击空白区域,选择新建文件夹
    • 输入目录名称KEY,并按回车键确认。

2. 创建bsp_key.cbsp_key.h文件

  • 确保bsp_key.cbsp_key.h文件当前位于Drivers/BSP/KEY目录下。

3. 更新项目包含路径

  1. 打开Keil MDK

    • 加载当前的STM32F407项目。
  2. 进入项目设置

    • 右键点击工程名称,选择Options for Target
  3. 添加新的包含路径

    • 在弹出的窗口中,选择C/C++选项卡。
    • Include Paths字段中,添加以下路径:
      Drivers/BSP/KEY
      
    • 确保路径正确无误,点击OK保存设置。

4. 更新main.c文件

  1. 打开main.c文件

    • 在Keil MDK的项目资源管理器中,找到并双击main.c文件。
  2. 包含新的头文件

    • 在文件顶部,添加以下包含语句:
      #include "bsp_key.h"
      
    • 确保路径正确,以便编译器能够找到该头文件。

5. 重新编译项目

  1. 编译工程

    • 点击工具栏中的Build按钮(或按F7键)。
    • 确保编译过程中没有错误和警告。
  2. 处理编译错误

    • 如果出现包含路径错误,请检查Include Paths设置是否正确。
    • 确保bsp_key.h文件位于Drivers/BSP/KEY目录下。

6. 更新项目分组

  1. 添加新的文件夹分组

    • 在Keil MDK的项目资源管理器中,右键点击BSP分组,选择Add Group
    • 输入分组名称KEY,点击OK
  2. 添加文件到分组

    • 右键点击新的KEY分组,选择Add New Item to Group
    • 选择Add Existing File,导航到Drivers/BSP/KEY目录,选择bsp_key.cbsp_key.h文件,点击OK
  3. 确保文件可见性

    • 确保bsp_key.cbsp_key.h文件在KEY分组下可见,并且路径正确。

按键扫描模块实现

bsp_key.h文件

#ifndef __BSP_KEY_H
#define __BSP_KEY_H

#include "stm32f4xx_hal.h"

#define ROWS 4    // 矩阵键盘的行数
#define COLS 4    // 矩阵键盘的列数

/**
 * @brief 初始化矩阵键盘
 */
void KEY_Init(void);

/**
 * @brief 扫描矩阵键盘并检测按键状态
 * @return 按键状态(0:未按下,1-16:按键编号)
 */
uint8_t KEY_Scan(void);

#endif /* __BSP_KEY_H */

bsp_key.c文件

#include "bsp_key.h"

static uint8_t key_state = 0;

/**
 * @brief 初始化矩阵键盘
 */
void KEY_Init(void) {
    GPIO_InitTypeDef GPIO_InitStruct = {0};

    // 使能GPIOB和GPIOC时钟
    __HAL_RCC_GPIOB_CLK_ENABLE();
    __HAL_RCC_GPIOC_CLK_ENABLE();

    // 配置PB0-PB3为推挽输出模式
    GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
    GPIO_InitStruct.Pull = GPIO_NOPULL;
    GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;

    HAL_GPIO_Init(GPIOB, &GPIO_InitStruct);

    // 配置PC0-PC3为上拉输入模式
    GPIO_InitStruct.Pin = GPIO_PIN_0 | GPIO_PIN_1 | GPIO_PIN_2 | GPIO_PIN_3;
    GPIO_InitStruct.Mode = GPIO_MODE_INPUT;
    GPIO_InitStruct.Pull = GPIO_PULLUP;

    HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);
}

/**
 * @brief 扫描矩阵键盘并检测按键状态
 * @return 按键状态(0:未按下,1-16:按键编号)
 */
uint8_t KEY_Scan(void) {
    static uint8_t row = 0;
    static uint8_t col = 0;
    static uint8_t key_pressed = 0;

    if (key_pressed) {
        // 检测按键是否释放
        if (HAL_GPIO_ReadPin(GPIOC, C0_Pin) == GPIO_PIN_SET &&
            HAL_GPIO_ReadPin(GPIOC, C1_Pin) == GPIO_PIN_SET &&
            HAL_GPIO_ReadPin(GPIOC, C2_Pin) == GPIO_PIN_SET &&
            HAL_GPIO_ReadPin(GPIOC, C3_Pin) == GPIO_PIN_SET) {
            key_pressed = 0;
        }
        return 0;
    }

    // 扫描行
    for (row = 0; row < ROWS; row++) {
        // 激活当前行
        switch (row) {
            case 0:
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_RESET);
                break;
            case 1:
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_RESET);
                break;
            case 2:
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_RESET);
                break;
            case 3:
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_RESET);
                break;
        }

        // 检测列
        for (col = 0; col < COLS; col++) {
            if (HAL_GPIO_ReadPin(GPIOC, (GPIO_PinTypeDef)(GPIO_PIN_0 << col)) == GPIO_PIN_RESET) {
                // 按键按下
                key_pressed = 1;
                return (row * COLS) + col + 1;
            }
        }

        // 禁用当前行
        switch (row) {
            case 0:
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_0, GPIO_PIN_SET);
                break;
            case 1:
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_1, GPIO_PIN_SET);
                break;
            case 2:
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_2, GPIO_PIN_SET);
                break;
            case 3:
                HAL_GPIO_WritePin(GPIOB, GPIO_PIN_3, GPIO_PIN_SET);
                break;
        }
    }

    return 0;
}

主程序实现

#include "stm32f4xx_hal.h"
#include "bsp_key.h"

int main(void) {
    // 系统初始化
    HAL_Init();
    SystemClock_Config();

    // 按键初始化
    KEY_Init();

    while (1) {
        // 扫描按键状态
        uint8_t key = KEY_Scan();

        if (key != 0) {
            // 按键按下,执行相应操作
            // 在此处添加按键处理代码
            // 例如,控制LED灯或播放声音
        }
    }
}

测试与验证

  1. 编译与下载

    • 将代码编译并下载到STM32F407开发板中。
  2. 观察按键状态

    • 连接好硬件后,按下矩阵键盘上的按键,观察是否能够正确检测按键按下事件。如果按键状态没有正确检测,请检查硬件连接和代码配置是否正确。
  3. 验证单个触发

    • 按下按键,确保每次按下只触发一次响应。如果触发不正常,请检查按键扫描逻辑以及相关处理代码。

总结

通过本教程,您已经掌握了如何使用STM32F407的GPIO引脚实现矩阵键盘的单个触发功能。矩阵键盘的扫描逻辑是关键步骤,选择合适的行和列配置以及扫描逻辑可以确保按键的稳定工作。模块化设计使代码更加清晰和易于维护,为后续的项目扩展奠定了良好的基础。希望本教程对您有所帮助,祝您在嵌入式开发的道路上取得更大的成功!


http://www.niftyadmin.cn/n/5869949.html

相关文章

SOME/IP-SD -- 协议英文原文讲解6

前言 SOME/IP协议越来越多的用于汽车电子行业中&#xff0c;关于协议详细完全的中文资料却没有&#xff0c;所以我将结合工作经验并对照英文原版协议做一系列的文章。基本分三大块&#xff1a; 1. SOME/IP协议讲解 2. SOME/IP-SD协议讲解 3. python/C举例调试讲解 5.1.3.1 E…

航空装配自动化神器Ethercat转profient网关搭配机器人精准控制

生产管理系统通过网关与装配机器人连接&#xff0c;加快航空器机身的装配速度&#xff0c;减少人为误差。 航空制造对装配线的精度和效率有着极高的要求。某航空制造厂使用的耐达讯Profinet转EtherCAT协议网关NY-PN-ECATM&#xff0c;将其生产管理系统与装配机器人连接&#xf…

【JavaEE进阶】Spring Boot 日志

欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗 如有错误&#xff0c;欢迎指出~ 目录 日志用途 1. 系统监控 2. 数据采集 3.⽇志审计 Spring Boot日志 打印⽇志 在程序中得到⽇志对象 日志框架 ⻔⾯模式(外观模式) ⻔⾯模式的优点 不引⼊⽇志⻔⾯ 存在问…

Java 内存泄漏排查指南:工具与实战技巧

内存泄漏是 Java 开发中常见的问题&#xff0c;会导致应用程序性能下降&#xff0c;甚至崩溃。本文将介绍 Java 内存泄漏的排查方法&#xff0c;包括常用工具和实战技巧。 一、内存泄漏概述 内存泄漏 是指程序在运行过程中&#xff0c;由于某些原因无法释放不再使用的对象&am…

深入Linux序列:进程的终止与等待

在之前的学习中&#xff0c;我们知道我们的进程在运行结束的时候&#xff0c;那么它并不会立即进入死亡状态&#xff0c;而是先进入僵尸状态&#xff0c;维持僵尸状态一段时间&#xff0c;那么此时在僵尸状态中的进程&#xff0c;那么它的内核数据已经移出内存被清理了&#xf…

9.编写负载均衡模块|编写judge功能|postman进行调试(C++)

编写负载均衡模块 代码整体结构 oj_control.hpp // code: #include... // input: "" void Judge(const std::string &number, const std::string in_json, std::string *out_json) {// 0. 根据题目编号&#xff0c;直接拿到对应的题目细节// 1. in_json进行反…

(学习总结26)Linux工具:make/Makefile 自动化构建、Git 版本控制器 和 gdb/cgdb 调试器

Linux工具&#xff1a;make/Makefile 自动化构建、Git 版本控制器 和 gdb/cgdb 调试器 make/Makefile 自动化构建在 Linux 命令行中的命令 makeMakefile 的基本操作1. 编写与删文件基本操作2. 总是执行操作3. 执行操作时的依赖性4. 定义变量或命令操作与其它简单操作5. 多文件操…

DDNS-GO 动态域名解析

什么是 DDNS DNS&#xff08;域名系统&#xff09; 是互联网的电话簿&#xff0c;将易于记忆的域名&#xff08;如 www.example.com&#xff09;转换为计算机可以理解的 IP 地址&#xff08;如 192.0.2.1&#xff09;。这使得用户无需记住复杂的数字地址即可访问网站。 DDNS&…