基于FPGA的多人搶答器設(shè)計(附源碼工程)
掃描二維碼
隨時隨地手機看文章
功能說明:
1. 搶答器為四人搶答器。
2. 利用六位數(shù)碼管進行顯示搶答器相關(guān)信息。
3. 第一個數(shù)碼管顯示搶答器的狀態(tài),第二個數(shù)碼管顯示搶到的編號,沒有搶答成功前顯示數(shù)字0;第三至六顯示搶答用的時間(以百分秒進行顯示)。
4. 板卡默認空閑狀態(tài)(第一個數(shù)碼管顯示字母A(空閑狀態(tài)),第二個數(shù)碼管顯示數(shù)字0),當(dāng)主持人按下?lián)尨鸢粹o后,進入搶答狀態(tài)(第一個數(shù)碼管顯示字母C(搶答狀態(tài)),第二個數(shù)碼管顯示0,第三至六數(shù)碼管開始記錄時間)
5. 在空閑狀態(tài)期間,選手按下按鍵沒有作用,當(dāng)進入搶答狀態(tài)后,選手誰先按下按鍵,第二個數(shù)碼管顯示對應(yīng)的編號(1、2、3、4)。
6. 當(dāng)選手搶答成功后,進入搶答成功狀態(tài)(第一個數(shù)碼管顯示F),但是第二個數(shù)碼管一直顯示上一次搶答成功的編號,第三至六顯示上一次搶答運行的時間。
7. 主持人按下復(fù)位按鈕后,再次進入空閑狀態(tài),第二個數(shù)碼顯示0,時間清除。
8. 每次主持人按下?lián)尨鸢粹o或者其他四人搶答成功時,蜂鳴器“叮”一聲。
使用平臺:本次設(shè)計應(yīng)用Altera的平臺設(shè)計(芯片:EP4CE10F17C8N)。
仿真平臺:Modelsim。
作者QQ:746833924
說明:本篇設(shè)計中不涉及到IP和原語,代碼在其他平臺依然可以適用;當(dāng)其他板卡電路不同時,會導(dǎo)致不同的現(xiàn)象出現(xiàn),如有需要修改代碼請聯(lián)系作者;如需作者使用的板卡,請聯(lián)系作者;
設(shè)計思想如下:
module responder ( input wire clk, // 50MHz input wire rst_n, input wire key_reset, // 主持人復(fù)位按鈕 input wire key_start, // 主持人開始搶答按鈕 input wire key_1, //第一組的搶答按鈕 input wire key_2, //第二組的搶答按鈕 input wire key_3, //第三組的搶答按鈕 input wire key_4, //第四組的搶答按鈕 output wire [5:0] sel, //數(shù)碼管的位選信號 output wire [7:0] seg, //數(shù)碼管的段選信號 output wire beep //蜂鳴器驅(qū)動信號); wire flag_reset; // 主持人復(fù)位脈沖 wire flag_start; // 主持人開始搶答脈沖 wire flag_1; //第一組搶答脈沖 wire flag_2; //第二組搶答脈沖 wire flag_3; //第三組搶答脈沖 wire flag_4; //第四組搶答脈沖 wire [23:0] show_data; // 顯示數(shù)據(jù) wire flag_beep; // 蜂鳴器“叮”一聲的脈沖
key_ctrl模塊負責(zé)將外部的按鍵信號進行消抖,并且產(chǎn)生對應(yīng)邊沿變化時的脈沖;responder_ctrl模塊負責(zé)根據(jù)脈沖信號和設(shè)計邏輯產(chǎn)生對應(yīng)數(shù)字邏輯;seven_tube_drive(七段數(shù)碼管驅(qū)動)模塊負責(zé)將responder_ctrl模塊產(chǎn)生的數(shù)字邏輯顯示到數(shù)碼管上;beep_ctrl模塊負責(zé)根據(jù)一個脈沖驅(qū)動蜂鳴器“?!币宦暋?
key_ctrl模塊設(shè)計思想為:按鍵信號是由外部機械式按鍵產(chǎn)生,每次按下或者抬起時,會產(chǎn)生一定的抖動。如果直接對其進行邊沿檢測就會導(dǎo)致多次觸發(fā)。故而需要設(shè)計按鍵消抖,進而對消抖之后的波形進行邊沿檢測。消抖原理為:外部按鍵信號發(fā)生改變后,如果能夠持續(xù)20ms,沒有新的改變,就認為此次改變不是抖動,而是真正的按下,然后進行采樣即可。
// 記錄任意邊沿之后沒有遇到新的邊沿的時間長度是否達到20Ms
//---------------------------------------------------------------------------------------
always@(posedgeclk)begin
if(rst_n ==1'b0)
cnt_20ms <=20'd0;
else
if(pulse_key_negedge ==1'b1||pulse_key_posedge ==1'b1)
cnt_20ms <=20'd1;
else
if(cnt_20ms >20'd0&&cnt_20ms <T_20ms)
cnt_20ms <=cnt_20ms +1'b1;
else
cnt_20ms <=20'd0;
end
// ---------------------------------------------------------------------------------------
// 任意邊沿之后沒有遇到新的邊沿的時間長度達到20Ms,認為按鍵穩(wěn)定,此時采樣
//--------------------------------------------------------------------------------------
always@(posedgeclk)begin
if(rst_n ==1'b0)
key_wave <=1'b1;
else
if(cnt_20ms ==T_20ms)
key_wave <=key_rr;
else
key_wave <=key_wave;
end
//--------------------------------------------------------------------------------------
// 對消抖之后的按鍵信號進行邊沿檢測
//---------------------------------------------------------------------------------------------
initialkey_wave_r =1'b1;
always@(posedgeclk)key_wave_r <=key_wave;
assignflag_neg =(key_wave_r ==1'b1&&key_wave ==1'b0)?1'b1:1'b0;
assignflag_pos =(key_wave_r ==1'b0&&key_wave ==1'b1)?1'b1:1'b0;
//--------------------------------------------------------------------------------------------
responder_ctrl模塊的設(shè)計思想:首先根據(jù)主持人復(fù)位、起始脈沖和四組搶答脈沖,確定運行的模式。
reg [1:0] state; // 00: 空閑狀態(tài) 01:搶答狀態(tài) 10:搶答成功狀態(tài) // 根據(jù)按鍵進行狀態(tài)切換 always @ (posedge clk) begin if (rst_n == 1'b0) state <= 2'd0; else if (flag_reset == 1'b1) state <= 2'd0; else if (state == 2'd0 && flag_start == 1'b1) state <= 2'd1; else if (state == 2'd1 && flag_1234 == 1'b1) state <= 2'd2; else state <= state; end
根據(jù)運行的模式,以及顯示的規(guī)則。
// 根據(jù)狀態(tài)進行對應(yīng)的顯示 initial show_data[23:20] = 4'ha; always @ (posedge clk) begin if (rst_n == 1'b0) show_data[23:20] <= 4'ha; else if (state == 2'd0) show_data[23:20] <= 4'ha; else if (state == 2'd1) show_data[23:20] <= 4'hc; else show_data[23:20] <= 4'hf; end
根據(jù)狀態(tài)和外部按鍵,記錄搶到的組號 ;
在空閑狀態(tài),顯示組號為0;在搶答狀態(tài),那個脈沖有效,顯示對應(yīng)的組號;在搶答成功狀態(tài),保持搶答成功的組號即可。
always @ (posedge clk) begin if (rst_n == 1'b0) show_data[19:16] <= 4'd0; else if (state == 2'd0) show_data[19:16] <= 4'd0; else if (state == 2'd1) begin if (flag_1 == 1'b1) show_data[19:16] <= 4'd1; if (flag_2 == 1'b1) show_data[19:16] <= 4'd2; if (flag_3 == 1'b1) show_data[19:16] <= 4'd3; if (flag_4 == 1'b1) show_data[19:16] <= 4'd4; end else show_data[19:16] <= show_data[19:16]; end
在空閑狀態(tài)中,10ms計時器清零,10ms的記錄次數(shù)也清零;在搶答狀態(tài)中,循環(huán)計時10ms,并且記錄10ms的次數(shù);在搶答成功狀態(tài),保持記錄10ms的次數(shù)。
在搶答狀態(tài)中,循環(huán)記錄10ms always @ (posedge clk) begin if (rst_n == 1'b0) cnt_10ms <= 19'd0; else if (state == 2'd1 && cnt_10ms < T_10ms - 1'b1) cnt_10ms <= cnt_10ms + 1'b1; else cnt_10ms <= 19'd0; end 在搶答狀態(tài)中,記錄經(jīng)過了多少個10ms always @ (posedge clk) begin if (rst_n == 1'b0) counter <= 32'd0; else if (state == 2'd0) counter <= 32'd0; else if (cnt_10ms == T_10ms - 1'b1) if (counter < 32'd9999) counter <= counter + 1'b1; else counter <= counter; else counter <= counter; end
將記錄的10ms的次數(shù),轉(zhuǎn)變?yōu)锽CD碼,輸出給數(shù)碼管的驅(qū)動模塊。
// 將記錄10ms的個數(shù)轉(zhuǎn)換為BCD碼輸出 always @ (posedge clk) begin if (rst_n == 1'b0) show_data[15:0] <= 16'd0; else show_data[3:0] <= counter%10; show_data[7:4] <= counter/10 % 10; show_data[11:8] <= counter/100 % 10; show_data[15:12] <= counter/1000; end
每次主持人按下?lián)尨鸢粹o(在空閑狀態(tài))或者其他四人搶答成功(在搶答狀態(tài))時,產(chǎn)生一個蜂鳴器“?!币宦暤拿}沖。
assign flag_beep = ((state == 2'd0 && flag_start == 1'b1) || (state == 2'd1 && flag_1234 == 1'b1)) ? 1'b1 : 1'b0;
beep_ctrl模塊的設(shè)計原理為:蜂鳴器為無源蜂鳴器,需要給一定頻率的方波才可以出聲音。
// 設(shè)計原理:外部輸入脈沖,蜂鳴器出聲音200ms(正好是“?!币宦暎?
// 蜂鳴器出聲音準(zhǔn)備采用500hz的方波來驅(qū)動無源蜂鳴器。
首先設(shè)計一個脈沖有效后,記錄200ms的時間。
// 脈沖拉高后,記錄200ms always @ (posedge clk) begin if (rst_n == 1'b0) cnt_200ms <= 24'd0; else if (flag_beep == 1'b1 && cnt_200ms == 0) cnt_200ms <= cnt_200ms + 1'b1; else if (cnt_200ms > 24'd0 && cnt_200ms < T_200ms - 1'b1) cnt_200ms <= cnt_200ms + 1'b1; else cnt_200ms <= 24'd0; end
在200ms期間,驅(qū)動蜂鳴器產(chǎn)生500hz的方波。
記錄1ms always @ (posedge clk) begin if (rst_n == 1'b0) cnt_1ms <= 16'd0; else if (cnt_1ms < T_1ms - 1'b1) cnt_1ms <= cnt_1ms + 1'b1; else cnt_1ms <= 16'd0; end 在200ms期間,每到1ms,beep進行取反 always @ (posedge clk) begin if (rst_n == 1'b0) beep <= 1'b0; else if (cnt_200ms > 24'd0 && cnt_1ms == T_1ms - 1'b1) beep <= ~beep; else beep <= beep; end
以上即為beep_ctrl模塊的設(shè)計思想;
七段數(shù)碼管為普通六位一體的共陽極數(shù)碼,采用動態(tài)驅(qū)動的方式,在此不再贅述。
空閑狀態(tài)如下:
搶答狀態(tài)中,百分秒開始運行。
當(dāng)有搶答按鍵按下后,進入搶答成功狀態(tài),鎖定組號和搶答運行的時間。
主持人再次按下復(fù)位按鍵,回到空閑狀態(tài),就可以重復(fù)以上過程。
下板后,演示視頻(鏈接)如下:
https://www.bilibili.com/video/BV1vnYFeNEfK/?vd_source=b5405faeab8632f02533bcbfc5e52e55
本設(shè)計所有內(nèi)容(設(shè)計代碼、設(shè)計工程)鏈接為:
鏈接:https://pan.baidu.com/s/1PAv-4N10u7K3EZQLdayxlA
提取碼:m3xw
END