Verliog 写电子密码锁

本文围绕Verilog实现电子密码锁展开,介绍了分频的原因与方法,通过计数器实现,上升沿和下降沿触发有差异。还阐述了键盘扫描、密码、报警、状态灯等模块的原理,以及电子密码锁密码输入、储存、删除、重置等功能的实现流程和主要模块。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.为什么要分频?
主要是为了给其他模块一个时钟源,不同的模块需要不同的时钟源,比如时间计数,时间分为一秒一秒的,LED的现实模块,LED是通过人的视觉差来达到让人觉
的它在亮的效果,实际上说它是扫描比较的闪烁比较好。

2.怎么分频?
通过计数器来实现分频,所需要的频率与基础频率之间的计数关系,因为采用的是上升沿或者下降沿触发计数,两者有些不同,上升沿触发的分频时钟输出,如果
N为奇数得到的时钟占空比不是50%;如果N为偶数得到的时钟占空比为50%,得到的分频时钟正周期比负周期多一个clk时钟,下降沿触发的分频时钟输出,和用上
升沿触发的相差半个时钟,如果是上升沿和下降沿混合用的,需要注意这个问题,特别是模块分不同的人写的时候,因为我们这个都是我自己写的,所以都是采用
的上升沿。(12MHz)

3.键盘扫描模块的原理
扫描并锁存行值,该行按下为1,否则为0,列同理,然后用case选择情况根据键盘所代表的数字在寄存器里存数字

4.密码模块
密码模块用了一个24位的寄存器,密码一共有4位,每位可以输入0-9十个任意数。

5.报警模块
判断锁的状态位,因为频率慢的时钟不能在快频率模块进行判断、赋值这些操作,我们把报警器和报警器所需要的计时模块放在了一起
在频率快的模块中赋值的量不能在在频率慢的模块中再进行赋值,慢频率模块的值可以在快模块中进行判断操作

6.状态灯模块

5.状态的判定
锁的状态、是否重置、是否开始输入密码、确认密码

3.总的流程?
实现电子密码锁密码输入、密码储存、密码删除、密码重置,要求在5秒内输入正确密码,则绿灯亮,锁打开;密码错误或者未在规定时间内输完都算解锁失败,
红灯亮,报警20秒,20秒后可重新输入,rst 键按下当前输入密码设置为电子锁的密码
主要模块:
1.分频模块
2.LED灯模块,LED灯显示输入密码和时间计数
3.键盘扫描模块
4.密码模块
5.辅助模块(防抖模块)【此次代码未添加该模块】

功能模块部分

module code123(sw1,sw2,sw3,clk,row,col,rst,key,alarm,clear,xianshi,play2,play3,play4,green_led,yellow_led,red_led,ring);
//parameter N=5     
input  sw1;
input sw2;
input sw3;
input rst;//复位
input clk;//时钟
input clear;//清除
input [3:0]row;
output reg[3:0]col;
output  reg ring;
input [3:0] key;
reg [3:0] key0=4'b1111;//预存密码都为1
reg [4:0] counter=5'b00000;
output [8:0]xianshi;
wire[8:0]shumaguan;
assign xianshi=shumaguan;//一个计算秒数,还有4位显示密码
output [8:0]play2;
wire[8:0] led2;
assign play2=led2;
output [7:0]play3;
wire[7:0] led3;
assign play3=led3;
output [7:0]play4;
wire[7:0] led4;
assign play4=led4;
reg clk1;
reg flag=0;
reg flag1=0;
reg flag2=0;
reg [23:0] previous_password=24'd0;
reg [23:0] password=24'd0;
reg flag_in=1'b0;
reg [3:0] keyboard_val;
//=========================
reg [3:0]cout=4'b0000;
//output [2:0] cout0;
//assign  cout0=cout;
//=========================
reg [3:0] temp=4'b0000;
//wire temp1;
//assign temp=temp1;
//output [3:0] temp1;
//assign temp1=temp;
wire clk2;
//========================
output reg green_led;
output reg yellow_led;
output reg red_led;
 output reg alarm;
//  assign ring=alarm;
reg [19:0] count1=20'b0;
reg [29:0] count2=30'b0;
reg [4:0] break1=5'b0;
reg [4:0] break2=5'b0;
reg [5:0] current_state, next_state;    // 现态、次态
reg key_pressed_flag=0;             // 键盘按下标志
reg [3:0] col_val, row_val; // 列值、行值
reg [19:0] cnt;            
parameter NO_KEY_PRESSED = 6'b000_001;  // 没有按键按下  
parameter SCAN_COL0      = 6'b000_010;  // 扫描第0列 
parameter SCAN_COL1      = 6'b000_100;  // 扫描第1列 
parameter SCAN_COL2      = 6'b001_000;  // 扫描第2列 
parameter SCAN_COL3      = 6'b010_000;  // 扫描第3列 
parameter KEY_PRESSED    = 6'b100_000;  // 有按键按下
//---------------------------------
parameter time1=1'd5;//在5秒内输入密码并确认
parameter time2=2'd20;//警报响触发后警报响20S
parameter N=5'd50000;
//警报置初始状态,alarm为灯,ring为铃
initial begin
alarm<=1'b1;
ring<=1'b0;
end
//==================================
//模块调用部分
LED u1(
      .seg_data_1(counter[3:0]),
		.seg_data_2(cout),
		.seg_led_1(shumaguan),
		.seg_led_2(led2)
		);
divide #(.WIDTH(32),.N(12000000)) u2(
              .clk(clk),
              .rst_n(rst),
              .clkout(clk2)
                );
LED_Display u3(
      .an(led4),
		.clk(clk),
		.seg(led3),
		.temp(password [23:20])
		);
 /*anjian u3(
.i_clk(clk),
.i_rst_n(rst),
.row(row),
.col(col),
.password(password),
.flag1(flag1)
);*/
//============================
//分频1:
always @(posedge clk)      
begin
	count1<=count1+1'b1;
	if (count1==N)
	begin
   clk1<=~clk1;
   count1<=20'd0;
	end
end 

always@(posedge clk2)begin//采用的秒作时钟
if(flag1==1) begin  //flag=1时,开始输入密码
	counter=counter+1;
	if(counter<5)begin
	if(green_led==0)begin  //绿灯亮
	ring<=1'b0;
	flag2=0;  //电铃是否响的标志
	counter=5'b00000;end end
	else if(counter<25 && counter>=5)begin
	ring<=1'b1;
   flag2=1;	
	end
	else begin  //保持初始状态
	ring<=1'b0;
	flag2=0;
	counter=5'b00000;
	end
	end
else begin
counter=5'b00000;
ring<=1'b0;
flag2=0;
end
end

always @ (posedge clk)
    cnt <= cnt + 1'b1;
wire key_clk = cnt[15];
always @ (posedge key_clk) begin
if(rst==1'b1)
    current_state <= next_state;end
always @ * begin
  case (current_state) 
    NO_KEY_PRESSED :                    // 没有按键按下
        if (row != 4'hF)
          next_state = SCAN_COL0;
        else
          next_state = NO_KEY_PRESSED;
    SCAN_COL0 :                         // 扫描第0列 
        if (row != 4'hF)
          next_state = KEY_PRESSED;
        else
          next_state = SCAN_COL1;
    SCAN_COL1 :                         // 扫描第1列 
        if (row != 4'hF)
          next_state = KEY_PRESSED;
        else
          next_state = SCAN_COL2;    
    SCAN_COL2 :                         // 扫描第2列
        if (row != 4'hF)
          next_state = KEY_PRESSED;
        else
          next_state = SCAN_COL3;
    SCAN_COL3 :                         // 扫描第3列
        if (row != 4'hF)
          next_state = KEY_PRESSED;
        else
          next_state = NO_KEY_PRESSED;
    KEY_PRESSED :                       // 有按键按下
        if (row != 4'hF)
          next_state = KEY_PRESSED;
        else
          next_state = NO_KEY_PRESSED;                      
  endcase
 end

 
// 根据次态,给相应寄存器赋值
always @ (posedge key_clk) begin
if(rst==1'b1)begin
    case (next_state)
      NO_KEY_PRESSED :                  // 没有按键按下
      begin
        col              <= 4'h0;
        key_pressed_flag <= 1'b0;       // 清键盘按下标志
      end
      SCAN_COL0 :                       // 扫描第0列
        col <= 4'b1110;
      SCAN_COL1 :                       // 扫描第1列
        col <= 4'b1101;
      SCAN_COL2 :                       // 扫描第2列
        col <= 4'b1011;
      SCAN_COL3 :                       // 扫描第3列
        col <= 4'b0111;
      KEY_PRESSED :                     // 有按键按下
      begin
        col_val          <= col;        // 锁存列值
        row_val          <= row;        // 锁存行值
        key_pressed_flag <= 1'b1;          // 置键盘按下标志  
      end
    endcase
	 end
	 end
//--------------------------------------
// 状态机部分 结束
//--------------------------------------
 
 
//++++++++++++++++++++++++++++++++++++++
// 扫描行列值部分 开始
//++++++++++++++++++1++++++++++++++++++++
always @ (posedge key_clk) begin
if(rst==1'b1)begin
    if (key_pressed_flag==1'b1)begin
      case ({col_val, row_val})
        8'b1110_1110 : begin keyboard_val <= 4'h0; flag<=1; end
        8'b1110_1101 : begin keyboard_val <= 4'h4; flag<=1; end
        8'b1110_1011 : begin keyboard_val <= 4'h8; flag<=1; end
       // 8'b1110_0111 : keyboard_val <= 4'hC;
         
        8'b1101_1110 : begin keyboard_val <= 4'h1; flag<=1;end
        8'b1101_1101 : begin keyboard_val <= 4'h5;flag<=1;end
        8'b1101_1011 : begin keyboard_val <= 4'h9;flag<=1;end
       // 8'b1101_0111 : keyboard_val <= 4'hD;
         
        8'b1011_1110 : begin keyboard_val <= 4'h2;flag<=1;end
        8'b1011_1101 : begin keyboard_val <= 4'h6;flag<=1;end
       // 8'b1011_1011 : keyboard_val <= 4'hA;
      //  8'b1011_0111 : keyboard_val <= 4'hE;
         
        8'b0111_1110 : begin keyboard_val <= 4'h3; flag<=1;end
        8'b0111_1101 : begin keyboard_val <= 4'h7;flag<=1;end
      //  8'b0111_1011 : keyboard_val <= 4'hB;
      //  8'b0111_0111 : keyboard_val <= 4'hF;
      default:flag=0;		
      endcase
		end
		else flag=0;
		end
		end
//--------------------------------------
//  扫描行列值部分 结束
//--------------------------------------
 //写入密码模块 
  always@(posedge flag)//flag 是判断一个数字按下并且生效时
 begin
     case(cout)
	 0:
	 begin
	 password [23:20]<=keyboard_val;
	 cout<=cout+1;
	 flag1=1;
	 end
	 1:
	 begin
	 password[19:16]<=keyboard_val;
	 cout<=cout+1;
	 end
	 2:
	 begin
	 password[15:12]<=keyboard_val;
	 cout<=cout+1;
	 end
	 3:
	 begin
	 password[11:8]<=keyboard_val;
	 cout<=cout+1;
	 end
	 4:
	 begin
	 password[7:4]<=keyboard_val;
	 cout<=cout+1;
	 end
	 5:
	 begin
	 password[3:0]<=keyboard_val;
	 cout<=4'd0;
	 end
	 default:begin cout<=4'd0;
	         flag1=0;end
endcase
end
//判断部分
 //密码操作部分:当按rst时当前的密码被设置为初始密码,否则将输入的密码与初始密码进行比较
 always@(posedge clk)
 begin
 if(rst==1'b1)
 begin
  if(sw1==1'b0)
    begin
     previous_password<=password;//当按下rst时当前的密码被存为所密码
	  end
  if(sw1==1'b1) begin 
			if(sw2==1'b0)
			 begin
            if(previous_password==password)begin
	            red_led<=1'b1;
	            green_led<=1'b0;	end
	         else  begin
	            red_led<=1'b0;
	            green_led<=1'b1;	end 
		    end
		   else begin
		    red_led<=1'b1;
	       green_led<=1'b1;  end
          end
if(flag2==1) begin
green_led<=1'b1;
yellow_led<=1'b1;
red_led<=1'b1; end
end 
end  
endmodule

数码管模块

module LED (seg_data_1,seg_data_2,seg_led_1,seg_led_2);
 
	input [3:0] seg_data_1;						//数码管需要显示0~9十个数字,所以最少需要4位输入做译码
	input [3:0] seg_data_2;						//小脚丫上第二个数码管
	output [8:0] seg_led_1;						//在小脚丫上控制一个数码管需要9个信号 MSB~LSB=DIG、DP、G、F、E、D、C、B、A
	output [8:0] seg_led_2;						//在小脚丫上第二个数码管的控制信号  MSB~LSB=DIG、DP、G、F、E、D、C、B、A
 
        reg [8:0] seg [9:0];                                            //定义了一个reg型的数组变量,相当于一个10*9的存储器,存储器一共有10个数,每个数有9位宽
 
        initial                                                         //在过程块中只能给reg型变量赋值,Verilog中有两种过程块always和initial
                                                                        //initial和always不同,其中语句只执行一次
	    begin
              seg[0] = 9'h3f;                                           //对存储器中第一个数赋值9'b00_0011_1111,相当于共阴极接地,DP点变低不亮,7段显示数字  0
	      seg[1] = 9'h06;                                           //7段显示数字  1
	      seg[2] = 9'h5b;                                           //7段显示数字  2
	      seg[3] = 9'h4f;                                           //7段显示数字  3
	      seg[4] = 9'h66;                                           //7段显示数字  4
	      seg[5] = 9'h6d;                                           //7段显示数字  5
	      seg[6] = 9'h7d;                                           //7段显示数字  6
	      seg[7] = 9'h07;                                           //7段显示数字  7
	      seg[8] = 9'h7f;                                           //7段显示数字  8
	      seg[9] = 9'h6f;                                           //7段显示数字  9
            end
 
        assign seg_led_1 = seg[seg_data_1];          //连续赋值,这样输入不同四位数,就能输出对于译码的9位输出
        assign seg_led_2 = seg[seg_data_2];
 
endmodule


分频模块

module divide (clk,rst_n,clkout);
 
        input 	clk,rst_n;                       //输入信号,其中clk连接到FPGA的C1脚,频率为12MHz
        output	clkout;                          //输出信号,可以连接到LED观察分频的时钟
 
     //parameter是verilog里常数语句
	parameter	WIDTH	= 3;             //计数器的位数,计数的最大值为 2**WIDTH-1
	parameter	N	= 12000000/8;          //分频系数,请确保 N < 2**WIDTH-1,否则计数会溢出
  // reg N1=N/count;
	reg 	[WIDTH-1:0]	cnt_p,cnt_n;     //cnt_p为上升沿触发时的计数器,cnt_n为下降沿触发时的计数器
	reg	clk_p,clk_n;     //clk_p为上升沿触发时分频时钟,clk_n为下降沿触发时分频时钟
 
 	
	//上升沿触发时计数器的控制
	always @ (posedge clk or negedge rst_n )         //posedge和negedge是verilog表示信号上升沿和下降沿
                                                         //当clk上升沿来临或者rst_n变低的时候执行一次always里的语句
		begin
			if(!rst_n)
				cnt_p<=0;
			else if (cnt_p==(N-1))
				cnt_p<=0;
			else cnt_p<=cnt_p+1;             //计数器一直计数,当计数到N-1的时候清零,这是一个模N的计数器
		end
         
//上升沿触发的分频时钟输出,如果N为奇数得到的时钟占空比不是50%;如果N为偶数得到的时钟占空比为50%
         always @ (posedge clk or negedge rst_n)
		begin
			if(!rst_n)
				clk_p<=0;
			else if (cnt_p<N>>1)          //N>>1表示右移一位,相当于除以2去掉余数
				clk_p<=0;
			else 
				clk_p<=1;               //得到的分频时钟正周期比负周期多一个clk时钟
		end	

	
	        //下降沿触发时计数器的控制        	
	always @ (negedge clk or negedge rst_n)
		begin
			if(!rst_n)
				cnt_n<=0;
			else if (cnt_n==(N-1))
				cnt_n<=0;
			else cnt_n<=cnt_n+1;
		end
		
 //下降沿触发的分频时钟输出,和clk_p相差半个时钟
	always @ (negedge clk)
		begin
			if(!rst_n)
				clk_n<=0;
			else if (cnt_n<N>>1)  
				clk_n<=0;
			else 
				clk_n<=1;                //得到的分频时钟正周期比负周期多一个clk时钟
		end
 
        assign clkout = (N==1)?clk:(N[0])?(clk_p&clk_n):clk_p;      //条件判断表达式
                                                                    //当N=1时,直接输出clk
                                                                    //当N为偶数也就是N的最低位为0,N(0)=0,输出clk_p
                                                                    //当N为奇数也就是N最低位为1,N(0)=1,输出clk_p&clk_n。正周期多所以是相与
endmodule     
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值