字頻率計設(shè)計:從Quartus工程到板級驗證的完整實戰(zhàn))
簡介這份資源是本科EDA課程期末大作業(yè)的完整提交包圍繞基于FPGA的數(shù)字頻率計設(shè)計展開適合電子信息、集成電路及相關(guān)專業(yè)學(xué)生參考課程設(shè)計、實驗報告撰寫與Quartus II開發(fā)入門。設(shè)計采用自上而下的EDA方法用Verilog將測頻模塊拆分為多個子模塊再通過頂層元件例化完成整體連接配套完整報告、代碼、PCB與原理圖便于理解從功能劃分到綜合實現(xiàn)的完整流程。壓縮包共744個文件約28.3MB涵蓋Verilog源碼與VHD文件、Quartus工程配置qpf、qsf、sof、綜合與仿真報告rpt、summary、vho、sdo、原理圖與PCB文檔schdoc、pcbdoc以及大量中間數(shù)據(jù)庫文件完整保留了工程編譯與調(diào)試痕跡。目前已有4695人學(xué)習(xí)下載可幫助讀者對照報告復(fù)現(xiàn)測頻邏輯、梳理模塊劃分思路并借鑒工程目錄組織與排錯經(jīng)驗。1. 從一份 EDA 課設(shè)壓縮包說起FPGA 數(shù)字頻率計到底能測什么如果你正在做 EDA 課程設(shè)計或者剛接觸 FPGA 想找一個能跑通完整鏈路的練手項目這份「基于 FPGA 的數(shù)字頻率計設(shè)計」壓縮包大概率能省掉你不少翻論壇的時間。它解決的是一個很具體的問題用 FPGA 測量外部輸入信號的頻率并把結(jié)果穩(wěn)定地顯示出來。聽起來簡單但真正動手時你會發(fā)現(xiàn)從分頻、計數(shù)、鎖存到數(shù)碼管動態(tài)掃描每一步都有坑。這個項目適合兩類人一類是課程設(shè)計需要交作業(yè)、想快速理解完整工程結(jié)構(gòu)的同學(xué)另一類是想通過一個「麻雀雖小五臟俱全」的案例把 Verilog 時序邏輯、Quartus 工程管理、Testbench 仿真串起來的自學(xué)者。頻率計本身不復(fù)雜但它把 FPGA 開發(fā)中最核心的幾個概念——時鐘域、邊沿檢測、計數(shù)器位寬、顯示驅(qū)動——全揉在了一起拿來當(dāng)入門模板剛剛好。2. 項目架構(gòu)拆解測頻原理與模塊劃分2.1 數(shù)字頻率計的核心邏輯閘門時間與計數(shù)數(shù)字頻率計的本質(zhì)是在一個已知寬度的閘門時間內(nèi)統(tǒng)計被測信號的脈沖個數(shù)。假設(shè)閘門時間為 T計數(shù)值為 N則頻率 f N / T。FPGA 實現(xiàn)時通常用一個 1Hz 或 0.5Hz 的使能信號作為閘門控制一個計數(shù)器對被測信號邊沿進(jìn)行累加。這里的關(guān)鍵參數(shù)是閘門時間的精度和計數(shù)器的位寬。閘門時間由系統(tǒng)時鐘分頻得到比如 50MHz 時鐘分頻到 1Hz需要計數(shù) 25,000,000 次這要求分頻計數(shù)器至少 25 位寬。被測信號頻率越高單位閘門時間內(nèi)計數(shù)值越大計數(shù)器位寬也要相應(yīng)增加。常見做法是閘門時間取 1 秒這樣計數(shù)值直接就是頻率值省去后續(xù)除法運算對初學(xué)者最友好。2.2 模塊劃分從頂層到子模塊的職責(zé)邊界一個可綜合的頻率計工程通常包含以下幾個模塊頂層模塊例化所有子模塊連接時鐘、復(fù)位、被測信號輸入和數(shù)碼管輸出。分頻模塊將板載高頻時鐘分頻為 1Hz 閘門信號和數(shù)碼管掃描所需的低頻時鐘。測頻控制模塊產(chǎn)生閘門信號、鎖存信號和計數(shù)器清零信號協(xié)調(diào)整個測量時序。計數(shù)器模塊在閘門有效期間對被測信號邊沿計數(shù)。鎖存器模塊在閘門結(jié)束時鎖存計數(shù)值保證顯示穩(wěn)定。數(shù)碼管驅(qū)動模塊將鎖存后的二進(jìn)制頻率值轉(zhuǎn)換為段選和位選信號動態(tài)掃描顯示。這種劃分方式的好處是每個模塊職責(zé)單一方便單獨仿真和調(diào)試。我一般會先寫測頻控制模塊因為它是整個系統(tǒng)時序的「大腦」閘門、鎖存、清零三個信號的先后順序一旦搞錯顯示就會跳變或閃爍。2.3 目錄結(jié)構(gòu)壓縮包里的工程長什么樣解壓后典型的 Quartus 工程目錄大致如下freq_meter/ ├── db/ # Quartus 編譯生成的數(shù)據(jù)文件 ├── incremental_db/ # 增量編譯數(shù)據(jù)庫 ├── output_files/ # 編譯輸出含 .sof 和 .pof 下載文件 ├── freq_meter.qpf # Quartus 工程文件 ├── freq_meter.qsf # 工程設(shè)置文件引腳分配在這里 ├── top.v # 頂層模塊 ├── divider.v # 分頻模塊 ├── measure_ctrl.v # 測頻控制模塊 ├── counter.v # 計數(shù)器模塊 ├── latch.v # 鎖存器模塊 ├── seg_driver.v # 數(shù)碼管驅(qū)動模塊 └── tb_freq_meter.v # Testbench 仿真文件其中.qsf文件里的引腳分配是新手最容易翻車的地方。不同開發(fā)板的引腳定義不同直接拿別人的工程下載很可能數(shù)碼管不亮或者被測信號輸入引腳接錯。常見做法是打開 Assignment - Pin Planner對照自己板子的原理圖逐個核對時鐘、復(fù)位、輸入和數(shù)碼管引腳。3. 跑通工程從 Quartus 編譯到板級驗證3.1 環(huán)境準(zhǔn)備與工程打開這個項目基于 Quartus 開發(fā)具體版本取決于壓縮包內(nèi)的工程文件格式。如果.qpf文件能直接打開說明版本兼容如果提示版本不匹配可以用文本編輯器打開.qpf把版本號改成你本地安裝的版本再重新打開。常見做法是安裝 Quartus Prime Lite 版它免費且支持大多數(shù) Cyclone 系列 FPGA。打開工程后先別急著編譯檢查兩件事一是頂層模塊是否設(shè)置為top二是.qsf里的器件型號是否和你板子上的 FPGA 型號一致。器件選錯編譯能過但下載會報錯。3.2 引腳分配與編譯下載引腳分配是硬件驗證的第一道坎。以常見的開發(fā)板為例系統(tǒng)時鐘通常接在專用的 CLK 引腳上復(fù)位按鍵接在某個 GPIO被測信號從擴展口輸入數(shù)碼管位選和段選分別接到對應(yīng)的 IO。下面是一個引腳分配的示例片段# freq_meter.qsf 引腳分配示例 set_location_assignment PIN_23 -to clk # 50MHz 系統(tǒng)時鐘 set_location_assignment PIN_25 -to rst_n # 復(fù)位按鍵低有效 set_location_assignment PIN_28 -to sig_in # 被測信號輸入 set_location_assignment PIN_100 -to seg[0] # 數(shù)碼管段選 a set_location_assignment PIN_101 -to seg[1] # 數(shù)碼管段選 b set_location_assignment PIN_102 -to seg[2] # 數(shù)碼管段選 c set_location_assignment PIN_103 -to seg[3] # 數(shù)碼管段選 d set_location_assignment PIN_104 -to seg[4] # 數(shù)碼管段選 e set_location_assignment PIN_105 -to seg[5] # 數(shù)碼管段選 f set_location_assignment PIN_106 -to seg[6] # 數(shù)碼管段選 g set_location_assignment PIN_107 -to seg[7] # 數(shù)碼管段選 dp set_location_assignment PIN_110 -to sel[0] # 數(shù)碼管位選 1 set_location_assignment PIN_111 -to sel[1] # 數(shù)碼管位選 2 set_location_assignment PIN_112 -to sel[2] # 數(shù)碼管位選 3 set_location_assignment PIN_113 -to sel[3] # 數(shù)碼管位選 4這段分配里clk是 50MHz 有源晶振輸入rst_n是低電平復(fù)位sig_in是被測信號輸入引腳。段選和位選的具體引腳號必須對照你的板子原理圖修改不能照抄。分配完成后點擊 Processing - Start Compilation等待編譯完成。如果報錯先看 Messages 窗口的 Error 信息常見的是引腳沖突或器件不支持某條約束。編譯通過后用 USB-Blaster 下載線連接板子打開 Programmer選擇output_files目錄下的.sof文件點擊 Start。下載成功后給sig_in接入一個已知頻率的信號源比如函數(shù)發(fā)生器輸出的 1kHz 方波觀察數(shù)碼管顯示是否接近 1000。3.3 仿真驗證先跑 Testbench 再上板上板之前強烈建議先跑一遍 Testbench 仿真。壓縮包里的tb_freq_meter.v通常會產(chǎn)生一個模擬的被測信號并觀察計數(shù)器和鎖存器的輸出。下面是一個簡化的 Testbench 結(jié)構(gòu)// tb_freq_meter.v 仿真激勵示例 timescale 1ns/1ps module tb_freq_meter; reg clk 0; reg rst_n 0; reg sig_in 0; wire [31:0] freq_val; wire [7:0] seg; wire [3:0] sel; // 50MHz 時鐘周期 20ns always #10 clk ~clk; // 模擬被測信號周期 1ms即 1kHz always #500000 sig_in ~sig_in; initial begin rst_n 0; #200 rst_n 1; // 釋放復(fù)位 #2000000000 $stop; // 仿真 2ms 后停止 end // 例化頂層模塊 freq_meter_top u_top ( .clk(clk), .rst_n(rst_n), .sig_in(sig_in), .seg(seg), .sel(sel) ); endmodule這段 Testbench 里clk周期 20ns 對應(yīng) 50MHzsig_in周期 1ms 對應(yīng) 1kHz。仿真跑 2ms 后停止理論上閘門時間 1 秒還沒到所以看不到完整的鎖存值。實際仿真時為了快速驗證可以把分頻模塊的閘門時間參數(shù)改小比如改成 1ms這樣仿真幾毫秒就能看到計數(shù)結(jié)果。參數(shù)化設(shè)計的好處就在這里——改一個parameter就能加速仿真不用改邏輯。注意仿真時如果計數(shù)器一直不變化先檢查閘門信號是否正常產(chǎn)生再看被測信號是否真的在翻轉(zhuǎn)。很多時候是 Testbench 里的always塊寫錯了時間單位。4. 核心代碼逐段拆解分頻、計數(shù)與數(shù)碼管驅(qū)動4.1 分頻模塊從 50MHz 到 1Hz 閘門分頻模塊的任務(wù)是把系統(tǒng)時鐘降到閘門信號所需的頻率。下面是一個參數(shù)化的分頻器實現(xiàn)// divider.v 參數(shù)化分頻器 module divider #( parameter CNT_MAX 25_000_000 // 50MHz 分頻到 1Hz )( input wire clk, input wire rst_n, output reg clk_out ); reg [24:0] cnt; always (posedge clk or negedge rst_n) begin if (!rst_n) begin cnt 25d0; clk_out 1b0; end else if (cnt CNT_MAX - 1) begin cnt 25d0; clk_out ~clk_out; // 翻轉(zhuǎn)輸出得到 1Hz 方波 end else begin cnt cnt 1b1; end end endmoduleCNT_MAX決定了輸出頻率。50MHz 時鐘下計數(shù) 25,000,000 次后翻轉(zhuǎn)輸出周期 1 秒即 1Hz。如果板載時鐘是 100MHz這個參數(shù)要改成 50,000,000。cnt的位寬必須足夠容納CNT_MAX - 125 位可以表示到 33,554,431夠用。如果位寬不夠計數(shù)器會提前溢出輸出頻率就錯了。我一般會在仿真時把CNT_MAX改小到 100這樣幾微秒就能看到翻轉(zhuǎn)省時間。4.2 測頻控制模塊閘門、鎖存、清零的時序配合測頻控制模塊是整個設(shè)計里時序最講究的部分。它需要產(chǎn)生三個信號閘門使能gate_en、鎖存使能latch_en和計數(shù)器清零cnt_clr。三者的順序是先清零計數(shù)器再打開閘門閘門結(jié)束后鎖存數(shù)據(jù)然后再次清零循環(huán)往復(fù)。// measure_ctrl.v 測頻時序控制 module measure_ctrl ( input wire clk, // 1Hz 閘門時鐘 input wire rst_n, output reg gate_en, // 閘門使能高有效 output reg latch_en, // 鎖存使能上升沿鎖存 output reg cnt_clr // 計數(shù)器清零高有效 ); reg [1:0] state; localparam IDLE 2d0, GATE 2d1, LATCH 2d2, CLEAR 2d3; always (posedge clk or negedge rst_n) begin if (!rst_n) begin state IDLE; gate_en 1b0; latch_en 1b0; cnt_clr 1b1; end else begin case (state) IDLE: begin cnt_clr 1b1; gate_en 1b0; latch_en 1b0; state GATE; end GATE: begin cnt_clr 1b0; gate_en 1b1; latch_en 1b0; state LATCH; end LATCH: begin gate_en 1b0; latch_en 1b1; state CLEAR; end CLEAR: begin latch_en 1b0; cnt_clr 1b1; state GATE; end default: state IDLE; endcase end end endmodule這個狀態(tài)機用 1Hz 時鐘驅(qū)動每個狀態(tài)持續(xù) 1 秒。GATE狀態(tài)下gate_en拉高計數(shù)器開始對被測信號計數(shù)LATCH狀態(tài)下latch_en拉高把計數(shù)值鎖存到顯示寄存器CLEAR狀態(tài)下cnt_clr拉高計數(shù)器歸零為下一輪測量做準(zhǔn)備。這里有個細(xì)節(jié)latch_en是電平信號鎖存器模塊應(yīng)該在latch_en的上升沿或高電平期間更新輸出。如果鎖存器對latch_en的邊沿不敏感顯示值可能會在閘門期間跟著計數(shù)器變化導(dǎo)致數(shù)碼管跳數(shù)。4.3 計數(shù)器與鎖存器位寬選擇和邊沿檢測計數(shù)器模塊對被測信號的上升沿進(jìn)行計數(shù)。這里需要一個邊沿檢測邏輯把被測信號的上升沿轉(zhuǎn)換成單周期脈沖// counter.v 帶邊沿檢測的計數(shù)器 module counter ( input wire clk, // 系統(tǒng)時鐘用于同步 input wire rst_n, input wire gate_en, // 閘門使能 input wire cnt_clr, // 清零 input wire sig_in, // 被測信號 output reg [31:0] cnt // 計數(shù)值 ); reg sig_d0, sig_d1; wire sig_posedge; // 兩級寄存器同步被測信號檢測上升沿 always (posedge clk or negedge rst_n) begin if (!rst_n) begin sig_d0 1b0; sig_d1 1b0; end else begin sig_d0 sig_in; sig_d1 sig_d0; end end assign sig_posedge sig_d0 ~sig_d1; // 上升沿脈沖 always (posedge clk or negedge rst_n) begin if (!rst_n) begin cnt 32d0; end else if (cnt_clr) begin cnt 32d0; end else if (gate_en sig_posedge) begin cnt cnt 1b1; end end endmodulesig_d0和sig_d1構(gòu)成兩級同步寄存器把異步的被測信號同步到系統(tǒng)時鐘域同時sig_d0 ~sig_d1產(chǎn)生一個單周期脈沖代表上升沿。計數(shù)器位寬取 32 位足夠測量到幾十 MHz 的頻率。如果被測信號頻率接近系統(tǒng)時鐘的一半邊沿檢測可能會漏掉脈沖這是奈奎斯特采樣定理的限制不是代碼問題。鎖存器模塊在latch_en有效時把cnt的值復(fù)制到輸出寄存器// latch.v 數(shù)據(jù)鎖存器 module latch ( input wire clk, input wire rst_n, input wire latch_en, input wire [31:0] cnt_in, output reg [31:0] freq_out ); always (posedge clk or negedge rst_n) begin if (!rst_n) freq_out 32d0; else if (latch_en) freq_out cnt_in; end endmodule這里用latch_en作為條件在系統(tǒng)時鐘上升沿采樣。如果latch_en持續(xù)多個時鐘周期freq_out會一直跟隨cnt_in所以latch_en最好只保持一個時鐘周期。但在 1Hz 時鐘域下一個周期就是 1 秒所以實際工程中常用邊沿檢測來縮短鎖存窗口。4.4 數(shù)碼管動態(tài)掃描段選與位選的配合數(shù)碼管驅(qū)動模塊負(fù)責(zé)把 32 位頻率值拆成十進(jìn)制數(shù)字并動態(tài)掃描顯示。下面是一個簡化的實現(xiàn)// seg_driver.v 數(shù)碼管動態(tài)掃描 module seg_driver ( input wire clk, // 掃描時鐘約 1kHz input wire rst_n, input wire [31:0] freq_val, output reg [7:0] seg, // 段選低有效 output reg [3:0] sel // 位選低有效 ); reg [16:0] scan_cnt; reg [1:0] digit_sel; reg [3:0] bcd_digit; reg [3:0] bcd [3:0]; // 4 位十進(jìn)制數(shù)字 // 掃描計數(shù)器產(chǎn)生位選切換信號 always (posedge clk or negedge rst_n) begin if (!rst_n) scan_cnt 17d0; else scan_cnt scan_cnt 1b1; end always (posedge clk or negedge rst_n) begin if (!rst_n) digit_sel 2d0; else if (scan_cnt 17d99_999) digit_sel digit_sel 1b1; end // 將頻率值拆分為 BCD 碼簡化版僅取低四位 always (*) begin bcd[0] freq_val % 10; bcd[1] (freq_val / 10) % 10; bcd[2] (freq_val / 100) % 10; bcd[3] (freq_val / 1000) % 10; end // 位選和段選輸出 always (*) begin case (digit_sel) 2d0: begin sel 4b1110; bcd_digit bcd[0]; end 2d1: begin sel 4b1101; bcd_digit bcd[1]; end 2d2: begin sel 4b1011; bcd_digit bcd[2]; end 2d3: begin sel 4b0111; bcd_digit bcd[3]; end default: begin sel 4b1111; bcd_digit 4d0; end endcase end // 段選譯碼共陽極數(shù)碼管低有效 always (*) begin case (bcd_digit) 4d0: seg 8b1100_0000; 4d1: seg 8b1111_1001; 4d2: seg 8b1010_0100; 4d3: seg 8b1011_0000; 4d4: seg 8b1001_1001; 4d5: seg 8b1001_0010; 4d6: seg 8b1000_0010; 4d7: seg 8b1111_1000; 4d8: seg 8b1000_0000; 4d9: seg 8b1001_0000; default: seg 8b1111_1111; endcase end endmodulescan_cnt每計數(shù)到 99,999 切換一次位選在 50MHz 時鐘下大約 2ms 切換一次人眼看起來是同時亮的。bcd數(shù)組存儲各位十進(jìn)制數(shù)字digit_sel循環(huán)選擇。段選譯碼表對應(yīng)共陽極數(shù)碼管低電平點亮。如果你的板子是共陰極段選值要取反。這個模塊的坑在于除法器和取模運算會消耗較多邏輯資源如果頻率值位寬很大綜合器可能會報資源不足。常見做法是用雙 dabble 算法把二進(jìn)制轉(zhuǎn) BCD或者限制顯示位數(shù)只顯示低四位。5. 避坑與排查上板不亮、跳數(shù)、編譯報錯怎么辦5.1 數(shù)碼管不亮或只亮一位現(xiàn)象下載后數(shù)碼管全滅或者只有一位亮且不切換。原因位選信號沒有正確掃描或者段選譯碼表與數(shù)碼管類型不匹配。共陽極和共陰極的段選值正好相反接錯就全滅。解決先用萬用表測數(shù)碼管公共端是接高還是接低確認(rèn)類型再檢查scan_cnt的比較值是否太小導(dǎo)致掃描頻率過高人眼看不到。把掃描時鐘降到 1kHz 左右位選切換間隔約 1ms顯示最穩(wěn)定。5.2 頻率顯示跳數(shù)或歸零現(xiàn)象數(shù)碼管顯示的數(shù)字一直在跳或者每隔一秒歸零一次。原因鎖存器的鎖存時機不對或者計數(shù)器清零和鎖存同時發(fā)生。解決檢查測頻控制模塊的狀態(tài)機確保latch_en在cnt_clr之前有效且兩者不同時拉高。如果鎖存器對latch_en的電平敏感改成邊沿敏感只在上升沿鎖存一次。另外閘門信號和鎖存信號如果跨時鐘域要加同步器。5.3 編譯報錯引腳沖突或資源不足現(xiàn)象Quartus 編譯報 Error提示引腳重復(fù)分配或邏輯單元不夠。原因.qsf文件里同一個引腳被分配給多個信號或者除法器、乘法器消耗了太多 LE。解決打開 Pin Planner 查看沖突引腳刪除重復(fù)分配把除法運算改成移位和查表或者用專用 DSP 塊。如果資源實在不夠降低顯示位數(shù)只顯示 4 位頻率值。5.4 被測信號無輸入時顯示亂碼現(xiàn)象不接信號源時數(shù)碼管顯示隨機數(shù)字。原因輸入引腳懸空FPGA 輸入緩沖器拾取噪聲計數(shù)器亂計數(shù)。解決在sig_in引腳上使能內(nèi)部上拉或下拉電阻或者在代碼里加一個輸入濾波連續(xù)采樣多次才認(rèn)為有效邊沿。常見做法是在.qsf里加set_instance_assignment -name WEAK_PULL_UP_RESISTOR ON -to sig_in。5.5 仿真通過但上板結(jié)果不對現(xiàn)象Testbench 波形完全正確下載到板子后頻率值偏差很大。原因板載時鐘頻率和代碼里假設(shè)的不一致比如代碼按 50MHz 分頻實際板子是 100MHz閘門時間變成 0.5 秒計數(shù)值翻倍。解決確認(rèn)板子晶振頻率修改分頻模塊的CNT_MAX參數(shù)。如果不確定可以用一個已知頻率的信號源校準(zhǔn)比如輸入 1kHz看顯示是 1000 還是 2000反推實際時鐘頻率。6. 二次開發(fā)把頻率計改成周期測量與占空比顯示這個工程跑通之后最自然的改造方向是擴展測量功能。頻率計只測頻率但同樣的硬件可以測周期和占空比。周期是頻率的倒數(shù)但直接做除法會消耗資源更常見的做法是反過來用被測信號作為閘門統(tǒng)計系統(tǒng)時鐘個數(shù)。具體來說把sig_in當(dāng)作閘門信號在它的高電平期間對 50MHz 時鐘計數(shù)計數(shù)值乘以 20ns 就是周期。這樣測低頻信號時精度更高因為系統(tǒng)時鐘頻率遠(yuǎn)高于被測信號。占空比測量則需要分別統(tǒng)計高電平時間和總周期時間。可以在被測信號的上升沿清零一個高電平計數(shù)器下降沿鎖存高電平計數(shù)值同時另一個計數(shù)器統(tǒng)計完整周期。兩者相除得到占空比。除法可以用移位近似或者把結(jié)果送到數(shù)碼管顯示百分比。改造時我一般會保留原來的測頻通道增加一個模式選擇開關(guān)。模式 0 顯示頻率模式 1 顯示周期模式 2 顯示占空比。這樣一份工程能覆蓋更多課設(shè)題目要求。下面是一個模式選擇的代碼框架// mode_sel.v 測量模式選擇 module mode_sel ( input wire [1:0] mode, // 模式選擇 input wire [31:0] freq_val, // 頻率值 input wire [31:0] period_val, // 周期值 input wire [31:0] duty_val, // 占空比 output reg [31:0] display_val ); always (*) begin case (mode) 2d0: display_val freq_val; 2d1: display_val period_val; 2d2: display_val duty_val; default: display_val freq_val; endcase end endmodule這個模塊把三個測量值映射到一個顯示通道數(shù)碼管驅(qū)動模塊不用改只需要在頂層根據(jù)模式選擇輸出。驗證時先用信號發(fā)生器輸出一個已知頻率和占空比的方波比如 1kHz、50% 占空比看三個模式下的顯示是否分別為 1000、1000000單位 ns和 50。如果周期顯示不對檢查系統(tǒng)時鐘頻率是否準(zhǔn)確如果占空比偏差大檢查高電平計數(shù)器的清零和鎖存時機。從那以后我每次做 FPGA 課設(shè)都會先把仿真跑通再上板而且一定會用已知信號源校準(zhǔn)一遍。這個頻率計工程雖然簡單但它把時序控制、跨時鐘域、顯示驅(qū)動這些核心問題都串了一遍改完周期和占空比測量基本就能應(yīng)付大多數(shù) EDA 期末大作業(yè)的要求了。希望幫到你。本文還有配套的精品資源點擊獲取