FPGA学习——数字密码锁(下)

数字密码锁(下)

上篇整理了前两个模块,可以做出来功能按键以及数字按键的信号,接着就要利用这些信号对密码锁进行控制。

3、控制模块

第三个模块为控制模块,主要实现的是一些信号的处理,也是整个工程的重中之重,主要完成了密码的接收、核对、更改、以及确认
首先就是接收从功能分配模块接受传送过来的信号,包括:

input clk;
input check;
input [3:0] number_key;
input state_lock;  //密码锁开启/关闭状态
input reset;
input clear_flag;  //清零状态
input lock;
input rece_flag;   //密码输入标志
input [3:0] rece_cnt;

接着就考虑输出的信号都有哪些。与控制模块的相接的就是最后的显示模块了,所以要考虑与显示相关的信号有哪些:

output reg  led;
output reg [3:0] disp_in1;
output reg [3:0] disp_in2;
output reg [3:0] disp_in3;
output reg [3:0] disp_in4;
output reg beep;		

做好接口以后,就开始做内部的逻辑电路设计。
首先做一个密码的初始化,即刚开机时候的密码:

initial
  begin
    password1<=4'd1;    //初始密码为1234
	 password2<=4'd2;
	 password3<=4'd3;
	 password4<=4'd4;
  end

然后接下来的设计主要分为三块:一是密码的接收,二是密码的判断,三是密码的更改
一共定义两组密码变量,一组是存储在寄存器中的真正密码,一组是用于接收按键输入的预备密码
下面为密码的接收部分:

always@(posedge clk)
    if(state_lock==1) begin
	   case(reset_state)
	    0:if(clear_flag==1)  begin
		        preword1<=4'd0;
				  preword2<=4'd0;
				  preword3<=4'd0;
				  preword4<=4'd0;
				 end
		   else begin
		       case(rece_cnt)
					0:begin
		            preword1<=4'd0;
					   preword2<=4'd0;
					   preword3<=4'd0;
					   preword4<=4'd0;
					  end
		         1:preword1<=number_key;
				 2:preword2<=number_key;
				 3:preword3<=number_key;
				 4:preword4<=number_key;
				default:begin
		           preword1<=preword1;
				   preword2<=preword2;
				   preword3<=preword3;
				   preword4<=preword4;
				   end
			endcase
	     end
		1:begin
			preword1<=password1;
			preword2<=password2;
			preword3<=password3;
			preword4<=password4;
		  end
		endcase 
   end
  

当输入的预备密码与存储在寄存器中的真正密码一致时,按下确认键才会打开密码锁

密码判断部分:

/****************密**码**核**实***************/  
always@(posedge clk)
 begin
  if(check==1)  confirm<=1;
  else if(clear_flag==1) confirm<=0;
  else if(state_lock==0) confirm<=0;
  else confirm<=confirm;
 end
  
assign right0[0]=(password1==preword1);
assign right0[1]=(password2==preword2);
assign right0[2]=(password3==preword3);
assign right0[3]=(password4==preword4);

always@(posedge clk)
   if(state_lock==1) begin
	    if(confirm==1)  begin
            if(right0==4'b1111)begin
					   led<=0; //输入密码正确指示灯亮
					   right<=1;
					   end
				else begin
			           led<=1;
					     right<=0;
					  end 
		      end
		 else if(reset_state==1)begin
		        led<=0;
				right<=1;
				end
		 else if((reset_state==0)&&(clear_flag==1))begin
			    led<=1;
				 right<=0;
				 end
		 else begin
		      led<=led;
				right<=right;
		      end
		end 
	else begin
	     led<=1;
		  right<=0;
		  end


由于只有三次输入密码的机会,所以也要对密码的确认次数进行限制,根据确认的次数以及密码的正确与否来对输入密码次数做出限制:

//*************密码确认次数控制****************//
always@(posedge confirm)
  begin
  if(state_lock==1) begin
     if(right==0) begin
        if(w_cnt==2'b11) w_cnt<=2'b00;
		  else w_cnt<=w_cnt+2'b1;
		  end
	  else w_cnt<=2'b00;	
     end
  else w_cnt<=2'b00;	  
  end

密码更改部分:

/*************更**改**密**码**********************/
  
always@(posedge clk)
   if(right==1) begin     //输入密码正确才可进入修改密码状态
      if(reset==1) reset_state<=1;
	   else if(lock==1) reset_state<=0;
		else reset_state<=reset_state;
		end
	else reset_state<=0;

always@(posedge clk)
    if(state_lock==1) begin
				case(reset_state)
				 1: if(clear_flag==1) begin
						 password1<=4'd0;
						 password2<=4'd0;
						 password3<=4'd0;
						 password4<=4'd0;
						 end
					else begin
			          case(rece_cnt)
					     0: begin
								 password1<=password1;
								 password2<=password2;
								 password3<=password3;
								 password4<=password4;
							  end
		              1:password1<=number_key;
					     2:password2<=number_key;
					     3:password3<=number_key;
					     4:password4<=number_key;
					     default:
								begin
								  password1<=password1;
								  password2<=password2;
								  password3<=password3;
								  password4<=password4;

								end
				        endcase
					      end
				0:begin
				     password1<=password1;
					  password2<=password2;
					  password3<=password3;
					  password4<=password4;
				     end
				endcase
	     end

always@(posedge clk)
   if(state_lock==1) begin
		   disp_in1<=preword1;
			disp_in2<=preword2;
			disp_in3<=preword3;
			disp_in4<=preword4;
			end

该模块完整程序

module lock_ctrl(clk,reset,clear_flag,lock,rece_cnt,rece_flag,
                 check,number_key,led,beep,disp_in1,
                 disp_in2,disp_in3,disp_in4,
                 state_lock
					  );
input clk;
input check;
input [3:0] number_key;
input state_lock;  //密码锁开启/关闭状态
input reset;
input clear_flag;  //清零状态
input lock;
input rece_flag;   //密码输入标志
input [3:0] rece_cnt;

output reg  led;
output reg [3:0] disp_in1;
output reg [3:0] disp_in2;
output reg [3:0] disp_in3;
output reg [3:0] disp_in4;
output reg beep;		

 reg [3:0] preword1;    //输入准密码
 reg [3:0] preword2; 
 reg [3:0] preword3; 
 reg [3:0] preword4; 

 reg [3:0] password1;  //设定密码
 reg [3:0] password2;  
 reg [3:0] password3;  
 reg [3:0] password4; 

reg warnning;  //密码错误三次警告
reg reset_state;  //重置密码状态

reg right;   //密码输入正确标志
reg   clk_beep;  //蜂鸣器驱动脉冲
reg [1:0] w_cnt;   //输入密码次数计数器 
reg [15:0]   delay_beep; //延时计数器
reg confirm;  //密码确认延时一个时钟
reg [23:0]   cnt_beep;  //分频计数器
wire [5:0] right0;

initial
  begin
    password1<=4'd1;    //初始密码为1234
	 password2<=4'd2;
	 password3<=4'd3;
	 password4<=4'd4;

    led<=1;
    warnning<=0;
    beep<=0;
	 w_cnt=2'b10;
	 right=0;
	 delay_beep<=0;
	 cnt_beep<=0;
	 clk_beep=0;
  end
/**********密**码**核**实********/  
always@(posedge clk)
 begin
  if(check==1)  confirm<=1;
  else if(clear_flag==1) confirm<=0;
  else if(state_lock==0) confirm<=0;
  else confirm<=confirm;
 end
  
assign right0[0]=(password1==preword1);
assign right0[1]=(password2==preword2);
assign right0[2]=(password3==preword3);
assign right0[3]=(password4==preword4);

always@(posedge clk)
   if(state_lock==1) begin
	    if(confirm==1)  begin
            if(right0==4'b1111)begin
					     led<=0; //输入密码正确指示灯亮
					     right<=1;
					   end
				else begin
			           led<=1;
					     right<=0;
					  end 
		      end
		 else if(reset_state==1)begin
		      led<=0;
				right<=1;
				end
		 else if((reset_state==0)&&(clear_flag==1))begin
			    led<=1;
				 right<=0;
				 end
		 else begin
		      led<=led;
				right<=right;
		      end
		end 
	else begin
	     led<=1;
		  right<=0;
		  end

//*****密码确认次数控制**********//
always@(posedge confirm)
  begin
  if(state_lock==1) begin
     if(right==0) begin
        if(w_cnt==2'b11) w_cnt<=2'b00;
		  else w_cnt<=w_cnt+2'b1;
		  end
	  else w_cnt<=2'b00;	
     end
  else w_cnt<=2'b00;	  
  end

//*********输入三次错误*********//

  always@(posedge clk)    //当第四次输入,触发警报
	 if(state_lock==1) begin
      if(w_cnt==2'b11)  warnning<=1;
      else warnning<=0;
      end
    else warnning<=0;
	 
always@(posedge clk)   //驱动蜂鸣器
  if(warnning==1) begin
      if(cnt_beep<=5_00)
		    cnt_beep<=cnt_beep+24'd1;
	   else begin
		     cnt_beep<=24'd0;
			  clk_beep<=~clk_beep;
	        end
		end
  else cnt_beep<=24'd0;
  
 always@(posedge clk_beep)  //蜂鸣持续时间
   if(delay_beep<=16'd800)  
	   begin
      beep=~beep;
		delay_beep<=delay_beep+16'd1;
		end
	else begin
	       if(w_cnt==2'b11)
		      delay_beep<=16'd8001;
			 else delay_beep<=16'd0;
		  end
 	
/*************密**码**接**收***************/		
always@(posedge clk)
    if(state_lock==1) begin
	   case(reset_state)
	    0:if(clear_flag==1)  begin
		        preword1<=4'd0;
				  preword2<=4'd0;
				  preword3<=4'd0;
				  preword4<=4'd0;
				 end
		   else begin
		       case(rece_cnt)
					0:begin
		            preword1<=4'd0;
					   preword2<=4'd0;
					   preword3<=4'd0;
					   preword4<=4'd0;
					  end
		         1:preword1<=number_key;
					2:preword2<=number_key;
					3:preword3<=number_key;
					4:preword4<=number_key;
					default:begin
		           preword1<=preword1;
				     preword2<=preword2;
					  preword3<=preword3;
					  preword4<=preword4;
					  end
				  endcase
	     end
		1:begin
			preword1<=password1;
			preword2<=password2;
			preword3<=password3;
			preword4<=password4;
		  end
		endcase 
   end
  
/**********更**改**密**码*****************/
  
always@(posedge clk)
   if(right==1) begin     //输入密码正确才可进入修改密码状态
      if(reset==1) reset_state<=1;
	   else if(lock==1) reset_state<=0;
		else reset_state<=reset_state;
		end
	else reset_state<=0;

always@(posedge clk)
    if(state_lock==1) begin
				case(reset_state)
				 1: if(clear_flag==1) begin
						 password1<=4'd0;
						 password2<=4'd0;
						 password3<=4'd0;
						 password4<=4'd0;
						 end
					else begin
			          case(rece_cnt)
					     0: begin
								 password1<=password1;
								 password2<=password2;
								 password3<=password3;
								 password4<=password4;
							  end
		              1:password1<=number_key;
					     2:password2<=number_key;
					     3:password3<=number_key;
					     4:password4<=number_key;
					     default:
								begin
								  password1<=password1;
								  password2<=password2;
								  password3<=password3;
								  password4<=password4;

								end
				        endcase
					      end
				0:begin
				     password1<=password1;
					  password2<=password2;
					  password3<=password3;
					  password4<=password4;
				     end
				endcase
	     end

always@(posedge clk)
   if(state_lock==1) begin
		   disp_in1<=preword1;
			disp_in2<=preword2;
			disp_in3<=preword3;
			disp_in4<=preword4;
			end
		

endmodule 

4、显示模块

控制模块完成了工程的主要内容,显示模块只需要将数值显示出来即可
首先,还是先定义接口:

 input clk;
 input state_lock;
 input [2:0] rece_cnt;
 input [3:0] disp_in1; 
 input [3:0] disp_in2;
 input [3:0] disp_in3;
 input [3:0] disp_in4;
output reg [7:0] dxuan;
output reg [5:0] wxuan;

接着明确该模块内的主要任务:
1、能显示输入的数字
2、最新输入的数字总是显示在最右侧
基于这两点,对程序大体部分进行划分。首先是做一个段数码管的显示部分,即八段数码管能显示正确的数值。对数字0-9进行八段数码管的定义:

//*************数码管数值定义******************//
parameter disp0=8'b1100_0000;
parameter disp1=8'b1111_1001;
parameter disp2=8'b1010_0100;
parameter disp3=8'b1011_0000;
parameter disp4=8'b1001_1001;
parameter disp5=8'b1001_0010;
parameter disp6=8'b1000_0010;
parameter disp7=8'b1111_1000;
parameter disp8=8'b1000_0000;
parameter disp9=8'b1001_0000;
parameter disp10=8'b1011_1111;

根据输入的值不同,让段数码管显示不同的数值:

always@(posedge clk)
  begin
    case(cnt_symin)
    4'd1:dxuan<=disp1;
	 4'd2:dxuan<=disp2;
	 4'd3:dxuan<=disp3;
	 4'd4:dxuan<=disp4;
	 4'd5:dxuan<=disp5;
	 4'd6:dxuan<=disp6;
	 4'd7:dxuan<=disp7;
	 4'd8:dxuan<=disp8;
	 4'd9:dxuan<=disp9;
	 4'd0:dxuan<=disp0;
	 4'd10:dxuan<=disp10;
	 default:dxuan<=disp0;
	 endcase
 end

其中,cnt_symin变量就需要根据不同的情况进行自动变更,利用case语句根据输入密码的位数来切换数码管显示的数字(即使输最新输入的密码显示在最右侧):

always@(posedge clk)
    if(state_lock==1) begin   //密码锁开关打开
	    case(rece_cnt)
		    3'd0:begin
	            case(disp_state)
		         2'd0:begin wxuan<=4'b0111; cnt_symin<=4'd0;end
		         2'd1:begin wxuan<=4'b1011; cnt_symin<=4'd0;end
	            2'd2:begin wxuan<=4'b1101; cnt_symin<=4'd0;end
	            2'd3:begin wxuan<=4'b1110; cnt_symin<=4'd0;end
	            endcase
					end
			3'd1:begin
	            case(disp_state)
		         2'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in1;end
		         2'd1:begin wxuan<=4'b1011; cnt_symin<=4'd0;end
	            2'd2:begin wxuan<=4'b1101; cnt_symin<=4'd0;end
	            2'd3:begin wxuan<=4'b1110; cnt_symin<=4'd0;end
	            endcase
					end
			3'd2:begin
	            case(disp_state)
		         2'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in2;end
		         2'd1:begin wxuan<=4'b1011; cnt_symin<=disp_in1;end
	            2'd2:begin wxuan<=4'b1101; cnt_symin<=4'd0;end
	            2'd3:begin wxuan<=4'b1110; cnt_symin<=4'd0;end
	            endcase
					end
			3'd3:begin
	            case(disp_state)
		         3'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in3;end
		         3'd1:begin wxuan<=4'b1011; cnt_symin<=disp_in2;end
	            3'd2:begin wxuan<=4'b1101; cnt_symin<=disp_in1;end
	            3'd3:begin wxuan<=4'b1110; cnt_symin<=4'd0;end
	            endcase
					end
			3'd4:begin
	            case(disp_state)
		         3'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in4;end
		         3'd1:begin wxuan<=4'b1011; cnt_symin<=disp_in3;end
	            3'd2:begin wxuan<=4'b1101; cnt_symin<=disp_in2;end
	            3'd3:begin wxuan<=4'b1110; cnt_symin<=disp_in1;end
	            endcase
					end
			default:begin
	            case(disp_state)
		         2'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in4;end
		         2'd1:begin wxuan<=4'b1011; cnt_symin<=disp_in3;end
	            2'd2:begin wxuan<=4'b1101; cnt_symin<=disp_in2;end
	            2'd3:begin wxuan<=4'b1110; cnt_symin<=disp_in1;end
	            endcase
					end
			endcase
	 end
	 else wxuan<=4'b1111;	

由于采用动态数码管的显示,所以需要对位数码管进行不断的切换,需要做一个分频模块对每位数码管循环显示:

/*********************分频*****************/

always@(posedge clk)  //位数码管切换控制
  begin
    if(count<28'd20_0000)
	    count<=count+28'd1;
	 else begin
	      count<=28'd0;
			if(disp_state<=2'd3)  disp_state<=disp_state+1;
			else disp_state<=0;
			end
  end

该模块完整程序:

module display(clk,state_lock,rece_cnt,
                  disp_in1,disp_in2,disp_in3,disp_in4,
						dxuan, wxuan);
 input clk;
 input state_lock;
 input [2:0] rece_cnt;
 input [3:0] disp_in1; 
 input [3:0] disp_in2;
 input [3:0] disp_in3;
 input [3:0] disp_in4;
output reg [7:0] dxuan;
output reg [5:0] wxuan;

reg [27:0] count;  //位数码管分频计数器

reg [1:0] disp_state;  //位数码管切换状态
reg [3:0] cnt_symin;

//*************数码管数值定义******************//
parameter disp0=8'b1100_0000;
parameter disp1=8'b1111_1001;
parameter disp2=8'b1010_0100;
parameter disp3=8'b1011_0000;
parameter disp4=8'b1001_1001;
parameter disp5=8'b1001_0010;
parameter disp6=8'b1000_0010;
parameter disp7=8'b1111_1000;
parameter disp8=8'b1000_0000;
parameter disp9=8'b1001_0000;
parameter disp10=8'b1011_1111;



/*********************分频*****************/

always@(posedge clk)  //位数码管切换控制
  begin
    if(count<28'd20_0000)
	    count<=count+28'd1;
	 else begin
	      count<=28'd0;
			if(disp_state<=2'd3)  disp_state<=disp_state+1;
			else disp_state<=0;
			end
  end
  
always@(posedge clk)
    if(state_lock==1) begin   //密码锁开关打开
	    case(rece_cnt)
		    3'd0:begin
	            case(disp_state)
		         2'd0:begin wxuan<=4'b0111; cnt_symin<=4'd0;end
		         2'd1:begin wxuan<=4'b1011; cnt_symin<=4'd0;end
	            2'd2:begin wxuan<=4'b1101; cnt_symin<=4'd0;end
	            2'd3:begin wxuan<=4'b1110; cnt_symin<=4'd0;end
	            endcase
					end
			3'd1:begin
	            case(disp_state)
		         2'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in1;end
		         2'd1:begin wxuan<=4'b1011; cnt_symin<=4'd0;end
	            2'd2:begin wxuan<=4'b1101; cnt_symin<=4'd0;end
	            2'd3:begin wxuan<=4'b1110; cnt_symin<=4'd0;end
	            endcase
					end
			3'd2:begin
	            case(disp_state)
		         2'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in2;end
		         2'd1:begin wxuan<=4'b1011; cnt_symin<=disp_in1;end
	            2'd2:begin wxuan<=4'b1101; cnt_symin<=4'd0;end
	            2'd3:begin wxuan<=4'b1110; cnt_symin<=4'd0;end
	            endcase
					end
			3'd3:begin
	            case(disp_state)
		         3'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in3;end
		         3'd1:begin wxuan<=4'b1011; cnt_symin<=disp_in2;end
	            3'd2:begin wxuan<=4'b1101; cnt_symin<=disp_in1;end
	            3'd3:begin wxuan<=4'b1110; cnt_symin<=4'd0;end
	            endcase
					end
			3'd4:begin
	            case(disp_state)
		         3'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in4;end
		         3'd1:begin wxuan<=4'b1011; cnt_symin<=disp_in3;end
	            3'd2:begin wxuan<=4'b1101; cnt_symin<=disp_in2;end
	            3'd3:begin wxuan<=4'b1110; cnt_symin<=disp_in1;end
	            endcase
					end
			default:begin
	            case(disp_state)
		         2'd0:begin wxuan<=4'b0111; cnt_symin<=disp_in4;end
		         2'd1:begin wxuan<=4'b1011; cnt_symin<=disp_in3;end
	            2'd2:begin wxuan<=4'b1101; cnt_symin<=disp_in2;end
	            2'd3:begin wxuan<=4'b1110; cnt_symin<=disp_in1;end
	            endcase
					end
			endcase
	 end
	 else wxuan<=4'b1111;	

  
always@(posedge clk)
  begin
    case(cnt_symin)
    4'd1:dxuan<=disp1;
	 4'd2:dxuan<=disp2;
	 4'd3:dxuan<=disp3;
	 4'd4:dxuan<=disp4;
	 4'd5:dxuan<=disp5;
	 4'd6:dxuan<=disp6;
	 4'd7:dxuan<=disp7;
	 4'd8:dxuan<=disp8;
	 4'd9:dxuan<=disp9;
	 4'd0:dxuan<=disp0;
	 4'd10:dxuan<=disp10;
	 default:dxuan<=disp0;
	 endcase
 end
endmodule

总结

在做这个工程的时候,出现了各种各样的问题。解决时间最长的是矩阵键盘的模块,由于矩阵键盘的错误导致后续模块都无法正常工作。
现在虽然能实现大多数功能,但仍然不能保证他的功能都是完整无错的。逻辑很复杂,思维难免出现漏洞,所以倘若发现有不正确的地方,请不吝告知,好及时更改。

【注】个人学习笔记,请不吝赐教!

猜你喜欢

转载自blog.csdn.net/zsisinterested/article/details/117630394