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