–作者:肖肖肖
本文为明德扬原创及录用文章,转载请注明出处!

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时,返回原来的状态。(即输入密码超时,返回原状态)
对于点拨开发板,使用矩阵按键输入(本文以点拨603开发板为例)。
对于Mp801开发板,密码显示及确认:无论在OPEN,还是LOCKED状态下,均可以通过拨码开关输入密码。当有拨码开关拨动时,数码管当前显示的OPEN或LOCKED消失,并显示当前输入的密码,暂未输入的密码位不显示。4位密码输入完毕后,再拨动拨码开关时视为无效输入,当前显示的密码不改变。4位密码输入完毕后,按下确认键后,系统判断密码是否正确。
拨码开关及按键:初始状态下,拨码开关全部往下拨。当拨码开关向上拨后,再向下拨(回到初始状态),表示一个数字的有效输入。按键每按下一次(会自动弹起),为一次有效输入(复位/确认)。

1.1.3 系统结构框图

系统结构框图如下图一所示:

图一

1.1.4 模块功能

按键检测模块实现功能
1、检测按键的数值

控制模块实现功能
1、对接收到的按键数值进行判断和控制对应的密码锁状态,实现对输入密码的正误判断和对密码锁的开启和闭合控制。

数码管显示模块实现功能
1、显示输入的密码数值;
2、显示当前密码锁的状态(开启状态或者闭锁状态);
3、提示密码输入错误的状态。

1.1.5 顶层信号

信号名 I/O 位宽 定义
clk I 1 系统工作时钟 50M
rst_n I 1 系统复位信号,低电平有效
key_col I 4 矩阵键盘列信号
key_row O 4 矩阵键盘行信号
seg_sel O 6 6位数码管位选信号
segment O 8 8位数码管段选信号

1.1.6 参考代码

下面是使用工程的顶层代码:

1.module top_mdyPwdlock_keyscan(
2.    clk             ,
3.    rst_n           ,
4.
5.    key_col         ,
6.    key_row         ,
7.
8.    seg_sel         ,
9.    segment
10.
11.    );
12.
13.    input               clk                 ;
14.    input               rst_n               ;
15.    input [3:0]         key_col             ;
16.
17.    output[5:0]         seg_sel             ;
18.    output[7:0]         segment             ;
19.    output[3:0]         key_row             ;
20.
21.    wire  [5:0]         seg_sel             ;
22.    wire  [7:0]         segment             ;
23.    wire  [3:0]         key_row             ;
24.
25.    wire  [3:0]         key_out             ;
26.    wire                key_vld             ;
27.    wire  [6*5-1:0]     seg_dout            ;
28.    wire  [5:0]         seg_dout_vld        ;
29.
30.
31.    key_scan u_key_scan(
32.        .clk                (clk           ),
33.        .rst_n              (rst_n         ),
34.        .key_col            (key_col       ),
35.        .key_row            (key_row       ),
36.        .key_out            (key_out       ),
37.        .key_vld            (key_vld       )
38.    );
39.
40.
41.    control u_ctrl(
42.        .clk                (clk            ),
43.        .rst_n              (rst_n          ),
44.
45.        .key_num            (key_out        ),
46.        .key_vld            (key_vld        ),
47.
48.        .seg_dout           (seg_dout       ),
49.        .seg_dout_vld       (seg_dout_vld   )
50.    );
51.
52.
53.    seg_display u_segment(
54.        .clk                (clk            ),
55.        .rst_n              (rst_n          ),
56.
57.        .din                (seg_dout       ),
58.        .din_vld            (seg_dout_vld   ),
59.
60.        .segment            (segment        ),
61.        .seg_sel            (seg_sel        )
62.    );
63.
64.
65.    endmodule

1.2 按键检测模块设计

1.2.1 接口信号

信号名 I/O 位宽 定义
clk I 1 系统工作时钟 50M
rst_n I 1 系统复位信号,低电平有效
key_col I 4 矩阵按键列信号
key_row O 4 矩阵按键行信号
key_out O 4 输出的按键有效数值
key_vld O 1 按键有效指示信号

1.2.2 设计思路

在前面的案例中已经有矩阵按键检测模块的介绍,所以这里不在过多介绍,详细介绍请看下方链接:
http://www.fpgabbs.cn/forum.php?mod=viewthread&tid=310&highlight=%BE%D8%D5%F3
其中,按键的功能面板如下图所示:

1.2.3 参考代码

1.module  key_scan(
2.                 clk    ,
3.                 rst_n  ,
4.                 key_col,
5.                 key_row,
6.                 key_out,
7.                 key_vld
8.               );
9.
10.    parameter      KEY_W  =         4 ;
11.    parameter      CHK_COL  =   0 ;
12.    parameter      CHK_ROW  =   1 ;
13.    parameter      DELAY    =   2 ;
14.    parameter      WAIT_END =   3 ;
15.    parameter      COL_CNT  =   16;
16.    parameter      TIME_20MS=   1000000;
17.
18.    input               clk    ;
19.    input               rst_n  ;
20.    input  [3:0]        key_col;
21.
22.    output              key_vld;
23.    output[3:0]         key_out;
24.    output[KEY_W-1:0]   key_row;
25.
26.    reg   [3:0]         key_out;
27.    reg   [KEY_W-1:0]   key_row;
28.    reg                 key_vld;
29.
30.    reg [3:0]           key_col_ff0;
31.    reg [3:0]           key_col_ff1;
32.    reg [1:0]           key_col_get;
33.    wire                shake_flag ;
34.    reg                 shake_flag_ff0;
35.    reg[3:0]            state_c;
36.    reg [19:0]          shake_cnt;
37.    reg[3:0]            state_n;
38.    reg [1:0]           row_index;
39.    reg[15:0]           row_cnt;
40.    wire                chk_col2chk_row ;
41.    wire                chk_row2delay   ;
42.    wire                delay2wait_end  ;
43.    wire                wait_end2chk_col;
44.
45.
46.always  @(posedge clk or negedge rst_n)begin
47.    if(rst_n==1'b0)begin
48.        key_col_ff0 <= 4'b1111;
49.        key_col_ff1 <= 4'b1111;
50.    end
51.    else begin
52.        key_col_ff0 <= key_col    ;
53.        key_col_ff1 <= key_col_ff0;
54.    end
55.end
56.
57.
58.wire        add_shake_cnt ;
59.always @(posedge clk or negedge rst_n) begin
60.    if (rst_n==0) begin
61.        shake_cnt <= 0;
62.    end
63.    else if(add_shake_cnt) begin
64.        if(shake_flag)
65.            shake_cnt <= 0;
66.        else
67.            shake_cnt <= shake_cnt+1 ;
68.   end
69.end
70.assign add_shake_cnt = key_col_ff1!=4'hf;
71.assign shake_flag = add_shake_cnt  && shake_cnt == TIME_20MS-1 ;
72.
73.
74.always  @(posedge clk or negedge rst_n)begin
75.    if(rst_n==1'b0)begin
76.        state_c <= CHK_COL;
77.    end
78.    else begin
79.        state_c <= state_n;
80.    end
81.end
82.
83.always  @(*)begin
84.    case(state_c)
85.        CHK_COL: begin
86.                     if(shake_flag && shake_flag_ff0==1'b0)begin
87.                         state_n = CHK_ROW;
88.                     end
89.                     else begin
90.                         state_n = CHK_COL;
91.                     end
92.                 end
93.        CHK_ROW: begin
94.                     if(row_index==3 && row_cnt==0)begin
95.                         state_n = DELAY;
96.                     end
97.                     else begin
98.                         state_n = CHK_ROW;
99.                     end
100.                 end
101.        DELAY :  begin
102.                     if(row_cnt==0)begin
103.                         state_n = WAIT_END;
104.                     end
105.                     else begin
106.                         state_n = DELAY;
107.                     end
108.                 end
109.        WAIT_END: begin
110.                     if(key_col_ff1==4'hf)begin
111.                         state_n = CHK_COL;
112.                     end
113.                     else begin
114.                         state_n = WAIT_END;
115.                     end
116.                  end
117.       default: state_n = CHK_COL;
118.    endcase
119.end
120.
121.assign chk_col2chk_row = shake_flag && shake_flag_ff0 ==1'b0;
122.assign chk_row2delay   = row_index==3 && row_cnt==0;
123.assign delay2wait_end  = row_cnt==0;
124.assign wait_end2chk_col= key_col_ff1==4'hf;
125.
126.always  @(posedge clk or negedge rst_n)begin
127.    if(rst_n==1'b0)begin
128.        key_row <= 4'b0;
129.    end
130.    else if(state_c==CHK_ROW)begin
131.        key_row <= ~(1'b1 << row_index);
132.    end
133.    else begin
134.        key_row <= 4'b0;
135.    end
136.end
137.
138.always  @(posedge clk or negedge rst_n)begin
139.    if(rst_n==1'b0)begin
140.        row_index <= 0;
141.    end
142.    else if(state_c==CHK_ROW)begin
143.       if(row_cnt==0)begin
144.           if(row_index==3)
145.               row_index <= 0;
146.           else
147.               row_index <= row_index + 1;
148.       end
149.    end
150.    else begin
151.        row_index <= 0;
152.    end
153.end
154.
155.
156.wire        add_row_cnt ;
157.wire        end_row_cnt ;
158.always @(posedge clk or negedge rst_n) begin
159.    if (rst_n==0) begin
160.        row_cnt <= COL_CNT;
161.    end
162.    else if(add_row_cnt) begin
163.        if(end_row_cnt)
164.            row_cnt <= COL_CNT;
165.        else
166.            row_cnt <= row_cnt-1 ;
167.   end
168.   else begin
169.       row_cnt <= COL_CNT;
170.   end
171.end
172.assign add_row_cnt = state_c==CHK_ROW || state_c==DELAY;
173.assign end_row_cnt = add_row_cnt  && row_cnt == 0 ;
174.
175.
176.
177.always  @(posedge clk or negedge rst_n)begin
178.    if(rst_n==1'b0)begin
179.        shake_flag_ff0 <= 1'b0;
180.    end
181.    else begin
182.        shake_flag_ff0 <= shake_flag;
183.    end
184.end
185.
186.always  @(posedge clk or negedge rst_n)begin
187.    if(rst_n==1'b0)begin
188.        key_col_get <= 0;
189.    end
190.    else if(state_c==CHK_COL && shake_flag==1'b1 && shake_flag_ff0==1'b0) begin
191.        if(key_col_ff1==4'b1110)
192.            key_col_get <= 0;
193.        else if(key_col_ff1==4'b1101)
194.            key_col_get <= 1;
195.        else if(key_col_ff1==4'b1011)
196.            key_col_get <= 2;
197.        else
198.            key_col_get <= 3;
199.    end
200.end
201.
202.
203.always  @(posedge clk or negedge rst_n)begin
204.    if(rst_n==1'b0)begin
205.        key_out <= 0;
206.    end
207.    else if(state_c==CHK_ROW && row_cnt==0)begin
208.        key_out <= {row_index,key_col_get};
209.    end
210.    else begin
211.        key_out <= 0;
212.    end
213.end
214.
215.always  @(posedge clk or negedge rst_n)begin
216.    if(rst_n==1'b0)begin
217.        key_vld <= 1'b0;
218.    end
219.    else if(state_c==CHK_ROW && row_cnt==0 && key_col_ff1[key_col_get]==1'b0)begin
220.        key_vld <= 1'b1;
221.    end
222.    else begin
223.        key_vld <= 1'b0;
224.    end
225.end
226.
227.endmodule

1.3 控制模块设计

1.3.1 接口信号

信号名 I/O 位宽 定义
clk I 1 系统工作时钟 50M
rst_n I 1 系统复位信号,低电平有效
key_num I 4 输入的按键号
key_vld I 1 按键有效指示信号
seg_dout O 30 30bit的数码管显示数据,每5bit为一个字符(对应一个数码管),一共表示6个数码管的显示数据。
seg_dout_vld O 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)。
以下为本模块的状态跳转图:

复位后,状态机进入LOCKED的状态,即初始状态为LOCKED;
在LOCKED状态下:
A.有按键按下,跳到PASSWORD状态;
B.否则,保持LOCKED状态不变;
在OPEN状态下:
A.有按键按下,跳到PASSWORD状态;
B.否则,保持OPEN状态不变;
在PASSWORD状态下:
A.有密码输入但超过10秒没有确认,跳到原来的LOCKED状态或者OPEN状态;
B.密码正确输入并确认两次,跳到OPEN状态;
C.密码错误输入并确认,跳到ERROR状态;
D.否则,保持PASSWORD状态不变;
在ERROR状态下:
A.提示输入错误2秒,跳到LOCKED状态;
B.否则,保持ERROR状态不变;
无论当前处于什么状态,只要不满足状态间的跳转条件就跳到LOCKED状态。

计数器架构
本模块的某些状态跳转之间存在一定的时间间隔,根据项目功能要求,一共有两种时间的间隔:10秒的等待输入时间间隔和2秒的显示提示时间间隔。
以下为计数器的架构示意图:

10秒计数器cnt_10s_nvld:用于计算10秒的时间。加一条件为state_c == PASSWORD,表示进入密码输入状态就开始计数。结束条件为数500_000_000个,系统时钟为50M,一个时钟周期为20ns,500_000_000个时钟周期就是10秒。
2秒计数器cnt_2s:用于计算2秒的时间。加一条件为state_c==ERROR,表示进入提示输入错误状态就开始计数。结束条件为数100_000_000个,系统时钟为50M,一个时钟周期为20ns,100_000_000个时钟周期就是2秒。

1.3.3 参考代码

1.module control(
2.    clk             ,
3.    rst_n           ,
4.
5.    key_num         ,
6.    key_vld         ,
7.
8.    seg_dout        ,
9.    seg_dout_vld
10.
11.    );
12.
13.    parameter PASSWORD_INI     = 16'h2345    ;
14.    parameter CHAR_O           = 5'h10       ;
15.    parameter CHAR_P           = 5'h11       ;
16.    parameter CHAR_E           = 5'h12       ;
17.    parameter CHAR_N           = 5'h13       ;
18.    parameter CHAR_L           = 5'h14       ;
19.    parameter CHAR_C           = 5'h15       ;
20.    parameter CHAR_K           = 5'h16       ;
21.    parameter CHAR_D           = 5'h17       ;
22.    parameter CHAR_R           = 5'h18       ;
23.    parameter NONE_DIS         = 5'h1F       ;
24.
25.    parameter C_10S_WID        = 29          ;
26.    parameter C_10S_NUM        = 500_000_000 ;
27.    parameter C_2S_WID         = 27          ;
28.    parameter C_2S_NUM         = 100_000_000 ;
29.    parameter C_PWD_WID        = 3           ;
30.
31.    input               clk                 ;
32.    input               rst_n               ;
33.    input [3:0]         key_num             ;
34.    input               key_vld             ;
35.
36.    output[6*5-1:0]     seg_dout            ;
37.    output[5:0]         seg_dout_vld        ;
38.
39.    reg   [6*5-1:0]     seg_dout            ;
40.    wire  [5:0]         seg_dout_vld        ;
41.
42.    reg   [1:0]         state_c   /* synthesis preserve */          ;
43.    reg   [1:0]         state_n             ;
44.    reg                 lock_stata_flag     ;
45.    reg                 password_correct_twice  ;
46.
47.    reg   [C_2S_WID-1:0]    cnt_2s          ;
48.    reg   [C_10S_WID-1:0]   cnt_10s_nvld    ;
49.    reg   [C_PWD_WID-1:0]   cnt_password    ;
50.
51.    reg   [15:0]            password        ;
52.
53.    parameter LOCKED    = 2'b00             ;
54.    parameter OPEN      = 2'b01             ;
55.    parameter PASSWORD  = 2'b10             ;
56.    parameter ERROR     = 2'b11             ;
57.
58.    always@(posedge clk or negedge rst_n)begin
59.        if(!rst_n)begin
60.            state_c <= LOCKED;
61.        end
62.        else begin
63.            state_c <= state_n;
64.        end
65.    end
66.
67.    always@(*)begin
68.        case(state_c)
69.            LOCKED:begin
70.                if(locked2password_switch)begin
71.                    state_n = PASSWORD;
72.                end
73.                else begin
74.                    state_n = state_c;
75.                end
76.            end
77.            OPEN:begin
78.                if(open2password_switch)begin
79.                    state_n = PASSWORD;
80.                end
81.                else begin
82.                    state_n = state_c;
83.                end
84.            end
85.            PASSWORD:begin
86.                if(password2locked_switch0)begin
87.                    state_n = LOCKED;
88.                end
89.                else if(password2open_switch0 || password2open_switch1)begin
90.                    state_n = OPEN;
91.                end
92.                else if(password2error_switch || password2locked_switch1)begin
93.                    state_n = ERROR;
94.                end
95.                else begin
96.                    state_n = state_c;
97.                end
98.            end
99.            ERROR:begin
100.                if(error2locked_switch0 )begin
101.                    state_n = LOCKED;
102.                end
103.                else begin
104.                    state_n = state_c;
105.                end
106.            end
107.            default:begin
108.                state_n = LOCKED;
109.            end
110.        endcase
111.    end
112.    assign locked2password_switch    = state_c==LOCKED   &&  lock_stata_flag && key_num<10 && key_vld;
113.    assign open2password_switch      = state_c==OPEN     && !lock_stata_flag && key_num<10 && key_vld;
114.    assign password2locked_switch0   = state_c==PASSWORD &&  lock_stata_flag && end_cnt_10s_nvld;
115.    assign password2locked_switch1   = state_c==PASSWORD &&  lock_stata_flag && confirm && password!=PASSWORD_INI ;
116.    assign password2open_switch0     = state_c==PASSWORD &&  lock_stata_flag && confirm && password==PASSWORD_INI &&  password_correct_twice;
117.    assign password2open_switch1     = state_c==PASSWORD && !lock_stata_flag && end_cnt_10s_nvld;
118.    assign password2error_switch     = state_c==PASSWORD && !lock_stata_flag && confirm && password!=PASSWORD_INI;
119.    assign error2locked_switch0      = state_c==ERROR    &&  end_cnt_2s;
120.
121.
122.    always  @(posedge clk or negedge rst_n)begin
123.        if(rst_n==1'b0)begin
124.            lock_stata_flag <= 1;
125.        end
126.        else if(password2locked_switch0 || password2locked_switch1 || error2locked_switch0)begin
127.            lock_stata_flag <= 1;
128.        end
129.        else if(password2open_switch0 || password2open_switch1 )begin
130.            lock_stata_flag <= 0;
131.        end
132.    end
133.
134.
135.    always  @(posedge clk or negedge rst_n)begin
136.        if(rst_n==1'b0)begin
137.            cnt_10s_nvld <= 0;
138.        end
139.        else if(end_cnt_10s_nvld)begin
140.            cnt_10s_nvld <= 0;
141.        end
142.        else if(add_cnt_10s_nvld)begin
143.            cnt_10s_nvld <= cnt_10s_nvld + 1;
144.        end
145.    end
146.    assign add_cnt_10s_nvld = state_c==PASSWORD;
147.    assign end_cnt_10s_nvld = add_cnt_10s_nvld && cnt_10s_nvld==C_10S_NUM-1;
148.
149.    assign confirm = key_num==10 && key_vld;
150.
151.
152.    always  @(posedge clk or negedge rst_n)begin
153.        if(rst_n==1'b0)begin
154.            password_correct_twice <= 0;
155.        end
156.        else if(state_c==PASSWORD && lock_stata_flag && confirm && password==PASSWORD_INI && !password_correct_twice)begin
157.            password_correct_twice <= 1;
158.        end
159.        else if(password2locked_switch0 || password2locked_switch1 || password2open_switch0 || password2open_switch1 || password2error_switch)begin
160.            password_correct_twice <= 0;
161.        end
162.    end
163.
164.
165.    always  @(posedge clk or negedge rst_n)begin
166.        if(rst_n==1'b0)begin
167.            cnt_2s <= 0;
168.        end
169.        else if(end_cnt_2s )begin
170.            cnt_2s <= 0;
171.        end
172.        else if(add_cnt_2s )begin
173.            cnt_2s <= cnt_2s + 1;
174.        end
175.    end
176.    assign add_cnt_2s = state_c==ERROR;
177.    assign end_cnt_2s = add_cnt_2s && cnt_2s==C_2S_NUM-1;
178.
179.
180.    always  @(posedge clk or negedge rst_n)begin
181.        if(rst_n==1'b0)begin
182.            seg_dout <= 0;
183.        end
184.        else if(state_c==OPEN)begin
185.            seg_dout <= {NONE_DIS,NONE_DIS,CHAR_O,CHAR_P,CHAR_E,CHAR_N};
186.        end
187.        else if(state_c==LOCKED)begin
188.            seg_dout <= {CHAR_L,CHAR_O,CHAR_C,CHAR_K,CHAR_E,CHAR_D};
189.        end
190.        else if(state_c==ERROR)begin
191.            seg_dout <= {NONE_DIS,CHAR_E,CHAR_R,CHAR_R,CHAR_O,CHAR_R};
192.        end
193.        else if(state_c==PASSWORD)begin
194.            if(cnt_password==0)
195.                seg_dout <= {NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS};
196.            else if(cnt_password==1)
197.                seg_dout <= {NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,{1'b0,password[3:0]}};
198.            else if(cnt_password==2)
199.                seg_dout <= {NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,{1'b0,password[7:4]},{1'b0,password[3:0]}};
200.            else if(cnt_password==3)
201.                seg_dout <= {NONE_DIS,NONE_DIS,NONE_DIS,{1'b0,password[11:8]},{1'b0,password[7:4]},{1'b0,password[3:0]}};
202.            else if(cnt_password==4)
203.                seg_dout <= {NONE_DIS,NONE_DIS,{1'b0,password[15:12]},{1'b0,password[11:8]},{1'b0,password[7:4]},{1'b0,password[3:0]}};
204.        end
205.    end
206.
207.    assign seg_dout_vld = 6'b11_1111;
208.
209.
210.    always  @(posedge clk or negedge rst_n)begin
211.        if(rst_n==1'b0)begin
212.            cnt_password <= 0;
213.        end
214.        else if(end_cnt_password)begin
215.            cnt_password <= 0;
216.        end
217.        else if(add_cnt_password)begin
218.            cnt_password <= cnt_password + 1;
219.        end
220.    end
221.    assign add_cnt_password = state_c!=ERROR && key_num<10 && key_vld && cnt_password<4;
222.    assign end_cnt_password = confirm || end_cnt_10s_nvld;
223.
224.
225.    always  @(posedge clk or negedge rst_n)begin
226.        if(rst_n==1'b0)begin
227.            password <= 16'h0000;
228.        end
229.        else if(add_cnt_password)begin
230.            password <= {password[11:0],key_num};
231.        end
232.    end
233.
234.
235.    endmodule

1.4 数码管显示模块设计

1.4.1 接口信号

信号名 I/O 位宽 定义
clk I 1 系统工作时钟 50M
rst_n I 1 系统复位信号,低电平有效
din I 30 30位的输入数码管显示数据。每5bit一个字符(对应一个数码管),6个数码管则一共30bit。
din_vld I 6 输入数据有效指示信号,din_vld[0]为1时,din[4:0]有效;din_vld[1]为1时,din[9:5]有效,以此类推。
segment O 8 8位数码管段选信号
seg_sel O 6 6位数码管位选信号

1.4.2 设计思路

在前面的案例中已经有数码管显示的介绍,所以这里不在过多介绍,详细介绍请看下方链接:
http://fpgabbs.com/forum.php?mod=viewthread&tid=1085&fromuid=100105
其中,数码管显示的数值和英文字母对应图像如下图所示:

1.4.3 参考代码

236.module seg_display(
237.    clk     ,
238.    rst_n   ,
239.    din     ,
240.    din_vld ,
241.    segment ,
242.    seg_sel
243.    );
244.
245.    parameter SEGMENT_NUM   = 6             ;
246.    parameter W_DATA        = 5             ;
247.
248.    parameter SEGMENT_WID   = 8             ;
249.    parameter TIME_300US    = 15_000         ;
250.
251.    parameter SEG_DATA_0    = 7'b100_0000   ;
252.    parameter SEG_DATA_1    = 7'b111_1001   ;
253.    parameter SEG_DATA_2    = 7'b010_0100   ;
254.    parameter SEG_DATA_3    = 7'b011_0000   ;
255.    parameter SEG_DATA_4    = 7'b001_1001   ;
256.    parameter SEG_DATA_5    = 7'b001_0010   ;
257.    parameter SEG_DATA_6    = 7'b000_0010   ;
258.    parameter SEG_DATA_7    = 7'b111_1000   ;
259.    parameter SEG_DATA_8    = 7'b000_0000   ;
260.    parameter SEG_DATA_9    = 7'b001_0000   ;
261.
262.    parameter SEG_CHAR_O    = 7'b010_0011   ;
263.    parameter SEG_CHAR_P    = 7'b000_1100   ;
264.    parameter SEG_CHAR_E    = 7'b000_0110   ;
265.    parameter SEG_CHAR_N    = 7'b010_1011   ;
266.    parameter SEG_CHAR_L    = 7'b100_0111   ;
267.    parameter SEG_CHAR_C    = 7'b100_0110   ;
268.    parameter SEG_CHAR_K    = 7'b000_0101   ;
269.    parameter SEG_CHAR_D    = 7'b010_0001   ;
270.    parameter SEG_CHAR_R    = 7'b010_1111   ;
271.    parameter SEG_NONE_DIS  = 7'b111_1111   ;
272.
273.    input                           clk         ;
274.    input                           rst_n       ;
275.    input [SEGMENT_NUM*W_DATA-1:0]  din         ;
276.    input [SEGMENT_NUM-1:0]         din_vld     ;
277.
278.    output[SEGMENT_WID-1:0]         segment     ;
279.    output[SEGMENT_NUM-1:0]         seg_sel     ;
280.
281.    reg   [SEGMENT_WID-1:0]         segment     ;
282.    reg   [SEGMENT_NUM-1:0]         seg_sel     ;
283.
284.    reg   [W_DATA-1:0]              segment_pre ;
285.    reg   [SEGMENT_NUM*W_DATA-1:0]  din_get     ;
286.    reg   [14:0]                    cnt_300us   ;
287.    reg   [2:0]                     cnt_sel     ;
288.    wire                            dot         ;
289.
290.
291.wire        add_cnt_300us ;
292.wire        end_cnt_300us ;
293.always @(posedge clk or negedge rst_n) begin
294.    if (rst_n==0) begin
295.        cnt_300us <= 0;
296.    end
297.    else if(add_cnt_300us) begin
298.        if(end_cnt_300us)
299.            cnt_300us <= 0;
300.        else
301.            cnt_300us <= cnt_300us+1 ;
302.   end
303.end
304.assign add_cnt_300us =1;
305.assign end_cnt_300us = add_cnt_300us  && cnt_300us == TIME_300US-1 ;
306.
307.
308.
309.wire        add_cnt_sel ;
310.wire        end_cnt_sel ;
311.always @(posedge clk or negedge rst_n) begin
312.    if (rst_n==0) begin
313.        cnt_sel <= 0;
314.    end
315.    else if(add_cnt_sel) begin
316.        if(end_cnt_sel)
317.            cnt_sel <= 0;
318.        else
319.            cnt_sel <= cnt_sel+1 ;
320.   end
321.end
322.assign add_cnt_sel = end_cnt_300us;
323.assign end_cnt_sel = add_cnt_sel  && cnt_sel == SEGMENT_NUM-1 ;
324.
325.
326.reg     [SEGMENT_NUM-1:0]   din_vvld;
327.always  @(posedge clk or negedge rst_n)begin
328.    if(rst_n==1'b0)begin
329.        din_vvld <= 0 ;
330.    end
331.    else begin
332.        din_vvld <= din_vld ;
333.    end
334.end
335.
336.
337.reg [ 2:0]  cnt     ;
338.wire        add_cnt ;
339.wire        end_cnt ;
340.always @(posedge clk or negedge rst_n) begin
341.    if (rst_n==0) begin
342.        cnt <= 0;
343.    end
344.    else if(add_cnt) begin
345.        if(end_cnt)
346.            cnt <= 0;
347.        else
348.            cnt <= cnt+1 ;
349.   end
350.end
351.assign add_cnt = 1;
352.assign end_cnt = add_cnt  && cnt == SEGMENT_NUM-1 ;
353.
354.always  @(posedge clk or negedge rst_n)begin
355.    if(rst_n==1'b0)begin
356.        din_get <= 0;
357.    end
358.    else if(din_vvld[cnt])begin
359.        din_get[W_DATA*(cnt+1)-1 -:W_DATA] <= din[W_DATA*(cnt+1)-1 -:W_DATA];
360.    end
361.end
362.
363.
364.    always  @(*)begin
365.        segment_pre = din_get[W_DATA*(cnt_sel+1)-1 -:W_DATA];
366.    end
367.
368.    always  @(posedge clk or negedge rst_n)begin
369.        if(rst_n==1'b0)begin
370.            segment <= {dot,SEG_NONE_DIS};
371.        end
372.        else if(add_cnt_300us  && cnt_300us ==10-1)begin
373.            case(segment_pre)
374.                5'h00: segment <= {dot,SEG_DATA_0};
375.                5'h01: segment <= {dot,SEG_DATA_1};
376.                5'h02: segment <= {dot,SEG_DATA_2};
377.                5'h03: segment <= {dot,SEG_DATA_3};
378.                5'h04: segment <= {dot,SEG_DATA_4};
379.                5'h05: segment <= {dot,SEG_DATA_5};
380.                5'h06: segment <= {dot,SEG_DATA_6};
381.                5'h07: segment <= {dot,SEG_DATA_7};
382.                5'h08: segment <= {dot,SEG_DATA_8};
383.                5'h09: segment <= {dot,SEG_DATA_9};
384.                5'h10: segment <= {dot,SEG_CHAR_O};
385.                5'h11: segment <= {dot,SEG_CHAR_P};
386.                5'h12: segment <= {dot,SEG_CHAR_E};
387.                5'h13: segment <= {dot,SEG_CHAR_N};
388.                5'h14: segment <= {dot,SEG_CHAR_L};
389.                5'h15: segment <= {dot,SEG_CHAR_C};
390.                5'h16: segment <= {dot,SEG_CHAR_K};
391.                5'h17: segment <= {dot,SEG_CHAR_D};
392.                5'h18: segment <= {dot,SEG_CHAR_R};
393.                5'h1F: segment <= {dot,SEG_NONE_DIS};
394.                default:segment <= {dot,SEG_NONE_DIS};
395.            endcase
396.        end
397.    end
398.    assign dot = 1'b1;
399.
400.    always@(posedge clk or negedge rst_n)begin
401.        if(rst_n==1'b0)begin
402.            seg_sel <= {SEGMENT_NUM{1'b0}};
403.        end
404.        else begin
405.            seg_sel <= ~(1'b1<<cnt_sel);
406.        end
407.    end
408.
409.
410.    endmodule

1.5 效果和总结

下图是该工程在db603开发板上的现象——密码锁初始状态和闭合状态

下图是该工程在db603开发板上的现象——提示输入错误状态

下图是该工程在db603开发板上的现象——密码锁开启状态

下图是该工程在db603开发板上的现象——输入密码状态

下图是该工程在mp801开发板上的现象——密码锁初始状态和闭合状态

下图是该工程在mp801开发板上的现象——提示输入错误状态

下图是该工程在mp801开发板上的现象——密码锁开启状态

下图是该工程在mp801开发板上的现象——输入密码状态

下图是该工程在ms980开发板上的现象——密码锁初始状态和闭合状态

下图是该工程在ms980开发板上的现象——提示输入错误状态

下图是该工程在ms980开发板上的现象——密码锁开启状态

下图是该工程在ms980开发板上的现象——输入密码状态

由于该项目的上板现象是在数码管上显示输入的密码,并且判断密码是否正确:正确则在数码管上显示OPEN,错误则在数码管上显示ERROR并提示输入错误2秒,然后数码管显示LOCKED。想观看完整现象的朋友可以看一下上板演示的视频。
感兴趣的朋友也可以访问mdy论坛进行FPGA相关工程设计学习,也可以看一下我们往期的文章。

至简设计系列_电子密码锁相关推荐

  1. 至简设计系列_简易计算器

    –作者:小黑同学 本文为明德扬原创及录用文章,转载请注明出处! 1.1 总体设计 1.1.1 概述 计算器是近代人发明的可以进行数字运算的机器.现代的电子计算器能进行数学运算的手持电子机器,拥有集成电 ...

  2. led计数电路实验报告_至简设计系列_状态机实现LED交通灯2

    --作者:肖肖肖 本文为明德扬原创及录用文章,转载请注明出处! 1.1 总体设计 1.1.1 概述 发光二极管简称为LED,是一种常用的发光器件,通过电子与空穴复合释放能量发光,可以高效的将电能转化为 ...

  3. 至简设计系列_定时转换的LED交通灯1

    –作者:肖肖肖 本文为明德扬原创及录用文章,转载请注明出处! 1.1 总体设计 1.1.1 概述 发光二极管简称为LED,是一种常用的发光器件,通过电子与空穴复合释放能量发光,它可以高效的将电能转化为 ...

  4. 至简设计系列_按键控制数字时钟

    –作者:小黑同学 本文为明德扬原创及录用文章,转载请注明出处! 1.1 总体设计 1.1.1 概述 数字时钟是采用数字电路技术实现时.分.秒计时显示的装置,可以用数字同时显示时,分,秒的精确时间并实现 ...

  5. 电子音调发生器c语言编程,单片机课程设计(论文)_电子音调发生器.doc

    单片机课程设计(论文)_电子音调发生器 电子音调发生器 姓 名 XXX 所在学院 电子信息工程学院 专业班级 通信XXXX 学 号 XXXXX 指导教师 XXXXXX 日 期 2011 年 12月 1 ...

  6. 【毕业设计-课程设计】-单片机电子密码锁设计

    资源链接在文章最后,订阅查看获取全部内容及资料,如需可私信提供硬件. 一. 主要功能: 1.按键设置6位密码,输入密码若密码正确,则锁打开.显示open! 2.密码可以自己修改(6位密码),必须是锁打 ...

  7. php中怎么判断输入密码与原密码一致_「每周FPGA案例」电子密码锁设计

    至简设计系列_电子密码锁 --作者:肖肖肖 --案例作者:WB_Yih 本文为明德扬原创及录用文章,转载请注明出处! 1.1 总体设计1.1.1 概述 随着生活质量的不断提高,加强家庭防盗安全变得非常 ...

  8. 基于51单片机的带记忆功能的电子密码锁设计

    设计要求: 1.利用51系列单片机为核心,矩阵键盘与LCD1602液晶显示屏组成硬件系统,设计一款电子密码锁: 2.该密码锁具有设置.修改六位用户密码.超次报警.超次锁定.密码错误报警等功能: 3.通 ...

  9. 基于fpga的数码管动态扫描电路设计_【至简设计案例系列】基于FPGA的密码锁设计(altera版)...

    秦红凯 明德扬FPGA科教 一.项目背景概述 随着生活质量的不断提高,加强家庭防盗安全变得非常重要,但传统机械锁的构造过于简单,很容易被打开,从而降低了安全性.数字密码锁因为它的保密性很高,安全系数也 ...

最新文章

  1. Power Query 应用领域有哪些?
  2. C#实现动态编译代码
  3. cesium js 路径_Cesium开发学习路径
  4. layui内置loading等待加载
  5. 树莓派移植SX1278 LoRa通信--使用wiringPi 移植GPIO中断
  6. 团队文化中的害群之马
  7. [field:picname/]和[field:litpic/]区别
  8. C语言——扫雷游戏详解
  9. win10重置计算机网络设置,为你解答win10下如何重置网络
  10. 人工智能发展历史 考题答案
  11. 总结一下关于扫描电子显微镜与背散射电子探测器
  12. Mesh网络,让世界“雾”起来 | INE创始人熊羽睿演讲实录
  13. 你还在纠结用什么库写 Python 命令行程序?看这一篇就够了
  14. 微信内置浏览器调试和调试微信内的H5页面汇总(持续更新...)
  15. [解决方案] LaTeX公式中的多行大括号 (如涵盖多个不等式)
  16. JSP页面分页显示数据
  17. 硬实时RTLinux安装配置详解 (一):准备工作
  18. LeetCode 300 最长上升子序列
  19. 互联网企业盈利模式全分析
  20. 面向對象在VB6語言中的應用

热门文章

  1. java小游戏贪吃蛇
  2. 微信小程序复制文本方法
  3. (干货)各大AI竞赛 Top 解决方案开源汇总+大牛经验(Kaggle,Ali,Tencent、JD、KDD Cup...)
  4. 全球经济寒冬将至?且看顶级资本大鳄的大数据分析预测
  5. 网络爬虫学习第二弹:requests库的使用
  6. 12864多级菜单实现方法
  7. 人工智能算法之三数码
  8. MySQL数据库多表查询练习题
  9. 图解JVM垃圾回收机制
  10. webRTC(十五):webrtc 文件实时传输