至简设计系列_电子密码锁
--作者:肖肖肖 --案例作者:WB_Yih 本文为明德扬原创及录用**,转载请注明出处! 1.1 总体设计
1.1.1 概述随着生活质量的不断提高,加强家庭防盗安全变得非常重要,但传统机械锁的构造过于简单,很容易被打开,从而降低了安全性。数字密码锁因为它的保密性很高,安全系数也非常高,再加上其不需要携带避免了丢失的可能,省去了因钥匙丢失而需要换锁的麻烦,受到了越来越多的人的欢迎。随看人们对高科技产品也越来越推崇,在当今社会科技的高度集中和创新,人们对日常生活中保护自身及财产安全的物品非常追捧,对其安全性的要求也非常的高。为了达到人们对锁具安全性的高要求,加强锁具的安全保密性,用密码锁来取代传统机械锁的锁具是必然趋势。数字密码锁比传统机械锁具更加的安全。在本案例的设计过程中,应用了至简设计法、状态机模板应用等,在经过逐步改进、调试等一系列工作之后,最终达到了设计目标。 基于明德扬至简设计法和明德扬设计规范,设计一个基于FPGA的密码锁、并将数值显示在数码管上,然后根据输入的键值判断密码是否正确。 1.1.2 设计目标
实现电子密码锁的功能,具体功能要求如下: 1. 密码4位,初始密码2345。 2. 密码锁状态:LOCKED和OPEN,初始状态为LOCKED。 1) 当在LOCKED状态时,连续两次输入正确密码,状态变为OPEN状态。当输入错误密码时(包括第一次就输入错误;或者第一次输入正确,第二次输入错误的情况),数码管显示ERROR 2秒后重新显示原来的状态(LOCKED)。 2) 当在OPEN状态时,一次输入错误密码,状态变为LOCKED状态。当输入正确密码时,数码管无显示,10秒后重新显示原来的状态(OPEN)。 3) 不管在何状态,当输入4位密码或者某几位密码,但未按下确认键,并超过10S时,返回原来的状态。(即输入密码超时,返回原状态) 1.1.3 系统结构框图
1.1.4模块功能按键检测模块实现功能
1、 检测按键的数值 控制模块实现功能
1、 对接收到的按键数值进行判断和控制对应的密码锁状态,实现对输入密码的正误判断和对密码锁的开启和闭合控制。 数码管显示模块实现功能
1、 显示输入的密码数值; 2、 显示当前密码锁的状态(开启状态或者闭锁状态); 3、 提示密码输入错误的状态。
1.1.5顶层信号
1.1.6参考代码
下面是使用工程的顶层代码: - module top_mdyPwdlock_keyscan(
- clk ,
- rst_n ,
- key_col ,
- key_row ,
- seg_sel ,
- segment
- );
- input clk ;
- input rst_n ;
- input [3:0] key_col ;
- output[5:0] seg_sel ;
- output[7:0] segment ;
- output[3:0] key_row ;
- wire [5:0] seg_sel ;
- wire [7:0] segment ;
- wire [3:0] key_row ;
- wire [3:0] key_out ;
- wire key_vld ;
- wire [6*5-1:0] seg_dout ;
- wire [5:0] seg_dout_vld ;
- key_scan u_key_scan(
- .clk (clk ),
- .rst_n (rst_n ),
- .key_col (key_col ),
- .key_row (key_row ),
- .key_out (key_out ),
- .key_vld (key_vld )
- );
- control u_ctrl(
- .clk (clk ),
- .rst_n (rst_n ),
- .key_num (key_out ),
- .key_vld (key_vld ),
- .seg_dout (seg_dout ),
- .seg_dout_vld (seg_dout_vld )
- );
- seg_display u_segment(
- .clk (clk ),
- .rst_n (rst_n ),
- .din (seg_dout ),
- .din_vld (seg_dout_vld ),
- .segment (segment ),
- .seg_sel (seg_sel )
- );
- endmodule
[color=rgb(51, 102, 153) !important]复制代码
1.2 按键检测模块设计1.2.1接口信号
1.2.2 设计思路
在前面的案例中已经有矩阵按键检测模块的介绍,所以这里不在过多介绍,详细介绍请看下方链接: http://www.fpgabbs.cn/forum.html?mod=viewthread&tid=310&highlight=%BE%D8%D5%F3 1.2.3参考代码- module key_scan(
- clk ,
- rst_n ,
- key_col,
- key_row,
- key_out,
- key_vld
- );
- parameter KEY_W = 4 ;
- parameter CHK_COL = 0 ;
- parameter CHK_ROW = 1 ;
- parameter DELAY = 2 ;
- parameter WAIT_END = 3 ;
- parameter COL_CNT = 16;
- parameter TIME_20MS= 1000000;
- input clk ;
- input rst_n ;
- input [3:0] key_col;
- output key_vld;
- output[3:0] key_out;
- output[KEY_W-1:0] key_row;
- reg [3:0] key_out;
- reg [KEY_W-1:0] key_row;
- reg key_vld;
- reg [3:0] key_col_ff0;
- reg [3:0] key_col_ff1;
- reg [1:0] key_col_get;
- wire shake_flag ;
- reg shake_flag_ff0;
- reg[3:0] state_c;
- reg [19:0] shake_cnt;
- reg[3:0] state_n;
- reg [1:0] row_index;
- reg[15:0] row_cnt;
- wire chk_col2chk_row ;
- wire chk_row2delay ;
- wire delay2wait_end ;
- wire wait_end2chk_col;
- always @(posedge clk or negedge rst_n) begin
- if (rst_n==0) begin
- row_cnt <= COL_CNT;
- end
- else if(add_row_cnt) begin
- if(end_row_cnt)
- row_cnt <= COL_CNT;
- else
- row_cnt <= row_cnt-1 ;
- end
- else begin
- row_cnt <= COL_CNT;
- end
- end
- assign add_row_cnt = state_c==CHK_ROW || state_c==DELAY;
- assign end_row_cnt = add_row_cnt && row_cnt == 0 ;
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- key_vld <= 1'b0;
- end
- else if(state_c==CHK_ROW && row_cnt==0 && key_col_ff1[key_col_get]==1'b0)begin
- key_vld <= 1'b1;
- end
- else begin
- key_vld <= 1'b0;
- end
- end
- endmodue
[color=rgb(51, 102, 153) !important]复制代码
1.3 控制模块设计1.3.1接口信号 信号名 | | | | | | | | | | | | | | | | | | | | | | | 30bit的数码管显示数据,每5bit为一个字符(对应一个数码管),一共表示6个数码管的显示数据。 | | | | 数码管显示数据有效指示信号,seg_dout_vld [0]为1时,seg_dout[4:0]有效;seg_dout_vld [1]为1时,seg_dout[9:5]有效,以此为推。 |
1.3.2设计思路
状态机架构 本模块的主要功能是根据输入的按键信息进行不同状态的判断和切换当前工作状态。根据项目功能要求,一共有四种工作状态:密码锁开启状态(open)、密码锁闭合状态(clocked)、输入密码状态(password)和提示输入错误状态(error)。 计数器架构 本模块的某些状态跳转之间存在一定的时间间隔,根据项目功能要求,一共有两种时间的间隔:10秒的等待输入时间间隔和2秒的显示提示时间间隔。 10秒计数器cnt_10s_nvld:用于计算10秒的时间。加一条件为state_c==PASSWORD,表示进入密码输入状态就开始计数。结束条件为数500_000_000个,系统时钟为50M,一个时钟周期为20ns500_000_000个时钟周期就是10秒。 2秒计数器cnt_2s:用于计算2秒的时间。加一条件为state_c==ERROR,表示进入提示输入错误状态就开始计数。结束条件为数100_000_000个,系统时钟为50M,一个时钟周期为20ns,100_000_000个时钟周期就是2秒。 1.3.3参考代码
- module control(
- clk ,
- rst_n ,
- key_num ,
- key_vld ,
- seg_dout ,
- seg_dout_vld
- );
- parameter PASSWORD_INI = 16'h2345 ;
- parameter CHAR_O = 5'h10 ;
- parameter CHAR_P = 5'h11 ;
- parameter CHAR_E = 5'h12 ;
- parameter CHAR_N = 5'h13 ;
- parameter CHAR_L = 5'h14 ;
- parameter CHAR_C = 5'h15 ;
- parameter CHAR_K = 5'h16 ;
- parameter CHAR_D = 5'h17 ;
- parameter CHAR_R = 5'h18 ;
- parameter NONE_DIS = 5'h1F ;
- parameter C_10S_WID = 29 ;
- parameter C_10S_NUM = 500_000_000 ;
- //cnt_2s
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- cnt_2s <= 0;
- end
- else if(end_cnt_2s )begin
- cnt_2s <= 0;
- end
- else if(add_cnt_2s )begin
- cnt_2s <= cnt_2s + 1;
- end
- end
- assign add_cnt_2s = state_c==ERROR;
- assign end_cnt_2s = add_cnt_2s && cnt_2s==C_2S_NUM-1;
- //seg_dout_vld
- assign seg_dout_vld = 6'b11_1111;
- //cnt_password
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- cnt_password <= 0;
- end
- else if(end_cnt_password)begin
- cnt_password <= 0;
- end
- else if(add_cnt_password)begin
- cnt_password <= cnt_password + 1;
- end
- end
- assign add_cnt_password = state_c!=ERROR && key_num<10 && key_vld && cnt_password<4;
- assign end_cnt_password = confirm || end_cnt_10s_nvld;
- //password
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- password <= 16'h0000;
- end
- else if(add_cnt_password)begin
- password <= {password[11:0],key_num};
- end
- end
- endmodule
[color=rgb(51, 102, 153) !important]复制代码
1.4 数码管显示模块设计1.4.1接口信号
信号名 | | | | | | | | | | | | | | | 30位的输入数码管显示数据。每5bit一个字符(对应一个数码管),6个数码管则一共30bit。 | | | | 输入数据有效指示信号,din_vld[0]为1时,din[4:0]有效;din_vld[1]为1时,din[9:5]有效,以此类推。 | | | | | | | | |
1.4.2设计思路
在前面的案例中已经有数码管显示的介绍,所以这里不在过多介绍,详细介绍请看下方链接: http://fpgabbs.com/forum.html?mod=viewthread&tid=1085&fromuid=100105 1.4.3参考代码
- module seg_display(
- clk ,
- rst_n ,
- din ,
- din_vld ,
- segment ,
- seg_sel
- );
- parameter SEGMENT_NUM = 6 ;
- parameter W_DATA = 5 ;
- parameter SEGMENT_WID = 8 ;
- parameter TIME_300US = 15_000 ;
- parameter SEG_DATA_0 = 7'b100_0000 ;
- parameter SEG_DATA_1 = 7'b111_1001 ;
- parameter SEG_DATA_2 = 7'b010_0100 ;
- parameter SEG_DATA_3 = 7'b011_0000 ;
- parameter SEG_DATA_4 = 7'b001_1001 ;
- parameter SEG_DATA_5 = 7'b001_0010 ;
- parameter SEG_DATA_6 = 7'b000_0010 ;
- parameter SEG_DATA_7 = 7'b111_1000 ;
- parameter SEG_DATA_8 = 7'b000_0000 ;
- parameter SEG_DATA_9 = 7'b001_0000 ;
- parameter SEG_CHAR_O = 7'b010_0011 ;
- parameter SEG_CHAR_P = 7'b000_1100 ;
- parameter SEG_CHAR_E = 7'b000_0110 ;
- parameter SEG_CHAR_N = 7'b010_1011 ;
- parameter SEG_CHAR_L = 7'b100_0111 ;
- parameter SEG_CHAR_C = 7'b100_0110 ;
- parameter SEG_CHAR_K = 7'b000_0101 ;
- parameter SEG_CHAR_D = 7'b010_0001 ;
- parameter SEG_CHAR_R = 7'b010_1111 ;
- parameter SEG_NONE_DIS = 7'b111_1111 ;
- input clk ;
- input rst_n ;
- input [SEGMENT_NUM*W_DATA-1:0] din ;
- input [SEGMENT_NUM-1:0] din_vld ;
- wire add_cnt_sel ;
- wire end_cnt_sel ;
- always @(posedge clk or negedge rst_n) begin
- if (rst_n==0) begin
- cnt_sel <= 0;
- end
- else if(add_cnt_sel) begin
- if(end_cnt_sel)
- cnt_sel <= 0;
- else
- cnt_sel <= cnt_sel+1 ;
- end
- end
- assign add_cnt_sel = end_cnt_300us;
- assign end_cnt_sel = add_cnt_sel && cnt_sel == SEGMENT_NUM-1 ;
- always @(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- segment <= {dot,SEG_NONE_DIS};
- end
- else if(add_cnt_300us && cnt_300us ==10-1)begin
- case(segment_pre)
- 5'h00: segment <= {dot,SEG_DATA_0};
- 5'h01: segment <= {dot,SEG_DATA_1};
- 5'h02: segment <= {dot,SEG_DATA_2};
- 5'h03: segment <= {dot,SEG_DATA_3};
- 5'h04: segment <= {dot,SEG_DATA_4};
- 5'h05: segment <= {dot,SEG_DATA_5};
- 5'h06: segment <= {dot,SEG_DATA_6};
- 5'h07: segment <= {dot,SEG_DATA_7};
- 5'h08: segment <= {dot,SEG_DATA_8};
- 5'h09: segment <= {dot,SEG_DATA_9};
- 5'h10: segment <= {dot,SEG_CHAR_O};
- 5'h11: segment <= {dot,SEG_CHAR_P};
- 5'h12: segment <= {dot,SEG_CHAR_E};
- 5'h13: segment <= {dot,SEG_CHAR_N};
- 5'h14: segment <= {dot,SEG_CHAR_L};
- 5'h15: segment <= {dot,SEG_CHAR_C};
- 5'h16: segment <= {dot,SEG_CHAR_K};
- 5'h17: segment <= {dot,SEG_CHAR_D};
- 5'h18: segment <= {dot,SEG_CHAR_R};
- 5'h1F: segment <= {dot,SEG_NONE_DIS};
- default:segment <= {dot,SEG_NONE_DIS};
- endcase
- end
- end
- assign dot = 1'b1;
- always@(posedge clk or negedge rst_n)begin
- if(rst_n==1'b0)begin
- seg_sel <= {SEGMENT_NUM{1'b0}};
- end
- else begin
- seg_sel <= ~(1'b1<<cnt_sel);
- end
- end
- endmodule
[color=rgb(51, 102, 153) !important]复制代码
|