【上板現象】
電子密碼鎖的在MP801的上板現象
https://www.bilibili.com/video/BV1Af4y117H4?p=43
電子密碼鎖的在點撥開發板的上板現象
https://www.bilibili.com/video/BV1Af4y117H4?p=42
電子密碼鎖的在實驗箱的上板現象
https://www.bilibili.com/video/BV1Af4y117H4?p=44
【設計教程
1.1 總體設計
1.1.1 概述
1.1.2設計目標
1.1.3系統結構框圖

1.1.4模塊功能按鍵檢測模塊實現功能
控制模塊實現功能
數碼管顯示模塊實現功能
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
1.2按鍵檢測模塊設計1.2.1接口信號
1.2.2 設計思路

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==1'b0)begin
key_col_ff0 <= 4'b1111;
key_col_ff1 <= 4'b1111;
end
else begin
key_col_ff0 <= key_col ;
key_col_ff1 <= key_col_ff0;
end
end
wire add_shake_cnt ;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
shake_cnt <= 0;
end
else if(add_shake_cnt) begin
if(shake_flag)
shake_cnt <= 0;
else
shake_cnt <= shake_cnt+1 ;
end
end
assign add_shake_cnt = key_col_ff1!=4'hf;
assign shake_flag = add_shake_cnt && shake_cnt == TIME_20MS-1 ;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
state_c <= CHK_COL;
end
else begin
state_c <= state_n;
end
end
always @(*)begin
case(state_c)
CHK_COL: begin
if(shake_flag && shake_flag_ff0==1'b0)begin
state_n = CHK_ROW;
end
else begin
state_n = CHK_COL;
end
end
CHK_ROW: begin
if(row_index==3 && row_cnt==0)begin
state_n = DELAY;
end
else begin
state_n = CHK_ROW;
end
end
DELAY : begin
if(row_cnt==0)begin
state_n = WAIT_END;
end
else begin
state_n = DELAY;
end
end
WAIT_END: begin
if(key_col_ff1==4'hf)begin
state_n = CHK_COL;
end
else begin
state_n = WAIT_END;
end
end
default: state_n = CHK_COL;
endcase
end
assign chk_col2chk_row = shake_flag && shake_flag_ff0 ==1'b0;
assign chk_row2delay = row_index==3 && row_cnt==0;
assign delay2wait_end = row_cnt==0;
assign wait_end2chk_col= key_col_ff1==4'hf;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_row <= 4'b0;
end
else if(state_c==CHK_ROW)begin
key_row <= ~(1'b1 << row_index);
end
else begin
key_row <= 4'b0;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
row_index <= 0;
end
else if(state_c==CHK_ROW)begin
if(row_cnt==0)begin
if(row_index==3)
row_index <= 0;
else
row_index <= row_index + 1;
end
end
else begin
row_index <= 0;
end
end
wire add_row_cnt ;
wire end_row_cnt ;
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
shake_flag_ff0 <= 1'b0;
end
else begin
shake_flag_ff0 <= shake_flag;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_col_get <= 0;
end
else if(state_c==CHK_COL && shake_flag==1'b1 && shake_flag_ff0==1'b0) begin
if(key_col_ff1==4'b1110)
key_col_get <= 0;
else if(key_col_ff1==4'b1101)
key_col_get <= 1;
else if(key_col_ff1==4'b1011)
key_col_get <= 2;
else
key_col_get <= 3;
end
end
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
key_out <= 0;
end
else if(state_c==CHK_ROW && row_cnt==0)begin
key_out <= {row_index,key_col_get};
end
else begin
key_out <= 0;
end
end
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
1.3控制模塊設計1.3.1接口信號
1.3.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 ;
parameter C_2S_WID = 27 ;
parameter C_2S_NUM = 100_000_000 ;
parameter C_PWD_WID = 3 ;
input clk ;
input rst_n ;
input [3:0] key_num ;
input key_vld ;
output[6*5-1:0] seg_dout ;
output[5:0] seg_dout_vld ;
reg [6*5-1:0] seg_dout ;
wire [5:0] seg_dout_vld ;
reg [1:0] state_c ;
reg [1:0] state_n ;
reg lock_stata_flag ;
reg password_correct_twice ;
reg [C_2S_WID-1:0] cnt_2s ;
reg [C_10S_WID-1:0] cnt_10s_nvld ;
reg [C_PWD_WID-1:0] cnt_password ;
reg [15:0] password ;
parameter LOCKED = 2'b00 ;
parameter OPEN = 2'b01 ;
parameter PASSWORD = 2'b10 ;
parameter ERROR = 2'b11 ;
//current state
always@(posedge clk or negedge rst_n)begin
if(!rst_n)begin
state_c <= LOCKED;
end
else begin
state_c <= state_n;
end
end
//next state and the condition of state LOCKEDtransition
always@(*)begin
case(state_c)
LOCKED:begin
if(locked2password_switch)begin
state_n = PASSWORD;
end
else begin
state_n = state_c;
end
end
OPEN:begin
if(open2password_switch)begin
state_n = PASSWORD;
end
else begin
state_n = state_c;
end
end
PASSWORD:begin
if(password2locked_switch0)begin
state_n = LOCKED;
end
else if(password2open_switch0 || password2open_switch1)begin
state_n = OPEN;
end
else if(password2error_switch || password2locked_switch1)begin
state_n = ERROR;
end
else begin
state_n = state_c;
end
end
ERROR:begin
if(error2locked_switch0 )begin
state_n = LOCKED;
end
else begin
state_n = state_c;
end
end
default:begin
state_n = LOCKED;
end
endcase
end
assign locked2password_switch = state_c==LOCKED && lock_stata_flag && key_num<10 && key_vld;
assign open2password_switch = state_c==OPEN && !lock_stata_flag && key_num<10 && key_vld;
assign password2locked_switch0 = state_c==PASSWORD && lock_stata_flag && end_cnt_10s_nvld;
assign password2locked_switch1 = state_c==PASSWORD && lock_stata_flag && confirm && password!=PASSWORD_INI ;//TO ERROR
assign password2open_switch0 = state_c==PASSWORD && lock_stata_flag && confirm && password==PASSWORD_INI && password_correct_twice;
assign password2open_switch1 = state_c==PASSWORD && !lock_stata_flag && end_cnt_10s_nvld;
assign password2error_switch = state_c==PASSWORD && !lock_stata_flag && confirm && password!=PASSWORD_INI;
assign error2locked_switch0 = state_c==ERROR && end_cnt_2s;
//lock_stata_flag
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
lock_stata_flag <= 1;
end
else if(password2locked_switch0 || password2locked_switch1 || error2locked_switch0)begin
lock_stata_flag <= 1;
end
else if(password2open_switch0 || password2open_switch1 )begin
lock_stata_flag <= 0;
end
end
//cnt_10s_nvld
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
cnt_10s_nvld <= 0;
end
else if(end_cnt_10s_nvld)begin
cnt_10s_nvld <= 0;
end
else if(add_cnt_10s_nvld)begin
cnt_10s_nvld <= cnt_10s_nvld + 1;
end
end
assign add_cnt_10s_nvld = state_c==PASSWORD;
assign end_cnt_10s_nvld = add_cnt_10s_nvld && cnt_10s_nvld==C_10S_NUM-1;
//confirm
assign confirm = key_num==10 && key_vld;
//password_correct_twice
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
password_correct_twice <= 0;
end
else if(state_c==PASSWORD && lock_stata_flag && confirm && password==PASSWORD_INI && !password_correct_twice)begin
password_correct_twice <= 1;
end
else if(password2locked_switch0 || password2locked_switch1 || password2open_switch0 || password2open_switch1 || password2error_switch)begin
password_correct_twice <= 0;
end
end
//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
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
seg_dout <= 0;
end
else if(state_c==OPEN)begin
seg_dout <= {NONE_DIS,NONE_DIS,CHAR_O,CHAR_P,CHAR_E,CHAR_N};
end
else if(state_c==LOCKED)begin
seg_dout <= {CHAR_L,CHAR_O,CHAR_C,CHAR_K,CHAR_E,CHAR_D};
end
else if(state_c==ERROR)begin
seg_dout <= {NONE_DIS,CHAR_E,CHAR_R,CHAR_R,CHAR_O,CHAR_R};
end
else if(state_c==PASSWORD)begin
if(cnt_password==0)
seg_dout <= {NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS};
else if(cnt_password==1)
seg_dout <= {NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,{1'b0,password[3:0]}};
else if(cnt_password==2)
seg_dout <= {NONE_DIS,NONE_DIS,NONE_DIS,NONE_DIS,{1'b0,password[7:4]},{1'b0,password[3:0]}};
else if(cnt_password==3)
seg_dout <= {NONE_DIS,NONE_DIS,NONE_DIS,{1'b0,password[11:8]},{1'b0,password[7:4]},{1'b0,password[3:0]}};
else if(cnt_password==4)
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]}};
end
end
//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
1.4數碼管顯示模塊設計1.4.1接口信號
1.4.2設計思路

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 ;
output[SEGMENT_WID-1:0] segment ;
output[SEGMENT_NUM-1:0] seg_sel ;
reg [SEGMENT_WID-1:0] segment ;
reg [SEGMENT_NUM-1:0] seg_sel ;
reg [W_DATA-1:0] segment_pre ;
reg [SEGMENT_NUM*W_DATA-1:0] din_get ;
reg [14:0] cnt_300us ;
reg [2:0] cnt_sel ;
wire dot ;
wire add_cnt_300us ;
wire end_cnt_300us ;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt_300us <= 0;
end
else if(add_cnt_300us) begin
if(end_cnt_300us)
cnt_300us <= 0;
else
cnt_300us <= cnt_300us+1 ;
end
end
assign add_cnt_300us =1;
assign end_cnt_300us = add_cnt_300us && cnt_300us == TIME_300US-1 ;
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 ;
reg [SEGMENT_NUM-1:0] din_vvld;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
din_vvld <= 0 ;
end
else begin
din_vvld <= din_vld ;
end
end
reg [ 2:0] cnt ;
wire add_cnt ;
wire end_cnt ;
always @(posedge clk or negedge rst_n) begin
if (rst_n==0) begin
cnt <= 0;
end
else if(add_cnt) begin
if(end_cnt)
cnt <= 0;
else
cnt <= cnt+1 ;
end
end
assign add_cnt = 1;
assign end_cnt = add_cnt && cnt == SEGMENT_NUM-1 ;
always @(posedge clk or negedge rst_n)begin
if(rst_n==1'b0)begin
din_get <= 0;
end
else if(din_vvld[cnt])begin
din_GET@[W_DATA*(cnt+1)-1 -:W_DATA] <= din[W_DATA*(cnt+1)-1 -:W_DATA];
end
end
always @(*)begin
segment_pre = din_GET@[W_DATA*(cnt_sel+1)-1 -:W_DATA];
end
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
1.5 效果和總結
下圖是該工程在db603開發板上的現象——密碼鎖初始狀態和閉合狀態

下圖是該工程在db603開發板上的現象——提示輸入錯誤狀態

下圖是該工程在db603開發板上的現象——密碼鎖開啟狀態

下圖是該工程在db603開發板上的現象——輸入密碼狀態

下圖是該工程在mp801開發板上的現象——密碼鎖初始狀態和閉合狀態

下圖是該工程在mp801開發板上的現象——提示輸入錯誤狀態

下圖是該工程在mp801開發板上的現象——密碼鎖開啟狀態
下圖是該工程在mp801開發板上的現象——輸入密碼狀態
下圖是該工程在ms980開發板上的現象——提示輸入錯誤狀態

下圖是該工程在ms980開發板上的現象——密碼鎖開啟狀態

下圖是該工程在ms980開發板上的現象——輸入密碼狀態

1.6 公司簡介
MP801開發板——千兆網、ADDA、大容量SDRAM等,學習和項目需求一步到位。網絡培訓班——不管時間和空間,明德揚隨時在你身邊,助你快速學習FPGA。周末培訓班——明天的你會感激現在的努力進取,升職加薪明德揚來助你。就業培訓班——七大企業級項目實訓,獲得豐富的項目經驗,高薪就業。專題課程——高手修煉課:提升設計能力;實用調試技巧課:提升定位和解決問題能力;FIFO架構設計課:助你快速成為架構設計師;時序約束、數字信號處理、PCIE、綜合項目實踐課等你來選。項目承接——承接企業FPGA研發項目。人才服務——提供人才推薦、人才代培、人才派遣等服務。
設計教程下載】 至簡設計系列_電子密碼鎖.pdf
設計視頻教程】
https://www.bilibili.com/video/BV1Af4y117H4?p=41
【工程源碼】 mdyPwdlock.zip
【答疑】
【問題1】
答:在頂層里,可以看到紅框里的信號的是相連的,
那么去control模塊看,這里的CHAR_O的參數的數值是隨便定的,只要可以與其它區分
再到seg_display模塊看,當得到數值為5'h10時則表示要顯示的是“O”,那么對應的數碼管段選信號segment的數值就取:信號dot的值和參數SIG_CHAR_O的值拼接得到的數據。
溫馨提示:明德揚2023推出了全新課程——邏輯設計基本功修煉課,降低學習FPGA門檻的同時,增加了學習的趣味性,并組織了考試贏積分活動
http://www.cqqtmy.cn/ffkc/415.html
(點擊→了解課程詳情?)感興趣請聯系易老師:13112063618(微信同步)
有問題請聯系陳老師:MDYfpga003