GPIO控制2
摘要:本文介绍了GPIO控制中独立按键和矩阵按键的实现方法,以及数码管静态显示的工作原理。独立按键通过检测引脚电平变化实现多事件识别,矩阵按键采用行列扫描法定位按键位置。数码管显示部分详细说明了段码驱动和位选控制的硬件配置,提供了完整的代码实现和调试经验。文章重点阐述了按键消抖处理、状态机思想和模块化封装等关键技术要点,为嵌入式系统开发提供了实用的参考方案。
·
GPIO控制|独立按键
一、模块功能
- 实现独立按键的单击、双击、长按等多事件识别
- 提供软件消抖,避免机械按键抖动导致的误触发
- 作为底层驱动,为菜单切换、参数调节等上层功能提供输入接口
二、工作原理
- 核心实现原理

当跳线帽连接 CON3 的 2 和 3 引脚时,独立按键模式被激活。此时,S4、S5、S6、S7 等独立按键的一端通过跳线连接到 GND,另一端则分别连接到单片机的 P30、P31、P32、P33 引脚。
在未按下按键时,这些引脚通过内部上拉电阻保持为高电平;当任意一个独立按键被按下时,对应的引脚会被直接拉低到低电平。单片机通过检测这些引脚的电平变化,即可判断按键是否被按下,从而实现按键信号的识别与数据传输。
三、硬件配置
跳线帽连接 CON3 的 2 和 3 引脚。
四、完整代码实现
底层驱动
#include <key.h>
unsigned char Key_Read_BTN(void) //独立按键
{
if(P33 == 0)
return 4;
else if(P32 == 0)
return 5;
else if(P31 == 0)
return 6;
else if(P30 == 0)
return 7;
else
return 0;
}
头文件声明
#include <STC15F2K60S2.H>
unsigned char Key_Read_BTN(void);
调用
#include <STC15F2K60S2.H>
#include <key.h>
//Key
unsigned char Key_Val = 0, Key_Val_Old = 0;
//Function
void Key_Proc(void);
void main(void)
{
while(1)
{
Key_Proc();
}
}
void Key_Proc(void)
{
Key_Val = Key_Read_BTN();//读取按键值
if(Key_Val == Key_Val_Old) //消抖(边沿检测)防止按一次按键 执行多次相关语句
return;
switch(Key_Val)
{
case 4://如果按键值为4时......
break;
case 5://如果按键值为5时......
break;
case 6://如果按键值为6时......
break;
default:
break;
}
Key_Val_Old=Key_Val;//记录当前按键值
}
GPIO控制|矩阵按键
一、模块功能
- 实现矩阵按键的单击、双击、长按等多事件识别
- 提供软件消抖,避免机械按键抖动导致的误触发
- 作为底层驱动,为菜单切换、参数调节等上层功能提供输入接口
二、工作原理
- 核心实现原理

当跳线帽连接 CON3 的 1 和 2 引脚时,独立按键模式关闭,矩阵键盘模式被激活。
此时,电路构成标准的4×4 矩阵键盘:
- 行列定义:P30、P31、P32、P33 引脚作为列线,P34、P35、P42、P44 引脚作为行线。
- 扫描逻辑:按键 S4~S19 跨接在行线与列线之间。单片机通过行列扫描法(逐行置低,逐列检测)来确定具体是哪一个按键被按下。
- 电平变化:未按下时,所有引脚通过内部上拉电阻保持高电平;当某按键按下时,对应的行线与列线导通,单片机检测到列线电平被拉低,从而定位按键位置。
三、硬件配置
- 跳线配置:将跳线帽连接 CON3 的 1 和 2 引脚,切断独立按键的 GND 通路,启用矩阵扫描电路。
- 引脚复用说明:
- 行线涉及 P42 (WR) 和 P44 (RD),需注意这两个引脚在 STC15 单片机中为复用功能,编程时需直接操作寄存器或定义为普通 IO 口使用。
- 列线保持 P30~P33 不变。
四、完整代码实现
底层驱动
#include <key.h>
unsigned char Key_Read_KBN(void) //矩阵按键
{
unsigned char temp = 0;
// 第 1 行扫描(P44=0,其余行线置 1)
P44 = 0;P42 = 1;P35 = 1;P34 = 1;
if (P33 == 0) temp = 4;
if (P32 == 0) temp = 5;
if (P31 == 0) temp = 6;
if (P30 == 0) temp = 7;
// 第 2 行扫描(P42=0,其余行线置 1)
P44 = 1;P42 = 0;P35 = 1;P34 = 1;
if (P33 == 0) temp = 8;
if (P32 == 0) temp = 9;
if (P31 == 0) temp = 10;
if (P30 == 0) temp = 11;
// 第 3 行扫描(P35=0,其余行线置 1)
P44 = 1;P42 = 1;P35 = 0;P34 = 1;
if (P33 == 0) temp = 12;
if (P32 == 0) temp = 13;
if (P31 == 0) temp = 14;
if (P30 == 0) temp = 15;
// 第 4 行扫描(P34=0,其余行线置 1)
P44 = 1;P42 = 1;P35 = 1;P34 = 0;
if (P33 == 0) temp = 16;
if (P32 == 0) temp = 17;
if (P31 == 0) temp = 18;
if (P30 == 0) temp = 19;
return temp;
}
头文件声明
#include <STC15F2K60S2.H>
unsigned char Key_Read_KBD(void);
调用
#include <STC15F2K60S2.H>
#include <key.h>
//Key
unsigned char Key_Val = 0, Key_Val_Old = 0;
//Function
void Key_Proc(void);
void main(void)
{
while(1)
{
Key_Proc();
}
}
void Key_Proc(void)
{
Key_Val = Key_Read_KBD();//读取按键值
if(Key_Val == Key_Val_Old) //消抖(边沿检测)防止按一次按键 执行多次相关语句
return;
switch(Key_Val)
{
case 4://如果按键值为4时......
break;
case 5://如果按键值为5时......
break;
case 6://如果按键值为6时......
break;
default:
break;
}
Key_Val_Old=Key_Val;//记录当前按键值
}
五、调试经验
- 消抖处理:原始代码未包含消抖,在竞赛中建议在
Key_Read_BTN函数中加入约 10ms 的软件延时,或使用定时器中断进行消抖,以避免机械按键抖动导致的误触发。 - 状态机思想:使用
key_val和key_val_old进行边沿检测,是蓝桥杯竞赛中处理按键的标准技巧,能有效防止按键长按导致的多次触发。 - 模块化封装:将按键驱动封装在独立的
key.c和key.h文件中,便于在不同题目中快速复用,是提升竞赛编码效率的关键。
GPIO|数码管显示
一、模块功能
- 实现 0~9、A~F 等字符在数码管上的静态显示。
- 作为基础显示模块,为传感器数据、系统状态等信息提供直观输出。
- 采用静态驱动方式,显示稳定无闪烁,适合单管或少量数码管场景。
二、工作原理





-
段码驱动:
- 单片机通过 P0 口 输出段码(a~g、dp),段码经过 74HC573(U7)锁存器 锁存后,驱动所有数码管的段引脚。
- 锁存器的 LE 引脚由 Y7C 信号控制,该信号由 74HC02(U25A) 或门产生,用于锁存段码数据。
-
位选控制:
- 在静态模式下,我们通过 74HC573(U8)锁存器 输出位选信号,将所有数码管的公共端(com1~com8)全部拉低(共阳数码管)。
- 这样一来,所有数码管的段引脚都被同时驱动,它们会显示完全相同的数字,实现 “静态显示”。
三、硬件配置
-
段选控制:
- 数据端口:P0 口(P0.0~P0.7),用于输出段码。
- 锁存控制:Y7C 信号,由 74HC138 译码器的 Y7 输出与 WR 信号经过或门产生。
- 操作流程:向 P0 口写入段码 → 拉低 Y7C → 短暂延时 → 拉高 Y7C,完成段码锁存。
-
位选控制:
- 数据端口:P0 口(P0.0~P0.7),用于输出位选信号。
- 锁存控制:Y6C 信号,由 74HC138 译码器的 Y6 输出与 WR 信号经过或门产生。
- 静态模式操作:向 P0 口写入
0x00(将所有 com 端拉低)→ 拉低 Y6C → 短暂延时 → 拉高 Y6C,完成位选锁存。此时所有数码管常亮。
-
常见问题:
- 所有数码管显示相同数字:这是静态模式的正常现象,若需显示不同数字,需切换到动态扫描模式。
- 数码管不亮:检查段码输出是否正确,锁存器是否使能,公共端是否全部拉低。
- 显示乱码:段码表定义错误,或段选与位选控制混淆。
四、完整代码实现
单一位静态显示
- 底层驱动层
#include <seg.h>
unsigned char seg_D[] =
{
0xC0, // 0: 0b11000000
0xF9, // 1: 0b11111001
0xA4, // 2: 0b10100100
0xB0, // 3: 0b10110000
0x99, // 4: 0b10011001
0x92, // 5: 0b10010010
0x82, // 6: 0b10000010
0xF8, // 7: 0b11111000
0x80, // 8: 0b10000000
0x90, // 9: 0b10010000
0xFF // 熄灭
};
unsigned char seg_W[] =
{
0x01, // 第1位
0x02, // 第2位
0x04, // 第3位
0x08, // 第4位
0x10, // 第5位
0x20, // 第6位
0x40, // 第7位
0x80 // 第8位
};
//数码管显示函数
void SEG_Disp(unsigned char D, unsigned char W, bit point)
{
// 消隐:关闭所有段,避免鬼影
P0 = 0xff;
P2 = (P2 & 0x1f)|0xe0;
P2 = P2 & 0x1f;
P0 = seg_W[W];
P2 = (P2 & 0x1f)|0xc0;
P2 = P2 & 0x1f;
P0 = seg_D[D];
if(point)
{ P0 &= 0x7F; } // 共阳极小数点是拉低电平,所以与0x7F清除最高位
P2 = (P2 & 0x1f)|0xe0;
P2 = P2 & 0x1f;
}
- 头文件声明
#include <STC15F2K60S2.H>
void Seg_Disp(unsigned char D, unsigned char W, bit point);
- 调用
#include <STC15F2K60S2.H>
#include <seg.h>
void main(void)
{
Seg_Disp(1,1,0);
while(1)
{
}
}
五、调试经验
- 锁存器操作:务必确保 P2 口的锁存操作(
0xc0和0xe0)顺序正确,先锁存位选,再锁存段码,避免显示瞬间乱码。 - 段码表核对:确认使用的是共阳数码管段码表,共阴和共阳的段码是反相的,使用前务必确认。
- 位选范围:注意
seg_W数组的索引是 0~7,对应第 1~8 位数码管,调用时不要超出范围。
六、应试要点
- 高频考点:
- 数码管静态 / 动态显示的切换。
- 段码表和位选表的正确使用。
- 锁存器的操作时序。
- 必记知识点:
- 共阳数码管段码表:0~9 对应
0xC0~0x90。 - 锁存器控制:
0xc0控制位选,0xe0控制段选。
- 共阳数码管段码表:0~9 对应
- 解题思路:
- 静态显示:适合显示固定数字,调用一次
Seg_Disp即可持续显示。 - 动态显示:在定时器中断中循环调用
Seg_Disp,依次点亮每一位数码管。
- 静态显示:适合显示固定数字,调用一次
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐


所有评论(0)