【普中 51-Ai8051 开发攻略】-- 第 9 章 按键控制实验

(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 亮灭。

相关推荐
曼亿点3 小时前
STM32HAL驱动L298N电机驱动模块(PWM调速+双电机控制)保姆级教程
stm32·单片机·嵌入式硬件
筱谙3 小时前
BES 芯片跨核通讯与共享内存设计原理
嵌入式硬件·音频·蓝牙
思为无线NiceRF3 小时前
高空线路安装智能安全帽全双工组网对讲系统(含优先级管控)应用方案
嵌入式硬件·物联网
独小乐5 小时前
012.整体框架适配SDRAM|千篇笔记实现嵌入式全栈/裸机篇
c语言·汇编·笔记·单片机·嵌入式硬件·arm·gnu
不吃鱼的羊5 小时前
ADC扫描组Scan Group
单片机
LCMICRO-133108477466 小时前
长芯微LPC556D1完全P2P替代DAC8830,是引脚兼容的16位数模转换器,该系列产品为单通道、低功耗、缓冲电压输出型DAC
stm32·单片机·嵌入式硬件·fpga开发·硬件工程·电压输出型dac
forAllforMe6 小时前
如何用定时器PWM产生SPWM?--电机驱动控制
嵌入式硬件
charlie1145141916 小时前
嵌入式C++教程实战之Linux下的单片机编程(9):HAL时钟使能 —— 不开时钟,外设就是一坨睡死的硅
linux·开发语言·c++·单片机·嵌入式硬件·c
钿驰科技6 小时前
水泵无刷电机驱动板如何实现恒压控制?
单片机·嵌入式硬件