普中51单片机学习笔记-按键

目录

写在前面

硬件电路

定时器消抖示例代码

四按键消抖示例代码


写在前面

由于机械点的弹性作用,按键开关在闭合时不会马上稳定的接通,在断开时也不会一下子断开,因而在闭合和断开的瞬间均伴随着一连串的抖动。抖动时间的长短由按键的机械特性决定的,一般为 5ms 到 10ms。

按键消抖有两种方式,一种是硬件消抖 ,另一种是软件消抖。为了使电路更加简单,通常采用软件消抖。

软件消抖,又分为 延时消抖定时器消抖

延时消抖工作原理:

  • 就是先读取按键的状态

  • 如果得到按键按下之后,延时 10ms,再次读取按键的状态

  • 如果按键还是按下状态,那么说明按键已经按下。

定时器消抖工作原理:

  • 当状态发生变化时,并不立即确认,而是开始计数,连续3次(15ms)都检测到相同的新状态才确认状态改变。

  • 只有确认状态改变且新状态为按下(0)时,才触发按键事件(设置key_press_flag)。

  • 如果状态变化过程中又变回原状态,则消抖计数器清零,不确认状态改变。

定时器消抖优势:

  • 不阻塞主程序运行

  • 更精确的时间控制

  • 支持多个按键同时处理

  • 可实现长按、连按等复杂功能

硬件电路

定时器消抖示例代码

复制代码
/********************************************
 * 文件头注释
 ********************************************/

#include <reg51.h>      // 51单片机头文件

/********************************************
 * 宏定义
 ********************************************/
#define DEBOUNCE_TIME  3    // 消抖时间 3×5ms=15ms

/********************************************
 * 引脚定义
 ********************************************/
sbit KEY = P3^0;
sbit LED = P2^0;

/********************************************
 * 全局变量声明
 ********************************************/
bit key_press_flag = 0;

/********************************************
 * 函数原型声明 - 重要!
 ********************************************/
void Timer0_Init(void);
void Key_Scan(void);
void Timer0_ISR(void);

/********************************************
 * 主函数
 ********************************************/
void main(void)
{
    // 初始化
    Timer0_Init();
    EA = 1;         // 开启总中断
    LED = 0;        // 初始化LED状态
    
    // 主循环
    while(1)
    {
        if(key_press_flag)
        {
            key_press_flag = 0;
            LED = ~LED;  // LED状态翻转
        }
        
        // 这里可以添加其他任务
    }
}

/********************************************
 * 定时器初始化函数
 ********************************************/
void Timer0_Init(void)
{
    TMOD = 0x01;    // 定时器0,模式1
    TH0 = (65536 - 4608) / 256;  // 5ms定时
    TL0 = (65536 - 4608) % 256;
    ET0 = 1;        // 开启定时器0中断
    TR0 = 1;        // 启动定时器0
}

/********************************************
 * 定时器中断服务函数
 ********************************************/
void Timer0_ISR(void) interrupt 1
{
    // 重装定时器初值
    TH0 = (65536 - 4608) / 256;
    TL0 = (65536 - 4608) % 256;
    
    // 调用按键扫描
    Key_Scan();
}

/********************************************
 * 按键扫描函数
 ********************************************/
void Key_Scan(void)
{
    static bit last_key_state = 1;
    static unsigned char debounce_count = 0;
    
    bit current_key_state = KEY;
    
    if(current_key_state != last_key_state)
    {
        debounce_count++;
        if(debounce_count >= DEBOUNCE_TIME)
        {
            last_key_state = current_key_state;
            if(current_key_state == 0)  // 检测到按下
            {
                key_press_flag = 1;     // 设置按键标志
            }
            debounce_count = 0;
        }
    }
    else
    {
        debounce_count = 0;
    }
}

四按键消抖示例代码

复制代码
/********************************************
 * 文件头注释
 ********************************************/
#include <reg51.h>
#include <intrins.h>

/********************************************
 * 宏定义
 ********************************************/
#define DEBOUNCE_TIME      3    // 消抖时间 3×5ms=15ms
#define KEY_NUM            4    // 按键数量
#define TIMER_5MS          (65536 - 4608)  // 11.0592MHz晶振,5ms定时

/********************************************
 * 引脚定义
 ********************************************/
// 按键引脚定义
sbit KEY1 = P3^0;
sbit KEY2 = P3^1;
sbit KEY3 = P3^2;
sbit KEY4 = P3^3;

// LED引脚定义
sbit LED1 = P2^0;
sbit LED2 = P2^1;
sbit LED3 = P2^2;
sbit LED4 = P2^3;

/********************************************
 * 全局变量声明
 ********************************************/
bit key_press_flag[KEY_NUM];    // 按键按下标志位
bit key_release_flag[KEY_NUM];  // 按键释放标志位

/********************************************
 * 函数原型声明
 ********************************************/
void Timer0_Init(void);
void Key_Scan(void);
void Key_Process(void);
void Timer0_ISR(void);

/********************************************
 * 主函数
 ********************************************/
void main(void)
{
    unsigned char i;
    
    // 初始化
    Timer0_Init();
    EA = 1;         // 开启总中断
    
    // 初始化LED状态
    LED1 = 1;       // 熄灭LED
    LED2 = 1;
    LED3 = 1;
    LED4 = 1;
    
    // 初始化按键标志位
    for(i = 0; i < KEY_NUM; i++)
    {
        key_press_flag[i] = 0;
        key_release_flag[i] = 0;
    }
    
    // 主循环
    while(1)
    {
        Key_Process();  // 按键处理
        
        // 这里可以添加其他任务
        // _nop_();     // 空操作,降低功耗
    }
}

/********************************************
 * 定时器初始化函数
 ********************************************/
void Timer0_Init(void)
{
    TMOD &= 0xF0;    // 清除T0控制位
    TMOD |= 0x01;    // 定时器0,模式1
    TH0 = TIMER_5MS / 256;    // 5ms定时高字节
    TL0 = TIMER_5MS % 256;    // 5ms定时低字节
    ET0 = 1;         // 开启定时器0中断
    TR0 = 1;         // 启动定时器0
}

/********************************************
 * 定时器中断服务函数
 ********************************************/
void Timer0_ISR(void) interrupt 1
{
    // 重装定时器初值
    TH0 = TIMER_5MS / 256;
    TL0 = TIMER_5MS % 256;
    
    // 调用按键扫描
    Key_Scan();
}

/********************************************
 * 按键扫描函数 - 优化版
 ********************************************/
void Key_Scan(void)
{
    static unsigned char last_key_state = 0xFF;  // 上次按键状态
    static unsigned char debounce_count[KEY_NUM] = {0}; // 消抖计数器
    
    unsigned char current_key_state;
    unsigned char i;
    
    // 读取当前所有按键状态(低电平有效)
    current_key_state = P3 & 0x0F;  // 只取P3.0~P3.3
    
    for(i = 0; i < KEY_NUM; i++)
    {
        // 检查按键状态是否变化
        if(((current_key_state >> i) & 0x01) != ((last_key_state >> i) & 0x01))
        {
            debounce_count[i]++;
            if(debounce_count[i] >= DEBOUNCE_TIME)
            {
                // 状态稳定,更新标志位
                if(((current_key_state >> i) & 0x01) == 0)  // 按键按下
                {
                    key_press_flag[i] = 1;
                }
                else  // 按键释放
                {
                    key_release_flag[i] = 1;
                }
                debounce_count[i] = 0;
            }
        }
        else
        {
            debounce_count[i] = 0;  // 状态稳定,清零计数器
        }
    }
    
    last_key_state = current_key_state;  // 更新按键状态
}

/********************************************
 * 按键处理函数
 ********************************************/
void Key_Process(void)
{
    unsigned char i;
    
    for(i = 0; i < KEY_NUM; i++)
    {
        // 处理按键按下事件
        if(key_press_flag[i])
        {
            key_press_flag[i] = 0;  // 清除标志位
            
            switch(i)
            {
                case 0:  // KEY1
                    LED1 = ~LED1;   // 翻转LED1
                    break;
                    
                case 1:  // KEY2
                    LED2 = ~LED2;   // 翻转LED2
                    break;
                    
                case 2:  // KEY3
                    LED3 = ~LED3;   // 翻转LED3
                    break;
                    
                case 3:  // KEY4
                    LED4 = ~LED4;   // 翻转LED4
                    break;
                    
                default:
                    break;
            }
        }
        
        // 处理按键释放事件(可根据需要添加功能)
        if(key_release_flag[i])
        {
            key_release_flag[i] = 0;  // 清除标志位
            // 这里可以添加按键释放时的处理代码
        }
    }
}

完结~撒花!

相关推荐
CodeLongBear2 小时前
MySQL进阶学习笔记:从单表查询到多表关联的深度解析(万字详解)
笔记·学习·mysql
Element_南笙3 小时前
吴恩达新课程:Agentic AI(笔记6)
人工智能·笔记
下午见。3 小时前
【C语言学习笔记】动态内存分配:malloc/free的正确打开方式
c语言·笔记·学习
01100001乄夵4 小时前
FPGA零基础入门教程——揭开可编程芯片的神秘面纱
经验分享·笔记·学习方法·fpga入门·fpga学习之路
草莓熊Lotso4 小时前
C++ 抽象类与多态原理深度解析:从纯虚函数到虚表机制(附高频面试题)
java·运维·服务器·开发语言·c++·人工智能·笔记
Rock_yzh4 小时前
LeetCode算法刷题——49. 字母异位词分组
数据结构·c++·学习·算法·leetcode·职场和发展·哈希算法
Wayfreem4 小时前
Spring AI Alibaba 学习之最简单的快速入门
人工智能·学习·spring
再玩一会儿看代码4 小时前
Ken的Java学习之路——Java中关于面向对象
java·开发语言·经验分享·python·学习
乄夜5 小时前
嵌入式面试高频!!!C语言(十四) STL(嵌入式八股文)
c语言·c++·stm32·单片机·mcu·面试·51单片机