一、核心功能:
1、系统状态流程:
-
上电初始化 → 显示"code"
-
输入密码 → 显示横杠
-
验证密码 → 正确显示"yes"/错误显示"no"
-
修改密码 → 验证原密码后输入新密码
-
系统锁定 → 3次错误后显示"lock"并报警
2、具体功能:
1. 密码验证功能
-
初始显示:数码管显示"code"
-
输入密码:通过矩阵键盘输入4位数字密码
-
输入显示:输入时显示横杠"-"代替实际数字
-
验证机制:输入完成后按"确定键"验证密码
-
反馈提示:
-
正确:显示"yes" + 蜂鸣器长响(100ms)
-
错误:显示"no" + 蜂鸣器长响(300ms)
-
2. 密码修改功能
-
进入条件:输入4位密码后按"修改键"
-
验证原密码:先验证输入的原密码是否正确
-
输入新密码:原密码正确后,输入4位新密码(直接显示数字)
-
保存密码:新密码自动保存到AT24C02 EEPROM
-
掉电保存:修改后的密码永久保存
3. 安全锁定功能
-
错误计数:记录连续错误次数
-
自动锁定:连续3次密码错误后系统锁定
-
锁定显示:显示"lock"
-
报警机制:锁定后蜂鸣器长时间报警
-
防暴力破解:锁定后键盘输入无效
4. 系统控制功能
-
清除功能:按"清除键"清空当前输入,恢复初始状态
-
状态管理:在密码验证模式和修改密码模式间切换
二、仿真实验(proteus)
总图:

矩阵键盘:(关于矩阵键盘为什么是这样排列,在代码后面有我的解答)

蜂鸣器:(注意这个蜂鸣器不响的问题所在)


其它元器件:
三极管:直接搜pnp即可
晶振:crystal
数码管:7SEG-MPX4-CC
三、仿真程序源码
#include <reg51.h>
#include <INTRINS.H>
// 蜂鸣器端口定义(PNP三极管,低电平导通)
sbit Buzzer = P3 ^ 7;
// I2C端口定义(AT24C02)
sbit I2C_SCL = P2 ^ 4; // CK接P2.4
sbit I2C_SDA = P2 ^ 5; // SDA接P2.5
// 数码管位选端口定义
sbit DIG1 = P2 ^ 0; // 第1位数码管
sbit DIG2 = P2 ^ 1; // 第2位数码管
sbit DIG3 = P2 ^ 2; // 第3位数码管
sbit DIG4 = P2 ^ 3; // 第4位数码管
// 矩阵键盘端口定义
sbit ROW1 = P1 ^ 7; // 第1行
sbit ROW2 = P1 ^ 6; // 第2行
sbit ROW3 = P1 ^ 5; // 第3行
sbit ROW4 = P1 ^ 4; // 第4行
sbit COL1 = P1 ^ 3; // 第1列
sbit COL2 = P1 ^ 2; // 第2列
sbit COL3 = P1 ^ 1; // 第3列
sbit COL4 = P1 ^ 0; // 第4列
// 数码管显示表(共阴极)- 添加了特殊字符
// 0-9, 不显示, c, o, d, e, y, s, n, 横杠
unsigned char NixieTable[] = {
0x3F, 0x06, 0x5B, 0x4F,
0x00,// 不显示
0x66, 0x6D, 0x7D, 0x07, 0x7F, 0x6F, // 0-9
0x00, // 11: 不显示
0x40, // 12: 横杠(输入密码时显示,逐个显示)
0x6E, // 13: y
0x79, // 14: e
0x6D, // 15: s
0x37, // 16: n
0x3F, // 17: o
0x5E, // 18: d
0x77, // 19: a
0x39 // 20: c
};
// AT24C02地址定义
#define AT24C02_ADDRESS 0xA0
// 全局变量
unsigned char InputPassword[4] = { 0 }; // 输入密码缓冲区(4位)
unsigned char StoredPassword[4] = { 1,1,1,1 }; // 存储的密码,默认1234
unsigned char InputIndex = 0; // 输入位置索引
unsigned char DisplayBuffer[4] = { 20,17,18,14 }; // 显示缓冲区,初始显示"code"
unsigned char ErrorCount = 0; // 错误计数
unsigned char Locked = 0;
void Delayms(unsigned int xms)
{
unsigned char i, j;
while (xms--)
{
i = 2;
j = 239;
do
{
while (--j);
} while (--i);
}
}
//蜂鸣器私有延时函数,延时500us
void Buzzer_Delay500us(void)
{
unsigned char data i;
_nop_();
i = 227;
while (--i);
}
//蜂鸣器发声(PNP三极管,低电平导通)
void Buzzer_Time(unsigned int ms)
{
unsigned int i;
// PNP三极管,低电平导通,高电平截止
for (i = 0; i < ms * 2; i++)
{
Buzzer = 0; // 低电平,导通发声
Buzzer_Delay500us();
Buzzer = 1; // 高电平,截止静音
Buzzer_Delay500us();
}
}
/**
* @brief 报警函数(连续报警)
* @param 无
* @retval 无
*/
void Buzzer_Alarm()
{
unsigned int i;
for (i = 0; i < 1000; i++) // 长时间报警
{
Buzzer = 0;
Buzzer_Delay500us();
Buzzer = 1;
Buzzer_Delay500us();
}
}
/**
* @brief I2C开始
* @param 无
* @retval 无
*/
void I2C_Start()
{
I2C_SDA = 1;
I2C_SCL = 1;
I2C_SDA = 0;
I2C_SCL = 0;
}
/**
* @brief I2C停止
* @param 无
* @retval 无
*/
void I2C_Stop()
{
I2C_SDA = 0;
I2C_SCL = 1;
I2C_SDA = 1;
}
/**
* @brief I2C发送一个字节
* @param Byte 要发送的字节
* @retval 无
*/
void I2C_SendByte(unsigned char Byte)
{
unsigned char i;
for (i = 0; i < 8; i++)
{
I2C_SDA = Byte & (0x80 >> i);
I2C_SCL = 1;
I2C_SCL = 0;
}
}
/**
* @brief I2C接收一个字节
* @param 无
* @retval 接收的一个字节
*/
unsigned char I2C_ReceiveByte()
{
unsigned char i, Byte = 0x00;
I2C_SDA = 1;
for (i = 0; i < 8; i++)
{
I2C_SCL = 1;
if (I2C_SDA) { Byte |= (0x80 >> i); }
I2C_SCL = 0;
}
return Byte;
}
/**
* @brief I2C发送应答
* @param AckBit 应答位,0为应答,1为非应答
* @retval 无
*/
void I2C_SendAck(unsigned char AckBit)
{
I2C_SDA = AckBit;
I2C_SCL = 1;
I2C_SCL = 0;
}
/**
* @brief I2C接收应答
* @param 无
* @retval 接收到的应答位 0为应答,1为非应答
*/
unsigned char I2C_ReceiveAck()
{
unsigned char AckBit;
I2C_SDA = 1;
I2C_SCL = 1;
AckBit = I2C_SDA;
I2C_SCL = 0;
return AckBit;
}
/**
* @brief AT24C02写入一个字节
* @param WordAddress 要写入字节的地址
* @param Data 要写入的数据
* @retval 无
*/
void AT24C02_WriteByte(unsigned char WordAddress, unsigned char Data)
{
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck();
I2C_SendByte(WordAddress);
I2C_ReceiveAck();
I2C_SendByte(Data);
I2C_ReceiveAck();
I2C_Stop();
Delayms(5); // 写入周期延时
}
/**
* @brief AT24C02读取一个字节
* @param WordAddress 要读出字节的地址
* @retval 读出的数据
*/
unsigned char AT24C02_ReadByte(unsigned char WordAddress)
{
unsigned char Data;
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS);
I2C_ReceiveAck();
I2C_SendByte(WordAddress);
I2C_ReceiveAck();
I2C_Start();
I2C_SendByte(AT24C02_ADDRESS | 0x01);
I2C_ReceiveAck();
Data = I2C_ReceiveByte();
I2C_SendAck(1);
I2C_Stop();
return Data;
}
/**
* @brief 从AT24C02读取密码
* @param 无
* @retval 无
*/
void ReadPasswordFromEEPROM()
{
unsigned char i;
for (i = 0; i < 4; i++)
{
StoredPassword[i] = AT24C02_ReadByte(i);
if (StoredPassword[i] > 9) // 如果读取的值无效,使用默认密码
StoredPassword[i] = i + 1;
}
}
/**
* @brief 保存密码到AT24C02
* @param 无
* @retval 无
*/
void SavePasswordToEEPROM()
{
unsigned char i;
for (i = 0; i < 4; i++)
{
AT24C02_WriteByte(i, StoredPassword[i]);
}
}
// 矩阵键盘扫描函数
unsigned char MatrixKey(unsigned char flag)
{
unsigned char Number;
// unsigned char temp; // 临时变量,减少重复IO读取
// 如果系统锁定,不响应任何按键
if (Locked)
return 0;
if (flag == 0)
return Number = 11;
// 第一行(ROW1对应P1^7)
P1 = 0XFF;
ROW1 = 0; // 用定义的ROW1替换P1^7
if (COL1 == 0) { Delayms(20); if (COL1 == 1) { Delayms(20); Number = 16; } } // 空
if (COL2 == 0) { Delayms(20); if (COL2 == 1) { Delayms(20); Number = 1; } }
if (COL3 == 0) { Delayms(20); if (COL3 == 1) { Delayms(20); Number = 2; } }
if (COL4 == 0) { Delayms(20); if (COL4 == 1) { Delayms(20); Number = 3; } }
// 第二行(ROW2对应P1^6)
P1 = 0XFF;
ROW2 = 0; // 用定义的ROW2替换P1^6
if (COL1 == 0) { Delayms(20); if (COL1 == 1) { Delayms(20); Number = 15; } } // 空
if (COL2 == 0) { Delayms(20); if (COL2 == 1) { Delayms(20); Number = 4; } }
if (COL3 == 0) { Delayms(20); if (COL3 == 1) { Delayms(20); Number = 5; } }
if (COL4 == 0) { Delayms(20); if (COL4 == 1) { Delayms(20); Number = 6; } }
// 第三行(ROW3对应P1^5)
P1 = 0XFF;
ROW3 = 0; // 用定义的ROW3替换P1^5
if (COL1 == 0) { Delayms(20); if (COL1 == 1) { Delayms(20); Number = 10; } } // (9)
if (COL2 == 0) { Delayms(20); if (COL2 == 1) { Delayms(20); Number = 7; } }
if (COL3 == 0) { Delayms(20); if (COL3 == 1) { Delayms(20); Number = 8; } }
if (COL4 == 0) { Delayms(20); if (COL4 == 1) { Delayms(20); Number = 9; } }
// 第四行(ROW4对应P1^4)
P1 = 0XFF;
ROW4 = 0; // 用定义的ROW4替换P1^4
if (COL1 == 0) { Delayms(20); if (COL1 == 1) { Delayms(20); Number = 13; } } // -
if (COL2 == 0) { Delayms(20); if (COL2 == 1) { Delayms(20); Number = 12; } } // 修改
if (COL3 == 0) { Delayms(20); if (COL3 == 1) { Delayms(20); Number = 0; } } // 0 // 10
if (COL4 == 0) { Delayms(20); if (COL4 == 1) { Delayms(20); Number = 11; } } // 确定
return Number;
}
/**
* @brief 数码管显示函数(共阴极)
* @param Location 位置(1-4)
* @param Number 数字(0-18)
*/
void Nixie(unsigned char Location, unsigned char Number)
{
// 先关闭所有数码管(位选高电平,共阴极)
DIG1 = 1;
DIG2 = 1;
DIG3 = 1;
DIG4 = 1;
// 位选 - 使用P2.0-P2.3控制4个数码管(共阴极,低电平选中)
switch (Location)
{
case 1: DIG1 = 0; break; // 第1位数码管
case 2: DIG2 = 0; break; // 第2位数码管
case 3: DIG3 = 0; break; // 第3位数码管
case 4: DIG4 = 0; break; // 第4位数码管
}
// 段选(共阴极,高电平点亮)
if (Number <= 20)
P0 = NixieTable[Number];
else
P0 = 0x00; // 不显示
Delayms(1);
P0 = 0x00; // 消隐
}
// 显示所有数码管
void ShowNixie()
{
unsigned char i;
for (i = 0; i < 4; i++)
{
Nixie(i + 1, DisplayBuffer[i]);
}
}
// 显示Init数码管
void ShowNixie_Init()
{
unsigned char i;
DisplayBuffer[0] = 11; DisplayBuffer[1] = 11; DisplayBuffer[2] = 11; DisplayBuffer[3] = 11;
for (i = 0; i < 4; i++)
{
Nixie(i + 1, DisplayBuffer[i]);
}
}
//* @brief 处理数字输入(密码验证模式)
//* @param num 输入的数字
void ProcessNumberInput(unsigned char num)
{
if (InputIndex < 4)
{
InputPassword[InputIndex] = num;
InputIndex++;
// 密码验证模式显示横杠
DisplayBuffer[InputIndex - 1] = 12; // 横杠
ShowNixie();
}
}
//* @brief 处理数字输入(修改密码模式)
//* @param num 输入的数字
void ProcessNumberInput_ChangeMode(unsigned char num)
{
if (InputIndex < 4)
{
InputPassword[InputIndex] = num;
InputIndex++;
// 修改密码模式显示数字
DisplayBuffer[InputIndex - 1] = num;
ShowNixie();
//Buzzer_Time(10); // 短提示音
}
}
//清除输入
void ClearInput()
{
unsigned char i;
for (i = 0; i < 4; i++)
InputPassword[i] = 0;
InputIndex = 0;
// 清除后显示"code"
DisplayBuffer[0] = 20; DisplayBuffer[1] = 17; DisplayBuffer[2] = 18; DisplayBuffer[3] = 14;
ShowNixie();
//Buzzer_Time(20); // 清除提示音
}
//验证密码
//无
//1-密码正确,0-密码错误
unsigned char VerifyPassword()
{
unsigned char i;
for (i = 0; i < 4; i++)
{
if (InputPassword[i] != StoredPassword[i])
return 0;
}
return 1;
}
//处理确定键(密码验证模式)
void ProcessEnter()
{
if (InputIndex == 4)
{
if (VerifyPassword())
{
// 密码正确
ErrorCount = 0; // 重置错误计数
//ShowNixie_Init();
Buzzer_Time(100); // 长提示音
// 显示正确提示 "yes"
DisplayBuffer[0] = 13; DisplayBuffer[1] = 14; DisplayBuffer[2] = 15; DisplayBuffer[3] = 15;
//ShowNixie();
//Delayms(10000);
}
else
{
// 密码错误
ErrorCount++;
//ShowNixie_Init();
Buzzer_Time(300); // 错误提示音
// 显示错误提示 "no"
DisplayBuffer[0] = 11; DisplayBuffer[1] = 11; DisplayBuffer[2] = 16; DisplayBuffer[3] = 17;
//ShowNixie();
//Delayms(10000);
// 检查是否达到3次错误
if (ErrorCount >= 3)
{
Locked = 1; // 锁定系统
// 显示锁定提示
DisplayBuffer[0] = 18; DisplayBuffer[1] = 14; DisplayBuffer[2] = 19; DisplayBuffer[3] = 18;
//ShowNixie();
//Delayms(10000);
Buzzer_Alarm(); // 报警
}
}
Delayms(1000);
//ClearInput();
}
else
{
// 输入位数不足
Buzzer_Time(50);
}
}
//处理修改密码
void ProcessChangePassword()
{
unsigned char i;
unsigned char newPassword[4] = { 0 };
unsigned char newIndex = 0;
unsigned char key;
unsigned char Matrix_flag = 1;
if (InputIndex == 4)
{
if (VerifyPassword())
{
// 原密码正确,等待输入新密码
ClearInput();
// 清空显示,准备输入新密码
for (i = 0; i < 4; i++)
DisplayBuffer[i] = 11;
// 等待输入新密码(修改密码模式)
while (newIndex < 4)
{
key = MatrixKey(Matrix_flag);
if (key >= 0 && key <= 10 && key != 4)
{
newPassword[newIndex] = key;
newIndex++;
// 更新显示(修改密码模式显示数字)
DisplayBuffer[newIndex - 1] = key;
//Buzzer_Time(10);
}
else if (key == 13) // 清除键
{
newIndex = 0;
for (i = 0; i < 4; i++)
newPassword[i] = 0;
// 清空显示
for (i = 0; i < 4; i++)
DisplayBuffer[i] = 10;
Buzzer_Time(20);
}
else if (key == 11) // 确定键(提前确认)
{
if (newIndex == 4)
break;
}
ShowNixie();
//Delayms(10000);
Delayms(10);
}
// 保存新密码
for (i = 0; i < 4; i++)
{
StoredPassword[i] = newPassword[i];
}
SavePasswordToEEPROM();
// 显示成功提示 "yes"
DisplayBuffer[0] = 13; DisplayBuffer[1] = 14; DisplayBuffer[2] = 15; DisplayBuffer[3] = 15;
//ShowNixie();
//Delayms(10000);
//Buzzer_Time(100);
//Delayms(1000);
}
else
{
// 原密码错误
Buzzer_Time(300);
// 显示错误提示 "no"
DisplayBuffer[0] = 11; DisplayBuffer[1] = 11; DisplayBuffer[2] = 16; DisplayBuffer[3] = 17;
//ShowNixie();
//Delayms(10000);
//Delayms(1000);
}
//ClearInput();
}
}
void main()
{
unsigned char Key;
unsigned char i;
unsigned char ChangeMode = 0; // 0:密码验证模式, 1:修改密码模式
unsigned char Matrix_flag = 0;
while (1)
{
Key = MatrixKey(Matrix_flag);
if (Key >= 0 && Key <= 10 && Key != 4)
{
if (ChangeMode)
ProcessNumberInput_ChangeMode(Key); // 修改密码模式
else
ProcessNumberInput(Key); // 密码验证模式
}
else if (Key == 13) // 清除键
{
ClearInput();
ChangeMode = 0; // 回到密码验证模式
}
else if (Key == 11) // 确定键
{
if (ChangeMode)
{
// 修改密码模式下的确定键处理
ProcessChangePassword();
ChangeMode = 0; // 回到密码验证模式
}
else
{
//密码验证模式下的确定键处理
ProcessEnter();
}
}
else if (Key == 12) // 修改密码键
{
if (InputIndex == 4) // 只有在输入了4位密码后才能进入修改模式
{
ChangeMode = 1;
}
//else
//{
//Buzzer_Time(50); // 提示音
//}
}
ShowNixie();
Matrix_flag = 1;
}
}
关于程序源码的一些问题:
在仿真实验当中,这个矩阵键盘总是用不好:
第一个问题:比如,按矩阵键盘的的某个按键,然后在数码管上显示该按键对应的数字,一上电,数码管就显示0000,你按其它键再显示其它数字,问题是为什么会显示0000啊,没有按0键啊,所以我在代码里弄了个Matrix_flag标志,以此来让数码管一上电什么都不显示,当你按对应数字的按键时,数码管显示相应数字;
第二个问题:一上电数码管就显示4444,可以看到矩阵键盘中4对应的按键往后移了一位,就是我对于这个问题的解决方法,它的问题是一上电,会显示第二行第二列的那个矩阵键盘的按键所对应的数字,所以我将该数字改成了什么都不显示。(在实际板子中,可以调节按键的相对位置,使得0~9排列有序且美观)
第三个问题:(这是个在代码中未修改的问题,但无大的影响。)数码管显示code,当你数入如:1234时,数码管是这样显示的:-ode、--de、---e、----。我的设想是:-、--、---、----。这是个可以修改的问题,但我没有再做了。
结语:这套51的是在B站上看的,在csdn找到了他的博客,当时先弄仿真,他的实物代码运行不了,就在这之上改了来(主要是他的仿真代码要钱)。最后没做实物,是器件不够的原因,后改弄了个简单的32电子密码锁(B站上的哥们的)。(最后说一句,AI给的代码要自己去看,自己去调,不要把问题抛给AI让它调,否则只会浪费你自己的时间)
1万+

被折叠的 条评论
为什么被折叠?



