至簡(jiǎn)設(shè)計(jì)系列_電子密碼鎖
--作者:肖肖肖
--案例作者:WB_Yih
本文為明德?lián)P原創(chuàng)及錄用文章,轉(zhuǎn)載請(qǐng)注明出處!
1.1 總體設(shè)計(jì)
1.1.1 概述
隨著生活質(zhì)量的不斷提高,加強(qiáng)家庭防盜安全變得非常重要,但傳統(tǒng)機(jī)械鎖的構(gòu)造過于簡(jiǎn)單,很容易被打開,從而降低了安全性。數(shù)字密碼鎖因?yàn)樗谋C苄院芨撸踩禂?shù)也非常高,再加上其不需要攜帶避免了丟失的可能,省去了因鑰匙丟失而需要換鎖的麻煩,受到了越來越多的人的歡迎。隨看人們對(duì)高科技產(chǎn)品也越來越推崇,在當(dāng)今社會(huì)科技的高度集中和創(chuàng)新,人們對(duì)日常生活中保護(hù)自身及財(cái)產(chǎn)安全的物品非常追捧,對(duì)其安全性的要求也非常的高。為了達(dá)到人們對(duì)鎖具安全性的高要求,加強(qiáng)鎖具的安全保密性,用密碼鎖來取代傳統(tǒng)機(jī)械鎖的鎖具是必然趨勢(shì)。數(shù)字密碼鎖比傳統(tǒng)機(jī)械鎖具更加的安全。在本案例的設(shè)計(jì)過程中,應(yīng)用了至簡(jiǎn)設(shè)計(jì)法、狀態(tài)機(jī)模板應(yīng)用等,在經(jīng)過逐步改進(jìn)、調(diào)試等一系列工作之后,最終達(dá)到了設(shè)計(jì)目標(biāo)。
基于明德?lián)P至簡(jiǎn)設(shè)計(jì)法和明德?lián)P設(shè)計(jì)規(guī)范,設(shè)計(jì)一個(gè)基于FPGA的密碼鎖、并將數(shù)值顯示在數(shù)碼管上,然后根據(jù)輸入的鍵值判斷密碼是否正確。
1.1.2 設(shè)計(jì)目標(biāo)
實(shí)現(xiàn)電子密碼鎖的功能,具體功能要求如下:
1. 密碼4位,初始密碼2345。
2. 密碼鎖狀態(tài):LOCKED和OPEN,初始狀態(tài)為L(zhǎng)OCKED。
1) 當(dāng)在LOCKED狀態(tài)時(shí),連續(xù)兩次輸入正確密碼,狀態(tài)變?yōu)镺PEN狀態(tài)。當(dāng)輸入錯(cuò)誤密碼時(shí)(包括第一次就輸入錯(cuò)誤;或者第一次輸入正確,第二次輸入錯(cuò)誤的情況),數(shù)碼管顯示ERROR 2秒后重新顯示原來的狀態(tài)(LOCKED)。
2) 當(dāng)在OPEN狀態(tài)時(shí),一次輸入錯(cuò)誤密碼,狀態(tài)變?yōu)長(zhǎng)OCKED狀態(tài)。當(dāng)輸入正確密碼時(shí),數(shù)碼管無顯示,10秒后重新顯示原來的狀態(tài)(OPEN)。
3) 不管在何狀態(tài),當(dāng)輸入4位密碼或者某幾位密碼,但未按下確認(rèn)鍵,并超過10S時(shí),返回原來的狀態(tài)。(即輸入密碼超時(shí),返回原狀態(tài))
對(duì)于點(diǎn)撥開發(fā)板,使用矩陣按鍵輸入(本文以點(diǎn)撥603開發(fā)板為例)。
對(duì)于Mp801開發(fā)板,密碼顯示及確認(rèn):無論在OPEN,還是LOCKED狀態(tài)下,均可以通過撥碼開關(guān)輸入密碼。當(dāng)有撥碼開關(guān)撥動(dòng)時(shí),數(shù)碼管當(dāng)前顯示的OPEN或LOCKED消失,并顯示當(dāng)前輸入的密碼,暫未輸入的密碼位不顯示。4位密碼輸入完畢后,再撥動(dòng)撥碼開關(guān)時(shí)視為無效輸入,當(dāng)前顯示的密碼不改變。4位密碼輸入完畢后,按下確認(rèn)鍵后,系統(tǒng)判斷密碼是否正確。
撥碼開關(guān)及按鍵:初始狀態(tài)下,撥碼開關(guān)全部往下?lián)堋.?dāng)撥碼開關(guān)向上撥后,再向下?lián)埽ɑ氐匠跏紶顟B(tài)),表示一個(gè)數(shù)字的有效輸入。按鍵每按下一次(會(huì)自動(dòng)彈起),為一次有效輸入(復(fù)位/確認(rèn))。
1.1.3 系統(tǒng)結(jié)構(gòu)框圖
系統(tǒng)結(jié)構(gòu)框圖如下圖所示:

1.1.4模塊功能按鍵檢測(cè)模塊實(shí)現(xiàn)功能
1、 檢測(cè)按鍵的數(shù)值
控制模塊實(shí)現(xiàn)功能
1、 對(duì)接收到的按鍵數(shù)值進(jìn)行判斷和控制對(duì)應(yīng)的密碼鎖狀態(tài),實(shí)現(xiàn)對(duì)輸入密碼的正誤判斷和對(duì)密碼鎖的開啟和閉合控制。
數(shù)碼管顯示模塊實(shí)現(xiàn)功能
1、 顯示輸入的密碼數(shù)值;
2、 顯示當(dāng)前密碼鎖的狀態(tài)(開啟狀態(tài)或者閉鎖狀態(tài));
3、 提示密碼輸入錯(cuò)誤的狀態(tài)。
1.1.5頂層信號(hào)

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接口信號(hào)

1.2.2 設(shè)計(jì)思路
在前面的案例中已經(jīng)有矩陣按鍵檢測(cè)模塊的介紹,所以這里不在過多介紹,詳細(xì)介紹請(qǐng)看下方鏈接:
http://www.fpgabbs.cn/forum.php?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==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 控制模塊設(shè)計(jì)
1.3.1接口信號(hào)

1.3.2設(shè)計(jì)思路
- 狀態(tài)機(jī)架構(gòu)
本模塊的主要功能是根據(jù)輸入的按鍵信息進(jìn)行不同狀態(tài)的判斷和切換當(dāng)前工作狀態(tài)。根據(jù)項(xiàng)目功能要求,一共有四種工作狀態(tài):密碼鎖開啟狀態(tài)(open)、密碼鎖閉合狀態(tài)(clocked)、輸入密碼狀態(tài)(password)和提示輸入錯(cuò)誤狀態(tài)(error)。
以下為本模塊的狀態(tài)跳轉(zhuǎn)圖:

復(fù)位后,狀態(tài)機(jī)進(jìn)入LOCKED的狀態(tài),即初始狀態(tài)為L(zhǎng)OCKED;
在LOCKED狀態(tài)下:
A. 有按鍵按下,跳到PASSWORD狀態(tài);
B. 否則,保持LOCKED狀態(tài)不變;
在OPEN狀態(tài)下:
A. 有按鍵按下,跳到PASSWORD狀態(tài);
B. 否則,保持OPEN狀態(tài)不變;
在PASSWORD狀態(tài)下:
A. 有密碼輸入但超過10秒沒有確認(rèn),跳到原來的LOCKED狀態(tài)或者OPEN狀態(tài);
B. 密碼正確輸入并確認(rèn)兩次,跳到OPEN狀態(tài);
C. 密碼錯(cuò)誤輸入并確認(rèn),跳到ERROR狀態(tài);
D. 否則,保持PASSWORD狀態(tài)不變;
在ERROR狀態(tài)下:
A. 提示輸入錯(cuò)誤2秒,跳到LOCKED狀態(tài);
B. 否則,保持ERROR狀態(tài)不變;
無論當(dāng)前處于什么狀態(tài),只要不滿足狀態(tài)間的跳轉(zhuǎn)條件就跳到LOCKED狀態(tài)。
- 計(jì)數(shù)器架構(gòu)
本模塊的某些狀態(tài)跳轉(zhuǎn)之間存在一定的時(shí)間間隔,根據(jù)項(xiàng)目功能要求,一共有兩種時(shí)間的間隔:10秒的等待輸入時(shí)間間隔和2秒的顯示提示時(shí)間間隔。
以下為計(jì)數(shù)器的架構(gòu)示意圖:

10秒計(jì)數(shù)器cnt_10s_nvld:用于計(jì)算10秒的時(shí)間。加一條件為state_c==PASSWORD,表示進(jìn)入密碼輸入狀態(tài)就開始計(jì)數(shù)。結(jié)束條件為數(shù)500_000_000個(gè),系統(tǒng)時(shí)鐘為50M,一個(gè)時(shí)鐘周期為20ns,500_000_000個(gè)時(shí)鐘周期就是10秒。
2秒計(jì)數(shù)器cnt_2s:用于計(jì)算2秒的時(shí)間。加一條件為state_c==ERROR,表示進(jìn)入提示輸入錯(cuò)誤狀態(tài)就開始計(jì)數(shù)。結(jié)束條件為數(shù)100_000_000個(gè),系統(tǒng)時(shí)鐘為50M,一個(gè)時(shí)鐘周期為20ns,100_000_000個(gè)時(shí)鐘周期就是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 數(shù)碼管顯示模塊設(shè)計(jì)
1.4.1接口信號(hào)

1.4.2設(shè)計(jì)思路
在前面的案例中已經(jīng)有數(shù)碼管顯示的介紹,所以這里不在過多介紹,詳細(xì)介紹請(qǐng)看下方鏈接:
http://fpgabbs.com/forum.php?mod=viewthread&tid=1085&fromuid=100105
其中,數(shù)碼管顯示的數(shù)值和英文字母對(duì)應(yīng)圖像如下圖所示:

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 效果和總結(jié)
下圖是該工程在db603開發(fā)板上的現(xiàn)象——密碼鎖初始狀態(tài)和閉合狀態(tài)

下圖是該工程在db603開發(fā)板上的現(xiàn)象——提示輸入錯(cuò)誤狀態(tài)

下圖是該工程在db603開發(fā)板上的現(xiàn)象——密碼鎖開啟狀態(tài)

下圖是該工程在db603開發(fā)板上的現(xiàn)象——輸入密碼狀態(tài)

下圖是該工程在mp801開發(fā)板上的現(xiàn)象——密碼鎖初始狀態(tài)和閉合狀態(tài)

下圖是該工程在mp801開發(fā)板上的現(xiàn)象——提示輸入錯(cuò)誤狀態(tài)

下圖是該工程在ms980開發(fā)板上的現(xiàn)象——密碼鎖初始狀態(tài)和閉合狀態(tài)

下圖是該工程在ms980開發(fā)板上的現(xiàn)象——提示輸入錯(cuò)誤狀態(tài)

下圖是該工程在ms980開發(fā)板上的現(xiàn)象——密碼鎖開啟狀態(tài)

下圖是該工程在ms980開發(fā)板上的現(xiàn)象——輸入密碼狀態(tài)

由于該項(xiàng)目的上板現(xiàn)象是在數(shù)碼管上顯示輸入的密碼,并且判斷密碼是否正確:正確則在數(shù)碼管上顯示OPEN,錯(cuò)誤則在數(shù)碼管上顯示ERROR并提示輸入錯(cuò)誤2秒,然后數(shù)碼管顯示LOCKED。想觀看完整現(xiàn)象的朋友可以看一下上板演示的視頻。
感興趣的朋友也可以訪問明德?lián)P論壇(http://www.fpgabbs.cn/)進(jìn)行FPGA相關(guān)工程設(shè)計(jì)學(xué)習(xí)。
源工程和設(shè)計(jì)教學(xué)視頻請(qǐng)到論壇下載。
明德?lián)P至簡(jiǎn)設(shè)計(jì)案例200例:
http://fpgabbs.net/thread-1134-1-1.html
(出處: 明德?lián)P論壇)
1.6 公司簡(jiǎn)介
明德?lián)P是一家專注于FPGA領(lǐng)域的專業(yè)性公司,公司主要業(yè)務(wù)包括開發(fā)板、教育培訓(xùn)、項(xiàng)目承接、人才服務(wù)等多個(gè)方向。點(diǎn)撥開發(fā)板——學(xué)習(xí)FPGA的入門之選。
MP801開發(fā)板——千兆網(wǎng)、ADDA、大容量SDRAM等,學(xué)習(xí)和項(xiàng)目需求一步到位。網(wǎng)絡(luò)培訓(xùn)班——不管時(shí)間和空間,明德?lián)P隨時(shí)在你身邊,助你快速學(xué)習(xí)FPGA。周末培訓(xùn)班——明天的你會(huì)感激現(xiàn)在的努力進(jìn)取,升職加薪明德?lián)P來助你。就業(yè)培訓(xùn)班——七大企業(yè)級(jí)項(xiàng)目實(shí)訓(xùn),獲得豐富的項(xiàng)目經(jīng)驗(yàn),高薪就業(yè)。專題課程——高手修煉課:提升設(shè)計(jì)能力;實(shí)用調(diào)試技巧課:提升定位和解決問題能力;FIFO架構(gòu)設(shè)計(jì)課:助你快速成為架構(gòu)設(shè)計(jì)師;時(shí)序約束、數(shù)字信號(hào)處理、PCIE、綜合項(xiàng)目實(shí)踐課等你來選。項(xiàng)目承接——承接企業(yè)FPGA研發(fā)項(xiàng)目。人才服務(wù)——提供人才推薦、人才代培、人才派遣等服務(wù)。