51电子密码锁仿真(单片机课程设计)

一、核心功能: 

1、系统状态流程:

  1. 上电初始化 → 显示"code"

  2. 输入密码 → 显示横杠

  3. 验证密码 → 正确显示"yes"/错误显示"no"

  4. 修改密码 → 验证原密码后输入新密码

  5. 系统锁定 → 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让它调,否则只会浪费你自己的时间)

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值