Ai8051 独立按键控制LED实验

Ai8051 独立按键控制LED实验

一、实验目标

  • 用板载 K1~K4 独立按键,控制对应 LED1~LED4 亮灭
  • 掌握:按键消抖、按键扫描、IO口配置、模块化编程

二、硬件电路(原理图)

  • 按键:P3.2、P3.3、P3.4、P3.5(低电平有效)
  • LED:P2.0、P2.1、P2.2、P2.3
  • 按键按下 → IO口为 低电平(0)

三、AiCube 配置步骤

  1. 开启系统时钟

    • 主时钟:内部高速 IRC
    • 系统时钟:40MHz
  2. 图形化IO配置

    • 按键端口(P3.2~P3.5):设为 上拉输入
    • LED端口(P2.0~P2.3):设为 推挽输出

  3. 勾选启用 P2、P3 端口


四、完整代码(逐行注释)

1. key.h(按键头文件)

c 复制代码
#ifndef _KEY_H  // 防止头文件重复包含(条件编译)
#define _KEY_H

#include "config.h"  // 包含系统配置头文件(Ai8051自动生成,含IO/时钟定义)

// 按键引脚定义(根据实际硬件修改,此处为示例:K1~K4对应P3.2~P3.5)
#define KEY1    P3_2  // 定义KEY1为P3.2引脚
#define KEY2    P3_3  // 定义KEY2为P3.3引脚
#define KEY3    P3_4  // 定义KEY3为P3.4引脚
#define KEY4    P3_5  // 定义KEY4为P3.5引脚

// 按键返回值宏定义(增强代码可读性,避免魔法数字)
#define KEY1_PRESS  1  // KEY1按下返回1
#define KEY2_PRESS  2  // KEY2按下返回2
#define KEY3_PRESS  3  // KEY3按下返回3
#define KEY4_PRESS  4  // KEY4按下返回4

// 按键扫描函数声明(mode参数:0=单次扫描,1=连续扫描,此处暂用0)
u8 KEY_Scan(u8 mode);

#endif  // 结束条件编译

2. key.c(按键驱动)

c 复制代码
#include "key.h"     
  

/**
 * @brief  按键扫描函数(修复版:单次触发,双次消抖+等待松开)
 * @param  mode:0=单次扫描(按一次只触发一次),1=连续扫描
 * @retval 按键值:1=KEY1,2=KEY2,3=KEY3,4=KEY4,0=无按键
 * @note   增加"等待松开"+"二次消抖",解决多次按才生效的问题
 */
u8 KEY_Scan(u8 mode)
{
    static u8 key_flag = 1;  // 按键状态标记(1=未按下,0=已按下)
    u8 key_val = 0;          // 临时存储按键值

    // 只有按键未按下时,才检测新的按键(防止连触发)
    if(key_flag == 1)
    {
        // 第一步:检测是否有按键按下(低电平有效)
        if(!KEY1 || !KEY2 || !KEY3 || !KEY4)
        {
            delay_ms(10);   // 第一次消抖:过滤按下时的机械抖动
            // 第二步:二次确认按键确实按下(避免误触发)
            if(!KEY1) key_val = KEY1_PRESS;
            else if(!KEY2) key_val = KEY2_PRESS;
            else if(!KEY3) key_val = KEY3_PRESS;
            else if(!KEY4) key_val = KEY4_PRESS;

            // 第三步:如果检测到有效按键,等待松开+松开消抖
            if(key_val != 0)
            {
                key_flag = 0;  // 标记按键已按下,防止重复检测
                // 等待按键松开(核心!解决"多次按才生效")
                while(!KEY1 || !KEY2 || !KEY3 || !KEY4);
                delay_ms(10);   // 第二次消抖:过滤松开时的机械抖动
                return key_val; // 返回有效按键值
            }
        }
    }
    // 第四步:所有按键都松开后,重置状态
    else if(KEY1 && KEY2 && KEY3 && KEY4)
    {
        key_flag = 1;  // 重置标记,允许下次检测
    }

    return 0;  // 无按键按下,返回0
}

3. led.h(LED头文件)

c 复制代码
#ifndef _LED_H  // 防止头文件重复包含
#define _LED_H

#include "config.h"  // 包含系统配置头文件

// LED引脚定义(根据实际硬件修改,此处为示例:LED1~LED4对应P2.0~P2.3)
#define LED1    P2_0  // 定义LED1为P2.0引脚
#define LED2    P2_1  // 定义LED2为P2.1引脚
#define LED3    P2_2  // 定义LED3为P2.2引脚
#define LED4    P2_3  // 定义LED4为P2.3引脚

#endif  // 结束条件编译

4. main.c(主函数)

c 复制代码
/*******************************************
 * 头文件包含
 *******************************************/
#include "config.h"  // 包含系统配置头文件(由AiCube生成,含时钟/IO初始化)
#include "key.h"     // 包含按键驱动头文件(定义按键引脚和扫描函数)
#include "led.h"     // 包含LED驱动头文件(定义LED引脚)

/*******************************************
 * 项目主函数
 * 入口参数:无
 * 函数返回:无
 * 作用:程序入口,死循环执行按键扫描和LED控制
 *******************************************/
void main(void)
{
    u8 key_val = 0;  // 定义变量存储按键返回值(0=无按键,1~4=对应KEY1~KEY4)
    SYS_Init();      // 系统初始化函数(配置时钟、IO口模式等,必须调用)

    while(1)        // 死循环(单片机程序核心,一直运行)
    {
        // ========== 按键扫描逻辑 ==========
        // 调用按键扫描函数,mode=0 表示【单次扫描】
        // 效果:按一次按键只触发一次,必须松开后才能再次触发
        key_val = KEY_Scan(0);  

        // ========== LED控制逻辑 ==========
        // 根据key_val的值,执行对应的LED动作
        switch(key_val)
        {
            case KEY1_PRESS:  // 如果检测到KEY1按下
                LED1 = !LED1;   // LED1状态翻转(灭变亮,亮变灭)
                break;         // 跳出switch,防止case穿透

            case KEY2_PRESS:  // 如果检测到KEY2按下
                LED2 = !LED2;   // LED2状态翻转
                break;

            case KEY3_PRESS:  // 如果检测到KEY3按下
                LED3 = !LED3;   // LED3状态翻转
                break;

            case KEY4_PRESS:  // 如果检测到KEY4按下
                LED4 = !LED4;   // LED4状态翻转
                break;

            default:          // 默认分支(无按键按下时,不执行操作)
                break;
        }

        // 主循环加短延时(可选)
        // 作用:降低CPU占用,避免程序跑飞,不影响按键检测灵敏度
        delay_ms(5);
    }
}

五、关键注释说明

  1. 静态变量 key_flag
    static u8 key_flag = 1; 是单次扫描的核心,静态变量只会初始化一次,按键按下时设为0,松开后重置为1,避免"按一次触发多次"。
  2. 消抖延时 delay_ms(10)
    机械按键按下/松开时会有5~10ms的电平抖动,必须加延时过滤,否则会误判"没按键"或"多次按键"。
  3. !LED1 翻转逻辑
    LED1 = !LED1 等价于 LED1 = (LED1 == 0) ? 1 : 0,实现"按一下亮、再按灭"的效果。