ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

AT89C51密码锁:矩阵键盘与AT24C02掉电存储设计

AT89C51密码锁:矩阵键盘与AT24C02掉电存储设计 简介这份面向单片机课程设计与电子制作入门的 Word 文档围绕 AT89C51 单片机电子密码锁展开适合电子信息、自动化等专业学生及嵌入式初学者参考。内容以 AT89C51 最小系统为核心串联 4×4 矩阵键盘、LCD1602 显示与报警模块讲解六位用户密码的设置与修改、超次报警、超次锁定及密码错误提示等功能的实现思路并涉及键盘扫描、密码校验流程、硬件调试与软件联调方法。文档包含绪论、总体方案、硬件设计、软件设计、调试、总结、参考文献及附录附录中给出硬件原理图与源程序便于读者对照理解电路连接与程序结构。整份资料为 1 个 doc 文件压缩包约 1.01MB目录结构清晰已有约 400 人学习下载可作为课程设计、毕业设计选题或单片机入门实践的参考资料。1. AT89C51密码锁的难点不在算法在4KB Flash和128字节RAM定下的约束宿舍楼下的储物柜原先挂机械锁钥匙丢过两次后来换成了柜门上贴一块小键盘加一个12V电磁锁键盘背后的主控就是AT89C51。这类题目年年出现在单片机课程设计和毕业设计的选题表里被默认当成简单题但真正动手的人很快会撞到三堵墙片内只有4KB Flash代码稍带一点格式化显示就吃紧只有128字节RAM一个6位密码缓冲区加一屏显示缓冲就得反复权衡AT89C51片内没有EEPROM掉电以后所有变量清零密码不能只放在数组里。这三条约束反过来决定了矩阵键盘怎么扫描、显示怎么分时复用、密码往哪里存也决定了这套设计能不能从PROTEUS仿真走到实物。适合正在做51单片机课设的人也适合想借这块经典芯片把IO口、定时器、软件模拟I2C一次摸清的工程师。2. AT89C51最小系统、4x4矩阵键盘与电磁锁驱动的硬件分配硬件这一层如果分配错了后面写多少软件都救不回来。AT89C51一共32根IO看起来宽裕但矩阵键盘要占8根、LCD1602要占3根控制线加8根数据线、电磁锁和蜂鸣器各占1根、AT24C02占2根算下来一点都不剩。所以先定引脚表再动烙铁。2.1 AT89C51最小系统里最容易接错的三只脚第一只是31脚EA/VPP。用片内Flash时它必须接VCC如果悬空或者误接GNDCPU会从外部程序存储器0000H取指现象是上电后完全不动很多人会误判成晶振没起振。第二只是9脚RST常见做法是10μF电解电容正极接VCC、负极接RST再并一只10k电阻到GND上电瞬间RST被拉高足够长的时间完成复位如果同时要按键复位按键并在电容两端即可。第三只是18、19脚XTAL1/XTAL2晶振两端各配一只30pF瓷片电容到地电容取值偏大启动会变慢偏小则容易在低温下不起振。晶振频率的取舍直接决定后面定时器和串口的参数晶振机器周期T0定时10ms初值9600bps时TH1适合场景12MHz1μs0xD8F0误差偏大只需定时和显示11.0592MHz1.085μs0xDBFF0xFD需要串口打印调试6MHz2μs0xEC78—低功耗、抗干扰优先如果计划用串口打印状态来排错直接选11.0592MHz省掉后面算波特率误差的麻烦纯做键盘和显示12MHz的计算更顺手。2.2 4x4矩阵键盘与LCD1602的引脚分配表键盘接P1口整字节行线走高、列线读低扫描时一次只拉低一行LCD1602的数据线接P0是因为P0可以整字节赋值省掉拆位的代码。具体分配如下功能端口电气注意矩阵键盘行线P1.0P1.3输出扫描时拉低矩阵键盘列线P1.4P1.7输入读之前必须写1LCD1602数据P0.0P0.7P0开漏必须加10k排阻上拉LCD1602 RS/RW/EP2.0/P2.1/P2.2直接驱动电磁锁驱动P2.3经三极管或光耦AT24C02 SCL/SDAP2.4/P2.54.7k上拉到VCC有源蜂鸣器P2.6三极管驱动加续流二极管串口调试P3.0/P3.1只用于开发阶段提示P0口内部没有上拉直接接LCD1602数据线时高电平建立不起来屏幕会显示乱码或者干脆黑屏这是新手最容易忽略的一处硬件错误。2.3 电磁锁与继电器驱动端口宏定义和三极管参数AT89C51单个IO的灌电流能力在1020mA量级拉电流更弱直接去带12V电磁锁一定烧口。常见做法是IO口经1k基极电阻驱动S8050集电极接继电器线圈线圈两端反向并一只1N4148做续流再用继电器触点切换电磁锁的12V回路。如果是35V的小型电磁锁也可以用AO3400这类MOS管直接低边驱动但续流二极管同样不能省。#include reg51.h #include intrins.h sbit LOCK P2^3; /* 电磁锁驱动高电平吸合 */ sbit BEEP P2^6; /* 有源蜂鸣器低电平响 */ sbit LCD_RS P2^0; sbit LCD_RW P2^1; sbit LCD_E P2^2; sbit SCL P2^4; /* AT24C02 时钟 */ sbit SDA P2^5; /* AT24C02 数据 */ #define LOCK_ON() (LOCK 1) #define LOCK_OFF() (LOCK 0) #define BEEP_ON() (BEEP 0) #define BEEP_OFF() (BEEP 1)用宏而不是直接写位操作是为了后面把驱动换成低电平有效时只改一行。P0口接LCD时记得在初始化里先写0xFF释放总线否则读忙标志会一直被拉低。硬件通电前先用万用表量一遍P0排阻到VCC的阻值确认是10k而不是1k阻值太小会把IO口灌穿。3. 定时器T0时基下的矩阵键盘扫描与LCD1602密码掩码显示键盘和显示如果都用延时函数硬等按键一多就会互相拖慢密码输入时的响应会明显发涩。把T0做成一个10ms的软时基消抖计数、显示刷新、开锁倒计时全部挂在这个时基上主循环只负责状态机思路会清爽很多。这也是蓝桥杯单片机赛题里矩阵键盘题的通用套路。3.1 用T0做10ms时基消抖、刷新与倒计时合一unsigned char KeyTick 0; /* 按键消抖计数 */ unsigned char LockTick 0; /* 开锁保持、错误锁定时长 */ void T0_Init(void) { TMOD 0xF0; TMOD | 0x01; /* T0 方式116 位定时器 */ TH0 0xDB; /* 11.0592MHz 下约 10ms */ TL0 0xFF; ET0 1; EA 1; TR0 1; } void T0_ISR(void) interrupt 1 { TH0 0xDB; /* 方式1不自动重装必须手动补 */ TL0 0xFF; KeyTick; if (LockTick) LockTick--; }方式1不会自动重装初值中断里忘了补TH0/TL0周期会立刻跑偏。11.0592MHz下一个机器周期约1.085μs10ms需要约9217个机器周期65536减9217等于56319也就是0xDBFF。如果换成12MHz初值改成0xD8F0代码里其他依赖时间的参数都要跟着重算所以频率一旦定下来就别来回改。3.2 行扫描法的C51实现与按键编码#define KEY_NONE 0xFF unsigned char KeyScan(void) { unsigned char row, col, code KEY_NONE; for (row 0; row 4; row) { P1 ~(0x01 row); /* 当前行拉低其余行和列线为高 */ _nop_(); _nop_(); /* 等电平稳定 */ col (P1 4) 0x0F; /* 读列线低电平有效 */ if (col ! 0x0F) { /* 有键按下 */ switch (col) { case 0x0E: code row * 4 0; break; case 0x0D: code row * 4 1; break; case 0x0B: code row * 4 2; break; case 0x07: code row * 4 3; break; default: break; } break; /* 单键优先扫到就退出 */ } } P1 0xFF; /* 恢复全高避免影响下一次扫描 */ return code; }P1赋值时把列线一起写成了高正好满足读输入前先写1的要求不需要额外一条语句。返回的编码015对应16个键09给数字10给清除键11给确认键13给改密键。扫描函数本身不消抖主循环里配合KeyTick判断连续两次读到同一个编码且间隔不小于20ms才认定有效这样能滤掉绝大多数抖动。矩阵键盘的按键排列如果和实物不一致只需改switch里的映射不用动扫描逻辑。3.3 LCD1602初始化指令与密码掩码显示void LCD_WriteCmd(unsigned char cmd) { LCD_RS 0; LCD_RW 0; P0 cmd; LCD_E 1; _nop_(); _nop_(); LCD_E 0; DelayMs(2); /* 普通指令约 40us留足余量 */ } void LCD_Init(void) { LCD_WriteCmd(0x38); /* 8位数据、2行显示、5x7点阵 */ LCD_WriteCmd(0x0C); /* 开显示、关光标、不闪烁 */ LCD_WriteCmd(0x06); /* 写入后地址自动加1屏幕不滚动 */ LCD_WriteCmd(0x01); /* 清屏 */ }0x38和0x0C这两条是必配的少一条屏幕要么只显示一行要么光标乱闪。显示密码时不要直接写数字用掩码代替输入第几位就在对应位置写一个*长度用变量控制退格时把该位置写空格覆盖。状态机的转移关系可以固化成一张表当前状态触发条件动作下一状态待机无按键显示INPUT PASSWORD待机输入数字键追加掩码星号输入输入确认键与存储密码比对开锁或报警开锁比对通过继电器吸合LockTick置位倒计时报警比对失败蜂鸣器响错误计数加一待机锁定错误满3次禁止键盘倒计时60s待机4. AT24C02存密码AT89C51软件模拟I2C与开锁时序控制4.1 为什么AT89C51必须外挂AT24C02AT89C51片内是Flash程序存储器不是数据存储器运行期无法按字节写入断电后RAM内容全部丢失。所以修改密码后下次上电仍然有效这个需求必须靠外部非易失存储实现。常见的几条路线差别不小方案存储介质优点代价AT89C51 AT24C02外置I2C EEPROM通用、擦写次数高占2个IO要软件模拟时序换用STC89C52RC片内Data Flash省一颗芯片和2个IO芯片和烧写方式都要换AT89C51 FM24C02外置I2C FRAM无写等待速度极快单价明显更高如果只是课设外挂AT24C02最能体现对I2C时序的理解如果已经把程序烧进STC单片机直接用片内Data Flash更省事但那是另一套寄存器操作。4.2 软件模拟I2C的起始、字节写与读校验void I2C_Delay(void) { _nop_(); _nop_(); _nop_(); _nop_(); } void I2C_Start(void) { SDA 1; SCL 1; I2C_Delay(); SDA 0; I2C_Delay(); /* SCL为高时SDA下降沿 起始 */ SCL 0; } void I2C_Stop(void) { SDA 0; SCL 1; I2C_Delay(); SDA 1; I2C_Delay(); /* SCL为高时SDA上升沿 停止 */ } void I2C_WriteByte(unsigned char dat) { unsigned char i; for (i 0; i 8; i) { SDA (dat 0x80) ? 1 : 0; dat 1; SCL 1; I2C_Delay(); SCL 0; I2C_Delay(); } SDA 1; /* 释放SDA读从机应答 */ SCL 1; I2C_Delay(); SCL 0; }AT24C02的写设备地址是0xA0读是0xA1。字节写完整流程是起始、发0xA0、发字节地址、发数据、停止然后必须等5ms让内部写周期结束紧接着去读会得到旧数据这是最典型的坑。密码建议存在0x000x05六个字节0x06放一个简单的校验和上电读取时先算一遍校验和不一致就回退到默认密码避免EEPROM被干扰后整机锁死。4.3 密码比对、错误锁定与继电器开锁时序比对不要用memcmpC51里没有标准库的字符串比较直接逐字节循环更可控。开锁动作由T0时基驱动参数集中成一张表改需求时只动这里参数建议取值说明密码长度6存AT24C02前6字节开锁保持3s定时器计时到点自动断开错误锁定60s锁定期间键盘完全不响应最大连错3次防止暴力试码按键消抖20ms两次采样一致才确认EEPROM写等待5ms写完必须延时的死区if (MatchPassword(input_buf)) { LOCK_ON(); LockTick 300; /* 10ms × 300 3s */ ErrCount 0; LCD_ShowStr(0, 1, UNLOCKED); } else { ErrCount; BEEP_ON(); if (ErrCount 3) { LockTick 6000; /* 10ms × 6000 60s */ LCD_ShowStr(0, 1, LOCKED); } }开锁保持时间和锁定时间都挂在同一个10ms时基上主循环里判断LockTick是否归零即可不需要再开一个定时器。继电器吸合和断开之间加一个几毫秒的间隔可以明显减小对电源的冲击。5. AT89C51密码锁从PROTEUS仿真到实物的联调顺序与三个抗干扰技巧5.1 先仿真后实物的分段验证顺序一次性把整机接好再上电出问题几乎没法定位。稳妥的顺序是分段点亮第一步只焊最小系统写一段P1.0每500ms翻转的程序用示波器或LED确认晶振和复位正常第二步接LCD1602只跑初始化和固定字符显示第三步加矩阵键盘用串口或LED把键码打出来核对第四步接AT24C02读写一个固定字节并回读校验最后才接继电器和电磁锁。PROTEUS里可以把电磁锁换成LED先跑通逻辑仿真通过再动实物能省掉大量反复拆焊的时间。5.2 串口调试打印与波特率参数void UART_Init(void) { SCON 0x50; /* 方式1允许接收 */ TMOD 0x0F; TMOD | 0x20; /* T1方式28位自动重装 */ TH1 0xFD; /* 11.0592MHz 下 9600bps */ TL1 0xFD; TR1 1; } void UART_SendByte(unsigned char c) { SBUF c; while (!TI); TI 0; }把每次按键编码、EEPROM读回值、当前状态机编号都通过串口打出来比盯着1602猜问题快得多。注意T1被占用后就不能再拿它做别的定时需要额外定时就再挂T0的软计数。5.3 三个能救命的工程技巧第一个是电磁锁反电动势。12V线圈断开的瞬间会产生几十伏的反压轻则复位重则打穿IO口。续流二极管要就近焊在线圈两端条件允许再加一级光耦把驱动地和单片机地分开这一条比任何软件滤波都管用。第二个是长按误判。有人按住数字键不放扫描函数会每轮都返回同一个编码密码被连续填入。解决办法是记录上一次的键码只有本次有键、上次无键时才认为是一次有效按下抬起沿清零状态。这个改动只有几行效果立竿见影。第三个是EEPROM写保护。改密后如果立刻断电AT24C02内部的5ms写周期还没结束数据可能只写了一半。写完延时后再回读一次比对不一致就重写最多重试三次。同时把默认密码写死在Flash里上电校验和不通过时自动恢复能避免整机变砖。实物上电前先用万用表确认P0排阻、SDA/SCL的4.7k上拉、继电器续流二极管三处都在位再插上电磁锁基本可以一次点亮。本文还有配套的精品资源点击获取
返回列表