1、实验内容:单片机 I/O 口的应用设计——输入控制
(1)独立键盘的使用
(2)矩阵键盘的使用
(3)蜂鸣器实验
2、参考资料:
(1) 教材;(2)百度网盘中实验板相关资料。
3、预习内容:
(1) 教材 5.7 键盘接口设计
(2) 参看开发板光盘视频材料及相关示例代码:独立按键;矩阵按键;蜂鸣器
4、 重难点:
(1) 实现按键消抖的方法。
(2) 扫描检测矩阵按键的原理与方法。
(3) 使蜂鸣器发出响亮声音的编程方法。
5、实验任务:要求用 Proteus 画原理图进行软件仿真,并下载到开发板上硬件验证。
任务(1):在参考示例代码的基础上修改,用独立按键 K1,K2,K3,K4 控制一个 LED 灯。 即按
下 K1 时,LED 灯闪烁;按下 K2 时,蜂鸣器发出叮咚门铃声。按下 K3 时,LED 灯左移;按下 K4 时,
LED 灯右移。
任务(2):理解参考代码,按下图所示修改矩阵键盘布局,每按一个按键,用1个数码管显示其键名(0-F)。
注意:在 Proteus仿真时,用 sperker(sounder)来代表蜂鸣器。
任务一
在参考示例代码的基础上修改,用独立按键 K1,K2,K3,K4 控制一个 LED 灯。 即按
下 K1 时,LED 灯闪烁;按下 K2 时,蜂鸣器发出叮咚门铃声。按下 K3 时,LED 灯左移;按下 K4 时,
可于Proteus中作仿真图如下:
代码实现如下:
#include<reg52.h>
#include<intrins.h>
typedef unsigned char u8;
typedef unsigned int u16;
#define LED P2
/*宏定义按键赋值,方便按键检测操作*/
#define KEY1 1
#define KEY2 2
#define KEY3 3
#define KEY4 4
/*定义蜂鸣器以及按键操作位*/
sbit beep=P1^5;
sbit K1=P3^1;
sbit K2=P3^0;
sbit K3=P3^2;
sbit K4=P3^3;
u8 code led[8] = {0x7f,0xbf,0xdf,0xef,0xf7,0xfb,0xfd,0xfe};//只点亮一个灯亮的状态
u16 shanshuo = 0;//标记闪烁状态
u8 num = 0;//标记对应位数的LED
u8 ding,dong,flag,stop;//计数标志
u16 time;//计时标志
/*延时函数*/
void delay(u16 i)
{
while(i--);
}
void delay500ms(void) //误差 0us
{
unsigned char a,b,c;
for(c=23;c>0;c--)
for(b=152;b>0;b--)
for(a=70;a>0;a--);
}
void time0init() //定时器0初始化
{
TMOD=0X01; //定时器0 方式1
TH0=0Xff;
TL0=0X06; //定时250us
// TR0=1;
EA=1;
ET0=1;
}
void biaohaoinit() //各个标号初始化
{
ding=0; //叮声音 计数标志
dong=0; //咚声音 计数标志
time=0; //定时0.5s标志
flag=0;
stop=0; //结束标志
}
/*按键扫描*/
u8 KeyScan(u8 mode)//mode=0 单次 1:连续
{
static u8 keyen=1;
if(mode==1)
{
keyen=1;
}
if(keyen==1&&(K1==0||K2==0||K3==0||K4==0))
{
delay(1000); //消抖处理
keyen=0;
if(K1==0)return KEY1;
else if(K2==0)return KEY2;
else if(K3==0)return KEY3;
else if(K4==0)return KEY4;
}
else if(K1==1&&K2==1&&K3==1&&K4==1)
{
keyen=1;
}
return 0;
}
/*按键处理*/
void KeyPros(u8 key)
{
if(key==KEY1) //LED闪烁
{
shanshuo=(shanshuo+1)%2;
delay(1000);
}
if(key==KEY2) //蜂鸣器发出叮咚门铃声
{
if(K2==0)
{
TR0=1; //打开定时器0
while(!stop);
}
}
if(key==KEY3) //LED左移
{
num++;
LED = _crol_(0xfe,num%8);
}
if(key==KEY4) //LED右移
{
num--;
LED = _crol_(0xfe,num%8);
}
}
/*主函数*/
void main()
{
time0init();
biaohaoinit();
LED = 0xfe;
while(1)
{
u8 key;//存放KeyScan()的返回值
key=KeyScan(0);
KeyPros(key);
if(shanshuo==1)//在主函数中实现闪烁开启/关闭
{
delay500ms();
LED = 0xff;
key=KeyScan(0);//LED闪烁间隙再次扫描
KeyPros(key);
delay500ms();
}
LED = _crol_(0xfe,num%8);
}
}
void time0() interrupt 1
{
time++;
TH0=0Xff;
TL0=0X06; //250us
if(time==2000) //定时0.5s 叮响0.5秒,咚响0.5秒
{
time=0;
if(flag==0)
{
flag=~flag;
}
else
{
flag=0;
stop=1;
TR0=0; //关闭定时器0
}
}
if(flag==0)
{ //通过改变定时计数时间可以改变门铃的声音
ding++; //叮
if(ding==1)
{
ding=0;
beep=~beep;
}
}
else
{
dong++;
if(dong==2) //咚
{
dong=0;
beep=~beep;
}
}
}
遗憾的是:以我现在的代码能力,要实现小灯闪烁同时能丝滑移位有点困难,目前的解决办法只是在while循环中使用两次按键扫描,但延时的期间必然无法进行检测。以及铃声的实现是移植大佬的代码来的,暂时缺少自己动手摸索的过程。
任务二
理解参考代码,按下图所示修改矩阵键盘布局,每按一个按键,用1个数码管显示其键
名(0-F)。
可于Proteus作仿真图如下:
代码实现如下:
/*******************************************************************************
* 实 验 名 : 矩阵键盘控制1位数码管显示
* 使用的IO : 数码管使用P0,P2.2,P2.3,P2.4,键盘使用P1
* 实验效果 : 按矩阵键盘分别显示在数码管上面显示十六进制的0到F。
* 注 意 :
*******************************************************************************/
#include<reg51.h>
/*数码管段选及矩阵键盘管脚定义*/
#define GPIO_DIG P0
#define GPIO_KEY P1
/*数码管位选控制位*/
sbit A0=P2^2;
sbit A1=P2^3;
sbit A2=P2^4;
unsigned char code DIG_CODE[17]={
0x3f,0x06,0x5b,0x4f,0x66,0x6d,0x7d,0x07,
0x7f,0x6f,0x77,0x7c,0x39,0x5e,0x79,0x71};
//0、1、2、3、4、5、6、7、8、9、A、b、C、d、E、F的显示码
unsigned char KeyValue;//存放读取到的键值
unsigned char KeyState; //记录按键的状态,0没有,1有
void Delay10ms(); //延时10ms
void KeyDown(); //检测按键函数
void DigDisplay(); //动态显示函数
/*******************************************************************************
* 函 数 名 : main
* 函数功能 : 主函数
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void main(void)
{
KeyState=0;
while(1)
{
KeyDown();
A0 = 1;
A1 = 0;
A2 = 0;
GPIO_DIG=DIG_CODE[KeyValue];
KeyState = 0;
}
}
/*******************************************************************************
* 函 数 名 : KeyDown
* 函数功能 : 检测有按键按下并读取键值
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void KeyDown(void)
{
unsigned int a=0;
GPIO_KEY=0x0f;
if(GPIO_KEY!=0x0f)
{
Delay10ms();
a++;
a=0;
if(GPIO_KEY!=0x0f)
{
KeyState=1;//有按键按下
//测试列
GPIO_KEY=0X0F;
switch(GPIO_KEY)
{
case(0X07): KeyValue=0;break;
case(0X0b): KeyValue=1;break;
case(0X0d): KeyValue=2;break;
case(0X0e): KeyValue=3;break;
}
//测试行
GPIO_KEY=0XF0;
Delay10ms();
switch(GPIO_KEY)
{
case(0X70): KeyValue=KeyValue*4+0;break;
case(0Xb0): KeyValue=KeyValue*4+1;break;
case(0Xd0): KeyValue=KeyValue*4+2;break;
case(0Xe0): KeyValue=KeyValue*4+3;break;
}
// //测试列
// GPIO_KEY=0X0F;
// switch(GPIO_KEY)
// {
// case(0X07): KeyValue=0;break;
// case(0X0b): KeyValue=1;break;
// case(0X0d): KeyValue=2;break;
// case(0X0e): KeyValue=3;break;
// }
// //测试行
// GPIO_KEY=0XF0;
// Delay10ms();
// switch(GPIO_KEY)
// {
// case(0X70): KeyValue=KeyValue;break;
// case(0Xb0): KeyValue=KeyValue+4;break;
// case(0Xd0): KeyValue=KeyValue+8;break;
// case(0Xe0): KeyValue=KeyValue+12;break;
// }
while((a<500)&&(GPIO_KEY!=0xf0)) //按键松手检测
{
Delay10ms();
a++;
}
a=0;
}
}
}
/*******************************************************************************
* 函 数 名 : Delay10ms
* 函数功能 : 延时函数,延时10ms
* 输 入 : 无
* 输 出 : 无
*******************************************************************************/
void Delay10ms(void) //误差 0us
{
unsigned char a,b,c;
for(c=1;c>0;c--)
for(b=38;b>0;b--)
for(a=130;a>0;a--);
}
在理解矩阵键盘扫描的原理基础上,简单修改例程即可。