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 矩阵键盘

    1. 行列定义:P30、P31、P32、P33 引脚作为列线,P34、P35、P42、P44 引脚作为行线
    2. 扫描逻辑:按键 S4~S19 跨接在行线与列线之间。单片机通过行列扫描法(逐行置低,逐列检测)来确定具体是哪一个按键被按下。
    3. 电平变化:未按下时,所有引脚通过内部上拉电阻保持高电平;当某按键按下时,对应的行线与列线导通,单片机检测到列线电平被拉低,从而定位按键位置。

    三、硬件配置

    • 跳线配置:将跳线帽连接 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;//记录当前按键值
    }
    
    

    五、调试经验

    1. 消抖处理:原始代码未包含消抖,在竞赛中建议在Key_Read_BTN函数中加入约 10ms 的软件延时,或使用定时器中断进行消抖,以避免机械按键抖动导致的误触发。
    2. 状态机思想:使用key_valkey_val_old进行边沿检测,是蓝桥杯竞赛中处理按键的标准技巧,能有效防止按键长按导致的多次触发。
    3. 模块化封装:将按键驱动封装在独立的key.ckey.h文件中,便于在不同题目中快速复用,是提升竞赛编码效率的关键。

    GPIO|数码管显示

    一、模块功能

    • 实现 0~9、A~F 等字符在数码管上的静态显示。
    • 作为基础显示模块,为传感器数据、系统状态等信息提供直观输出。
    • 采用静态驱动方式,显示稳定无闪烁,适合单管或少量数码管场景。

    二、工作原理

    • 段码驱动

      • 单片机通过 P0 口 输出段码(a~g、dp),段码经过 74HC573(U7)锁存器 锁存后,驱动所有数码管的段引脚。
      • 锁存器的 LE 引脚由 Y7C 信号控制,该信号由 74HC02(U25A) 或门产生,用于锁存段码数据。
    • 位选控制

      • 在静态模式下,我们通过 74HC573(U8)锁存器 输出位选信号,将所有数码管的公共端(com1~com8)全部拉低(共阳数码管)。
      • 这样一来,所有数码管的段引脚都被同时驱动,它们会显示完全相同的数字,实现 “静态显示”。

    三、硬件配置

    1. 段选控制

      • 数据端口:P0 口(P0.0~P0.7),用于输出段码。
      • 锁存控制:Y7C 信号,由 74HC138 译码器的 Y7 输出与 WR 信号经过或门产生。
      • 操作流程:向 P0 口写入段码 → 拉低 Y7C → 短暂延时 → 拉高 Y7C,完成段码锁存。
    2. 位选控制

      • 数据端口:P0 口(P0.0~P0.7),用于输出位选信号。
      • 锁存控制:Y6C 信号,由 74HC138 译码器的 Y6 输出与 WR 信号经过或门产生。
      • 静态模式操作:向 P0 口写入 0x00(将所有 com 端拉低)→ 拉低 Y6C → 短暂延时 → 拉高 Y6C,完成位选锁存。此时所有数码管常亮。
    3. 常见问题

      • 所有数码管显示相同数字:这是静态模式的正常现象,若需显示不同数字,需切换到动态扫描模式。
      • 数码管不亮:检查段码输出是否正确,锁存器是否使能,公共端是否全部拉低。
      • 显示乱码:段码表定义错误,或段选与位选控制混淆。

    四、完整代码实现

    单一位静态显示

    • 底层驱动层
    #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)
    	{
    		
    	}
    }

    五、调试经验

    1. 锁存器操作:务必确保 P2 口的锁存操作(0xc00xe0)顺序正确,先锁存位选,再锁存段码,避免显示瞬间乱码。
    2. 段码表核对:确认使用的是共阳数码管段码表,共阴和共阳的段码是反相的,使用前务必确认。
    3. 位选范围:注意seg_W数组的索引是 0~7,对应第 1~8 位数码管,调用时不要超出范围。

    六、应试要点

    1. 高频考点
      • 数码管静态 / 动态显示的切换。
      • 段码表和位选表的正确使用。
      • 锁存器的操作时序。
    2. 必记知识点
      • 共阳数码管段码表:0~9 对应0xC0~0x90
      • 锁存器控制:0xc0控制位选,0xe0控制段选。
    3. 解题思路
      • 静态显示:适合显示固定数字,调用一次Seg_Disp即可持续显示。
      • 动态显示:在定时器中断中循环调用Seg_Disp,依次点亮每一位数码管。

      Logo

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

      更多推荐