H37 四位密码锁用 4×4 矩阵键盘输入、四位共阳数码管显示,位选走四只三极管驱动。初始密码 1234,有 9999 万能钥匙、B 键常开和 # 键逐位回删;错误锁 3 秒,期内乱按重新计时。作为密码锁系列第三篇,重点在两级比对链复用同一个循环变量。

01 设计任务
功能介绍:
使用51单片机设计一个四位电子密码锁,矩阵键盘输入、数码管显示、LED 指示锁状态,要求如下:
1、初始密码 1234:输入四位密码后按"D"确定,密码正确绿灯亮表示锁开,约 1 秒后自动熄灭锁闭;
2、万能钥匙 9999:输入"9999"按确定直接开锁——物业、管理员应急开门的标配功能,这一篇的重点就讲它怎么藏在比对逻辑里;
3、"B"键一键常开锁:按下就常开(万能开锁模式),要关锁按"复位"按键即可;
4、密码错误处理:数码管显示 Error、绿灯闪烁 6 下、蜂鸣器响 2 声,并锁定 3 秒禁止输入——更狠的是,惩罚期内乱按任何键会重新计时 3 秒并再响 2 声;
5、"#"键逐位回删:像计算器退格一样删掉最后输的一位,输错一位不用全部重来;
6、输入超时保护:输到一半 10 秒没动静,自动清空已输密码,蜂鸣器响 4 声提醒。
特别注意:这是密码锁系列第三篇。H23(8位)讲的是扫描和三重定时保护,H28(6位)讲的是首次输入自设密码和三错锁死,这一篇的差异化在"万能钥匙"——很多课设答辩会被老师问"管理员忘 了密码怎么办",这个 9999 就是标准答案。另外它还有一个前三篇都没有的逐位回删键,03 节细讲。



02 三极管位选驱动:为什么位选线上要加四个三极管
先看硬件,这一篇的显示电路和前两篇密码锁都不一样:
· 段码:P0 口接 4 位一体的共阳数码管段选,串 RP1 排阻限流——P0 开漏必须上拉,老规矩;
· 位选:P2.0~P2.3 没有直接接数码管,而是各经一个 1k 电阻接 NPN 三极管(Q2~Q5),由三极管去驱动位选线。
为什么多此一举?四位数码管 8 段全亮时,位选公共端要流过 8 段电流之和,单片机 IO 口一个引脚只能拉 20mA 左右,直接驱动会发烫甚至带不动。加一级三极管,IO 只出"开关信号",电流走三极管——IO 出逻辑、三极管扛负载,和 H30 里 PNP 驱动数码管是同一个思路,只是极性相反。
注意软件上的配合:位选是高电平选通(seg1=1; seg2=0; seg3=0; seg4=0;),段码是共阳低电平点亮。Dis_data() 里还有个 case 4:四路位选全关、段码全灭——多刷一轮"全灭"作消隐,防止上一位的残影串到下一位。
段码表也提前备好了字母:0xc6 是 C、0x86 是 E、0xaf 是 r——Error 三个字母就是靠这几个索引拼出来的(显示 16、14、17、17 四个下标:熄灭、E、r、r)。用"下标 16=全灭"混进段码表,消隐和 Error 显示就都不用写特殊分支,这是把资源表用活的小技巧。
03 矩阵键盘与逐位回删:输错一位不用重来
键盘是 4×4 矩阵接 P3 口,行线翻转扫描:先写 0xf0 检测有无按键,确认后交换行列方向读回键值,再和 keynum_tab[16] 查表对号。消抖借的是定时器余热——key_delay_start 置位后中断里 key_delay 开始计数,满 100ms 才认账,且 key_down 标志保证按住不连发,主循环一行 delay 都不用。
每按下一个数字键,先把这位数字写进密码记账数组、位置下标加一,下标超过 4 就封顶在 4;下标没到 4 时,显示缓冲整体左移一格,新数字填到最右一位。
新数字从右边挤进来,旧的依次左移——数码管上永远是"最近输入的 4 位",像计算器。第 5 位开始只记账不显示(position 封顶为 4),防止数组越界。
按一次"#"键,显示缓冲整体右移一格、最左一位补熄灭,同时密码记账数组的位置下标减一、把退掉的那一位清掉。
显示右移一格、左边补熄灭,记账数组回退一位清零。输错一位删一位,这是 H23/H28 都没做的体验细节——那两篇错了只能整个清零重来。课设答辩时演示一遍回删,比讲十句"我考虑了用户体验"都管用。
04 万能钥匙与惩罚叠加:比对链怎么设计
核心的"D 确定键"处理,是一条两级比对链:
按"D"键后先跑一轮逐位比较,四位都和用户密码一致就算通过;没通过就把同一个循环变量归零,再逐位比一遍是不是每位都是 9,两轮都跑满四位才走开锁分支。
第一轮比用户密码(1234),没过就复用同一个循环变量 i 再比一轮"是不是每一位都是 9"——是,i 就跑满,走开锁分支。不引入第二个标志变量,i < 4 这个判断本身当了三用:密码对 / 万能钥匙对 / 都不对。这招和 H28 的"循环变量终值当比对结论"一脉相承,但它示范了级联复用:第二级比对直接搭在第一级的结果上,代码短一半。
两个延伸点:
1. 扩展性白送:Password_table[8] 定义了 8 个元素({1,2,3,4,5,6,7,8}),只是比对时取前 4 位。想升级成 8 位密码锁?循环上限改一个数字、键盘多输四位就行,硬件一点不动。
2. 实际行为要说清:D 键开锁后绿灯亮 1 秒自动熄灭(LED_on_delay > 999 关灯),仿真图上的说明文字写"开锁几分钟后锁自动关闭"——以程序为准,Close_Lock_time 的 30 秒只是清内部状态,灯是 1 秒就灭的。
惩罚叠加是这篇比 H28 更"凶"的地方。错密码后 password_wrong_flag 置位,锁 3 秒。但看惩罚期内的处理:
锁定期内如果又检测到按键,就把蜂鸣器待响次数设为 2,同时把惩罚计时的计数器清零、重新开始计满 3 秒。
锁着不让输是对的,但每次乱按都把 3 秒惩罚重新计一遍、再警告两声——胡乱试锁的行为本身就是信号,惩罚要跟着延长。这个逻辑只有 4 行,防暴力破解的"味道"却全出来了,答辩讲安全性时直接引用。
05 非阻塞蜂鸣器:Buzz_on 的"响停节拍器"
蜂鸣器依旧不做 delay 硬等(响的时候数码管黑屏的坑,H23 讲过),这篇的 Buzz_on() 是个两步状态机:
· case 0:把要响的总段数算好(BUZZ_on_num * 2,响一段停一段交替),置入 temp_on_num,进入节拍;
· case 1:temp_on_num 是奇数就停、偶数就响,每 200ms 切换一次,走完清零归位。
于是"错误响 2 声"就是 BUZZ_on_num = 2,"超时响 4 声"就是 BUZZ_on_num = 4——次数变成了一个变量赋值,主循环该干嘛干嘛,蜂鸣器自己在节拍上走。这个模式从 H23 到 H29 已经反复出现,现在应该形成条件反射了:凡是要"响 N 声/闪 N 下",都做成带计数的非阻塞状态机。
绿灯闪烁同理:LED_Flashnum 计 6 次(亮灭各 3 轮),每 500ms 翻一次,奇数次全灭、偶数次恢复 Error 显示——闪烁和 Error 文字共用一个显示缓冲,轮流"借屏",结束后统一清空归零。
06 定时器心跳与多计数器并行
1ms 的定时器 T0(TMOD |= 0x01,方式 1)当下水道,中断里一口气养了六七个软计时器:按键消抖的 key_delay(100ms 判定、500ms 封顶防溢出)、蜂鸣器 200ms 节拍、LED 500ms 闪烁、1s 关灯、3s 惩罚、10s 输入超时、30s 状态清理。每个计数器都遵循同一套纪律:主循环里按需清零,中断里只加不减、加到上限封顶——if(++x > N) x = N 这句封顶是防止 uint 溢出回绕的标准写法。
不过源码里有一个没防住的:Enter_password_outtime_delay 超过 20000 后被赋值 200000,uint 最大才 65535,200000 直接回绕成 3392。好在主循环的超时判断(>9999 即 10 秒)会先一步清零,实际影响不大,但这是"封顶值也要算一遍能不能装下"的反面教材,07 节排错里单独列了。
07 排错清单
1、上电数码管黑屏或乱码:先查 P0 有没有接 RP1 排阻上拉——共阳段码靠 P0 灌电流,开漏不上拉什么都点不亮;
2、某一位比其他位暗:位选三极管的基极电阻(1k)或三极管本身问题,四位亮度不一致大概率是个别管子 β 值低;
3、按键没反应:Key_Port 是 P3,先确认矩阵键盘行列线没接反,再查 keynum_tab 键值表是否和自己的接线对应——这张表是按特定行列顺序写的,接线不同就要重排;
4、Error 一直闪不停:LED_Flashnum 减到 0 时才有清屏动作,若闪烁分支里误动了计数变量就会死循环闪烁;
5、输满 5 位后按键无效:position 封顶在 4 是正常设计,按"#"回删一位就能继续输;
6、万能钥匙开不了锁:确认输的是"9999"四个 9 再按 D——比对条件是"每一位都是 9",输 3 个 9 按回删再补也会通过,但输"1234"以外的杂码不行;
7、源码三处小瑕疵,改不改随意但要心里有数:① 闪烁结束清屏 for(i=0;i<8;i++) Display_table[i]=0; —— Display_table 只定义了 4 个元素,越界写会踩到相邻的 Save_password_table,改成 4 就干净了;② 中断里 Enter_password_outtime_delay 封顶值 200000 超出 uint 范围(见 06 节);③ Time0_init 里装的是 1000(1ms),注释却写"初始值10ms",看代码别看注释。
08 总结
密码锁三篇到这里凑齐了三种"锁的性格":H23 是规则全面型(改密/重置/三重定时),H28 是流程拟真型(首次输入自设密码、三错锁死),这一篇是管理员友好型(万能钥匙、常开模式、逐位回删)。三者程序骨架高度相似——矩阵键盘查表、左移/回删显示队列、非阻塞蜂鸣器、多计数器心跳——差别只在比对逻辑和惩罚策略这几行代码上。
下期开始进入通信类设计,敬请期待。
欢迎关注,持续更新!
09 文件日期修改
如果你想把文件修改为最新的日期,又不想新建工程,那么你可以参考下面的文章。
"如何不打开程序的情况下更改proteus和keil程序的日期?"
工具和仿真文件在一起,见下面的下载链接。
10 资料下载
全套资料包含 Keil 工程(C 语言源码 + hex 文件)、Proteus 仿真工程、设计报告、程序流程图、讲解视频,以及编译截图和仿真截图。下载后可直接打开运行,也可用于课程设计、毕业设计参考。
也可以关注公众号「单片机仿真」,回复 H37 获取下载入口。公众号内还有 A / T / H 全系列 400 多个单片机仿真实例,回复「目录」可查看完整清单。

