矩阵式键盘数字密码锁
矩阵式键盘数字密码锁设计
一、实验目的
- 掌握微机系统总线与各芯片管脚连接方法,提高接口扩展硬件电路的连接能力。
- 初步掌握键盘扫描,密码修改和计时报警程序的编写方法。
- 掌握通过矩阵式键盘扫描实现密码锁功能的设计思路和实现方法。
二、实验内容
根据设定好的密码,采用 4x4 矩阵键盘实现密码的输入功能。当密码输入正确之后,锁就打开(绿灯亮),10 秒之后,锁自动关闭(红灯亮);如果连续输入三次密码不正确,就锁定按键 5 秒钟,同时发出报警(黄灯闪),5 秒后,解除按键锁定,恢复密码输入。
三、系统功能与设计要求
1.基本功能要求
- 具有开锁、修改用户密码等基本的密码锁功能。
- 对于超过 3 次密码密码错误,锁定键盘 5 秒,系统报警。5 秒后解除锁定。
- 通过 LCD 字符液晶和 LED 指示灯(红,绿,黄)实时显示相关信息。
- 用户密码为 6 位数字,显示采用 “*” 号表示。
- 密码锁键盘设计合理,功能完善,方便用户使用。
2.相关提示
数字密码锁操作键盘自行设计。用户初始密码为“123456”,系统加电运行后,密码锁初始状态为常闭(红灯亮),用户可以选择开锁或修改密码:
如果选择开锁就按“Open”键,系统提示输入密码,输入用户密码+“#”键后,如果密码正确,就打开锁(绿灯亮),系统等待 10 秒,然后重新关闭密码锁,若密码错,提示重新输入,连续三次错误,提示警告词同时报警(黄灯闪),锁定键盘 5 秒,然后重新进入初始状态;
如果选择修改密码就按”Modify Secret”键,系统提示输入旧密码,输入旧密码+“#”键后,如果正确,系统提示输入新密码,输入新密码+“#”后,新密码起效,重新进入初始状态;如果旧密码错,不能修改密码,密码锁直接进入初始状态。
3.我们的附加实现
(1)增加管理员(Admin)功能
如果用户忘记密码可向管理员求助。管理员密码为 8 位数字(系统内预先已设定,我们默认为“12345678”),管理员按“Admin”键后,系统提示输入管理员密码,输入管理员密码+“#”键后,如果正确,系统自动恢复用户初始密码为“123456”;如果错误,程序停止运行,系统退出。
(2)增加组合键(Shift)功能
使系统的密码选择范围除数字外,另外增加a,b,c,d,e,f,g,h,i,j 等 10 个字母。数字直接输入,字母“Shift”+“字母”。
(3)增加输入清除功能(Delete)
在输入密码过程中,如果中途按下“Del”键,则清除前面的输入,开始重新输入密码。
*用户的密码输入部分可以实现“退格”,管理员的密码输入部分可以实现“清空”。
(4)增加密码输入错误提示功能
用户第一次输入密码错误会提醒“还有两次机会”,第二次输入密码错误会提醒“还有一次机会”。
(5)增加密码输入阈值
管理员输入密码错误次数大于三次,就会转回到最初的模式选择页面。
四、实验环境
1.硬件配置:
微机一台 (Pentium 4)
微机接口技术实验箱 一个
ISA – PCI 转接卡 一块
连接电缆 一条
万用表 一块
微机接口技术实验讲义 一本
连接导线
2.软件环境:
Windows XP/2000/Win 7 平台
Visual C++ 6.0 编译器
五、设计思路
- 延时时间通过计数/定时器(8253)来控制。8253的时钟源采用时钟信号发生器与分频电路提供,通过计算获得计数初值,按照设定的工作方式获得精确的延时时间。
- 密码锁键盘采用4x4矩阵键盘,液晶显示LCD和LED灯可以采用系统提供的相应模块。
- 控制通过8255可编程并行接口,如:PA口控制LCD液晶显示,PB口和PC口低四位控制键盘扫描,PC口高四位控制LED灯。
- 编程通过键盘扫描实时处理键盘输入数据,完成密码锁相关操作。
1.硬件设计连接图
F5区:键盘LED
D3区:8255
A3区:与总线的接口部分
F8区:蜂鸣器
F4区 :发光管
A1区:液晶屏
2.密码锁键盘设计图
3.运行流程图
4.硬件连线图
六、代码分析
实验过程中,我们参考了PC中的很多实验例程,如
先后完成了液晶显示器功能,并且借助字模转换器实现了符号点阵图的提取,接着完成了左右半屏的基本控制模块,实现了左右半屏文字分布的控制以及清屏功能,实现了字符串的显示;接着我们通过各端口号配置了8255,实现了延时子程序、判断是否有键被按下、读取键盘输入、shift键获取字母、灯的闪烁以及蜂鸣器发出警报声等功能。
下面是我们原创性较高的功能函数部分:
1.密码比较模块
u16 checkPwd()
{
int i = 0;
u8 buf[7];
// u16 inputPassword = 0;
// 重复扫描6次键盘输入
//for(i = 0;i < 6;)
while(i < 6)
{
flag1: buf[i] = key();
if(buf[i] == 0x0f) // 若按下了我们设定的微机键盘del键,则清屏后重新输入密码
{
i--;
if(i<0) i = 0;
LCD_INIT(); // 液晶初始化
showStars(i-1);
continue; // 进入下一循环
}
if(i < 6)
{
if(buf[i] == 0x0e)
buf[i] = shift();
else
buf[i] += (u8)'0';
LCD_INIT(); // 清屏,液晶初始化
if(i == 0)
show1Star(); // 按下1个键,显示1个*
else if(i == 1)
show2Stars(); // 按下2个键,显示2个*
else if(i == 2)
show3Stars(); // 按下3个键,显示3个*
else if(i == 3)
show4Stars(); // 按下4个键,显示4个*
else if(i == 4)
show5Stars(); // 按下5个键,显示5个*
else if(i == 5)
show6Stars(); // 按下6个键,显示6个*
}
i++;
}
while(1)
{
//依位比较输入密码和保存的密码,如果中间有一位不匹配,则返回0,完全匹配说明密码正确
u8 temp = key();
if(temp == 0x0f)
{
i--;
if(i<0) i = 0;
LCD_INIT(); // 液晶初始化
showStars(i-1);
goto flag1;
}
if(temp == 0x0a) {
for(i = 0; i < 6; i++)
{
if(buf[i] != pwd[i])
return 0;
}
return 1;
}
}
}
输入密码并与原密码进行比较,如果正确则返回1,否则返回0。这段代码的巧妙之处在于,我们实现了按位删除,扩大了对应的数组进行最后一位是删除还是#或者是其他字符的判断;另外巧妙使用了无条件跳转”goto“。
2.更改密码模块
void changePwd()
{
int i = 0;
u8 buf[7];
//u16 inputPassword = 0;
// 重复6次读取键盘输入
//for(i = 0;i < 6;)
while(i < 6)
{
flag2: buf[i] = key();
if(buf[i] == 0x0f) // 若按下了我们设定的微机键盘del键,则清屏后重新输入密码
{
i--;
if(i<0) i = 0;
LCD_INIT(); // 液晶初始化
showStars(i-1);
continue; // 进入下一循环
}
if(i < 6)
{
if(buf[i] == 0x0e) //如果按下shift键
buf[i] = shift(); //获取字母型变量,原先0-9的数字键现在变为a-j字母键
else //如果未按下shift键
buf[i] += (u8)'0'; //数字变量要转成unsigned char类型,即转成ASCII码型数字,方便显示
LCD_INIT();
switch(i)
{
case 0:
show1Star();
break;
case 1:
show2Stars();
break;
case 2:
show3Stars();
break;
case 3:
show4Stars();
break;
case 4:
show5Stars();
break;
case 5:
show6Stars();
break;
}
}
i++;
}
while(1)
{ u8 temp = key();
if(temp == 0x0f)
{
i--;
if(i<0) i = 0;
LCD_INIT(); // 液晶初始化
showStars(i-1);
goto flag2;
}
if(temp == 0x0a) { //依位次替换原先密码为新密码
for(i = 0; i < 6; i++)
pwd[i] = buf[i];
break;
}
}
}
//修改密码,如果修改密码成功,返回1,否则返回0
u16 modify()
{
if(checkPwd()) // 首先让用户重新输入密码并与原密码进行比较,如果正确则返回1,否则返回0
{ // 原密码输入正确
LCD_INIT(); // 清屏,液晶初始化
showNewPwd(); // 显示"新的密码"汉字
changePwd(); // 让用户输入新密码,实现更改密码
return 1;
}
else
return 0;
}
这段代码比较好的地方在于实现了对输入字母还是数字的分开处理。液晶初始化也完成了对应的刷新功能。
3.管理员功能
//管理员功能:如果管理员密码输入正确,则将开锁密码重置为123456,并返回1,管理员密码只允许数字出现。
u16 adminOption()
{
u8 i;
u16 buf[8];
u16 errornumber=0;
while(1)
{
u16 inputPassword = 0; // !!!!!!!!!初始化!!!!!!!!!
for(i = 0;i < 8;i++) // 管理员输入密码(扫描8次键盘)
{
buf[i] = key();
if(buf[i] == 0x0f) // 若按下了我们设定的微机键盘del键,则清屏后重新输入密码
{
i = -1; // 先置i=-1,以使continue之后i=0,可以继续按键盘6次
LCD_INIT(); // 液晶初始化
showReEnter(); // 显示"重新输入"汉字
delayTime(); // 延时一段时间
LCD_INIT(); // 液晶初始化
continue; // 进入下一循环
}
LCD_INIT();
switch(i)
{
case 0:
show1Star();
break;
case 1:
show2Stars();
break;
case 2:
show3Stars();
break;
case 3:
show4Stars();
break;
case 4:
show5Stars();
break;
case 5:
show6Stars();
break;
case 6:
show7Stars();
break;
case 7:
show8Stars();
break;
}
}
for(i = 0; i < 8; i++) // 计算管理员密码对应的十进制值
{
inputPassword = inputPassword*10 + buf[i];
}
while(1)
{
if(key() == 0x0a)
break;
}
if(inputPassword == adminPassword)
{
//password = 123456; // 重置密码为123456
for(i = 0; i < 6; i++)
{
pwd[i] = i + (u8)'1';
}
LCD_INIT(); // 液晶初始化
showCorrectPwd(); // 显示密码正确
delayTime();
break;
}
else
{
errornumber++;
if(errornumber==3)
return 0;
LCD_INIT(); // 液晶初始化
showReEnter(); // 显示重新输入
delayTime();
}
}
return 1;
}
这段代码比较巧妙的部分在于errornumber的设置,让管理员不再一直循环在”输入密码-输入错误-重新输入-输入错误“的循环中。另外,管理员密码是按照整数进行比较的,每位*10的循环也是一个亮点。
4.错误密码处理
u16 openLock()
{
u16 errorcount = 0;
u16 t;
while(1)
{
// 若输错3次,返回0
if(errorcount >= 3)
return 0;
// 若输错没到3次,液晶显示提示信息"再次输入"
if(errorcount != 0 && errorcount != 3)
{
LCD_INIT();
delayTime();
//showEnterAgain(); // 第2行显示"再次输入"
if(errorcount == 1)
{
showEntertwo();
}
//若输错2次,液晶显示提示信息“还有一次机会”
if(errorcount == 2)
{
showEnterone();
}
}
t = checkPwd(); // 输入密码并与原密码进行比较,如果正确则返回1,否则返回0
if(t)
return 1;
else
errorcount++; // 输入错误,error数加一
}
}
a键代表开锁,如果开锁成功则返回1,如果密码错误超过三次则返回0。这里的“还有一次机会”、‘’还有两次机会“是实现的亮点,我认为这与用户间提供了更好的交互功能。
5.主函数部分
void main()
{
outportb(CON_Addr, 0x81); // 写控制字1000 0001————A口、B口方式0,输出;C口低4位:输入
outportb(CON_Addr,0x0f); // 写控制字0000 1111————C口位操作:将PC7置位,控制蜂鸣器
blackl(); // 一加电应该保持8个LED灯全灭
while(1)
{
//液晶显示 请输入按键
LCD_INIT();
delayTime();
showInputButton();
tt = key();
// 输入a,尝试开锁
if(tt == 0x0a)
{
LCD_INIT();
delayTime();
showEnterPwd(); // 第2行显示"输入密码"
// 如果开锁成功则返回1,如果密码错误超过三次则返回0
if(openLock())
{
LCD_INIT(); // 液晶初始化
delayTime();
showCorrectPwd(); // 第2行显示"密码正确"
delayTime();
greenl(); // 绿灯亮10s
redl(); // 红灯亮5s
blackl(); // 全熄灭
continue; // 进入下一循环
}
// 三次密码错误,锁定系统
else
{
LCD_INIT();
delayTime();
showSystemLocked(); // 第2行显示"系统锁定"
yellowl(); // 黄灯闪烁5s
continue; // 进入下一循环
}
}
// 输入b,修改密码功能
else if(tt == 0x0b)
{
// 打印输入原密码
LCD_INIT();
showUsedPwd();
if(modify()) // 修改密码,如果修改密码成功,返回1,否则返回0
{
LCD_INIT();
showModifiedSuccess();
delayTime();
}
}
//输入c,进入管理员功能:将开锁密码重置为123456
else if(tt == 0x0c)
{
LCD_INIT();
showEnterPwd(); // 第2行显示"输入密码"
delayTime();
if(adminOption()) // adminOption()函数:如果管理员密码输入正确,将开锁密码重置为123456并返回1
{
LCD_INIT();
showResetSuccess(); // 显示"重置成功"
delayTime();
}
}
}
}
主函数部分很好地完成了分模块处理。
七、实验测试
1.按”#“键,进入输入用户密码部分;
2.输入”123456“按”#“键,显示”密码正确“,返回”输入按键“页面;
3.按”#“键,进入输入用户密码部分;
4.输入”123454“,密码错误,显示‘’还有两次机会“;
5.输入”12345a“,密码错误,显示 “还有一次机会”;
6.输入”12345b“,密码错误,显示"系统锁定",黄灯闪烁伴有蜂鸣器警告,返回”输入按键“页面;
7.输入”Modify“键,输入正确旧密码后可以修改新密码为”123457“,返回”输入按键“页面;
8.输入”Admin“键,三次以上输入错误密码会返回”输入按键“页面,按”del“可以进行退格操作;
9.输入”Admin“键,输入密码”12345678“,可初始化用户密码为”123456“;
10.按”#“键,进入输入用户密码部分,输入”123456“,显示”密码正确“。