(1)实验平台:
普中 51-Ai8051 开发板
https://item.taobao.com/item.htm?abbucket=17&id=1026052331067(2)资料下载 :普中科技-各型号产品资料下载链接
前几章介绍的都是 IO 口输出的使用, 本章我们通过按键实验来介绍 IO 口作为输入的使用。 本章分为如下几部分内容:
[9.1 实验介绍](#9.1 实验介绍)
[9.1.1 实验简介](#9.1.1 实验简介)
[9.1.2 实验目的](#9.1.2 实验目的)
[9.2 硬件设计](#9.2 硬件设计)
[9.3 软件设计](#9.3 软件设计)
[9.3.1 创建和配置工程](#9.3.1 创建和配置工程)
[9.3.1.1 GPIO 配置](#9.3.1.1 GPIO 配置)
[9.3.1.2 生成工程](#9.3.1.2 生成工程)
[9.3.2 添加用户驱动代码](#9.3.2 添加用户驱动代码)
[9.3.2.1 key.h 文件](#9.3.2.1 key.h 文件)
[9.3.2.2 key.c 文件](#9.3.2.2 key.c 文件)
[9.3.2.3 main.c 文件](#9.3.2.3 main.c 文件)
[9.4 实验现象](#9.4 实验现象)
9.1 实验介绍
9.1.1 实验简介
按键是一种电子开关, 使用时轻轻按开关按钮就可使开关接通, 当松开手时,开关断开。 一般机械按键按下和松开时存在抖动情况, 如下:

这种抖动可能会影响程序误判, 造成严重后果, 通常会使用软件延时 10ms来消斗。 例如, 当按键按下后, 引脚为低电平; 所以首先读取引脚电平, 若引脚为低电平, 则延时 10ms 后再次读取引脚电平, 若为低电平, 则证明按键已按下。
有了按键输入功能, 我们就可以做很多好玩的东西了。
9.1.2 实验目的
通过板载按键 K1-K4 控制 LED 指示灯亮灭。
9.2 硬件设计
本实验使用到硬件资源如下:
(1) 按键模块
(2) GPIO
(3) LED 模块
按键模块电路如下所示:

从电路图中可以看到, K1-K4 按键分别接在芯片的 P32-P35 管脚; 当有按键按下, 对应管脚被拉低; 按键松开, 由软件配置管脚内部上拉, 对应管脚被拉高;因此可以通过检测 IO 口输入电平来判断按键有无按下。
9.3 软件设计
9.3.1 创建和配置工程
按照前面章节内容创建一份新工程, 并命名为 05-key, 如下图所示:

9.3.1.1 GPIO 配置
使能端口和时钟, 按键对应 IO 设置为输入模式, 使能上拉电阻; 将所控制的 LED 设置为推挽输出模式, 使能上拉电阻等, 如下图所示:


9.3.1.2 生成工程
配置完成后, 按下代码生成按钮, 自动创建工程, 系统开始生成初始化代码。生成工程文件目录如下图所示:

在工程文件夹内新建一个 APP 文件夹, 用于存储用户编写的外设驱动, 方便后期工程管理和程序移植。 在 APP 文件夹下新建 key 文件夹, 并在该文件夹内新建 key.c 和 key.h 文件, 用于保存按键相关驱动; 如下所示:


然后在导出的工程中添加 APP 组, 并将 key.c 和 led.c 导入到工程组内, 最后添加头文件路径, 否则编译将报错。 如下:


添加完以后, 如下图所示:

9.3.2 添加用户驱动代码
9.3.2.1 key.h 文件
cpp
#ifndef _key_H
#define _key_H
#include "config.h"
//管脚定义
#define KEY1 P32
#define KEY2 P33
#define KEY3 P34
#define KEY4 P35
//定义各个按键值
#define KEY1_PRESS 1
#define KEY2_PRESS 2
#define KEY3_PRESS 3
#define KEY4_PRESS 4
//函数声明
u8 KEY_Scan(u8 mode);
#endif
9.3.2.2 key.c 文件
cpp
#include "key.h"
/*******************************************************************************
* 函 数 名 : KEY_Scan
* 函数功能 : 按键扫描检测
* 输 入 : mode=0:单次按下按键
mode=1:连续按下按键
* 输 出 : 0:未有按键按下
KEY1_PRESS:KEY1键按下
KEY2_PRESS:KEY2键按下
KEY3_PRESS:KEY3键按下
KEY4_PRESS:KEY4键按下
*******************************************************************************/
u8 KEY_Scan(u8 mode)
{
static u8 key=1;
if(mode==1) //连续按键按下
key=1;
if(key==1&&(KEY1==0||KEY2==0||KEY3==0||KEY4==0)) //任意一个按键按下
{
delay_ms(10); //消抖
key=0;
if(KEY1==0)
return KEY1_PRESS;
else if(KEY2==0)
return KEY2_PRESS;
else if(KEY3==0)
return KEY3_PRESS;
else if(KEY4==0)
return KEY4_PRESS;
}
else if(KEY1==1&&KEY2==1&&KEY3==1&&KEY4==1) //无按键按下
key=1;
return 0;
}
KEY_Scan 函数用于扫描这 4 个 IO 口是否有按键按下。 支持两种扫描方式, 通过 mode 参数来设置。
当 mode 为 0 的时候, 函数将不支持连续按, 扫描某个按键, 该按键按下之后必须要松开, 才能第二次触发, 否则不会再响应这个按键, 这样的好处就是可以防止按一次多次触发, 而坏处就是在需要长按的时候比较不合适。
当 mode 为 1 的时候, 函数将支持连续按, 如果某个按键一直按下, 则会一直返回这个按键的键值, 这样可以方便的实现长按检测。
有了 mode 这个参数, 大家就可以根据自己的需要, 选择不同的方式。 这里要提醒大家, 因为该函数里面有 static 变量, 所以该函数不是一个可重入函数。可以看到该函数的消抖延时是 10ms。 同时还有一点要注意的是, 该函数的按键扫描是有优先级的, 最优先的是 KEY1, 第二优先的是 KEY2, 依次排列, 最后是按键 KEY4。 该函数有返回值, 如果有按键按下, 则返回非 0 值, 如果没有或者按键不正确, 则返回 0。
led.h 文件内定义了 4 个 LED 灯的控制引脚宏定义。
9.3.2.3 main.c 文件
cpp
//<<AICUBE_USER_HEADER_REMARK_BEGIN>>
/* 深圳市普中科技有限公司(PRECHIN 普中)
* 在线视频:https://space.bilibili.com/2146492485/video
官网:www.prechin.cn
* 实验名称:独立按键实验
*
* 接线说明:参考电路图
*
* 实验现象:程序下载成功后,K1控制D1亮灭,K2控制D2亮灭,K3控制D3亮灭,K4控制D4亮灭
*
* 注意事项:
*
*/
//<<AICUBE_USER_HEADER_REMARK_END>>
#include "config.h" //默认已包含stdio.h、intrins.h、ai_usb.h等头文件
//<<AICUBE_USER_INCLUDE_BEGIN>>
// 在此添加用户头文件包含
#include "key.h"
#include "led.h"
//<<AICUBE_USER_INCLUDE_END>>
//<<AICUBE_USER_GLOBAL_DEFINE_BEGIN>>
// 在此添加用户全局变量定义、用户宏定义以及函数声明
//<<AICUBE_USER_GLOBAL_DEFINE_END>>
////////////////////////////////////////
// 项目主函数
// 入口参数: 无
// 函数返回: 无
////////////////////////////////////////
void main(void)
{
//<<AICUBE_USER_MAIN_INITIAL_BEGIN>>
// 在此添加用户主函数初始化代码
u8 key=0;
//<<AICUBE_USER_MAIN_INITIAL_END>>
SYS_Init();
//<<AICUBE_USER_MAIN_CODE_BEGIN>>
// 在此添加主函数中运行一次的用户代码
//<<AICUBE_USER_MAIN_CODE_END>>
while (1)
{
//<<AICUBE_USER_MAIN_LOOP_BEGIN>>
// 在此添加主函数中用户主循环代码
key=KEY_Scan(0); //扫描按键
switch(key)
{
case KEY1_PRESS: LED1=!LED1;break;
case KEY2_PRESS: LED2=!LED2;break;
case KEY3_PRESS: LED3=!LED3;break;
case KEY4_PRESS: LED4=!LED4;break;
}
//<<AICUBE_USER_MAIN_LOOP_END>>
}
}
主函数实现功能主要初始化系统时钟及相应外设端口, while 循环内不断扫描按键, 如有按键按下, 控制对应 LED 亮灭。
9.4 实验现象
将程序编译下载到目标板运行, 实验现象: K1 控制 D1 亮灭, K2 控制 D2 亮灭, K3 控制 D3 亮灭, K4 控制 D4 亮灭。