发 帖  
原厂入驻New
[资料]

【每周FPGA案例】至简设计法-电子密码锁

2020-9-25 09:55:32  75 电子密码锁
分享

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

--作者:肖肖肖
--案例作者: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时,返回原来的状态。(即输入密码超时,返回原状态)

对于点拨开发板,使用矩阵按键输入(本文以点拨603开发板为例)。
对于Mp801开发板,密码显示及确认:无论在OPEN,还是LOCKED状态下,均可以通过拨码开关输入密码。当有拨码开关拨动时,数码管当前显示的OPENLOCKED消失,并显示当前输入的密码,暂未输入的密码位不显示。4位密码输入完毕后,再拨动拨码开关时视为无效输入,当前显示的密码不改变。4位密码输入完毕后,按下确认键后,系统判断密码是否正确。

拨码开关及按键:初始状态下,拨码开关全部往下拨。当拨码开关向上拨后,再向下拨(回到初始状态),表示一个数字的有效输入。按键每按下一次(会自动弹起),为一次有效输入(复位/确认)。



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

图一
1.1.4模块功能按键检测模块实现功能
1、  检测按键的数值


控制模块实现功能

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

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

1.1.5顶层信号
  
信号名
  
位宽
定义
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.     key_col         ,
  5.     key_row         ,

  6.     seg_sel         ,  
  7.     segment            
  8.    
  9.     );

  10.     input               clk                 ;
  11.     input               rst_n               ;
  12.     input [3:0]         key_col             ;

  13.     output[5:0]         seg_sel             ;
  14.     output[7:0]         segment             ;
  15.     output[3:0]         key_row             ;

  16.     wire  [5:0]         seg_sel             ;
  17.     wire  [7:0]         segment             ;
  18.     wire  [3:0]         key_row             ;

  19.     wire  [3:0]         key_out             ;
  20.     wire                key_vld             ;
  21.     wire  [6*5-1:0]     seg_dout            ;
  22.     wire  [5:0]         seg_dout_vld        ;

  23.    
  24.    
  25.     key_scan u_key_scan(
  26.         .clk                (clk           ),        
  27.         .rst_n              (rst_n         ),     
  28.         .key_col            (key_col       ),
  29.         .key_row            (key_row       ),
  30.         .key_out            (key_out       ),
  31.         .key_vld            (key_vld       )
  32.     );


  33.     control u_ctrl(
  34.         .clk                (clk            ),      
  35.         .rst_n              (rst_n          ),      
  36.                                       
  37.         .key_num            (key_out        ),      
  38.         .key_vld            (key_vld        ),      
  39.                                       
  40.         .seg_dout           (seg_dout       ),      
  41.         .seg_dout_vld       (seg_dout_vld   )        
  42.     );

  43.     seg_display u_segment(
  44.         .clk                (clk            ),      
  45.         .rst_n              (rst_n          ),      

  46.         .din                (seg_dout       ),      
  47.         .din_vld            (seg_dout_vld   ),      

  48.         .segment            (segment        ),      
  49.         .seg_sel            (seg_sel        )        
  50.     );

  51.     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.     parameter      KEY_W  =         4 ;
  10.     parameter      CHK_COL  =   0 ;
  11.     parameter      CHK_ROW  =   1 ;
  12.     parameter      DELAY    =   2 ;
  13.     parameter      WAIT_END =   3 ;
  14.     parameter      COL_CNT  =   16;
  15.     parameter      TIME_20MS=   1000000;

  16.     input               clk    ;
  17.     input               rst_n  ;
  18.     input  [3:0]        key_col;

  19.     output              key_vld;
  20.     output[3:0]         key_out;
  21.     output[KEY_W-1:0]   key_row;

  22.     reg   [3:0]         key_out;
  23.     reg   [KEY_W-1:0]   key_row;
  24.     reg                 key_vld;


  25.     reg [3:0]           key_col_ff0;
  26.     reg [3:0]           key_col_ff1;
  27.     reg [1:0]           key_col_get;
  28.     wire                shake_flag ;
  29.     reg                 shake_flag_ff0;
  30.     reg[3:0]            state_c;
  31.     reg [19:0]          shake_cnt;
  32.     reg[3:0]            state_n;
  33.     reg [1:0]           row_index;
  34.     reg[15:0]           row_cnt;
  35.     wire                chk_col2chk_row ;
  36.     wire                chk_row2delay   ;
  37.     wire                delay2wait_end  ;
  38.     wire                wait_end2chk_col;


  39. always  @(posedge clk or negedge rst_n)begin
  40.     if(rst_n==1'b0)begin
  41.         key_col_ff0 <= 4'b1111;
  42.         key_col_ff1 <= 4'b1111;
  43.     end
  44.     else begin
  45.         key_col_ff0 <= key_col    ;
  46.         key_col_ff1 <= key_col_ff0;
  47.     end
  48. end


  49. wire        add_shake_cnt ;
  50. always @(posedge clk or negedge rst_n) begin
  51.     if (rst_n==0) begin
  52.         shake_cnt <= 0;
  53.     end
  54.     else if(add_shake_cnt) begin
  55.         if(shake_flag)
  56.             shake_cnt <= 0;
  57.         else
  58.             shake_cnt <= shake_cnt+1 ;
  59.    end
  60. end
  61. assign add_shake_cnt = key_col_ff1!=4'hf;
  62. assign shake_flag = add_shake_cnt  && shake_cnt == TIME_20MS-1 ;

  63. always  @(posedge clk or negedge rst_n)begin
  64.     if(rst_n==1'b0)begin
  65.         state_c <= CHK_COL;
  66.     end
  67.     else begin
  68.         state_c <= state_n;
  69.     end
  70. end

  71. always  @(*)begin
  72.     case(state_c)
  73.         CHK_COL: begin
  74.                      if(shake_flag && shake_flag_ff0==1'b0)begin
  75.                          state_n = CHK_ROW;
  76.                      end
  77.                      else begin
  78.                          state_n = CHK_COL;
  79.                      end
  80.                  end
  81.         CHK_ROW: begin
  82.                      if(row_index==3 && row_cnt==0)begin
  83.                          state_n = DELAY;
  84.                      end
  85.                      else begin
  86.                          state_n = CHK_ROW;
  87.                      end
  88.                  end
  89.         DELAY :  begin
  90.                      if(row_cnt==0)begin
  91.                          state_n = WAIT_END;
  92.                      end
  93.                      else begin
  94.                          state_n = DELAY;
  95.                      end
  96.                  end
  97.         WAIT_END: begin
  98.                      if(key_col_ff1==4'hf)begin
  99.                          state_n = CHK_COL;
  100.                      end
  101.                      else begin
  102.                          state_n = WAIT_END;
  103.                      end
  104.                   end
  105.        default: state_n = CHK_COL;
  106.     endcase
  107. end

  108. assign chk_col2chk_row = shake_flag && shake_flag_ff0 ==1'b0;
  109. assign chk_row2delay   = row_index==3 && row_cnt==0;
  110. assign delay2wait_end  = row_cnt==0;
  111. assign wait_end2chk_col= key_col_ff1==4'hf;

  112. always  @(posedge clk or negedge rst_n)begin
  113.     if(rst_n==1'b0)begin
  114.         key_row <= 4'b0;
  115.     end
  116.     else if(state_c==CHK_ROW)begin
  117.         key_row <= ~(1'b1 << row_index);
  118.     end
  119.     else begin
  120.         key_row <= 4'b0;
  121.     end
  122. end

  123. always  @(posedge clk or negedge rst_n)begin
  124.     if(rst_n==1'b0)begin
  125.         row_index <= 0;
  126.     end
  127.     else if(state_c==CHK_ROW)begin
  128.        if(row_cnt==0)begin
  129.            if(row_index==3)
  130.                row_index <= 0;
  131.            else
  132.                row_index <= row_index + 1;
  133.        end
  134.     end
  135.     else begin
  136.         row_index <= 0;
  137.     end
  138. end


  139. wire        add_row_cnt ;
  140. wire        end_row_cnt ;
  141. always @(posedge clk or negedge rst_n) begin
  142.     if (rst_n==0) begin
  143.         row_cnt <= COL_CNT;
  144.     end
  145.     else if(add_row_cnt) begin
  146.         if(end_row_cnt)
  147.             row_cnt <= COL_CNT;
  148.         else
  149.             row_cnt <= row_cnt-1 ;
  150.    end
  151.    else begin
  152.        row_cnt <= COL_CNT;
  153.    end
  154. end
  155. assign add_row_cnt = state_c==CHK_ROW || state_c==DELAY;
  156. assign end_row_cnt = add_row_cnt  && row_cnt == 0 ;



  157. always  @(posedge clk or negedge rst_n)begin
  158.     if(rst_n==1'b0)begin
  159.         shake_flag_ff0 <= 1'b0;
  160.     end
  161.     else begin
  162.         shake_flag_ff0 <= shake_flag;
  163.     end
  164. end

  165. always  @(posedge clk or negedge rst_n)begin
  166.     if(rst_n==1'b0)begin
  167.         key_col_get <= 0;
  168.     end
  169.     else if(state_c==CHK_COL && shake_flag==1'b1 && shake_flag_ff0==1'b0) begin
  170.         if(key_col_ff1==4'b1110)
  171.             key_col_get <= 0;
  172.         else if(key_col_ff1==4'b1101)
  173.             key_col_get <= 1;
  174.         else if(key_col_ff1==4'b1011)
  175.             key_col_get <= 2;
  176.         else
  177.             key_col_get <= 3;
  178.     end
  179. end


  180. always  @(posedge clk or negedge rst_n)begin
  181.     if(rst_n==1'b0)begin
  182.         key_out <= 0;
  183.     end
  184.     else if(state_c==CHK_ROW && row_cnt==0)begin
  185.         key_out <= {row_index,key_col_get};
  186.     end
  187.     else begin
  188.         key_out <= 0;
  189.     end
  190. end

  191. always  @(posedge clk or negedge rst_n)begin
  192.     if(rst_n==1'b0)begin
  193.         key_vld <= 1'b0;
  194.     end
  195.     else if(state_c==CHK_ROW && row_cnt==0 && key_col_ff1[key_col_get]==1'b0)begin
  196.         key_vld <= 1'b1;
  197.     end
  198.     else begin
  199.         key_vld <= 1'b0;
  200.     end
  201. end

  202. endmodue
复制代码







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,一个时钟周期20ns500_000_000个时钟周期就是10秒。
2秒计数器cnt_2s:用于计算2秒的时间。加一条件为state_c==ERROR,表示进入提示输入错误状态就开始计数。结束条件为数100_000_000个,系统时钟为50M,一个时钟周期为20ns100_000_000个时钟周期就是2秒。




1.3.3参考代码

  1. module control(
  2.     clk             ,
  3.     rst_n           ,

  4.     key_num         ,
  5.     key_vld         ,

  6.     seg_dout        ,
  7.     seg_dout_vld     
  8.    
  9.     );

  10.     parameter PASSWORD_INI     = 16'h2345    ;
  11.     parameter CHAR_O           = 5'h10       ;
  12.     parameter CHAR_P           = 5'h11       ;
  13.     parameter CHAR_E           = 5'h12       ;
  14.     parameter CHAR_N           = 5'h13       ;
  15.     parameter CHAR_L           = 5'h14       ;
  16.     parameter CHAR_C           = 5'h15       ;
  17.     parameter CHAR_K           = 5'h16       ;
  18.     parameter CHAR_D           = 5'h17       ;
  19.     parameter CHAR_R           = 5'h18       ;
  20.     parameter NONE_DIS         = 5'h1F       ;

  21.     parameter C_10S_WID        = 29          ;
  22.     parameter C_10S_NUM        = 500_000_000 ;
  23.     parameter C_2S_WID         = 27          ;
  24.     parameter C_2S_NUM         = 100_000_000 ;
  25.     parameter C_PWD_WID        = 3           ;

  26.     input               clk                 ;
  27.     input               rst_n               ;
  28.     input [3:0]         key_num             ;
  29.     input               key_vld             ;

  30.     output[6*5-1:0]     seg_dout            ;
  31.     output[5:0]         seg_dout_vld        ;

  32.     reg   [6*5-1:0]     seg_dout            ;
  33.     wire  [5:0]         seg_dout_vld        ;

  34.     reg   [1:0]         state_c             ;
  35.     reg   [1:0]         state_n             ;
  36.     reg                 lock_stata_flag     ;
  37.     reg                 password_correct_twice  ;
  38.    
  39.     reg   [C_2S_WID-1:0]    cnt_2s          ;
  40.     reg   [C_10S_WID-1:0]   cnt_10s_nvld    ;
  41.     reg   [C_PWD_WID-1:0]   cnt_password    ;

  42.     reg   [15:0]            password        ;

  43.    
  44.     parameter LOCKED    = 2'b00             ;
  45.     parameter OPEN      = 2'b01             ;
  46.     parameter PASSWORD  = 2'b10             ;
  47.     parameter ERROR     = 2'b11             ;

  48.     //current state
  49.     always@(posedge clk or negedge rst_n)begin
  50.         if(!rst_n)begin
  51.             state_c <= LOCKED;
  52.         end
  53.         else begin
  54.             state_c <= state_n;
  55.         end
  56.     end

  57.     //next state and the condition of state LOCKEDtransition
  58.     always@(*)begin
  59.         case(state_c)
  60.             LOCKED:begin
  61.                 if(locked2password_switch)begin
  62.                     state_n = PASSWORD;
  63.                 end
  64.                 else begin
  65.                     state_n = state_c;
  66.                 end
  67.             end
  68.             OPEN:begin
  69.                 if(open2password_switch)begin
  70.                     state_n = PASSWORD;
  71.                 end
  72.                 else begin
  73.                     state_n = state_c;
  74.                 end
  75.             end            
  76.             PASSWORD:begin
  77.                 if(password2locked_switch0)begin
  78.                     state_n = LOCKED;
  79.                 end
  80.                 else if(password2open_switch0 || password2open_switch1)begin
  81.                     state_n = OPEN;
  82.                 end
  83.                 else if(password2error_switch || password2locked_switch1)begin
  84.                     state_n = ERROR;
  85.                 end
  86.                 else begin
  87.                     state_n = state_c;
  88.                 end
  89.             end
  90.             ERROR:begin
  91.                 if(error2locked_switch0 )begin
  92.                     state_n = LOCKED;
  93.                 end
  94.                 else begin
  95.                     state_n = state_c;
  96.                 end
  97.             end
  98.             default:begin
  99.                 state_n = LOCKED;
  100.             end
  101.         endcase
  102.     end
  103.     assign locked2password_switch    = state_c==LOCKED   &&  lock_stata_flag && key_num<10 && key_vld;
  104.     assign open2password_switch      = state_c==OPEN     && !lock_stata_flag && key_num<10 && key_vld;
  105.     assign password2locked_switch0   = state_c==PASSWORD &&  lock_stata_flag && end_cnt_10s_nvld;
  106.     assign password2locked_switch1   = state_c==PASSWORD &&  lock_stata_flag && confirm && password!=PASSWORD_INI ;//TO ERROR
  107.     assign password2open_switch0     = state_c==PASSWORD &&  lock_stata_flag && confirm && password==PASSWORD_INI &&  password_correct_twice;
  108.     assign password2open_switch1     = state_c==PASSWORD && !lock_stata_flag && end_cnt_10s_nvld;
  109.     assign password2error_switch     = state_c==PASSWORD && !lock_stata_flag && confirm && password!=PASSWORD_INI;
  110.     assign error2locked_switch0      = state_c==ERROR    &&  end_cnt_2s;


  111.     //lock_stata_flag
  112.     always  @(posedge clk or negedge rst_n)begin
  113.         if(rst_n==1'b0)begin
  114.             lock_stata_flag <= 1;
  115.         end
  116.         else if(password2locked_switch0 || password2locked_switch1 || error2locked_switch0)begin
  117.             lock_stata_flag <= 1;
  118.         end
  119.         else if(password2open_switch0 || password2open_switch1 )begin
  120.             lock_stata_flag <= 0;
  121.         end
  122.     end

  123.     //cnt_10s_nvld
  124.     always  @(posedge clk or negedge rst_n)begin
  125.         if(rst_n==1'b0)begin
  126.             cnt_10s_nvld <= 0;
  127.         end
  128.         else if(end_cnt_10s_nvld)begin
  129.             cnt_10s_nvld <= 0;
  130.         end
  131.         else if(add_cnt_10s_nvld)begin
  132.             cnt_10s_nvld <= cnt_10s_nvld + 1;
  133.         end
  134.     end
  135.     assign add_cnt_10s_nvld = state_c==PASSWORD;
  136.     assign end_cnt_10s_nvld = add_cnt_10s_nvld && cnt_10s_nvld==C_10S_NUM-1;

  137.     //confirm
  138.     assign confirm = key_num==10 && key_vld;

  139.     //password_correct_twice   
  140.     always  @(posedge clk or negedge rst_n)begin
  141.         if(rst_n==1'b0)begin
  142.             password_correct_twice <= 0;
  143.         end
  144.         else if(state_c==PASSWORD && lock_stata_flag && confirm && password==PASSWORD_INI && !password_correct_twice)begin
  145.             password_correct_twice <= 1;
  146.         end
  147.         else if(password2locked_switch0 || password2locked_switch1 || password2open_switch0 || password2open_switch1 || password2error_switch)begin
  148.             password_correct_twice <= 0;
  149.         end
  150.     end

  151.     //cnt_2s
  152.     always  @(posedge clk or negedge rst_n)begin
  153.         if(rst_n==1'b0)begin
  154.             cnt_2s <= 0;
  155.         end
  156.         else if(end_cnt_2s )begin
  157.             cnt_2s <= 0;
  158.         end
  159.         else if(add_cnt_2s )begin
  160.             cnt_2s <= cnt_2s + 1;
  161.         end
  162.     end
  163.     assign add_cnt_2s = state_c==ERROR;
  164.     assign end_cnt_2s = add_cnt_2s && cnt_2s==C_2S_NUM-1;



  165.     //seg_dout
  166.     always  @(posedge clk or negedge rst_n)begin
  167.         if(rst_n==1'b0)begin
  168.             seg_dout <= 0;
  169.         end
  170.         else if(state_c==OPEN)begin
  171.             seg_dout <= {NONE_DIS,NONE_DIS,CHAR_O,CHAR_P,CHAR_E,CHAR_N};
  172.         end
  173.         else if(state_c==LOCKED)begin
  174.             seg_dout <= {CHAR_L,CHAR_O,CHAR_C,CHAR_K,CHAR_E,CHAR_D};
  175.         end
  176.         else if(state_c==ERROR)begin
  177.             seg_dout <= {NONE_DIS,CHAR_E,CHAR_R,CHAR_R,CHAR_O,CHAR_R};
  178.         end
  179.         else if(state_c==PASSWORD)begin
  180.             if(cnt_password==0)
  181.                 seg_dout <= {NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS};
  182.             else if(cnt_password==1)
  183.                 seg_dout <= {NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,{1'b0,password[3:0]}};
  184.             else if(cnt_password==2)
  185.                 seg_dout <= {NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,{1'b0,password[7:4]},{1'b0,password[3:0]}};
  186.             else if(cnt_password==3)
  187.                 seg_dout <= {NONE_DIS,NONE_DIS,NONE_DIS,{1'b0,password[11:8]},{1'b0,password[7:4]},{1'b0,password[3:0]}};
  188.             else if(cnt_password==4)
  189.                 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]}};
  190.         end
  191.     end
  192.    
  193.     //seg_dout_vld
  194.     assign seg_dout_vld = 6'b11_1111;

  195.     //cnt_password
  196.     always  @(posedge clk or negedge rst_n)begin
  197.         if(rst_n==1'b0)begin
  198.             cnt_password <= 0;
  199.         end
  200.         else if(end_cnt_password)begin
  201.             cnt_password <= 0;
  202.         end
  203.         else if(add_cnt_password)begin
  204.             cnt_password <= cnt_password + 1;
  205.         end
  206.     end
  207.     assign add_cnt_password = state_c!=ERROR && key_num<10 && key_vld && cnt_password<4;
  208.     assign end_cnt_password = confirm || end_cnt_10s_nvld;

  209.     //password
  210.     always  @(posedge clk or negedge rst_n)begin
  211.         if(rst_n==1'b0)begin
  212.             password <= 16'h0000;
  213.         end
  214.         else if(add_cnt_password)begin
  215.             password <= {password[11:0],key_num};
  216.         end
  217.     end



  218.     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参考代码
  1. module seg_display(
  2.     clk     ,      
  3.     rst_n   ,      
  4.     din     ,      
  5.     din_vld ,      
  6.     segment ,      
  7.     seg_sel         
  8.     );

  9.     parameter SEGMENT_NUM   = 6             ;   
  10.     parameter W_DATA        = 5             ;   

  11.     parameter SEGMENT_WID   = 8             ;   
  12.     parameter TIME_300US    = 15_000         ;  

  13.     parameter SEG_DATA_0    = 7'b100_0000   ;  
  14.     parameter SEG_DATA_1    = 7'b111_1001   ;
  15.     parameter SEG_DATA_2    = 7'b010_0100   ;
  16.     parameter SEG_DATA_3    = 7'b011_0000   ;
  17.     parameter SEG_DATA_4    = 7'b001_1001   ;
  18.     parameter SEG_DATA_5    = 7'b001_0010   ;
  19.     parameter SEG_DATA_6    = 7'b000_0010   ;
  20.     parameter SEG_DATA_7    = 7'b111_1000   ;
  21.     parameter SEG_DATA_8    = 7'b000_0000   ;
  22.     parameter SEG_DATA_9    = 7'b001_0000   ;  

  23.     parameter SEG_CHAR_O    = 7'b010_0011   ;  
  24.     parameter SEG_CHAR_P    = 7'b000_1100   ;  
  25.     parameter SEG_CHAR_E    = 7'b000_0110   ;  
  26.     parameter SEG_CHAR_N    = 7'b010_1011   ;  
  27.     parameter SEG_CHAR_L    = 7'b100_0111   ;  
  28.     parameter SEG_CHAR_C    = 7'b100_0110   ;  
  29.     parameter SEG_CHAR_K    = 7'b000_0101   ;  
  30.     parameter SEG_CHAR_D    = 7'b010_0001   ;  
  31.     parameter SEG_CHAR_R    = 7'b010_1111   ;  
  32.     parameter SEG_NONE_DIS  = 7'b111_1111   ;  

  33.     input                           clk         ;
  34.     input                           rst_n       ;
  35.     input [SEGMENT_NUM*W_DATA-1:0]  din         ;
  36.     input [SEGMENT_NUM-1:0]         din_vld     ;

  37.     output[SEGMENT_WID-1:0]         segment     ;
  38.     output[SEGMENT_NUM-1:0]         seg_sel     ;

  39.     reg   [SEGMENT_WID-1:0]         segment     ;
  40.     reg   [SEGMENT_NUM-1:0]         seg_sel     ;

  41.     reg   [W_DATA-1:0]              segment_pre ;
  42.     reg   [SEGMENT_NUM*W_DATA-1:0]  din_get     ;
  43.     reg   [14:0]                    cnt_300us   ;
  44.     reg   [2:0]                     cnt_sel     ;
  45.     wire                            dot         ;


  46.    
  47. wire        add_cnt_300us ;
  48. wire        end_cnt_300us ;
  49. always @(posedge clk or negedge rst_n) begin
  50.     if (rst_n==0) begin
  51.         cnt_300us <= 0;
  52.     end
  53.     else if(add_cnt_300us) begin
  54.         if(end_cnt_300us)
  55.             cnt_300us <= 0;
  56.         else
  57.             cnt_300us <= cnt_300us+1 ;
  58.    end
  59. end
  60. assign add_cnt_300us =1;
  61. assign end_cnt_300us = add_cnt_300us  && cnt_300us == TIME_300US-1 ;

  62.    


  63. wire        add_cnt_sel ;
  64. wire        end_cnt_sel ;
  65. always @(posedge clk or negedge rst_n) begin
  66.     if (rst_n==0) begin
  67.         cnt_sel <= 0;
  68.     end
  69.     else if(add_cnt_sel) begin
  70.         if(end_cnt_sel)
  71.             cnt_sel <= 0;
  72.         else
  73.             cnt_sel <= cnt_sel+1 ;
  74.    end
  75. end
  76. assign add_cnt_sel = end_cnt_300us;
  77. assign end_cnt_sel = add_cnt_sel  && cnt_sel == SEGMENT_NUM-1 ;


  78. reg     [SEGMENT_NUM-1:0]   din_vvld;
  79. always  @(posedge clk or negedge rst_n)begin
  80.     if(rst_n==1'b0)begin
  81.         din_vvld <= 0 ;
  82.     end
  83.     else begin
  84.         din_vvld <= din_vld ;
  85.     end
  86. end


  87. reg [ 2:0]  cnt     ;
  88. wire        add_cnt ;
  89. wire        end_cnt ;
  90. always @(posedge clk or negedge rst_n) begin
  91.     if (rst_n==0) begin
  92.         cnt <= 0;
  93.     end
  94.     else if(add_cnt) begin
  95.         if(end_cnt)
  96.             cnt <= 0;
  97.         else
  98.             cnt <= cnt+1 ;
  99.    end
  100. end
  101. assign add_cnt = 1;
  102. assign end_cnt = add_cnt  && cnt == SEGMENT_NUM-1 ;

  103. always  @(posedge clk or negedge rst_n)begin
  104.     if(rst_n==1'b0)begin
  105.         din_get <= 0;
  106.     end
  107.     else if(din_vvld[cnt])begin
  108.         din_get[W_DATA*(cnt+1)-1 -:W_DATA] <= din[W_DATA*(cnt+1)-1 -:W_DATA];
  109.     end
  110. end


  111.     always  @(*)begin
  112.         segment_pre = din_get[W_DATA*(cnt_sel+1)-1 -:W_DATA];
  113.     end


  114.     always  @(posedge clk or negedge rst_n)begin
  115.         if(rst_n==1'b0)begin
  116.             segment <= {dot,SEG_NONE_DIS};
  117.         end
  118.         else if(add_cnt_300us  && cnt_300us ==10-1)begin
  119.             case(segment_pre)
  120.                 5'h00: segment <= {dot,SEG_DATA_0};
  121.                 5'h01: segment <= {dot,SEG_DATA_1};
  122.                 5'h02: segment <= {dot,SEG_DATA_2};
  123.                 5'h03: segment <= {dot,SEG_DATA_3};
  124.                 5'h04: segment <= {dot,SEG_DATA_4};
  125.                 5'h05: segment <= {dot,SEG_DATA_5};
  126.                 5'h06: segment <= {dot,SEG_DATA_6};
  127.                 5'h07: segment <= {dot,SEG_DATA_7};
  128.                 5'h08: segment <= {dot,SEG_DATA_8};
  129.                 5'h09: segment <= {dot,SEG_DATA_9};
  130.                 5'h10: segment <= {dot,SEG_CHAR_O};
  131.                 5'h11: segment <= {dot,SEG_CHAR_P};
  132.                 5'h12: segment <= {dot,SEG_CHAR_E};
  133.                 5'h13: segment <= {dot,SEG_CHAR_N};
  134.                 5'h14: segment <= {dot,SEG_CHAR_L};
  135.                 5'h15: segment <= {dot,SEG_CHAR_C};
  136.                 5'h16: segment <= {dot,SEG_CHAR_K};
  137.                 5'h17: segment <= {dot,SEG_CHAR_D};
  138.                 5'h18: segment <= {dot,SEG_CHAR_R};
  139.                 5'h1F: segment <= {dot,SEG_NONE_DIS};
  140.                 default:segment <= {dot,SEG_NONE_DIS};
  141.             endcase
  142.         end
  143.     end
  144.     assign dot = 1'b1;

  145.     always@(posedge clk or negedge rst_n)begin
  146.         if(rst_n==1'b0)begin
  147.             seg_sel <= {SEGMENT_NUM{1'b0}};
  148.         end
  149.         else begin
  150.             seg_sel <= ~(1'b1<<cnt_sel);
  151.         end
  152.     end


  153.     endmodule
复制代码







1.5 效果和总结


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



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





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





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





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





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



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




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





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





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





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




由于该项目的上板现象是在数码管上显示输入的密码,并且判断密码是否正确:正确则在数码管上显示OPEN,错误则在数码管上显示ERROR并提示输入错误2秒,然后数码管显示LOCKED。想观看完整现象的朋友可以看一下上板演示的视频。
感兴趣的朋友也可以访问明德扬论坛(http://www.fpgabbs.cn/)进行FPGA相关工程设计学习,也可以看一下我们往期的文章:
波形相位频率可调DDS信号发生器
基于FPGA的曼彻斯特编码解码设计
基于FPGA的出租车计费系统
数电基础与Verilog设计
基于FPGA的频率、电压测量
基于FPGA的汉明码编码解码设计
关于锁存器问题的讨论
阻塞赋值与非阻塞赋值
参数例化时自动计算位宽的解决办法

1.6 公司简介
明德扬是一家专注于FPGA领域的专业性公司,公司主要业务包括开发板、教育培训、项目承接、人才服务等多个方向。点拨开发板——学习FPGA的入门之选。
MP801
开发板——千兆网ADDA、大容量SDRAM等,学习和项目需求一步到位。网络培训班——不管时间和空间,明德扬随时在你身边,助你快速学习FPGA周末培训班——明天的你会感激现在的努力进取,升职加薪明德扬来助你。就业培训班——七大企业级项目实训,获得丰富的项目经验,高薪就业。专题课程——高手修炼课:提升设计能力;实用调试技巧课:提升定位和解决问题能力;FIFO架构设计课:助你快速成为架构设计师;时序约束、数字信号处理、PCIE、综合项目实践课等你来选。项目承接——承接企业FPGA研发项目。人才服务——提供人才推荐、人才代培、人才派遣等服务。




【设计教程下载
至简设计系列_电子密码锁.pdf (3.41 MB, 下载次数: 0)


【设计代码下载】
mdyPwdlock.zip (66.36 KB, 下载次数: 2)



0
分享淘帖 显示全部楼层
最近下载过的用户(1)

评论

高级模式
您需要登录后才可以回帖 登录 | 注册

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容图片侵权或者其他问题,请联系本站作侵删。 侵权投诉
发资料
关闭

站长推荐 上一条 /8 下一条

快速回复 返回顶部 返回列表