news 2026/9/18 16:04:51

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
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,如果悬空或者误接GND,CPU会从外部程序存储器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.0~P1.3输出,扫描时拉低
矩阵键盘列线P1.4~P1.7输入,读之前必须写1
LCD1602数据P0.0~P0.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的灌电流能力在10~20mA量级,拉电流更弱,直接去带12V电磁锁一定烧口。常见做法是IO口经1k基极电阻驱动S8050,集电极接继电器线圈,线圈两端反向并一只1N4148做续流,再用继电器触点切换电磁锁的12V回路。如果是3~5V的小型电磁锁,也可以用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 方式1:16 位定时器 */ 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μs,10ms需要约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"的要求,不需要额外一条语句。返回的编码0~15对应16个键,0~9给数字,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必须外挂AT24C02

AT89C51片内是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让内部写周期结束,紧接着去读会得到旧数据,这是最典型的坑。密码建议存在0x00~0x05六个字节,0x06放一个简单的校验和,上电读取时先算一遍校验和,不一致就回退到默认密码,避免EEPROM被干扰后整机锁死。

4.3 密码比对、错误锁定与继电器开锁时序

比对不要用memcmp,C51里没有标准库的字符串比较,直接逐字节循环更可控。开锁动作由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方式2,8位自动重装 */ 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上拉、继电器续流二极管三处都在位,再插上电磁锁,基本可以一次点亮。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/18 16:04:25

电力系统优化:蒙特卡洛与Copula在可再生能源调度中的应用

1. 项目背景与核心价值这个项目本质上是在解决一个现代电力系统面临的复杂优化问题&#xff1a;如何在高比例可再生能源接入和电动汽车大规模普及的背景下&#xff0c;实现电网的经济高效运行。我去年参与过某省级电网的类似项目&#xff0c;深刻体会到这类问题的挑战性——你不…

作者头像 李华
网站建设 2026/9/18 15:57:26

跨应用电脑操作,TaoToken Key 在 MiMo Desktop 中如何审计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/18 15:50:12

Matlab Simulink变压器仿真建模与特性分析:从参数设置到效率曲线

简介&#xff1a;一份面向电气类专业课程设计/毕业设计场景的Matlab变压器仿真建模与特性分析完整设计报告。报告以Matlab电力系统模块为平台&#xff0c;给出三相双绕组变压器&#xff08;500kV/230kV、450MVA&#xff09;仿真模型搭建思路&#xff0c;涵盖变压器饱和特性、磁…

作者头像 李华