新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 比賽計(jì)分系統(tǒng)設(shè)計(jì)

比賽計(jì)分系統(tǒng)設(shè)計(jì)

作者: 時(shí)間:2023-12-11 來源:電子森林 收藏
  • 任務(wù):基于核心板 和 底板 完成設(shè)計(jì)并觀察調(diào)試結(jié)果
  • 要求:按動(dòng)核心板獨(dú)立按鍵,驅(qū)動(dòng)底板上8位數(shù)碼管為比賽雙方在0~999內(nèi)計(jì)分。
  • 解析:驅(qū)動(dòng)獨(dú)立按鍵,當(dāng)按動(dòng)兩隊(duì)加分按鍵時(shí),控制兩隊(duì)分?jǐn)?shù)調(diào)整,最后通過驅(qū)動(dòng)底板上的數(shù)碼管電路將得分值顯示在數(shù)碼管上。

根據(jù)前面的實(shí)驗(yàn)解析我們可以得知,該設(shè)計(jì)可以拆分成三個(gè)功能模塊實(shí)現(xiàn),

  • Debounce:通過驅(qū)動(dòng)獨(dú)立按鍵工作獲取操作信息數(shù)據(jù)。
  • Counter:根據(jù)按鍵的操作信息控制雙方比賽分值調(diào)整。
  • Segment_scan:通過驅(qū)動(dòng)串轉(zhuǎn)并芯片74HC595控制數(shù)碼管掃描顯示比賽分值。

頂層模塊Game_Score通過實(shí)例化三個(gè)子模塊并將對(duì)應(yīng)的信號(hào)連接,最終實(shí)現(xiàn)的總體設(shè)計(jì)。

Top-Down層次設(shè)計(jì)

模塊結(jié)構(gòu)設(shè)計(jì)

數(shù)碼管連接方式

在前面之前的教程中數(shù)碼管章節(jié)已為大家介紹了數(shù)碼管獨(dú)立顯示的相關(guān)內(nèi)容,關(guān)于獨(dú)立顯示這里就不在贅述。我們的實(shí)驗(yàn)平臺(tái)的擴(kuò)展板卡上有8位數(shù)碼管,根據(jù)驅(qū)動(dòng)方法不同,有以下比較:

獨(dú)立顯示數(shù)碼管

獨(dú)立顯示:控制每個(gè)數(shù)碼管至少需要8個(gè)I/O口控制,8位數(shù)碼管就需要8*8 = 64根信號(hào)線才能分別顯示。獨(dú)立顯示實(shí)現(xiàn)簡(jiǎn)單,但是需要大量的信號(hào)線。

掃描顯示數(shù)碼管

掃描顯示:將每位數(shù)碼管的同一段選信號(hào)連接在一起,這樣我們就只需要8根段選信號(hào)和8根位選信號(hào),共計(jì)16根信號(hào)。掃描顯示可以有效節(jié)約I/O口資源,實(shí)現(xiàn)起來稍顯復(fù)雜。

上圖中我們用了4位數(shù)碼管,共用了段選控制8+位選控制4=12管腳,如果是8位數(shù)碼管,按照上面方式連接,段選控制還是8管腳,位選控制也增加到8管腳,共計(jì)16管腳,當(dāng)然硬件的連接還需要結(jié)合軟件的驅(qū)動(dòng),掃描顯示數(shù)碼管的驅(qū)動(dòng)方法和獨(dú)立顯示數(shù)碼管驅(qū)動(dòng)方法不同,接下來我們一起來學(xué)習(xí)。

數(shù)碼管模塊電路連接

數(shù)碼管連接方式部分我們了解到數(shù)碼管常用的兩種連接方式,獨(dú)立顯示數(shù)碼管的原理及驅(qū)動(dòng)方法我們?cè)诨A(chǔ)數(shù)字電路實(shí)驗(yàn)部分就已經(jīng)詳細(xì)學(xué)習(xí)了,本實(shí)驗(yàn)我們來學(xué)習(xí)掃描顯示數(shù)碼管工作原理及驅(qū)動(dòng)方法。

前面我們說8位數(shù)碼管通過掃描顯示方式連接需要16管腳,對(duì)于大部分控制器件來說依然有點(diǎn)多,所以我們需要一種串行通信控制并行輸出的驅(qū)動(dòng)器,處理器通過串行接口(引腳占用少)驅(qū)動(dòng)驅(qū)動(dòng)器完成16或更多信號(hào)的控制,74HC595就是這么一款驅(qū)動(dòng)器。通過3路串行輸入(兼容SPI協(xié)議)控制8路并行輸出,而且可以級(jí)聯(lián)使用。

74HC595驅(qū)動(dòng)數(shù)碼管電路

數(shù)碼管模塊驅(qū)動(dòng)設(shè)計(jì)

我們知道數(shù)碼管分位共陰極和共陽(yáng)極,我們底板上使用的就是8位共陰極數(shù)碼管,驅(qū)動(dòng)數(shù)碼管顯示需要字庫(kù),方便程序中通過數(shù)據(jù)索引對(duì)應(yīng)字庫(kù),在基礎(chǔ)數(shù)字電路實(shí)驗(yàn)部分我們已經(jīng)學(xué)習(xí)過。

7段共陰極數(shù)碼管字庫(kù)定義如下:

reg[6:0] seg [15:0]; 
always @(negedge rst_n) begin
	seg[0]	=	7'h3f;   // 0
	seg[1]	=	7'h06;   // 1
	seg[2]	=	7'h5b;   // 2
	seg[3]	=	7'h4f;   // 3
	seg[4]	=	7'h66;   // 4
	seg[5]	=	7'h6d;   // 5
	seg[6]	=	7'h7d;   // 6
	seg[7]	=	7'h07;   // 7
	seg[8]	=	7'h7f;   // 8
	seg[9]	=	7'h6f;   // 9
	seg[10]	=	7'h77;   // A
	seg[11]	=	7'h7c;   // b
	seg[12]	=	7'h39;   // C
	seg[13]	=	7'h5e;   // d
	seg[14]	=	7'h79;   // E
	seg[15]	=	7'h71;   // F
	end

數(shù)碼管顯示需要段選(a b c d e f g)輸出字庫(kù)數(shù)據(jù),位選(dig)輸出選通信號(hào),前面硬件電路連接部分看到8位數(shù)碼管的段選全部對(duì)應(yīng)連在了一起,好像不能同時(shí)顯示8個(gè)不同的數(shù)字,是的,我們理解的沒錯(cuò),想要掃描顯示數(shù)碼管工作需要采用分時(shí)掃描的方式驅(qū)動(dòng)。怎么樣分時(shí)掃描?

  • 首先我們考慮怎么樣讓第1個(gè)數(shù)碼管顯示數(shù)字1,控制數(shù)碼管段選端輸出1的字庫(kù)7'h06(G=0、F=0、E=0、D=0、C=1、B=1、A=0),同時(shí)控制數(shù)碼管位選端只選通第1個(gè)數(shù)碼管顯示(DIG1=0、DIG2~DIG8都為1)
  • 然后如果我們有8秒的時(shí)間,第1秒時(shí)間內(nèi)控制第1個(gè)數(shù)碼管顯示數(shù)字1,第2秒時(shí)間內(nèi)控制第2個(gè)數(shù)碼管顯示數(shù)字2,依次類推,這樣我們就可以在8秒時(shí)間內(nèi)將8個(gè)不同數(shù)字分時(shí)顯示出來
  • 最后我們將8秒的時(shí)間變成8毫秒,每個(gè)數(shù)碼管顯示1毫秒,這樣我們?cè)?秒時(shí)間內(nèi)將8個(gè)數(shù)字顯示(掃描)125次,即刷新率為125次/秒,人眼睛有視覺暫留效應(yīng),當(dāng)數(shù)碼管閃爍刷新頻率足夠高(例如125)時(shí),我們看到的是8個(gè)數(shù)碼管同時(shí)顯示。

以上描述的就是掃描顯示的數(shù)碼管的工作原理及驅(qū)動(dòng)方法

// dat_1[3:0]   //SEG1 顯示的數(shù)據(jù)輸入
......
// dat_8[3:0]   //SEG8 顯示的數(shù)據(jù)輸入
// dat_en[7:0]  //數(shù)碼管數(shù)據(jù)位顯示使能,[MSB~LSB]=[SEG1~SEG8]
// dot_en[7:0]  //數(shù)碼管小數(shù)點(diǎn)位顯示使能,[MSB~LSB]=[SEG1~SEG8]
// data[15:0]    //數(shù)碼管掃描控制數(shù)據(jù),[15:8]為段選,[7:0]為位選
MAIN:begin
	cnt_main <= cnt_main + 1'b1;
	state <= WRITE;		//在配置完發(fā)給74HC595的數(shù)據(jù)同時(shí)跳轉(zhuǎn)至WRITE狀態(tài),完成串行時(shí)序
	case(cnt_main)
		//對(duì)8位數(shù)碼管逐位掃描
		//data          [15:8]為段選,         [7:0]為位選
		3'd0: data <= {{dot_en[7],seg[dat_1]},dat_en[7]?8'hfe:8'hff};
		3'd1: data <= {{dot_en[6],seg[dat_2]},dat_en[6]?8'hfd:8'hff}; 
		3'd2: data <= {{dot_en[5],seg[dat_3]},dat_en[5]?8'hfb:8'hff}; 
		3'd3: data <= {{dot_en[4],seg[dat_4]},dat_en[4]?8'hf7:8'hff}; 
		3'd4: data <= {{dot_en[3],seg[dat_5]},dat_en[3]?8'hef:8'hff};
		3'd5: data <= {{dot_en[2],seg[dat_6]},dat_en[2]?8'hdf:8'hff}; 
		3'd6: data <= {{dot_en[1],seg[dat_7]},dat_en[1]?8'hbf:8'hff}; 
		3'd7: data <= {{dot_en[0],seg[dat_8]},dat_en[0]?8'h7f:8'hff}; 
		default: data <= {8'h00,8'hff};
	endcase
	end

如果不考慮74HC595驅(qū)動(dòng)芯片,F(xiàn)PGA直接連接數(shù)碼管模塊的段選和位選信號(hào),data作為輸出端口分配給段選和位選控制管腳,程序到這里差不多就OK了,然而為了節(jié)約IO管腳資源的占用,我們電路里有串轉(zhuǎn)并的驅(qū)動(dòng)芯片74HC595,所以我們還需要增加程序設(shè)計(jì),將data的數(shù)據(jù)通過串行的方式傳輸?shù)?4HC595芯片,然后74HC595芯片將會(huì)根據(jù)data的內(nèi)容控制數(shù)碼管模塊顯示。

74HC595是較為常用的串行轉(zhuǎn)并行的芯片,內(nèi)部集成了一個(gè)8位移位寄存器、一個(gè)存儲(chǔ)器和8個(gè)三態(tài)緩沖輸出。在最簡(jiǎn)單的情況下我們只需要控制3根引腳輸入得到8根引腳并行輸出信號(hào),而且可以級(jí)聯(lián)使用,我們使用3個(gè)I/O口控制兩個(gè)級(jí)聯(lián)的74HC595芯片,產(chǎn)生16路并行輸出,連接到掃描顯示的8位數(shù)碼管上。

不同的IC廠家都可以生產(chǎn)74HC595芯片,功能都是一樣的,然而不同廠家的芯片手冊(cè)對(duì)于管腳的命名會(huì)存在差異,管腳順序相同,大家可以對(duì)應(yīng)識(shí)別 上圖是本設(shè)計(jì)中74HC595芯片的硬件電路連接,參考74HC595數(shù)據(jù)手冊(cè)了解其具體用法,下圖中我們了解到OE#(G#)和MR#(SCLR#)信號(hào)分別為輸出使能(低電平輸出)和復(fù)位管腳(低電平復(fù)位),OE#(G#)我們接GND讓芯片輸出使能,MR#(SCLR#)我們接VCC讓芯片的移位寄存器永遠(yuǎn)不復(fù)位,如此FPGA只需要控制SHCP(SCK)、STCP(RCK)和DS(SER)即可。

74HC595引腳功能描述

74HC595內(nèi)部結(jié)構(gòu)圖

74HC595時(shí)序圖

根據(jù)74HC595內(nèi)部結(jié)構(gòu)及時(shí)序圖可以得知,SHCP(SCK)每個(gè)上升沿都會(huì)將DS(SER)的數(shù)據(jù)采樣到8位移位寄存器中,當(dāng)STCP(RCK)上升沿時(shí),8位移位寄存器中的數(shù)據(jù)被所存到8位鎖存器中,同時(shí)對(duì)應(yīng)Q0~Q7管腳刷新對(duì)應(yīng)輸出。對(duì)于我們的硬件,我們首先通過SHCP(SCK)和DS(SER)配合將需要傳輸?shù)?6位數(shù)據(jù)輸出,然后控制STCP(RCK)產(chǎn)生上升沿,所以我們需要至少16個(gè)SH_CP(SCK)周期完成1次數(shù)碼管控制。

74HC595串行驅(qū)動(dòng) 程序?qū)崿F(xiàn)如下:

//seg_rck     //74HC595的RCK管腳
//seg_sck     //74HC595的SCK管腳
//seg_din     //74HC595的SER管腳
WRITE:begin
	if(cnt_write >= 6'd33) cnt_write <= 1'b0;
	else cnt_write <= cnt_write + 1'b1;
	case(cnt_write)
		//74HC595是串行轉(zhuǎn)并行的芯片,3路輸入可產(chǎn)生8路輸出,而且可以級(jí)聯(lián)使用
		//74HC595的時(shí)序?qū)崿F(xiàn),參考74HC595的芯片手冊(cè)
		6'd0:  begin seg_sck <= LOW; 
		seg_din <= data[15]; 
		end		//SCK下降沿時(shí)SER更新數(shù)據(jù)
		6'd1:  begin seg_sck <= HIGH; 
		end				//SCK上升沿時(shí)SER數(shù)據(jù)穩(wěn)定
		6'd2:  begin seg_sck <= LOW; 
		seg_din <= data[14]; end
		6'd3:  begin seg_sck <= HIGH; 
		end
		6'd4:  begin seg_sck <= LOW; 
		seg_din <= data[13]; 
		end
		6'd5:  begin seg_sck <= HIGH; 
		end
		6'd6:  begin seg_sck <= LOW; 
		seg_din <= data[12]; 
		end
		6'd7:  begin seg_sck <= HIGH; 
		end
		6'd8:  begin seg_sck <= LOW; 
		seg_din <= data[11]; end
		6'd9:  begin seg_sck <= HIGH; 
		end
		6'd10: begin seg_sck <= LOW; 
		seg_din <= data[10]; 
		end
		6'd11: begin seg_sck <= HIGH; 
		end
		6'd12: begin seg_sck <= LOW; 
		seg_din <= data[9]; end
		6'd13: begin seg_sck <= HIGH; 
		end
		6'd14: begin seg_sck <= LOW; 
		seg_din <= data[8]; 
		end
		6'd15: begin seg_sck <= HIGH; 
		end
		6'd16: begin seg_sck <= LOW; 
		seg_din <= data[7]; end
		6'd17: begin seg_sck <= HIGH; 
		end
		6'd18: begin seg_sck <= LOW; 
		seg_din <= data[6]; end
		6'd19: begin seg_sck <= HIGH; 
		end
		6'd20: begin seg_sck <= LOW; 
		seg_din <= data[5]; end
		6'd21: begin seg_sck <= HIGH; 
		end
		6'd22: begin seg_sck <= LOW; 
		seg_din <= data[4]; end
		6'd23: begin seg_sck <= HIGH; 
		end
		6'd24: begin seg_sck <= LOW; 
		seg_din <= data[3]; end
		6'd25: begin seg_sck <= HIGH; 
		end
		6'd26: begin seg_sck <= LOW; 
		seg_din <= data[2]; end
		6'd27: begin seg_sck <= HIGH; 
		end
		6'd28: begin seg_sck <= LOW; 
		seg_din <= data[1]; 
		end
		6'd29: begin seg_sck <= HIGH; 
		end
		6'd30: begin seg_sck <= LOW; 
		seg_din <= data[0]; 
		end
		6'd31: begin seg_sck <= HIGH; 
		end
		6'd32: begin seg_rck <= HIGH; 
		end			//當(dāng)16位數(shù)據(jù)傳送完成后RCK拉高,輸出生效
		6'd33: begin seg_rck <= LOW; 
		state <= MAIN; 
		end
		default: ;
	endcase
	end

如果我們?cè)O(shè)計(jì)一個(gè)狀態(tài)機(jī),將數(shù)碼管掃描的程序和74HC595串行驅(qū)動(dòng)程序分別做成兩個(gè)狀態(tài)MAIN和WRITE,控制數(shù)碼管掃描程序每次產(chǎn)生一組控制數(shù)據(jù),執(zhí)行一次74HC595串行通信,就可以完成我們數(shù)碼管模塊電路的驅(qū)動(dòng)設(shè)計(jì)了。

狀態(tài)機(jī)設(shè)計(jì)框架

8位數(shù)碼管刷新1次需要8個(gè)數(shù)碼管各點(diǎn)亮1次,每個(gè)數(shù)碼管點(diǎn)亮1次需要16位數(shù)據(jù),16位數(shù)據(jù)通過串行方式傳輸給74HC595需要至少16個(gè)SHCP(SCK)周期,按照我們前面說的數(shù)碼管刷新率達(dá)到125次/秒,掃描方式下每個(gè)數(shù)碼管會(huì)點(diǎn)亮1毫秒,數(shù)碼管點(diǎn)亮的時(shí)間應(yīng)該等于1次數(shù)碼管控制的時(shí)間,也就是16個(gè)SHCP(SCK)周期,所以我們可以控制74HC595的SHCP(SCK)時(shí)鐘周期計(jì)算: SHCP(SCK)周期 = 1ms / 16 = 62.5us 即是說,當(dāng) SHCP(SCK)周期小于62.5us,刷新率就應(yīng)該大于125次/秒(注:以上為估算的結(jié)果) 為了計(jì)算方便,我們就取SHCP(SCK)周期為50us,那么觸發(fā)74HC595串行驅(qū)動(dòng)執(zhí)行的敏感變量周期應(yīng)該為25us,我們可以通過分頻產(chǎn)生周期為25us時(shí)鐘觸發(fā)完成以上所以操作。

時(shí)鐘分頻程序?qū)崿F(xiàn)如下:

//計(jì)數(shù)器對(duì)系統(tǒng)時(shí)鐘信號(hào)進(jìn)行計(jì)數(shù)
reg [9:0] cnt = 1'b0;
always@(posedge clk or negedge rst_n) begin
	if(!rst_n) cnt <= 1'b0;
	else if(cnt>=(CNT_40KHz-1)) cnt <= 1'b0;
	else cnt <= cnt + 1'b1;
	end //根據(jù)計(jì)數(shù)器計(jì)數(shù)的周期產(chǎn)生分頻的脈沖信號(hào)
	reg clk_40khz = 1'b0; 
	always@(posedge clk or negedge rst_n) begin
	if(!rst_n) clk_40khz <= 1'b0;
	else if(cnt<(CNT_40KHz>>1)) clk_40khz <= 1'b0;
	else clk_40khz <= 1'b1;
	end

狀態(tài)機(jī)狀態(tài)轉(zhuǎn)移圖

系統(tǒng)總體實(shí)現(xiàn)

按鍵消抖模塊我們前面基礎(chǔ)數(shù)字電路實(shí)驗(yàn)中詳細(xì)介紹過,這里我們直接調(diào)用消抖模塊,記分器邏輯部分其實(shí)就是對(duì)按鍵按動(dòng)次數(shù)計(jì)數(shù),輸出0~999之間的BCD碼制數(shù)據(jù),這里也不再贅述,最后例化數(shù)碼管模塊將兩隊(duì)的比分?jǐn)?shù)據(jù)顯示出來。最后顯示的數(shù)據(jù)為000~999,本實(shí)驗(yàn)例程中為了顯示最小有效數(shù)據(jù)位,增加了將最高位為0的數(shù)據(jù)位不顯示的設(shè)計(jì),例如當(dāng)分?jǐn)?shù)為5分時(shí),數(shù)碼管本來會(huì)顯示005,現(xiàn)在控制高兩位的00不顯示,只顯示最低位5。

顯示控制程序?qū)崿F(xiàn)如下:

wire	[7:0]	dat_en;		//控制數(shù)碼管點(diǎn)亮
assign	dat_en[7] = 1'b0;
assign	dat_en[6] = red_seg[11:8]? 1'b1:1'b0;
assign	dat_en[5] = red_seg[11:4]? 1'b1:1'b0;
assign	dat_en[4] = 1'b1; 
assign	dat_en[3] = 1'b0;
assign	dat_en[2] = blue_seg[11:8]? 1'b1:1'b0;
assign	dat_en[1] = blue_seg[11:4]? 1'b1:1'b0;
assign	dat_en[0] = 1'b1;

數(shù)碼管顯示模塊例化 程序?qū)崿F(xiàn)如下:

//segment_scan display module
Segment_scan u4
(
.clk					(clk			),	//系統(tǒng)時(shí)鐘 12MHz
.rst_n					(rst_n			),	//系統(tǒng)復(fù)位 低有效
.dat_1					(0				),	//SEG1 顯示的數(shù)據(jù)輸入
.dat_2					(red_seg[11:8]	),	//SEG2 顯示的數(shù)據(jù)輸入
.dat_3					(red_seg[7:4]	),	//SEG3 顯示的數(shù)據(jù)輸入
.dat_4					(red_seg[3:0]	),	//SEG4 顯示的數(shù)據(jù)輸入
.dat_5					(0				),	//SEG5 顯示的數(shù)據(jù)輸入
.dat_6					(blue_seg[11:8]	),	//SEG6 顯示的數(shù)據(jù)輸入
.dat_7					(blue_seg[7:4]	),	//SEG7 顯示的數(shù)據(jù)輸入
.dat_8					(blue_seg[3:0]	),	//SEG8 顯示的數(shù)據(jù)輸入
.dat_en					(dat_en			),	//數(shù)碼管數(shù)據(jù)位顯示使能,[MSB~LSB]=[SEG1~SEG8]
.dot_en					(8'b0001_0001	),	//數(shù)碼管小數(shù)點(diǎn)位顯示使能,[MSB~LSB]=[SEG1~SEG8]
.seg_rck				(seg_rck		),	//74HC595的RCK管腳
.seg_sck				(seg_sck		),	//74HC595的SCK管腳
.seg_din				(seg_din		)	//74HC595的SER管腳);

綜合后的設(shè)計(jì)框圖如下:

RTL設(shè)計(jì)框圖

實(shí)驗(yàn)步驟

  1. 雙擊打開Quartus Prime工具軟件;
  2. 新建工程:File → New Project Wizard(工程命名,工程目錄選擇,設(shè)備型號(hào)選擇,EDA工具選擇);
  3. 新建文件:File → New → Verilog HDL File,鍵入設(shè)計(jì)代碼并保存;
  4. 設(shè)計(jì)綜合:雙擊Tasks窗口頁(yè)面下的Analysis & Synthesis對(duì)代碼進(jìn)行綜合;
  5. 管腳約束:Assignments → Assignment Editor,根據(jù)項(xiàng)目需求分配管腳;
  6. 設(shè)計(jì)編譯:雙擊Tasks窗口頁(yè)面下的Compile Design對(duì)設(shè)計(jì)進(jìn)行整體編譯并生成配置文件;
  7. 程序燒錄:點(diǎn)擊Tools → Programmer打開配置工具,Program進(jìn)行下載;
  8. 觀察設(shè)計(jì)運(yùn)行結(jié)果。

將程序加載到FPGA開發(fā)平臺(tái),底板數(shù)碼管左邊4位為紅隊(duì)比分,右邊4位為藍(lán)隊(duì)比分,初始都為0分,核心板K3按鍵為紅隊(duì)加分按鍵,核心板K4按鍵為藍(lán)隊(duì)加分按鍵,按動(dòng)K3、K4按鍵,觀察紅隊(duì)和藍(lán)隊(duì)比分變化。



評(píng)論


相關(guān)推薦

技術(shù)專區(qū)

關(guān)閉