比賽計分系統設計
實(shí)驗任務(wù)
實(shí)驗目的
在基礎數字電路實(shí)驗部分我們已經(jīng)掌握了FPGA驅動(dòng)獨立按鍵的原理及方法,控制數碼管顯示十進(jìn)制數的BCD碼方案前面也多次介紹,本實(shí)驗主要學(xué)習數碼管掃描顯示的原理及方法。
本文引用地址:http://dyxdggzs.com/article/202312/453767.htm設計框圖
根據前面的實(shí)驗解析我們可以得知,該設計可以拆分成三個(gè)功能模塊實(shí)現,
頂層模塊Game_Score通過(guò)實(shí)例化三個(gè)子模塊并將對應的信號連接,最終實(shí)現比賽計分系統的總體設計。
實(shí)驗原理
數碼管連接方式
在前面之前的教程中數碼管章節已為大家介紹了數碼管獨立顯示的相關(guān)內容,關(guān)于獨立顯示這里就不在贅述。我們的實(shí)驗平臺的擴展板卡上有8位數碼管,根據驅動(dòng)方法不同,有以下比較:
獨立顯示:控制每個(gè)數碼管至少需要8個(gè)I/O口控制,8位數碼管就需要8*8 = 64根信號線(xiàn)才能分別顯示。獨立顯示實(shí)現簡(jiǎn)單,但是需要大量的信號線(xiàn)。
掃描顯示:將每位數碼管的同一段選信號連接在一起,這樣我們就只需要8根段選信號和8根位選信號,共計16根信號。掃描顯示可以有效節約I/O口資源,實(shí)現起來(lái)稍顯復雜。
上圖中我們用了4位數碼管,共用了段選控制8+位選控制4=12管腳,如果是8位數碼管,按照上面方式連接,段選控制還是8管腳,位選控制也增加到8管腳,共計16管腳,當然硬件的連接還需要結合軟件的驅動(dòng),掃描顯示數碼管的驅動(dòng)方法和獨立顯示數碼管驅動(dòng)方法不同,接下來(lái)我們一起來(lái)學(xué)習。
數碼管模塊電路連接
數碼管連接方式部分我們了解到數碼管常用的兩種連接方式,獨立顯示數碼管的原理及驅動(dòng)方法我們在基礎數字電路實(shí)驗部分就已經(jīng)詳細學(xué)習了,本實(shí)驗我們來(lái)學(xué)習掃描顯示數碼管工作原理及驅動(dòng)方法。
前面我們說(shuō)8位數碼管通過(guò)掃描顯示方式連接需要16管腳,對于大部分控制器件來(lái)說(shuō)依然有點(diǎn)多,所以我們需要一種串行通信控制并行輸出的驅動(dòng)器,處理器通過(guò)串行接口(引腳占用少)驅動(dòng)驅動(dòng)器完成16或更多信號的控制,74HC595就是這么一款驅動(dòng)器。通過(guò)3路串行輸入(兼容SPI協(xié)議)控制8路并行輸出,而且可以級聯(lián)使用。
數碼管模塊驅動(dòng)設計
我們知道數碼管分位共陰極和共陽(yáng)極,我們底板上使用的就是8位共陰極數碼管,驅動(dòng)數碼管顯示需要字庫,方便程序中通過(guò)數據索引對應字庫,在基礎數字電路實(shí)驗部分我們已經(jīng)學(xué)習過(guò)。
7段共陰極數碼管字庫定義如下:
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
數碼管顯示需要段選(a b c d e f g)輸出字庫數據,位選(dig)輸出選通信號,前面硬件電路連接部分看到8位數碼管的段選全部對應連在了一起,好像不能同時(shí)顯示8個(gè)不同的數字,是的,我們理解的沒(méi)錯,想要掃描顯示數碼管工作需要采用分時(shí)掃描的方式驅動(dòng)。怎么樣分時(shí)掃描?
以上描述的就是掃描顯示的數碼管的工作原理及驅動(dòng)方法
// dat_1[3:0] //SEG1 顯示的數據輸入 ...... // dat_8[3:0] //SEG8 顯示的數據輸入 // dat_en[7:0] //數碼管數據位顯示使能,[MSB~LSB]=[SEG1~SEG8] // dot_en[7:0] //數碼管小數點(diǎn)位顯示使能,[MSB~LSB]=[SEG1~SEG8] // data[15:0] //數碼管掃描控制數據,[15:8]為段選,[7:0]為位選 MAIN:begin cnt_main <= cnt_main + 1'b1; state <= WRITE; //在配置完發(fā)給74HC595的數據同時(shí)跳轉至WRITE狀態(tài),完成串行時(shí)序 case(cnt_main) //對8位數碼管逐位掃描 //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驅動(dòng)芯片,FPGA直接連接數碼管模塊的段選和位選信號,data作為輸出端口分配給段選和位選控制管腳,程序到這里差不多就OK了,然而為了節約IO管腳資源的占用,我們電路里有串轉并的驅動(dòng)芯片74HC595,所以我們還需要增加程序設計,將data的數據通過(guò)串行的方式傳輸到74HC595芯片,然后74HC595芯片將會(huì )根據data的內容控制數碼管模塊顯示。
74HC595是較為常用的串行轉并行的芯片,內部集成了一個(gè)8位移位寄存器、一個(gè)存儲器和8個(gè)三態(tài)緩沖輸出。在最簡(jiǎn)單的情況下我們只需要控制3根引腳輸入得到8根引腳并行輸出信號,而且可以級聯(lián)使用,我們使用3個(gè)I/O口控制兩個(gè)級聯(lián)的74HC595芯片,產(chǎn)生16路并行輸出,連接到掃描顯示的8位數碼管上。
不同的IC廠(chǎng)家都可以生產(chǎn)74HC595芯片,功能都是一樣的,然而不同廠(chǎng)家的芯片手冊對于管腳的命名會(huì )存在差異,管腳順序相同,大家可以對應識別 上圖是本設計中74HC595芯片的硬件電路連接,參考74HC595數據手冊了解其具體用法,下圖中我們了解到OE#(G#)和MR#(SCLR#)信號分別為輸出使能(低電平輸出)和復位管腳(低電平復位),OE#(G#)我們接GND讓芯片輸出使能,MR#(SCLR#)我們接VCC讓芯片的移位寄存器永遠不復位,如此FPGA只需要控制SHCP(SCK)、STCP(RCK)和DS(SER)即可。
根據74HC595內部結構及時(shí)序圖可以得知,SHCP(SCK)每個(gè)上升沿都會(huì )將DS(SER)的數據采樣到8位移位寄存器中,當STCP(RCK)上升沿時(shí),8位移位寄存器中的數據被所存到8位鎖存器中,同時(shí)對應Q0~Q7管腳刷新對應輸出。對于我們的硬件,我們首先通過(guò)SHCP(SCK)和DS(SER)配合將需要傳輸的16位數據輸出,然后控制STCP(RCK)產(chǎn)生上升沿,所以我們需要至少16個(gè)SH_CP(SCK)周期完成1次數碼管控制。
74HC595串行驅動(dòng) 程序實(shí)現如下:
//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是串行轉并行的芯片,3路輸入可產(chǎn)生8路輸出,而且可以級聯(lián)使用 //74HC595的時(shí)序實(shí)現,參考74HC595的芯片手冊 6'd0: begin seg_sck <= LOW; seg_din <= data[15]; end //SCK下降沿時(shí)SER更新數據 6'd1: begin seg_sck <= HIGH; end //SCK上升沿時(shí)SER數據穩定 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 //當16位數據傳送完成后RCK拉高,輸出生效 6'd33: begin seg_rck <= LOW; state <= MAIN; end default: ; endcase end
如果我們設計一個(gè)狀態(tài)機,將數碼管掃描的程序和74HC595串行驅動(dòng)程序分別做成兩個(gè)狀態(tài)MAIN和WRITE,控制數碼管掃描程序每次產(chǎn)生一組控制數據,執行一次74HC595串行通信,就可以完成我們數碼管模塊電路的驅動(dòng)設計了。
8位數碼管刷新1次需要8個(gè)數碼管各點(diǎn)亮1次,每個(gè)數碼管點(diǎn)亮1次需要16位數據,16位數據通過(guò)串行方式傳輸給74HC595需要至少16個(gè)SHCP(SCK)周期,按照我們前面說(shuō)的數碼管刷新率達到125次/秒,掃描方式下每個(gè)數碼管會(huì )點(diǎn)亮1毫秒,數碼管點(diǎn)亮的時(shí)間應該等于1次數碼管控制的時(shí)間,也就是16個(gè)SHCP(SCK)周期,所以我們可以控制74HC595的SHCP(SCK)時(shí)鐘周期計算: SHCP(SCK)周期 = 1ms / 16 = 62.5us 即是說(shuō),當 SHCP(SCK)周期小于62.5us,刷新率就應該大于125次/秒(注:以上為估算的結果) 為了計算方便,我們就取SHCP(SCK)周期為50us,那么觸發(fā)74HC595串行驅動(dòng)執行的敏感變量周期應該為25us,我們可以通過(guò)分頻產(chǎn)生周期為25us時(shí)鐘觸發(fā)完成以上所以操作。
時(shí)鐘分頻程序實(shí)現如下:
//計數器對系統時(shí)鐘信號進(jìn)行計數 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 //根據計數器計數的周期產(chǎn)生分頻的脈沖信號 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
系統總體實(shí)現
按鍵消抖模塊我們前面基礎數字電路實(shí)驗中詳細介紹過(guò),這里我們直接調用消抖模塊,記分器邏輯部分其實(shí)就是對按鍵按動(dòng)次數計數,輸出0~999之間的BCD碼制數據,這里也不再贅述,最后例化數碼管模塊將兩隊的比分數據顯示出來(lái)。最后顯示的數據為000~999,本實(shí)驗例程中為了顯示最小有效數據位,增加了將最高位為0的數據位不顯示的設計,例如當分數為5分時(shí),數碼管本來(lái)會(huì )顯示005,現在控制高兩位的00不顯示,只顯示最低位5。
顯示控制程序實(shí)現如下:
wire [7:0] dat_en; //控制數碼管點(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í)現如下:
//segment_scan display module Segment_scan u4 ( .clk (clk ), //系統時(shí)鐘 12MHz .rst_n (rst_n ), //系統復位 低有效 .dat_1 (0 ), //SEG1 顯示的數據輸入 .dat_2 (red_seg[11:8] ), //SEG2 顯示的數據輸入 .dat_3 (red_seg[7:4] ), //SEG3 顯示的數據輸入 .dat_4 (red_seg[3:0] ), //SEG4 顯示的數據輸入 .dat_5 (0 ), //SEG5 顯示的數據輸入 .dat_6 (blue_seg[11:8] ), //SEG6 顯示的數據輸入 .dat_7 (blue_seg[7:4] ), //SEG7 顯示的數據輸入 .dat_8 (blue_seg[3:0] ), //SEG8 顯示的數據輸入 .dat_en (dat_en ), //數碼管數據位顯示使能,[MSB~LSB]=[SEG1~SEG8] .dot_en (8'b0001_0001 ), //數碼管小數點(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í)驗步驟
實(shí)驗現象
將程序加載到FPGA開(kāi)發(fā)平臺,底板數碼管左邊4位為紅隊比分,右邊4位為藍隊比分,初始都為0分,核心板K3按鍵為紅隊加分按鍵,核心板K4按鍵為藍隊加分按鍵,按動(dòng)K3、K4按鍵,觀(guān)察紅隊和藍隊比分變化。
評論