
簡介一份圍繞電子設計自動化EDA課程設計的籃球比賽計分器完整項目包適合高校電子類學生、FPGA/CPLD初學者作為期末課題或綜合實訓參考。項目覆蓋HDL編程、數字邏輯電路、仿真驗證、FPGA實現與實物調試等核心環節能夠幫助讀者系統掌握用Verilog/VHDL編寫計分邏輯、驅動七段數碼管顯示以及處理回合計時等設計方法。壓縮包共41個文件以png圖片、xml配置、emf矢量圖為主要類型png可查看電路截圖與界面效果emf保留矢量圖形便于插入報告xml和rels則對應Word文檔的結構關系與自定義屬性整套資料約1.96MB輕量易用。已有317人學習使用。內含課程設計文檔、相關圖片素材和工程配置文件可作為計分器設計思路、文檔排版和硬件實現的直接參考適合在備賽或課程答辯前對照梳理。1. 為什么“EDA籃球計分器”每年都是課程設計的高頻選題單片機課設里寫個籃球計分器通常就是按鍵掃描、數碼管查表十幾行 C 代碼搞定。換到 EDA 課程設計同樣是計分功能卻要在 Verilog/VHDL 里自己搭時鐘分頻、狀態機、按鍵消抖、七段碼譯碼和動態掃描任何一個環節的時序不對仿真過了上板也會花屏。網上流傳的“EDA籃球比賽計分器課程設計”資源里面大多是 word 報告加 ModelSim 波形截圖很多人下載后只抄代碼卻沒意識到真正值錢的是把比賽規則映射成有限狀態機的過程。這個項目適合電子信息、自動化和計算機方向的學生也適合已經會用 Quartus II/ModelSim但沒完整走過“仿真到上板”這個閉環的初學工程師。2. 籃球計分器需求建模從比賽規則到狀態機與模塊劃分2.1 計分規則的數字化兩分、三分、罰球與清零的沖突籃球一次進攻最多拿 3 分罰球 1 分死球后可能需清零。按課程設計要求計分器至少要有四個輸入加 1、加 2、加 3、清零。如果只用四個按鍵直接驅動加法器最直接的沖突是“同時按下時誰生效”。真實比賽場景里按鍵確實可能被同時觸發比如裁判剛按完 2 分又立刻補按 1 分罰球。硬件上若兩個 key 信號同時為 1加法器會在同一拍收到多個增量行為不確定。常見做法是給按鍵事件定優先級或者用一個按鍵編碼狀態機把多路輸入仲裁成單路增量脈沖。2.1.1 事件優先級表按鍵含義分數變化優先級key_rst清空當前比分0最高key_3三分命中3次高key_2兩分命中2次高key_1罰球命中1最低清零按鍵的優先級必須單獨討論。有些課程設計把清零做成異步復位直接清 score 寄存器但如果加分鍵與清零鍵同時按下異步復位會讓加法使能落到亞穩態所以更穩妥的做法是把清零也納入狀態機同一時刻只接受一個事件。這個設計決策要寫進報告答辯經常被問。2.1.2 按鍵編碼狀態機localparam S_IDLE 2d0; localparam S_DETECT 2d1; localparam S_UPDATE 2d2; localparam S_WAIT_REL 2d3;狀態含義S_IDLE 等待任意按鍵按下S_DETECT 判斷按鍵并鎖存增量值S_UPDATE 把增量寫入計分寄存器S_WAIT_REL 等待所有按鍵釋放。S_WAIT_REL 不是多余的它能保證一次按下只加一次分避免按住按鍵時計數器持續累加。如果后續要擴展 24 秒進攻倒計時這個狀態機里的等待釋放邏輯會打斷計時所以正規做法是按下時記錄事件釋放只影響下一次觸發不阻塞主時鐘。2.2 頂層模塊劃分與信號約定模塊劃分直接決定仿真和上板排錯的難度。常見拆法如下表頂層用 wire 把各模塊串起來。模塊功能關鍵端口clk_div50MHz 到 1kHz 掃描時鐘clk_50m, rst_n, clk_1kkey_debounce按鍵同步與消抖key_in, key_pulsescore_ctrl計分狀態機與寄存器key_event, add_val, score[7:0]seg_display動態掃描與七段譯碼score[7:0], seg[6:0], sel[2:0]這樣劃分的好處是可以單獨仿真 display 模塊而不被按鍵邏輯干擾。寫報告時框圖直接按這個模塊列表畫評審老師一眼看到邊界。2.3 時鐘分頻從 50MHz 到 1kHz 的掃描時鐘為什么需要分頻數碼管動態掃描靠“每位輪流點亮”如果直接用 50MHz 時鐘切位選每位點亮時間只有 20ns遠小于人眼視覺暫留和數碼管最小響應亮度會非常低。常見做法是分頻到 500Hz~1kHz掃描周期 1~2ms每位點亮約 0.33~0.66ms亮度均勻且無閃爍。module clk_div #(parameter DIV 25_000) ( input wire clk, input wire rst_n, output reg clk_out ); reg [14:0] cnt; always (posedge clk or negedge rst_n) begin if (!rst_n) begin cnt 15d0; clk_out 1b0; end else if (cnt DIV - 1) begin cnt 15d0; clk_out ~clk_out; end else begin cnt cnt 1b1; end end endmodule參數 DIV25_000 時50MHz 時鐘每 25_000 拍翻轉一次輸出為 1kHz 方波周期 1ms。計數位寬必須能容納 24_999二進制需要 15 位寫成 8 位會在仿真里回繞。如果板載晶振是 40MHzDIV 改成 20_000如果是 27MHz改成 13_500。不要只改一處位寬也要同步檢查。仿真時建議把 DIV 參數下調到 2 或 4否則 20ms 消抖要在 testbench 里跑上百萬拍ModelSim 會卡到懷疑人生。3. Verilog HDL 實現計分核心與七段顯示驅動3.1 計分核心可加可清的寄存器堆score_ctrl對應狀態機的 S_UPDATE 動作en_score 是該狀態的有效信號add_val 是 S_DETECT 階段鎖存的增量。計分寄存器這里采用 BCD 碼顯示模塊直接查表省掉二進制轉十進制的除法器也更接近計分器的真實邏輯。module score_ctrl ( input wire clk, input wire rst_n, input wire en_score, input wire [1:0] add_val, input wire clr, output reg [7:0] score ); wire [3:0] score_l score[3:0]; wire [3:0] score_h score[7:4]; always (posedge clk or negedge rst_n) begin if (!rst_n) begin score 8d0; end else if (clr) begin score 8d0; end else if (en_score) begin if (score_l add_val 4d9) begin score[3:0] score_l add_val - 4d10; score[7:4] score_h 1b1; end else begin score[3:0] score_l add_val; end end end endmoduleen_score 來自按鍵狀態機單周期高有效add_val 用 2 位二進制編碼1/2/3 對應三檔加分。分數上限這里沒有處理實際比賽比分可能超過 99如果只用兩位數碼管score_h 會從 9 翻到 10顯示變成 A。要在報告里寫明這個邊界條件或者把 score 擴展成 12 位做三位顯示。加法后 score_l 大于 9 的判斷是最容易寫錯的地方加 3 時9 加 3 等于 12必須減 10而不是減 9進位單獨寫不能和個位更新擠在一個 if 語句里否則綜合工具會報鎖存器。3.2 七段數碼管動態掃描位選與段選時序顯示部分的核心是掃描計數器加查表。先產生一個 1kHz 左右的掃描使能counter 每來一個使能脈沖切換一次位選段選根據當前位對應的 BCD 值輸出七段編碼。scan_cnt位選 sel點亮內容2d03b110個位2d13b101十位2d23b011預留/最高位reg [1:0] scan_cnt; reg [2:0] sel; reg [6:0] seg; always (posedge clk_1k or negedge rst_n) begin if (!rst_n) scan_cnt 2d0; else scan_cnt scan_cnt 1b1; end always (*) begin case (scan_cnt) 2d0: sel 3b110; 2d1: sel 3b101; 2d2: sel 3b011; default: sel 3b111; endcase end function [6:0] seg_encode; input [3:0] bcd; begin case (bcd) 4d0: seg_encode 7b1111110; 4d1: seg_encode 7b0110000; 4d2: seg_encode 7b1101101; 4d3: seg_encode 7b1111001; 4d4: seg_encode 7b0110011; 4d5: seg_encode 7b1011011; 4d6: seg_encode 7b1011111; 4d7: seg_encode 7b1110000; 4d8: seg_encode 7b1111111; 4d9: seg_encode 7b1111011; default: seg_encode 7b0000000; endcase end endfunction這是共陰極段碼若板子是共陽極直接按位取反即可。位選編碼必須和硬件原理圖方向一致我這里假設的是高電平有效位選實際板子可能是低有效意味著代碼里所有 0/1 全部取反。function 只能做組合邏輯所以段選輸出建議在同一個 always 塊里先寄存再輸出否則能看到毛刺導致數字邊緣發虛。3.3 跨模塊信號同步異步復位與按鍵跨時鐘域按鍵從外部引腳進來異步于 clk直接拿 key 信號做條件判斷綜合后電路會出現亞穩態。常見做法是兩級同步器加邊沿檢測reg key_meta, key_sync; reg key_press_d1; always (posedge clk or negedge rst_n) begin if (!rst_n) begin key_meta 1b1; key_sync 1b1; key_press_d1 1b0; end else begin key_meta key_in; key_sync key_meta; key_press_d1 key_press; end end wire key_press ~key_sync; wire key_pulse key_press !key_press_d1;key_pulse 是單周期脈沖表示某一次按下。上一級狀態機拿這個脈沖做事件觸發就不會出現按鍵電平持續拉高導致重復計數。兩級同步只是降低亞穩態進入下一級的概率并不能完全消除但課程設計里這個程度已經夠用。要注意key_meta 和 key_sync 必須用同一個時鐘域不能把掃描時鐘和主時鐘混著用。4. Quartus II ModelSim 仿真把 testbench 寫到能復制4.1 Quartus II 工程創建與引腳約束如果手頭是 Altera 板子課程設計一般用 Quartus II。新建工程時器件型號按實際板卡選比如 Cyclone IV 的 EP4CE6E22C8如果只做仿真不準備上板可以隨便選一個型號但報告里最好保持一致。添加完 clk_div、key_debounce、score_ctrl、seg_display 這些源文件后先做一次 Analysis Elaboration檢查模塊例化是否匹配。上板前必須做引腳約束。QSF 文件里寫法如下set_location_assignment PIN_E1 -to clk set_location_assignment PIN_E2 -to rst_n set_location_assignment PIN_A1 -to key_1 set_location_assignment PIN_A2 -to key_2 set_location_assignment PIN_B1 -to key_3 set_location_assignment PIN_B2 -to key_rst set_location_assignment PIN_C1 -to seg[0] set_location_assignment PIN_C2 -to seg[1]引腳編號每個板子都不一樣必須看原理圖不能照抄。常犯的錯誤是把網上別人工程的 PIN 直接粘進來編譯能過下載后完全沒有反應就是因為引腳根本沒連到對應按鍵或數碼管段。約束后再編譯一次看 Fitter Report 里的 Pin Usage 是否和預期一致。如果后續要把這個計分器做成獨立 PCB畫板階段可以切到嘉立創EDA這類國產工具但前提是先把 FPGA 原理圖封裝和 QSF 引腳一一對應封裝名寫錯會在布板時發現位選全亂。4.2 ModelSim 仿真testbench 怎么寫才不“仿真一時爽上板全完蛋”這里給一個針對score_ctrl的單模塊 testbench驗證復位、加分和清零三個關鍵動作timescale 1ns/1ps module tb_score; reg clk; reg rst_n; reg en_score; reg [1:0] add_val; reg clr; wire [7:0] score; score_ctrl u_score ( .clk(clk), .rst_n(rst_n), .en_score(en_score), .add_val(add_val), .clr(clr), .score(score) ); initial clk 0; always #10 clk ~clk; initial begin rst_n 0; en_score 0; add_val 2d0; clr 0; #100 rst_n 1; // 第一次加分兩分 (posedge clk); en_score 1; add_val 2d2; (posedge clk); en_score 0; // 空跑兩拍確認分數保持 (posedge clk); (posedge clk); // 清零 (posedge clk); clr 1; (posedge clk); clr 0; #200; $stop; end endmodule#10 clk ~clk表示時鐘周期 20ns對應 50MHz。加法通過(posedge clk)后給 en_score 置 1下一個上升沿 score 更新這是比直接在時鐘邊沿寫阻塞賦值更穩的寫法。testbench 里沒有模擬抖動因為這里只驗證 count 模塊如果要從頂層驗證完整按鍵路徑必須把 key_debounce 也例化進來并把分頻器改成小參數。下面的激勵表可以在報告里直接用作仿真用例描述階段激勵預期 score復位rst_n0 保持 100ns0兩分en_score1 且 add_val2 一個周期2保持en_score0 兩個周期仍為 2清零clr1 一個周期04.3 仿真中容易忽略的初始值和復位問題很多同學在 ModelSim 里看到的波形是 X 態然后懷疑代碼有問題第一步先查復位。rst_n 低電平保持至少 100ns且必須在 initial 塊里顯式拉低不能依賴 FPGA 上電默認值。另外聲明的 wire 如 add_val、en_score 在仿真開始為高阻 Z如果 testbench 里沒給初值狀態機可能一直停在 S_DETECT。建議在 initial 塊開頭給所有輸入變量賦一個確定值。還有一個細節如果只從頂層仿真分頻參數是 25_000run 1ms 實際要跑 50 萬個 50MHz 時鐘波形文件會很大。所以單獨仿真核心模塊比跑完整頂層更高這也是把模塊邊界切清楚的價值所在。5. 按鍵消抖與硬件在線調試從仿真正確到上板穩定5.1 機械按鍵抖動與計數消抖的窗口參數機械按鍵的抖動一般在 5~10ms 之間有些劣質按鍵能達到 15ms。如果直接把按鍵電平送給狀態機一次按下可能在幾毫秒內反復跳變導致狀態機連續進入 S_UPDATE 多次一次按壓被記錄成兩三分。最穩妥的消抖方案是用掃描時鐘計數檢測到按鍵電平發生變化后連續采樣 N 個周期若電平始終保持一致才認為按鍵到達穩定狀態。module key_debounce #(parameter STABLE_CNT 20) ( input wire clk_1k, input wire rst_n, input wire key_in, output reg key_stable ); reg [4:0] cnt; reg key_cur; always (posedge clk_1k or negedge rst_n) begin if (!rst_n) begin cnt 5d0; key_cur 1b1; key_stable 1b1; end else begin if (key_in ! key_cur) begin cnt 5d0; key_cur key_in; end else if (cnt STABLE_CNT) begin cnt cnt 1b1; end else begin key_stable key_cur; end end end endmodule參數 STABLE_CNT20 表示在 1kHz 時鐘下持續 20ms。這個值不能拍腦袋用示波器測一下你的按鍵抖動波形再定抖動小于 10ms 就取 20ms大于 10ms 就取 30ms。消抖窗口太短會漏過抖動太長會讓人感覺按鍵響應遲鈍。key_in 進消抖模塊之前最好再加兩級同步器否則消抖模塊本身也可能采到亞穩態。5.2 SignalTap II 在線調試與 LED 點燈定位法仿真只能驗證邏輯不能驗證引腳約束和電氣連接。上板后如果沒反應第一步是用 SignalTap II 看內部信號。Quartus 菜單里打開 Tools - SignalTap II Logic Analyzer添加需要觀測的信號key_sync、key_pulse、scan_cnt、score[7:0]。設置觸發條件為 key_pulse 是 1采樣深度 256編譯下載后運行然后按一次加分鍵就能在波形窗口看到按鍵脈沖是否到達計分模塊。set_location_assignment PIN_L1 -to debug_led如果板上找不到 JTAG 或者 SignalTap 版本不匹配最土但最有效的定位法是 LED 點燈。把 score 寄存器的低四位接四個 LED每加 1 分亮一盞燈或者臨時把 scan_cnt 接 LED看掃描頻率是否正常。這種方法能快速區分“按鍵沒進來”和“顯示模塊壞了”兩大類問題。我一般會保留一個 debug_led 端口排錯結束再刪除比翻來覆去改引腳約束快很多。5.3 硬件實測故障排查順序現象先看哪里常見根因數碼管全滅萬用表量位選三極管掃描時鐘沒到、位選方向反按加分沒反應SignalTap 看 key_pulse消抖窗口太長、按鍵沒接對引腳一次按變多次示波器量按鍵處波形沒有消抖或 STABLE_CNT 太小數字閃爍看掃描頻率分頻 DIV 太大低于 200Hz某段 LED 總不亮量對應段碼引腳共陰/共陽反、該段電阻虛焊排查順序永遠是電源、時鐘、復位、按鍵輸入、內部寄存器、顯示驅動。不要一上來就懷疑 HDL 代碼先把按鍵一端對地短接觀察數碼管是否變化。如果短接都沒反應問題大概率在引腳約束或硬件連接而不是邏輯。6. 課程設計報告與答辯把工程能力量化成文檔6.1 報告結構從需求到實測的完整鏈路評審老師看報告希望看到的是“為什么這樣做”不是代碼堆砌。建議按需求分析、模塊設計、仿真、上板實測四段組織。需求分析部分把狀態轉移表放上去并在旁邊標注“清零優先”的決策理由模塊設計部分畫一張頂層結構圖每個模塊標出端口名仿真部分不要貼整段 testbench截取 ModelSim 波形用箭頭圈出按鍵按下、score 變化、位選切換三個關鍵點上板部分放實物照片并寫清楚遇到的一個 Bug 和解決過程。最終 PDF 里波形截圖注釋要跟代碼模塊對應一句“波形中 score 從 19 變到 22對應一次二分命中”比泛泛而談“功能正確”有說服力。下表是報告各部分與打分點的對應關系寫的時候逐項核對。報告章節評審關注點本設計素材來源需求分析規則是否完整事件優先級表、狀態轉移表模塊設計模塊邊界是否清晰頂層框圖、端口列表仿真驗證是否有邊界場景ModelSim 波形、testbench 說明上板實測是否真實可運行實物照片、故障排查記錄6.2 答辯時的三個關鍵取舍答辯被問到最多的問題集中在三個點上狀態機里為什么要有 S_WAIT_REL、計分用 BCD 還是二進制、消抖時間為什么是 20ms 而不是 1ms。每個問題都要給出“如果換一種做法會怎樣”的對比沒有 S_WAIT_REL按住按鍵會重復加分用二進制顯示前必須寫 bin2bcd 轉換模塊面積增加但加減法更簡單消抖時間小于 10ms部分抖動劇烈的按鍵會誤觸發。講清楚這三點基本能覆蓋大多數提問。如果現場有條件把消抖參數從 20ms 改成 5ms 編譯下載然后按鍵用示波器給老師看抖動毛刺這一手比任何背稿都直觀。答辯前建議把 SignalTap 導出的波形和 ModelSim 仿真波形放在同一頁做對比說明仿真與硬件行為的一致性。本文還有配套的精品資源點擊獲取