基于FPGA的电子密码锁设计论文(含视频代码仿真)

写在前面:本设计仅供学习参考,不保证正确,免费分享,恳请关注一下

源码来自大佬:http://t.csdn.cn/Oxtcg 稍作改动

实物演示视频:基于FPGA的电子密码锁,Verilog HDL语言实现_哔哩哔哩_bilibili

基于FPGA的电子密码锁设计

摘要

基于FPGA的电子密码锁已经是现代生活中经常用到的工具之一,用于各类保险柜、房门、防盗门等等。用电子密码锁代替传统的机械式密码锁,克服了机械式密码锁密码量少、安全性能差的缺点。由于采用的是可编程逻辑器件FPGA,使得系统有相当大的灵活性,随时可以进行硬件升级、扩展。而且由于硬件可升级,还可随时增加密码位数或增加新的功能,使得密码锁有更高的安全性、可靠性和方便性。因此对于基于FPGA的电子密码锁的探讨和研究具有较大的理论意义和实践意义。

本次基于现场可编程门阵列FPGA的电子密码锁设计方法。课题主要解决系统硬件和软件两方面的问题。硬件方面要解决FPGA可编程器件与其外围电路的接口设计的问题;软件方面主要问题是利用Verilog HDL语言完成基于FPGA的电子密码锁的编程问题。除此之外,程序还要完成基本的密码开锁功能。本设计是由FPGA可编程逻辑器件编程实现的控制电路,具体有按键指示、密码有效指示、控制开锁、控制复位等功能。它具有安全可靠、连接方便、简单易用、结构紧凑、系统可扩展性好等特点。

关键字:电子密码锁;现场可编程门阵列;Verilog;仿真验证

目录:

一、课程设计任务及要求

1.1设计要求

1.2 设计报告要求

二、总体电路设计

2.1总体设计方案

2.2设计方案比较

2.3总体设计流程图

三、模块设计

3.1初始化模块

3.2显示模块

3.3分频模块

3.4消抖模块

3.5密码处理模块

3.6蜂鸣器模块

四、管脚绑定

4.1管脚绑定表

4.2管脚绑定说明

五、仿真及FPGA开发板调试

5.1仿真调试

5.2开发板整体调试

六、 设计总结与心得体会

6.1总结与心得

6.2成本分析

七、参考文献

八、附录

附录 1 RTL 代码:

附录 2 Testbench 代码

一、课程设计任务及要求

1.1设计要求

(1)设计一个密码锁的控制电路,当输入正确代码时,输出开锁信号以推动执行机构工作,用红灯亮、绿灯熄灭表示关锁,用绿灯亮、红灯熄灭表示开锁。

(2)在锁的控制电路中储存一个可以修改的4位代码,当开锁按钮开关(可设置成6位至8位,其中实际有效为4位,其余为虚设)的输入代码等于储存代码时,开锁。

(3)从第一个按钮触动后的5秒内若未将锁打开,则电路自动复位并进入自锁状态,使之无法再打开,并由扬声器发出持续20秒的报警信号。

1.2 设计报告要求

(1)分析系统的工作原理。

(2)画出顶层原理图,写出顶层文件源程序。

(3)写出各功能模块的源程序。

(4)仿真各功能模块,要求有仿真波形。

(5)书写实验报告应结构合理,层次分明。


二、总体电路设计

2.1总体设计方案

       采用可编程逻辑器件FPGA,成本较低,性能优秀,可以使得系统有相当大的灵活性,随时可以进行硬件升级、扩展。而且由于硬件可升级,还可随时增加密码位数或增加新的功能,使得密码锁有更高的安全性、可靠性和方便性。

       在电路设计方面,我们可以用同bit位宽的寄存器把密码寄存起来,在按键时不断将现在按键的值与密码的寄存器对比,当所有对应位都相同时,蜂鸣器响,可以用一个标志位表示所有对应位相同,蜂鸣器工作的条件就是标志位给出的。

       由于需要按键手动修改密码,那么液晶屏显示就有两种状态,一种是修改密码,一种是输入密码。我通过增加一个按键来控制这两种状态,当按下这个按键时进入修改密码阶段,当再次按下时进入输入密码阶段。

       并且由于开关的波动,可能会产生抖动导致密码输入错误,为此设计了消抖模块,对需要用到的按键进行消抖。

2.2设计方案比较

FPGA设计密码锁相比其他方案的好处包括以下几个方面:

(1)灵活性:FPGA可以灵活地实现各种密码锁的功能和算法,可以根据用户需求、安全要求和硬件资源的情况进行优化和定制,使其更加适合具体的应用场景。

(2)可编程性:FPGA可以被编程多次,可以随时升级和修改密码锁的算法,以应对新的安全威胁和攻击手段。

(3) 高性能:FPGA可以实现并行计算,具有高性能和低延迟的特点,能够快速响应和处理密码验证请求,降低用户等待的时间。

(4)高安全性:FPGA设计密码锁可以实现硬件加密和安全认证,防止密码泄露和攻击,提高系统的安全性和可靠性。

(5)低功耗:FPGA设计密码锁可以利用其优异的低功耗性能,降低系统的能耗和运行成本。

除了FPGA以外,还有不少方法能实现电子密码锁功能,可以利用非编程电子元件电路、51单片机,STM32等实现电子密码锁功能,但是存在很多缺点和问题,不能便捷的修改密码,安全性低,易损坏。

2.3总体设计流程图


三、模块设计

3.1初始化模块

初始化主要是针对FPGA内部有记忆的单元,例如寄存器、BLOCK RAM等,而对于无记忆的单元,例如硬件连线,没有必要也无法对它们赋初值。而对于支持赋初值的FPGA芯片,我们可以利用这一特性让FPGA在上电后就处于一个可被确定的状态。除此以外,赋初值对于仿真也有类似的影响。

运用initial语句给变量赋初值,初始密码为“00000000”,d48为0的ASCLL值,初始化完以后,就显示空字符让初始化显示的地址。按键按下后,数字会加一,9加一变为10,10以后跳会0,所以数字变量设为9,ASCLL码值为57。同理,初始化字母为小写z,按键按下后,字母会加一,z加一变为a,所以字母变量设为9,ASCLL码值为122。

3.2显示模块

本次设计中在FPGA开发板外接了一个LCD1602,相比于数码管,LCD1602显示更加直观,能显示大小写字母和数字等。可以显示16列2行的字符,LCD1602显示的原理 跟 8* 8的点阵显示原理很相似,1602显示字符的原理也是8*8的点组成的。通过点亮相应的点就可以,但与点阵不同的是,因为其内部自带了字库,LCD1602不需要取字模,通过对照ASCLL码表就可以显示相应的数字:例如还是要显示数字“0”的话,我们只需要往DB0-DB7写入数值48,就可显示数字“0”。

(1)VSS接电源地。

(2)VDD接+5V。

(3)VO是液晶显示的偏压信号,可接电位器,改变背光,电压过高和过低会看不清字符。

(4)RS是命令/数据选择引脚,接FPGA的一个I/O,当RS为低电平时,选择命令;当RS为高电平时,选择数据。

(5)RW是读/写选择引脚,接FPGA一个I/O,当RW为低电平时,向LCD1602写入命令或数据;当RW为高电平时,从LCD1602读取状态或数据。

共有四种状态:

输入RS=0,RW=1,E=高脉冲。输出:D0—D7为状态字。(读命令)

输入RS=1,RW=1,E=高脉冲。输出:D0—D7为数据。 (读数据)

输入RS=0,RW=0,E=高脉冲。输出:无。 (写命令)

输入RS=1,RW=0,E=高脉冲。输出:无。(写数据)

(6)E,执行的使能引脚,接FPGA的一个I/O。

(7)D0—D7,并行数据输入/输出引脚,接FPGA的的8个I/O口。

(8)A背光正极,可接到VCC。

(9)K背光负极,接GND。

LCD控制使能端,rs=0时为写命令,命令值为H38,设置8位格式,2行,5*7,LCD的使能端E为1(en_sel=1);rs=1时为写数据,en为LCD的E引脚。rw=0时为写数据,rw=1为读数据(读数据没有用到,所以rw始终为0)。同时需延时一段时间10MS,LCD速度较慢必须延时。LCD的使能端E为0(en_sel=0),下降沿的时候把数据读进去,lcd_flag=0表示LCD正在写入一个数据,lcd_flag为1时表示数据写入完成,可以写入下一个数据。

3.3分频模块

由于开发板提供的时钟脉冲频率20MHz和50MHz,远高于各模块工作所需要的时钟频率。因此设计了分频模块对开发板的时钟脉冲进行分频。分频模块含有两个输入信号(20MHz的时钟脉冲信号clk、复位信号rst_n),两个输出信号(2Hz的时钟脉冲信号clk2hz、1kHz的时钟脉冲信号clk1000hz)。

分频原理是用可并行预置的加法计数器设计完成的,当加法计数器溢出时进行并行预置。例如,对20MHz分出2Hz频率时,需要给加法计数器预置终点4999999,该加法计数器的工作频率为20MHz。当计数器计数到4999999时,当且仅当,时钟上升沿到来(即20MHz脉冲的第500万的周期的上升沿),分频模块的输出信号clk2hz发生翻转,从计数开始到clk2hz发生翻转的时间即为2Hz信号的半个周期时长。

3.4消抖模块

因为我们使用的按键开关为机械弹性快关,当机械触点断开、闭合时,由于机械触点的弹性作用,按键开关在闭合时不会马上稳定地接通,在断开时也不会一下子断开。因而在闭合及断开的瞬间均伴随有一连串的抖动。为了不使抖动影响我们芯片的控制产生误判,就必须要进行按键消抖。

按键处于低电平的时刻大于20ms时,按键为按下状态,而按键按下的干扰是短时、多次的从高电平到低电平的跳变。因此,我们可以用一个计数模块,当输入信号为低电平时,开始计时,计时途中,如果跳变为高电平,即计数器清0,等待下一次低电平到来后再次开始计时,如此反复,如果记满20ms,那么,认为按键为一次按下状态,使按键标志产生一个脉冲,代表按键按下。

该模块含有四个输入信号,通过检测时钟的上升沿和复位的下降沿,复位信号低有效,则计数器清 0,20ms 扫描一次按键,20ms 计数(50M/50-1=999_999) ,计数器计到 20ms,计数器清零,采样按键输入电平计数器加 1。

3.5密码处理模块

i用于指示密码的第几位,flag用于判断判断密码输入错误的次数,3次后,键盘锁定,不可以输入数据。定义xianshi为显示的变量,shuzi,zimu显示的暂存变量,定义add_flag为地址改变标志位。led,led1,led2,led3为输出。a,b,c,d,e,f,g,h,A,B,C,D,E,F,G,H为存储输入的密码和设定密码的变量。如果显示的地址发生变化且LCD显示完成(lcd_flag=1),才显示为空字符;按键 KEY0 按下时,数字加一,第一次按下时,初始shuzi为9,按下后加一,等于10,就自动变成0。key_flag==0表示输入密码状态,key_flag==1表示设置密码状态,i表示给第i位密码赋值。密码的位数与显示的地址同步,按键 KEY2 值变化时,地址显示加一,字符右移一位,到达第8后不再加,因为密码为8位。按键 KEY3 按下时,一位一位的比对密码,如果密码输入正确,按下KEY4按键可以修改密码,按下KEY5按键可以退回上一位。最后shuzi复位为数字9,zimu复位为小写字母z。

LED功能:

LED1:亮起表示密码正确。

LED2:亮起表示密码错误。

LED3:亮起表示密码输入错误次数为3次。

LED4:亮起表示修改密码状态。

按键功能:

DKAO:输入数字。

DKA1:输入字母。

DKA2:进位(最高八位)。

DKA3:密码输入完成确认键。

DKA4:修改密码状态开关。

DKA5:删除一位。

DKA6:自定义按键。

3.6蜂鸣器模块

       密码输入正确后,蜂鸣器响一声(当bee_clk=0,bee_flag=1时(也就是密码输入正确时)蜂鸣器在响),当蜂鸣器时钟bee_clk=1且蜂鸣器标志位bee_flag为1时,蜂鸣器停止。 p=1用来表示蜂鸣器已经响了,p=0          清零等待下一次密码输入正确时响起。

四、管脚绑定

4.1管脚绑定表

Node Name

Direction

Location

Node Name

Direction

Location

bee

Output

PIN_A20

key_in[4]

Input

PIN_G13

clk

Input

PIN_G1

key_in[3]

Input

PIN_F13

data[7]

Output

PIN_U8

key_in[2]

Input

PIN_E13

data[6]

Output

PIN_AB8

key_in[1]

Input

PIN_D13

data[5]

Output

PIN_U7

key_in[0]

Input

PIN_C13

data[4]

Output

PIN_V7

led

Output

PIN_AB14

data[3]

Output

PIN_W7

led1

Output

PIN_AA14

data[2]

Output

PIN_Y7

led2

Output

PIN_W14

data[1]

Output

PIN_AA7

led3

Output

PIN_V14

data[0]

Output

PIN_AB7

rs

Output

PIN_Y4

en

Input

PIN_AA5

rst

Input

PIN_E4

key_in[6]

Input

PIN_E11

rw

Output

PIN_AB5

key_in[5]

Input

PIN_E12

4.2管脚绑定说明

bee:用于蜂鸣器发出声音

clk:外部为20MHZ的时钟,用于整个系统的基础时序

data[0-7]: LCD1602并行数据输入/输出引脚

key_in[0-7]: 8个按键用于按键扫描

led[0-3]: 分别绑定4个LED灯,用于密码正确与否和修改状态的显示

en: 执行的使能引脚

rs: 命令/数据选择引脚

rw: 读/写选择引脚

rst:用于整个系统的初始化,是整个系统的复位信号


五、仿真及FPGA开发板调试

在 Quartus II 中的 Modelsim 仿真平台对各个模块及总体设计进行 仿真及测试,用于验证系统的可行性及稳定性。

5.1仿真调试

在Testbench中给一个时钟信号让它每25ns翻转一次,先让复位信号为高电平和令clk信号为1;再给他100ns使复位信号为低电平,使整个系统初始化,然后让开始信号KEY_IN输入密码。密码输入正确时。如图5-1所示,led显示为高电平,这则说明密码锁有效,密码输入错误时,LED高低电平波动,发生闪烁,如图5-2所示,由此可以看出系统主体功能正常,所以可以得到这个设计是可行的。


5.2开发板整体调试

系统上电,首先进行系统初始化,液晶屏上显示出两个动态的光标,如图5—3所示,等待输入密码。

按下DKA0输入初始密码为00000000,按下DKA3后,LED1亮,如图5—4所示,说明密码正确。

密码输入正确后,按下DKA4后,LED4亮起,说明目前状态为修改密码状态。按下DKA4删除之前的密码,重新输入想要设置的密码,如图5—5所示,再次按下DKA4,LED4熄灭,证明密码修改成功。

密码修改成功后,按下复位键,重新输入修改后的密码,LED1亮,说明密码修改成功,如图5—6所示。

    结论:密码锁功能实现成功,并且能实现修改密码的功能。

六、设计总结与心得体会

6.1总结与心得

作为一种智能安全设备,电子密码锁在现代家居中越来越受到欢迎。针对这种需求,我们设计了一种基于FPGA的电子密码锁系统。在设计过程中,我们遇到了许多挑战与问题,下面我将总结一些心得和体会。

首先,设计电子密码锁系统需要较高的安全性和可靠性。我们采用了多种加密算法,并且保证系统在密码错误、电源变化等复杂环境下也能正常运行。其次,在选用FPGA芯片时,需要根据系统的需要选择适当的芯片,并且针对系统的各种需求进行定制化设计。我们设计的电子密码锁系统采用了Altera Cyclone IV芯片,方便进行逻辑扩展和状态转移。同时,我们也考虑到了系统的可维护性和固件升级问题。

其次,设计电子密码锁系统需要考虑用户的操作体验。在界面设计上,我们采用了物理按键控制和LCD1602液晶屏显示,使得用户可以直观地看到系统的工作状态和操作结果。另外,在密码输入过程中,我们增加了音效提示,让用户在输入错误时能够及时调整,提升用户体验。最后,在设计电子密码锁系统时,需要考虑到系统的可扩展性和应用场景。我们采用了模块化设计,并且保证各个模块的独立性,使得系统能够灵活扩展。此外,在应用场景方面,我们也考虑到了多种需求,例如支持多种开锁方式、多种开锁设备等,提升了系统的应用范围。

总之,设计电子密码锁系统需要考虑到安全性、可靠性、用户体验、可维护性、固件升级等多个方面,这需要我们在实践中不断探索和改进。

在设计基于FPGA的电子密码锁时,成本分析是必不可少的一部分。下面我将从硬件成本和软件成本两个方面对其进行分析。

6.2成本分析

硬件成本分析:

1. FPGA芯片成本:FPGA芯片是电子密码锁的核心部件,成本较高,但不需要大量采购。

2. 显示屏:我们采用了LCD1602液晶屏,成本较低。

3. 蓝牙、Wi-Fi等通信模块:如果需要增加通信功能,需要采购通信模块,其中Wi-Fi模块成本较高,蓝牙模块成本较低。

4. 电源模块:电子密码锁需要电源模块来支持,成本中等。

软件成本分析:

1. FPGA开发工具成本:FPGA设计过程中需要使用由芯片厂商提供的开发工具,常见的如Quartus II,成本较低。

2. 系统集成成本:电子密码锁的系统集成需要大量的时间和人力成本,较高。

3. 软件维护成本:电子密码锁在运行过程中可能会出现一些问题或需求,需要进行软件维护和升级,成本较低。

根据以上分析,我们可以得到以下结论:基于FPGA的电子密码锁的硬件成本相对较高,但不需要大量采购;软件成本相对较低,除了系统集成和一些特定需求的软件开发,维护和升级成本较低。综合起来,设计基于FPGA的电子密码锁的成本相对较高,但可以通过不断调整和优化方案,逐步降低成本。同时,在设计过程中还需充分考虑成本与功能之间的平衡,确保设计方案能够实现业务需求和市场需求,达到收益最大化。

七、参考文献

[1]康华光,张林,数字电子技术基础(第七版)[M],北京:高等教育出版社,2021.

[2]宋万杰,罗杰,吴顺君.CPLD技术及其应用[M].西安:西安电子科技大学出版社,1999.

[3]王金明,杨吉斌.数字系统设计与Verilong HDL[M].北京:北京电子工业出版社,2002.

[4]夏宇闻. Verilog数字系统设计教程[M].北京:北京航天航空大学出版社,2003.

[5]罗杰. VerilogHDL与FPGA数字系统设计[M].北京:机械工业出版社,2022.

八、附录

附录 1 RTL 代码:

module CD
 (
  rst,
  key_in,
  clk,
  rw,
  rs,
  en,
  data,
  led,
  led1,
  led2,
  led3,
  bee,
 );
 input clk,rst;    //CLK 50M,RSTw为复位信号
 
/***********************给变量赋初值************************/
initial A<=8'd48;//初始密码 0000——0000,d48为0的ASCALL值
initial B<=8'd48;
initial C<=8'd48;
initial D<=8'd48;
initial E<=8'd48;
initial F<=8'd48;
initial G<=8'd48;
initial H<=8'd48;

initial bee=0;
initial bee_flag=0;
initial p=0;

initial i<=1;
initial led=0; 
initial led1=0; 
initial led2=0; 
initial led3=0; 
initial flag=0; 
initial xianshi<=8'd16;  //让初始化完以后,就显示空字符
initial add<=8'h80;      //让初始化显示的地址
initial shuzi<=8'd57;    //让初始化完以后,数字变量为9,ascll码值为57,
initial zimu<=8'd122;    //为什么是9,因为按键按下后,数字会加一,9加一变为10,10以后跳会0,
								 //所以第一按按键显示0  
								 //让初始化字母为小写z
/**************************LCD的初始化函数********************************/
/*******************计数器函数用于在状态机中做延时使用********************/
reg [31:0]count;
 always @(posedge clk )  
 begin
 if(!rst)
 count<=0;
 else 
     begin
     count<=count+1;
     if(count==2_000_001)
        begin
        count<=0;
		  end 
	 
     end
 end 
 
 /********************************状态定义********************************/
 parameter  state0  =6'h00,  //设置8位格式,2行显示,5*7                 8'h38; 
            state1  =6'h01,  //开显示,开光标,闪烁  8'h0e    关光标,不闪烁 8'h0c
            state2  =6'h02,  //输入数据光标右移,显示字符不移动            8'h06
            state3  =6'h03,  //清屏     8'h01
            state4  =6'h04,  //显示的地址  add
            state5  =6'h05;  //显示的字符

output rs,en,rw;  //LCD控制使能端,rs=0时为写命令,rs=1;为写数据,en为LCD的E引脚,rw=0时,为写,rw=1为读数据(读数据没有用到,所以rw始终为0)
output [7:0] data;//输出LCD显示的字符//LCD的D0到D7引脚

 reg rs,en_sel;    //OUTput的变量要为定义为reg类型,en_sel为LCD  E引脚的寄存器变量,最终为E=en_sel
 reg [7:0]data;    
 reg [7:0]add;     //LCD字符显示的地址
 reg [7:0] next;    //next为状态定义
 reg[3:0]lcd_flag; // lcd_flag=0表示LCD正在写入字符,lcd_flag=1;表示LCD写入数据完成
 
 always @(posedge clk,negedge rst)
 begin
  if(!rst)
  begin
 next<=state0;
 lcd_flag=0;
  end
  else
  begin
   case(next)
    state0 :
     begin if(count<1000_000)begin rs=0;en_sel=1;data=8'h38;end //rs=0;为写命令;命令值为H38,设置8位格式,2行,5*7,LCD的使能端E为1(en_sel=1); 
	        if(count>1000_000)begin en_sel=0;end                  //LCD的使能端E为0(en_sel=0),下降沿的时候把数据读进去; 
			  if(count>2000_000)begin next<=state1;end             //延时一段时间10MS,LCD速度较慢必须延时
	  end
    state1 :
     begin if(count<1000_000)begin rs=0;en_sel=1;data=8'h0e;end 
	        if(count>1000_000)begin en_sel=0;end 
			  if(count>2000_000)begin next<=state2;end 
	  end
    state2 :
     begin if(count<1000_000)begin rs=0;en_sel=1;data=8'h06;end 
	        if(count>1000_000)begin en_sel=0;end 
			  if(count>2000_000)begin next<=state3;end 
	  end
    state3 :
     begin if(count<1000_000)begin rs=0;en_sel=1;data=8'h01;end 
	        if(count>1000_000)begin en_sel=0;end 
			  if(count>2000_000)begin next<=state4;end 
	  end    
    state4 :
     begin if(count<1000_000)begin lcd_flag=0; rs=0;en_sel=1;data=add;end //lcd_flag=0表示LCD正在写入一个数据,
	        if(count>1000_000)begin en_sel=0;end                           // 此时的数据应该保持稳定
			  if(count>2000_000)begin next<=state5;end 
	 end 
	 state5 :
     begin if(count<1000_000)begin rs=1;en_sel=1;data=xianshi;end //rs=1;为写数据,xinashi是要显示的数据
	        if(count>1000_000)begin en_sel=0;lcd_flag=1;end       //lcd_flag为1时表示数据写入完成,可以写入下一个数据
			  if(count>2000_000)begin next<=state4;end 
	 end 
   default:   next<=state0; 
   endcase
  end
 end
/*************************以下函数为密码锁功能实现************************/

/********************计数器函数用于在蜂鸣器中做延时***********************/
reg [31:0]count2; // CLK 分频计数器
 output bee;       //蜂鸣器控制端,bee为高电平时蜂鸣器响
 reg bee;      
 reg bee_flag;    //蜂鸣器使能标志
 reg bee_clk;    //蜂鸣器时钟
 reg p;          //蜂鸣器响标志

 always @(posedge clk )  
 begin
  if(!rst)
 count2<=0;
 else
   count2<=count2+1;
   if(count2==50_000_000)
   begin
   bee_clk=1;      
   end
   if(count2==50_900_000)
   begin
   bee_clk=0;
   end
   if(count2==51_000_000)
   begin
   count2<=0;
   end
  
 end 
 
 
 
 
 //按键检测函数
reg [31:0]count1; // CLK 分频计数器
always @(posedge clk or negedge rst) //检测时钟的上升沿和复位的下降沿
begin
 if(!rst)                 //复位信号低有效
 count1 <= 32'd0;         //计数器清 0
 else
 begin
 if(count1 ==32'd999_999) //20ms 扫描一次按键,20ms 计数(50M/50-1=999_999)
 begin
 count1 <= 1'b0;          //计数器计到 20ms,计数器清零
 key_scan <= key_in;      //采样按键输入电平
 end
 else
 count1 <= count1 + 1'b1; //计数器加 1
 end
end

// 按键信号锁存一个时钟节拍

input [6:0] key_in;//按键的定义 key0为数字0-9输入 key1为大小写字母输入 key2为输入密码的下一位 key3为确定键
reg [6:0] key_scan_r;
reg [6:0] key_scan;
always @(posedge clk)
key_scan_r <= key_scan;//利用非阻塞赋值实现下降沿检测
wire [6:0] flag_key = key_scan_r[6:0] & (~key_scan[6:0]); //检测按键的下降沿

//密码输入处理函数

reg key_flag; //修改密码标记位
reg [3:0]i,flag;//i用于指示密码的第几位,flag用于判断判断密码输入错误的次数,3次后,键盘锁定,不可以输入数据
reg[7:0]xianshi,shuzi,zimu;//定义xianshi为显示的变量,shuzi,zimu显示的暂存变量
reg add_flag;//地址改变标志位

output led,led1,led2,led3;//LED灯的定义
reg led,led1,led2,led3; 

reg[7:0] a,b,c,d,e,f,g,h,A,B,C,D,E,F,G,H;//存储输入的密码和设定密码的变量

always @ (posedge clk or negedge rst) 
begin
 if (!rst) //复位信号低有效
 begin 
  key_flag=0;
  shuzi<=8'd57;  //复位为数字9
  zimu<=8'd122;  //复位为小写字母z
  xianshi<=8'd16;//复位为空字符
  add=8'h80;    //复位第一行第一个显示
  i=1;          
  flag=0;
  led=0;
  led1=0;
  led2=0;
  led3=0;
  bee=0;
  add_flag=0;
  a=0;
  b=0;
  c=0;
  d=0;
  e=0;
  f=0;
  g=0;
  h=0;
 end 
 else if(flag<=2)  //flag为错误密码的次数,等于3if条件不满足,键盘不能使用
 begin
 if(lcd_flag&&add_flag)//如果显示的地址发生变化且LCD显示完成(lcd_flag=1),才显示为空字符,
 begin
 xianshi=8'd16;add_flag=0;
 end
 if ( flag_key[0] )begin  shuzi=shuzi+1'b1;if(shuzi==8'd58)shuzi=8'd48;xianshi=shuzi; //按键 KEY0 按下时,数字加一,第一次按下时,
                                                                                        // 初始shuzi为9,按下后加一,等于10,就自动变成0
								  if(key_flag==0) //  key_flag==0表示输入密码状态
                          begin								  
								  if(i==1)a=shuzi;                                                     
                          if(i==2)b=shuzi;
                          if(i==3)c=shuzi;
								  if(i==4)d=shuzi;
								  if(i==5)e=shuzi;
								  if(i==6)f=shuzi;
								  if(i==7)g=shuzi;
								  if(i==8)h=shuzi;
								  end
								  if(key_flag==1) //  key_flag==1表示设置密码状态,i表示给第i位密码赋值
                          begin								  
								  if(i==1)A=shuzi;                                                     
                          if(i==2)B=shuzi;
                          if(i==3)C=shuzi;
								  if(i==4)D=shuzi;
								  if(i==5)E=shuzi;
								  if(i==6)F=shuzi;
								  if(i==7)G=shuzi;
								  if(i==8)H=shuzi;
								  end

						 end 
if ( flag_key[1] ) begin zimu=zimu+1;if(zimu==91)zimu=8'd97;if(zimu==8'd123)zimu=8'd65;xianshi=zimu;//65为A,97为a,122为小写字母z
                          if(key_flag==0) //  key_flag==0表示输入密码状态
								  begin
								  if(i==1)a=zimu;                                                     
                          if(i==2)b=zimu;
                          if(i==3)c=zimu;
								  if(i==4)d=zimu;
								  if(i==5)e=zimu;
								  if(i==6)f=zimu;
								  if(i==7)g=zimu;
								  if(i==8)h=zimu; 
								  end
								  
								  if(key_flag==1) //  key_flag==1表示设置密码状态
                          begin								  
								  if(i==1)A=zimu;                                                     
                          if(i==2)B=zimu;
                          if(i==3)C=zimu;
								  if(i==4)D=zimu;
								  if(i==5)E=zimu;
								  if(i==6)F=zimu;
								  if(i==7)G=zimu;
								  if(i==8)H=zimu;
								  end
                     end 

if ( flag_key[2] ) begin add_flag<=1;
                         i=i+1;if(i>7)i=8; //密码的位数与显示的地址应该同步
								 add<=add+1'b1;    //按键 KEY2 值变化时,地址显示加一,字符右移一位
								 if(add>8'h86)add<=8'h87;//到达第8后不再加,因为密码为8位,可根据需要修改
								 shuzi<=8'd57;zimu<=8'd122;end
                                                      

 if ( flag_key[3] )begin        //按键 KEY3 按下时,一位一位的比对密码
                   if(a==A)
						 begin
						      if(b==B)
								begin
								     if(c==C)
									  begin
									       if(d==D)
											 begin
											      if(e==E)
													begin
													     if(f==F)
														  begin
														       if(g==G)
																 begin
																      if(h==H)
																      begin
																		led=1;led1=0;bee_flag=1;bee=1;
																
																		end
																		else begin led1=1;led=0; end
																 end
														       else begin led1=1;led=0; end
														  end
														  else begin led1=1;led=0; end
													end
													else begin led1=1;led=0; end
											 end
											 else begin led1=1;led=0; end
									  end
									  else begin led1=1;led=0; end
								end
								else begin led1=1;led=0; end
						 end
						 else begin led1=1;led=0; end
						 if(led==1)flag=0;
						 if(led1==1)flag=flag+1;
						 if(flag==3)led2=1;
						 end
 if ( flag_key[4] )begin //如果密码输入正确,按下此按键可以修改密码 
                   if(led==1)
						          begin
									 key_flag=~key_flag;
									 if(key_flag==1)
									 led3=1;
									 else
									 led3=0;
						          end
                   end 
						 if ( flag_key[5] )begin//按下此按键可以退回上一位
                    xianshi=8'd16; //显示为空字符
                    shuzi<=8'd57;  //复位为数字9
                    zimu<=8'd122;  //复位为小写字母z 
	                 add<=add-1;
						  if(add<8'h80)add<=8'h87;
						  i=i-1;
						  if(i==0)i=8;

						 end

						 
//****************************蜂鸣器代码*********************************//

下面这段代码实现的功能为:密码输入正确后,蜂鸣器响一声//(当bee_clk=0,bee_flag=1时(也就是密码输入正确时)蜂鸣器在响)
						 if(bee_clk&&bee_flag==1)//当蜂鸣器时钟bee_clk=1且蜂鸣器标志位bee_flag为1时
						 begin
						 bee=0;                   //蜂鸣器停止
						 p=1;                    //p=1用来表示蜂鸣器已经响了
						 end
						 else if(p==1)
						 begin
						 p=0;          //清零等待下一次响
						 bee_flag=0;   //清零等待下一次密码输入正确
						 end
						 
end
end

 assign en=en_sel; 
 assign rw=0;//rw为0表示LCD的rw引脚为低电平,此时为写,rw为1时读
endmodule

附录 2 Testbench 代码:

`timescale 1ns/1ns
module CD;
    reg clk;
    reg [3:0]key_in;
    reg rst;
    wire [2:0]led;
    wire [2:0]led_test;
    CD uut(.clk(clk),.key_in(key_in),.rst(rst),.led(led),.led_test(led_test));
initial begin
    clk = 1;
    forever #25 clk = ~clk;
        end 
 
initial begin
    rst = 1;
    #100;
    rst = 0;
    key_in = 4'b0000;
    #100;
    key_in = 4'b0001;
    #200000000;
    key_in = 4'b0000;
    #10000;
    key_in = 4'b0010;
    #200000000;
    key_in = 4'b0000;
        #10000;
    key_in = 4'b0100;
    #200000000;
    key_in = 4'b0000;
        #10000;
    key_in = 4'b1000;
    #200000000;
    key_in = 4'b0000;
        #10000;
    key_in = 4'b0010;
        #200000000;
    key_in = 4'b0100;
        #200000000;
    key_in = 4'b0000;
    #200000000;
    $stop;
    end   
endmodule

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码

)">
< <上一篇
下一篇>>