STM32学习7 按键扫描
STM32学习7 按键扫描
- 一、实验电路介绍
- 二、按键GPIO初始化
- 三、扫描原理
- 1. GPIO引脚配置
- 2. 状态轮询
- 3. 按键状态检测
- 4. 循环扫描的优缺点
- 优点:
- 缺点:
- 四、一次扫描与持续扫描
- 五、代码实现
- 1. 头文件定义
- 2. 函数实现
- 3. 主体函数
一、实验电路介绍
本实验使用普中STM32-F1开发板,芯片型号是STM32F103ZET6。
其按键电路如下:

对应的芯片引脚:

从电路可以看出,键盘的 KEY_UP 键如果接通,会连接高电平 。
其它几个按键在按下的时候连接低电平,对应的GPIO口:
- KEY_UP:GPIOA GPIO_Pin0 引脚
- KEY_LEFT:GPIOE GPIO_Pin2 引脚
- KEY_RIGHT:GPIOE_GPIO_Pin4 引脚
- KEY_DOWN:GPIOE_GPIO_Pin3 引脚
二、按键GPIO初始化
按键 KEY_UP 和其它三个按键的接法不同,需要不同的配置方式。
其中 KEY_UP 按下后接高电平,在默认情况下需要置低,初始化时设置为输入下拉,代码如下:
// 开启GPIOA的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 设置上引脚GPIO_InitStructure.GPIO_Pin = KEY_UP_PIN;// 设置输入下拉模式GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;GPIO_Init(KEY_UP_PORT, &GPIO_InitStructure); // 初始化GPIOA
其它三个按键,按下时接低电平,默认置高,初始化设置为输入上拉,代码如下:
// 开 E 时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);// 设置下、左、右引脚GPIO_InitStructure.GPIO_Pin = KEY_DOWN_PIN | KEY_LEFT_PIN | KEY_RIGHT_PIN;// 设置输入上拉模式GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(KEY_DOWN_PORT, &GPIO_InitStructure); // 初始化GPIOE
三、扫描原理
1. GPIO引脚配置
首先,需要将用于连接按键的GPIO引脚配置为输入模式。
2. 状态轮询
然后轮询每个按键的状态,以确定按键是否被按下或释放。轮询扫描可以通过在主循环中定期检查每个按键的状态来实现。例如,在每次主循环迭代中,都检查一次按键的状态。
3. 按键状态检测
一般来说,按键有两种状态:按下和释放。在检测按键状态时,需要注意去除按键的抖动干扰。抖动是指在按键被按下或释放时,由于机械接触或物理特性导致的瞬间状态变化。为了应对抖动,可以采用软件方法或硬件滤波器。
本示例采用延时10ms读取值的方法来去抖,示例:
if(key_up_value == 1 || key_down_value ==0 || key_left_value ==0 || key_right_value ==0){delay_ms(10);
}
硬件方法去抖可以参考实现:SR触发器去抖
4. 循环扫描的优缺点
优点:
-
简单直观: 在循环中进行按键扫描的方法简单易懂,逻辑清晰,易于理解和实现。
-
灵活性: 可以根据具体需求灵活调整扫描的频率和方式,满足不同场景下的要求。
-
适用性广: 适用于小型嵌入式系统或者对按键响应速度要求不高的场景,适用性广泛。
-
资源消耗低: 相比于中断方式,循环扫描不需要额外的中断处理函数,减少了系统资源的占用。
缺点:
-
效率低下: 在循环中进行按键扫描会占用 CPU 的时间片,降低了系统的处理效率,特别是当系统有其他紧急任务需要处理时,会影响响应速度和实时性。
-
实时性差: 循环扫描需要不断地遍历所有按键状态,导致按键的检测周期相对较长,实时性差,无法满足对按键响应速度要求较高的场景。
-
占用 CPU 资源: 循环扫描需要持续占用 CPU 资源,特别是在大型系统中,可能会影响其他任务的执行,降低系统的整体性能。
-
功耗高: 循环扫描需要 CPU 不断地处于工作状态,会增加系统的功耗,对于对功耗要求较高的场景不太适用。
后面学习中会采用中断的方式来读取键盘。
四、一次扫描与持续扫描
这里的一次扫描,是指按下按键后,如果不松开,键盘的扫描函数不会继续输出所按键值。
而持续扫描,在按下按键后,如果手不松开,键盘的扫描函数仍会持续输出按键值。
五、代码实现
为方便看到演示效果,示例的代码在获取到扫描的按键后,会在数码管显示不同的数值。
- 上:显示0
- 下:显示1
- 左:显示2
- 右:显示3
1. 头文件定义
key_utils.h
#ifndef __KEY_UTILS_H__
#define __KEY_UTILS_H__
#include "stm32f10x.h"// 引脚和端口
#define KEY_UP_PIN GPIO_Pin_0
#define KEY_UP_PORT GPIOA
#define KEY_LEFT_PIN GPIO_Pin_2
#define KEY_LEFT_PORT GPIOE
#define KEY_DOWN_PIN GPIO_Pin_3
#define KEY_DOWN_PORT GPIOE
#define KEY_RIGHT_PIN GPIO_Pin_4
#define KEY_RIGHT_PORT GPIOE// 读取引脚状态
#define key_up_value GPIO_ReadInputDataBit(KEY_UP_PORT, KEY_UP_PIN)
#define key_down_value GPIO_ReadInputDataBit(KEY_DOWN_PORT, KEY_DOWN_PIN)
#define key_left_value GPIO_ReadInputDataBit(KEY_LEFT_PORT, KEY_LEFT_PIN)
#define key_right_value GPIO_ReadInputDataBit(KEY_RIGHT_PORT, KEY_RIGHT_PIN)// 按键
#define KEY_UP 0
#define KEY_DOWN 1
#define KEY_LEFT 2
#define KEY_RIGHT 3
#define KEY_NONE 4void key_init(void);
u8 key_scan(u8 mode);
#endif
2. 函数实现
#include "key_utils.h"
#include "sys_tick_utils.h"void key_init(void)
{GPIO_InitTypeDef GPIO_InitStructure; // 定义GPIO初始化结构体// 开启GPIOA的时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);// 设置上引脚GPIO_InitStructure.GPIO_Pin = KEY_UP_PIN;// 设置输入下拉模式GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPD;GPIO_Init(KEY_UP_PORT, &GPIO_InitStructure); // 初始化GPIOA// 开 E 时钟RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOE, ENABLE);// 设置下、左、右引脚GPIO_InitStructure.GPIO_Pin = KEY_DOWN_PIN | KEY_LEFT_PIN | KEY_RIGHT_PIN;// 设置输入上拉模式GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;GPIO_Init(KEY_DOWN_PORT, &GPIO_InitStructure); // 初始化GPIOE}
static u8 key_read(void){if(key_up_value == 1 || key_down_value ==0 || key_left_value ==0 || key_right_value ==0){delay_ms(10);if(key_up_value == 1){return KEY_UP;}else if(key_down_value == 0){return KEY_DOWN;}else if(key_left_value == 0){return KEY_LEFT;}else if(key_right_value == 0){return KEY_RIGHT;}}return KEY_NONE;
}
u8 last_key;
/*** @brief 按键扫描函数* @param mode: 0 单次扫描 1: 连续扫描*/
u8 key_scan(u8 mode)
{if(mode==0){u8 key = key_read();if(key != KEY_NONE){if(key == last_key){return KEY_NONE;}else{last_key = key;return key;}}else{last_key = KEY_NONE;}}else{return key_read();}return KEY_NONE;
}
3. 主体函数
#include "gpio_utils.h"
#include "rcc_utils.h"
#include "stm32f10x.h"
#include "sys_tick_utils.h"
#include "led_utils.h"
#include "key_utils.h"// 主函数
int main(void)
{GPIO_Configuration(); //调用GPIO配置函数sys_tick_init(72);led_all_off();key_init();while (1) //无限循环{delay_ms(10);u8 key = key_scan(0);if(key==KEY_UP){led_lightn(0);}else if(key==KEY_DOWN){led_lightn(1);}else if(key==KEY_LEFT){led_lightn(2);}else if(key==KEY_RIGHT){led_lightn(3);}else{led_all_off();}}
}
本文源码地址:
https://gitee.com/xundh/stm32_arm_learn/tree/master/lesson7_key
相关文章:
STM32学习7 按键扫描
STM32学习7 按键扫描 一、实验电路介绍二、按键GPIO初始化三、扫描原理1. GPIO引脚配置2. 状态轮询3. 按键状态检测4. 循环扫描的优缺点优点:缺点: 四、一次扫描与持续扫描五、代码实现1. 头文件定义2. 函数实现3. 主体函数 一、实验电路介绍 本实验使用…...
图像物体的边界- 华为OD统一考试(C卷)
OD统一考试(C卷) 分值: 200分 题解: Java / Python / C 题目描述 给定一个二维数组M行N列,二维数组里的数字代表图片的像素,为了简化问题,仅包含像素1和5两种像素,每种像素代表一个…...
.idea文件详解
.idea文件的作用: .idea文件夹是存储IntelliJ IDEA项目的配置信息,主要内容有IntelliJ IDEA项目本身的一些编译配置、文件编码信息、jar包的数据源和相关的插件配置信息。一般用git做版本控制的时候会把.idea文件夹排除,因为这个文件下保存的…...
安卓JNI基础知识
JNI基础知识 JNI简介NDK配置开发环境JNI实践配置CMakeJNI编码JNI注册1.静态注册2.动态注册 编译方式CMakeLists编译Makefile编译命令编译 JNI和C/C代码分离Java调用C/C查看so中包含的方法 C/C调用Java打印C/C的log生成多个共享库soJNI调试 本文整理了JNI技术基础知识 JNI简介 …...
Nginx高级技巧:实现负载均衡和反向代理
文章目录 Nginx概述Nginx作用正向代理反向代理负载均衡动静分离 Nginx的安装 -->Docker3.1 安装Nginx3.2 Nginx的配置文件3.3 修改docker-compose文件 Nginx源码安装nginx常用命令nginx配置文件配置文件位置配置文件结构详情 Nginx的反向代理【重点】基于Nginx实现反向代理4…...
2024年2月最新微信域名检测拦截接口源码
这段PHP代码用于检测指定域名列表中的域名是否被封。代码首先定义了一个包含待检测域名的数组 $domainList,然后遍历该数组,对每个域名发送HTTP请求并检查响应内容以判断域名是否被封。 具体步骤如下: 1. 定义待检测的域名列表。 2. 遍历域名…...
1、Linux-安装
一、Linux和Windows的一些区别 1、Linux严格区分大小写——【Windows创建文件夹时不区分大小写】 2、Linux中所有内容都以文件形式存储,包括硬件 3、Linux不靠拓展名区分文件类型,而是可以通过读取文件开头的一些字节来区分。 但是在实际使用中一般要…...
flutter 父组件调用子组件方法
当子组件是有状态组件 声明GlobalKey 如 声明 GlobalKey formKey GlobalKey<FormState>(); Form( key: formKey, autovalidateMode: AutovalidateMode.always, child: Column( children: <Widget>[ TextFormField( autofocus: true, initialValue: "a&quo…...
京东云硬钢阿里云:承诺再低10%
关注卢松松,会经常给你分享一些我的经验和观点。 阿里云刚刚宣布史上最大规模的全线产品降价20%,这热度还没过,京东云当晚就喊话:“随便降、比到底!,全网比价,击穿低价,再低10%”,并…...
Phoncent博客:探索AI写作与编程的无限可能
Phoncent博客,一个名为Phoncent的创新AIGC博客网站,于2023年诞生。它的创始人是庄泽峰,一个自媒体人和个人站长,他在网络营销推广领域有着丰富的经验。庄泽峰深知人工智能技术在内容创作和编程领域的潜力和创造力,因此…...
【Go-Zero】测试API查询信息无法返回数据库信息与api、rpc文件编写规范
【Go-Zero】测试API查询信息无法返回数据库信息与api、rpc文件编写规范 大家好 我是寸铁👊 总结了一篇测试API查询信息无法返回数据库信息与api、rpc文件编写规范的文章✨ 喜欢的小伙伴可以点点关注 💝 问题背景 大家好,我是寸铁!…...
SpringBootWeb快速入门
1.创建springboot工程,新建module 2.勾选web开发相关依赖 3.删除多余文件 4.新建类 5.启动类中运行main方法 6.启动 默认端口号8080 7.打开浏览器,地址栏输入 8.报错 9.原因,控制层位置放错,剪切controller层放进com.example …...
【书生·浦语大模型实战营】第 2 节 -课后作业
第二节 -轻松玩转书生浦语大模型趣味 Demo-课后作业 0.课程体验0.1 鸡兔同笼0.2 逻辑推理0.3 AI会毁灭人类吗? 1.课后作业1.1 基础作业1.1.1 作业11.1.2 作业2 0.课程体验 课程链接:https://github.com/internLM/tutorial 首先,这个课程是免费…...
Java如何使用OpenCV
背景:利用OpenCV功能批量处理视频 OpenCV(Open Source Computer Vision Library)是一个广泛应用于计算机视觉和图像处理领域的开源库。它提供了丰富的功能和工具,可用于处理图像和视频数据,并在各种应用中发挥着重要作…...
C++指针(三)
个人主页:PingdiGuo_guo 收录专栏:C干货专栏 文章目录 前言 1.字符指针 1.1字符指针的概念 1.2字符指针的用处 1.3字符指针的操作 1.3.1定义 1.3.2初始化 1.4字符指针使用注意事项 2.数组参数,指针参数 2.1数组参数 2.1.1数组参数的概念 2.1…...
消息中间件之RocketMQ源码分析(二十七)
Broker提交或回滚事务消息 当生产者本地事务处理完成并且Broker回查事务消息后,不管执行Commit还是Rollback,都会根据用户本地事务的执行结果发送一个End_transaction的RPC请求给Broker,Broker端处理该请求的类是EndTransactionProcessor 第一步&…...
C习题002:澡堂洗澡
问题 输入样例 在这里给出一组输入。例如: 2 5 1 3 3 2 3 3 输出样例 在这里给出相应的输出。例如: No代码长度限制 16 KB 时间限制 400 ms 内存限制 64 MB 栈限制 8192 KB 代码 #include<stdio.h> int main() {int N,W,s,t,p;int arr_s[…...
智能双星:遥测终端机与柳林“巡检机器人“,助力智能运维新升级!
随着科技的不断发展,智能化、自动化的运维管理已经成为企业追求高效、稳定运营的重要方向。柳林遥测终端机、柳林e拍云平台以及巡检机器人的组合,为企业带来了一种全新的、前置的、无感的智能运维体验。 柳林遥测终端机,以其强大的数据采集和…...
算法复习之前缀和【备战蓝桥杯】
一维前缀和 S[i] a[1] a[2] ... a[i] a[l] ... a[r] S[r] - S[l - 1]二维前缀和 S[i, j] 第i行j列格子左上部分所有元素的和 以(x1, y1)为左上角,(x2, y2)为右下角的子矩阵的和为: S[x2, y2] - S[x1 - 1, y2] - S[x2, y1 - 1] S[x1 - 1, y1 - …...
IDEA基础——Maven配置tomcat
配置方案 一、配置maven-tomcat plugin插件(只最高支持到tomcat 8)~~1.添加镜像源,获取tomcat 8插件配置~~~~1.1 在pom.xml里先添加镜像源~~~~1.2 添加tomcat插件配置~~ 2. 添加tomact官方发布的插件配置(无需添加镜像源ÿ…...
蓝桥杯 2024 15届国赛 A组 儿童节快乐
P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡,轻快的音乐在耳边持续回荡,小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下,六一来了。 今天是六一儿童节,小蓝老师为了让大家在节…...
什么是EULA和DPA
文章目录 EULA(End User License Agreement)DPA(Data Protection Agreement)一、定义与背景二、核心内容三、法律效力与责任四、实际应用与意义 EULA(End User License Agreement) 定义: EULA即…...
【学习笔记】深入理解Java虚拟机学习笔记——第4章 虚拟机性能监控,故障处理工具
第2章 虚拟机性能监控,故障处理工具 4.1 概述 略 4.2 基础故障处理工具 4.2.1 jps:虚拟机进程状况工具 命令:jps [options] [hostid] 功能:本地虚拟机进程显示进程ID(与ps相同),可同时显示主类&#x…...
代理篇12|深入理解 Vite中的Proxy接口代理配置
在前端开发中,常常会遇到 跨域请求接口 的情况。为了解决这个问题,Vite 和 Webpack 都提供了 proxy 代理功能,用于将本地开发请求转发到后端服务器。 什么是代理(proxy)? 代理是在开发过程中,前端项目通过开发服务器,将指定的请求“转发”到真实的后端服务器,从而绕…...
React---day11
14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store: 我们在使用异步的时候理应是要使用中间件的,但是configureStore 已经自动集成了 redux-thunk,注意action里面要返回函数 import { configureS…...
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材)
推荐 github 项目:GeminiImageApp(图片生成方向,可以做一定的素材) 这个项目能干嘛? 使用 gemini 2.0 的 api 和 google 其他的 api 来做衍生处理 简化和优化了文生图和图生图的行为(我的最主要) 并且有一些目标检测和切割(我用不到) 视频和 imagefx 因为没 a…...
【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
Python Einops库:深度学习中的张量操作革命
Einops(爱因斯坦操作库)就像给张量操作戴上了一副"语义眼镜"——让你用人类能理解的方式告诉计算机如何操作多维数组。这个基于爱因斯坦求和约定的库,用类似自然语言的表达式替代了晦涩的API调用,彻底改变了深度学习工程…...
Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)
引言 工欲善其事,必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后,我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集,就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...
Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?
Pod IP 的本质与特性 Pod IP 的定位 纯端点地址:Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址(如 10.244.1.2)无特殊名称:在 Kubernetes 中,它通常被称为 “Pod IP” 或 “容器 IP”生命周期:与 Pod …...
