单片机课程设计——基于C51电子密码锁(源代码)

简介: 单片机课程设计——基于C51电子密码锁(源代码)


效果展示

我们先来看效果展示,公主王子请看视频:

课程设计——基于c51单片机的电子密码锁

实现任务

电子密码锁在当前市场运用较为广泛,其主要通过对外接密码输入来实现对系统或电路的控制,并进一步控制系统机械开关的操作。相关电子产品通过系统编程控制能够以电子密码锁的形式来实现系统的开关,尤其是对机械产品而言具有较高的安全性与可靠性。本文提出的电子密码锁系统设计主要以AT89C51单片机为基础,通过矩阵式键盘、LCD显示、蜂鸣器以及二极管等部件组成主系统。该系统设计编程方便、操作性强、具有较高的实用性,且耗能较低,在市场具有广阔的应用范围。

实现思路

本设计采用了AT89C51单片机电子锁系统,进行初始化设置,启动密码锁功能,密码输入显示屏幕采用了LCD1602液晶显示屏。系统控制指令通过单片机进行实现,单片机根据实际操作情况分配部件工作内容,程序操作性强、方便简洁。在用户输入密码过程中,单片机将会采用矩阵按键布局模式记录当前输入密码,并将该密码与系统设置密码进行比对,若密码匹配则开启密码锁,若密码错误则需要用户按下删除键重新输入密码。

拓展实现

当用户输入密码错误次数超过3次,电子密码锁将会自动锁定,需要用户切换管理员模式输入管理员密码才可解锁当前状态,并重新输入解锁密码。我们这里先实现基础的功能,其他的会慢慢更新。

准备工作

我们这里需要一个STC89C51单片机,一块LCD1602显示屏,数据线一根,开发板原理图一份。

A2单片机

我们这里使用的是普中A2单片机,如果你是其他板子,原理都一样,可能管脚不一样,需要自己根据管脚改代码。

我们这里使用的是普中自带的LCD1602显示屏,我们这里还需要一个开发板原理图,我们得知道矩阵按键的管脚。

原理图

首先是LCD1602的原理图:

然后是矩阵按键的原理图:

代码实现

我们接下来就是开始写代码,矩阵按键一共有16个按键,我们只要用12个按键,用左边的三列,我看网上有些教程是用前12个按键,我感觉好不习惯,还是常见的慢慢键盘舒服一些,类似于这种:

我们把左下角的按键第一位取消,右下角定义为确定。那我们接下来,先写键盘相关的代码。

MatrixKey.C

#include <REGX52.H>
#include "Delay.h"
 
 
unsigned char MatrixKey()
{
  unsigned char KeyNumber=0;
  
  P1=0xFF;
  P1_3=0;
  if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=1;}
  if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=4;}
  if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=7;}
  if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=11;}
  
  P1=0xFF;
  P1_2=0;
  if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=2;}
  if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=5;}
  if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=8;}
  if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=10;}
  
  P1=0xFF;
  P1_1=0;
  if(P1_7==0){Delay(20);while(P1_7==0);Delay(20);KeyNumber=3;}
  if(P1_6==0){Delay(20);while(P1_6==0);Delay(20);KeyNumber=6;}
  if(P1_5==0){Delay(20);while(P1_5==0);Delay(20);KeyNumber=9;}
  if(P1_4==0){Delay(20);while(P1_4==0);Delay(20);KeyNumber=12;}
  
  
  return KeyNumber;
}

我们这里采用的是先逐列扫描,然后是逐行扫描,这里的KeyNumber代表的意思如下(对应于上图的位置):

KeyNumber对应含义

KeyNumber 代表 KeyNumber 代表
1 1 7 7
2 2 8 8
3 3 9 9
4 4 10 0
5 5 11 取消(*)
6 6 12 确认(#)

KeyNumber.h

#ifndef __MATRIXKEY_H__
#define __MATRIXKEY_H__
 
unsigned char MatrixKey();
 
#endif

这里就是函数申明了,就不解释了,我们接下来,先写一个延时函数:

Delay.C

void Delay(unsigned int xms)
{
  unsigned char i, j;
  while(xms--)
  {
    i = 2;
    j = 239;
    do
    {
      while (--j);
    } while (--i);
  }
}
 

这个我更不解释了,如果这个都看不懂,就重新入门吧。

Delay.H

#ifndef __DELAY_H__
#define __DELAY_H__
 
void Delay(unsigned int xms);
 
#endif

我们这里还要用到LCD1602相关的代码,我这里直接给大家,大家可以不用去弄懂这个代码怎么写的,只要会用就行,这个只要是来初始化和显示内容的相关功能代码:

LCD1602.C

#include <REGX52.H>
 
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
 
//函数定义:
/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()
{
  unsigned char i, j;
 
  i = 2;
  j = 239;
  do
  {
    while (--j);
  } while (--i);
}
 
/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command)
{
  LCD_RS=0;
  LCD_RW=0;
  LCD_DataPort=Command;
  LCD_EN=1;
  LCD_Delay();
  LCD_EN=0;
  LCD_Delay();
}
 
/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
  LCD_RS=1;
  LCD_RW=0;
  LCD_DataPort=Data;
  LCD_EN=1;
  LCD_Delay();
  LCD_EN=0;
  LCD_Delay();
}
 
/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
  if(Line==1)
  {
    LCD_WriteCommand(0x80|(Column-1));
  }
  else if(Line==2)
  {
    LCD_WriteCommand(0x80|(Column-1+0x40));
  }
}
 
/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init()
{
  LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
  LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
  LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
  LCD_WriteCommand(0x01);//光标复位,清屏
}
 
/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
  LCD_SetCursor(Line,Column);
  LCD_WriteData(Char);
}
 
/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
  unsigned char i;
  LCD_SetCursor(Line,Column);
  for(i=0;String[i]!='\0';i++)
  {
    LCD_WriteData(String[i]);
  }
}
 
/**
  * @brief  返回值=X的Y次方
  */
int LCD_Pow(int X,int Y)
{
  unsigned char i;
  int Result=1;
  for(i=0;i<Y;i++)
  {
    Result*=X;
  }
  return Result;
}
 
/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
  unsigned char i;
  LCD_SetCursor(Line,Column);
  for(i=Length;i>0;i--)
  {
    LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
  }
}
 
/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
  unsigned char i;
  unsigned int Number1;
  LCD_SetCursor(Line,Column);
  if(Number>=0)
  {
    LCD_WriteData('+');
    Number1=Number;
  }
  else
  {
    LCD_WriteData('-');
    Number1=-Number;
  }
  for(i=Length;i>0;i--)
  {
    LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
  }
}
 
/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFF
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
  unsigned char i,SingleNumber;
  LCD_SetCursor(Line,Column);
  for(i=Length;i>0;i--)
  {
    SingleNumber=Number/LCD_Pow(16,i-1)%16;
    if(SingleNumber<10)
    {
      LCD_WriteData(SingleNumber+'0');
    }
    else
    {
      LCD_WriteData(SingleNumber-10+'A');
    }
  }
}
 
/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
  unsigned char i;
  LCD_SetCursor(Line,Column);
  for(i=Length;i>0;i--)
  {
    LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
  }
}
#include <REGX52.H>
 
//引脚配置:
sbit LCD_RS=P2^6;
sbit LCD_RW=P2^5;
sbit LCD_EN=P2^7;
#define LCD_DataPort P0
 
//函数定义:
/**
  * @brief  LCD1602延时函数,12MHz调用可延时1ms
  * @param  无
  * @retval 无
  */
void LCD_Delay()
{
  unsigned char i, j;
 
  i = 2;
  j = 239;
  do
  {
    while (--j);
  } while (--i);
}
 
/**
  * @brief  LCD1602写命令
  * @param  Command 要写入的命令
  * @retval 无
  */
void LCD_WriteCommand(unsigned char Command)
{
  LCD_RS=0;
  LCD_RW=0;
  LCD_DataPort=Command;
  LCD_EN=1;
  LCD_Delay();
  LCD_EN=0;
  LCD_Delay();
}
 
/**
  * @brief  LCD1602写数据
  * @param  Data 要写入的数据
  * @retval 无
  */
void LCD_WriteData(unsigned char Data)
{
  LCD_RS=1;
  LCD_RW=0;
  LCD_DataPort=Data;
  LCD_EN=1;
  LCD_Delay();
  LCD_EN=0;
  LCD_Delay();
}
 
/**
  * @brief  LCD1602设置光标位置
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @retval 无
  */
void LCD_SetCursor(unsigned char Line,unsigned char Column)
{
  if(Line==1)
  {
    LCD_WriteCommand(0x80|(Column-1));
  }
  else if(Line==2)
  {
    LCD_WriteCommand(0x80|(Column-1+0x40));
  }
}
 
/**
  * @brief  LCD1602初始化函数
  * @param  无
  * @retval 无
  */
void LCD_Init()
{
  LCD_WriteCommand(0x38);//八位数据接口,两行显示,5*7点阵
  LCD_WriteCommand(0x0c);//显示开,光标关,闪烁关
  LCD_WriteCommand(0x06);//数据读写操作后,光标自动加一,画面不动
  LCD_WriteCommand(0x01);//光标复位,清屏
}
 
/**
  * @brief  在LCD1602指定位置上显示一个字符
  * @param  Line 行位置,范围:1~2
  * @param  Column 列位置,范围:1~16
  * @param  Char 要显示的字符
  * @retval 无
  */
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char)
{
  LCD_SetCursor(Line,Column);
  LCD_WriteData(Char);
}
 
/**
  * @brief  在LCD1602指定位置开始显示所给字符串
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  String 要显示的字符串
  * @retval 无
  */
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String)
{
  unsigned char i;
  LCD_SetCursor(Line,Column);
  for(i=0;String[i]!='\0';i++)
  {
    LCD_WriteData(String[i]);
  }
}
 
/**
  * @brief  返回值=X的Y次方
  */
int LCD_Pow(int X,int Y)
{
  unsigned char i;
  int Result=1;
  for(i=0;i<Y;i++)
  {
    Result*=X;
  }
  return Result;
}
 
/**
  * @brief  在LCD1602指定位置开始显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~65535
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
  unsigned char i;
  LCD_SetCursor(Line,Column);
  for(i=Length;i>0;i--)
  {
    LCD_WriteData(Number/LCD_Pow(10,i-1)%10+'0');
  }
}
 
/**
  * @brief  在LCD1602指定位置开始以有符号十进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:-32768~32767
  * @param  Length 要显示数字的长度,范围:1~5
  * @retval 无
  */
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length)
{
  unsigned char i;
  unsigned int Number1;
  LCD_SetCursor(Line,Column);
  if(Number>=0)
  {
    LCD_WriteData('+');
    Number1=Number;
  }
  else
  {
    LCD_WriteData('-');
    Number1=-Number;
  }
  for(i=Length;i>0;i--)
  {
    LCD_WriteData(Number1/LCD_Pow(10,i-1)%10+'0');
  }
}
 
/**
  * @brief  在LCD1602指定位置开始以十六进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~0xFFFF
  * @param  Length 要显示数字的长度,范围:1~4
  * @retval 无
  */
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
  unsigned char i,SingleNumber;
  LCD_SetCursor(Line,Column);
  for(i=Length;i>0;i--)
  {
    SingleNumber=Number/LCD_Pow(16,i-1)%16;
    if(SingleNumber<10)
    {
      LCD_WriteData(SingleNumber+'0');
    }
    else
    {
      LCD_WriteData(SingleNumber-10+'A');
    }
  }
}
 
/**
  * @brief  在LCD1602指定位置开始以二进制显示所给数字
  * @param  Line 起始行位置,范围:1~2
  * @param  Column 起始列位置,范围:1~16
  * @param  Number 要显示的数字,范围:0~1111 1111 1111 1111
  * @param  Length 要显示数字的长度,范围:1~16
  * @retval 无
  */
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length)
{
  unsigned char i;
  LCD_SetCursor(Line,Column);
  for(i=Length;i>0;i--)
  {
    LCD_WriteData(Number/LCD_Pow(2,i-1)%2+'0');
  }
}

LCD1602.H

#ifndef __LCD1602_H__
#define __LCD1602_H__
 
//用户调用函数:
void LCD_Init();
void LCD_ShowChar(unsigned char Line,unsigned char Column,char Char);
void LCD_ShowString(unsigned char Line,unsigned char Column,char *String);
void LCD_ShowNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowSignedNum(unsigned char Line,unsigned char Column,int Number,unsigned char Length);
void LCD_ShowHexNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
void LCD_ShowBinNum(unsigned char Line,unsigned char Column,unsigned int Number,unsigned char Length);
 
#endif

main.c

我们前面铺垫了很多,接下来,我们就可以写我们的主函数了,我们这里实现的是4位密码锁,当数字1~0被按下,执行下面的代码:

if(KeyNum)            //如果有按键按下
    {
      if(KeyNum==1||KeyNum==2||KeyNum==3||KeyNum==4||KeyNum==5||KeyNum==6||KeyNum==7||KeyNum==8||KeyNum==9||KeyNum==10)
      {
        if (count<4)
        {
        Password*=10;
        Password += KeyNum%10;
        }
        count++;
        
      }
      LCD_ShowNum(2,1,Password,4);  //LCD显示键码

如果我们发下密码按错了,想取消,我们可以加这样的功能:

        if (KeyNum ==11)//取消
        {
          Password =0;
          count =0;
          LCD_ShowNum(2,1,Password,4);  //更新LCD显示键码
        }

我这里设置的初始密码是8888,大家也可以设置其他的,大家还可以根据我的代码实现修改密码的功能,我这里基础的功能是密码正确屏幕显示OK,密码错误显示ERROR:

        if(Password == 8888)
        {
          LCD_ShowString(1,14,"OK");
          Password =0;
          count =0;
          LCD_ShowNum(2,1,Password,4);  //更新LCD显示键码
        }
        else
        {
          LCD_ShowString(1,11,"ERROR");
          Password =0;
          count =0;
          LCD_ShowNum(2,1,Password,4);  //更新LCD显示键码
        }       
      }

到这里,我们的所有功能都完成了,下面是完整的主函数代码:

#include <REGX52.H>
#include "Delay.h"    //包含Delay头文件
#include "LCD1602.h"  //包含LCD1602头文件
#include "MatrixKey.h"  //包含矩阵键盘头文件
 
unsigned char KeyNum;
unsigned int Password,count;
 
void main()
{
  LCD_Init();             //LCD初始化
  LCD_ShowString(1,1,"PASSWORD:");  //LCD显示字符串
  while(1)
  {
    KeyNum=MatrixKey();       //获取矩阵键盘键码
    if(KeyNum)            //如果有按键按下
    {
      if(KeyNum==1||KeyNum==2||KeyNum==3||KeyNum==4||KeyNum==5||KeyNum==6||KeyNum==7||KeyNum==8||KeyNum==9||KeyNum==10)
      {
        if (count<4)
        {
        Password*=10;
        Password += KeyNum%10;
        }
        count++;
        
      }
      LCD_ShowNum(2,1,Password,4);  //LCD显示键码
      
      
      if (KeyNum ==12)//确认
      {
        LCD_ShowString(1,11,"     ");
        if(Password == 8888)
        {
          LCD_ShowString(1,14,"OK");
          Password =0;
          count =0;
          LCD_ShowNum(2,1,Password,4);  //更新LCD显示键码
        }
        else
        {
          LCD_ShowString(1,11,"ERROR");
          Password =0;
          count =0;
          LCD_ShowNum(2,1,Password,4);  //更新LCD显示键码
        }       
      }
      if (KeyNum ==11)//取消
        {
          Password =0;
          count =0;
          LCD_ShowNum(2,1,Password,4);  //更新LCD显示键码
        }
    }
  }
}

总结

我们使用的STC89C52单片机,我们用LCD1602作为显示屏,实现了电子密码锁的功能,大家还可以修改密码,或者密码错误三次以上,蜂鸣器发出警报,或者密码正确,蜂鸣器滴一声,LED灯闪烁一下,等等,可以都可以在此基础上升级。希望我的文章对你有帮助。


相关文章
|
5月前
|
存储 数据安全/隐私保护
基于51单片机的6位密码锁设计
该文档描述了一个基于AT89C52单片机的多功能电子密码锁系统设计,具备6位密码设置与修改功能,输入错误三次会锁定,并在LCD屏上以*号隐藏真实密码。系统包括矩阵键盘、LCD显示、AT24C02存储及报警系统,当密码错误时,会有蜂鸣器报警和LED灯闪烁。此外,还提供了密码重置(通过管理员密码131420)和输入错误提示。文中附有Proteus仿真及原理图,展示了开锁、重设密码和错误输入时的响应。
88 0
|
3月前
单片机课程设计——PWM电机调速
单片机课程设计——PWM电机调速
|
3月前
|
传感器 存储 程序员
《单片机原理与应用及C51编程技术》期末复习笔记
《单片机原理与应用及C51编程技术》期末复习笔记
|
5月前
|
存储 移动开发
80C51单片机----数据传送类指令
80C51单片机----数据传送类指令
69 2
|
5月前
|
存储 移动开发 程序员
80C51单片机的七种寻址方式
80C51单片机的七种寻址方式
56 1
|
5月前
|
数据安全/隐私保护
【51单片机】初学者必学的一个矩阵键盘基本项目——(矩阵键盘密码锁)(8)
【51单片机】初学者必学的一个矩阵键盘基本项目——(矩阵键盘密码锁)(8)
|
5月前
|
编译器 C语言 开发者
单片机原理与应用:探索微型计算机世界
单片机原理与应用:探索微型计算机世界
45 1
|
5月前
|
数据采集 数据处理 C语言
单片机:探索其原理、应用与编程实践
单片机:探索其原理、应用与编程实践
66 1
|
5月前
|
物联网
STC51单片机-实验开发装置仿真-物联网应用系统设计
STC51单片机-实验开发装置仿真-物联网应用系统设计
112 0
|
5月前
|
物联网
STC51单片机-控制LED闪亮的仿真-物联网应用系统设计
STC51单片机-控制LED闪亮的仿真-物联网应用系统设计
71 0

热门文章

最新文章