打印
[FPGA]

至简设计系列_电子密码锁

[复制链接]
696|0
手机看帖
扫描二维码
随时随地手机跟帖
跳转到指定楼层
楼主
guyu_1|  楼主 | 2020-11-7 16:58 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
至简设计系列_电子密码锁

--作者:肖肖肖
--案例作者: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顶层信号
  
信号名
  
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参考代码

下面是使用工程的顶层代码:
  • 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接口信号
  
信号名
  
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.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接口信号
  
信号名
  
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)。
计数器架构
本模块的某些状态跳转之间存在一定的时间间隔,根据项目功能要求,一共有两种时间的间隔: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,一个时钟周期为20ns100_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接口信号
  
信号名
  
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.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]复制代码


使用特权

评论回复

相关帖子

发新帖 我要提问
您需要登录后才可以回帖 登录 | 注册

本版积分规则

53

主题

61

帖子

2

粉丝