一、設計需求
設計一個可以顯示分、秒的電子鐘模塊并在紅色颶風E45開發板的四個的數碼管進行顯示。
二、設計思路
首先,我們得了解板上四個八段數碼管的特性進行了解。圖1所示為數碼管的原理圖,從中可以知道數碼是共陰的,即當LED_AN0~LED_AN3為高電平時三極管導通,LED_S0~LED_S3為低電平,數碼管被選中,其中LED_AN0~LED_AN3、LED_A~LED_G和LED_DP是直接連接到FPGA管腳上的。
圖1 數碼管原理圖
其次,根據數碼管的原理圖給出數碼管的編碼列表,如表1所示。其中的點號DP根據需要進行亮與滅的選擇。
表1 數碼管編碼對應表
接著,我們要對設計的功能進行合理的劃分。根據需求電子鐘的功能包括計數和編碼顯示兩部分,故本設計的功能模塊組成如圖2所示。
圖2 定時器設計框架
最后,再補充一點,由于數碼管是動態掃描顯示的,利用的人眼的視覺暫留效應及發光二極管的余暉效應,只要使掃描速度足夠快(低于0.1秒),就不會看到由于數碼管切換顯示時的閃爍感。
三、設計實現
timer.v:
/**********************************************版權申明************************************************* ** **--------------------------------------------文件信息-------------------------------------------------- ** 文件名: timer.v ** 創建者: CrazyBird ** 創建日期: 2015-7-26 ** 版本號: v1.0 ** 功能描述: 電子鐘頂層模塊 ** ********************************************************************************************************/ // synopsys translate_off `timescale 1 ns / 1 ps // synopsys translate_on module timer( rst_n, clk, seg, sel ); //****************************************************************************** // 參數定義 //****************************************************************************** // 修改以下參數以滿足需求 parameter CLK_CYCLE = 20; // 時鐘周期,單位ns parameter T0 = 1000_000; // 1ms延時 // 修改以上參數以滿足需求 //****************************************************************************** // 端口定義 //****************************************************************************** input rst_n; // 全局復位,低電平有效 input clk; // 全局時鐘,50MHz output [7:0] seg; // 編碼后的數碼管輸出 output [3:0] sel; // 數碼管的位選 //****************************************************************************** // 變量定義 //****************************************************************************** wire [2:0] min_h; // 分的十位數 wire [3:0] min_l; // 分的個位數 wire [2:0] sec_h; // 秒的十位數 wire [3:0] sec_l; // 秒的個位數 wire display_flag; // 數碼管動態顯示標志位 //****************************************************************************** // 模塊例化 //****************************************************************************** // 例化time_counter模塊 time_counter #( .CLK_CYCLE ( CLK_CYCLE ), .T0 ( T0 ) ) u_time_counter ( .rst_n ( rst_n ), .clk ( clk ), .min_h ( min_h ), .min_l ( min_l ), .sec_h ( sec_h ), .sec_l ( sec_l ), .display_flag ( display_flag) ); // 例化display模塊 display u_display( .rst_n ( rst_n ), .clk ( clk ), .min_h ( min_h ), .min_l ( min_l ), .sec_h ( sec_h ), .sec_l ( sec_l ), .display_flag ( display_flag), .seg ( seg ), .sel ( sel ) ); //****************************************************************************** endmodule //*********************************************文件結束*****************************************************
time_counter.v:
/**********************************************版權申明************************************************* ** 電子技術應用網站, CrazyBird ** **--------------------------------------------文件信息-------------------------------------------------- ** 文件名: time_counter.v ** 創建者: CrazyBird ** 創建日期: 2015-7-26 ** 版本號: v1.0 ** 功能描述: 時間計數 ** ********************************************************************************************************/ // synopsys translate_off `timescale 1 ns / 1 ps // synopsys translate_on module time_counter( rst_n, clk, min_h, min_l, sec_h, sec_l, display_flag ); //****************************************************************************** // 參數定義 //****************************************************************************** // 修改以下參數以滿足需求 parameter CLK_CYCLE = 20; // 時鐘周期,單位ns parameter T0 = 1000_000; // 1ms延時 // 修改以上參數以滿足需求 // 不要修改以下參數 parameter T0_VAL = T0/CLK_CYCLE-1; // 1ms延時 // 不要修改以上參數 //****************************************************************************** // 端口定義 //****************************************************************************** input rst_n; // 全局復位,低電平有效 input clk; // 全局時鐘,50MHz output reg [2:0] min_h; // 分的十位數 output reg [3:0] min_l; // 分的個位數 output reg [2:0] sec_h; // 秒的十位數 output reg [3:0] sec_l; // 秒的個位數 output display_flag; // 數碼管動態掃描標志位 //****************************************************************************** // 1ms延時 //****************************************************************************** reg [15:0] cnt; always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) cnt <= (0); else if(cnt < T0_VAL) cnt <= cnt + 1'b1; else cnt <= (0); end assign delay_1ms = (cnt == T0_VAL); // 1ms延時完成標志位 assign display_flag = delay_1ms; // 數碼管動態掃描標志位 //****************************************************************************** // 1s延時 //****************************************************************************** reg [9:0] mse; always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) mse <= (0); else begin if(delay_1ms == 1'b1) begin if(mse < 10'd9) mse <= mse + 1'b1; else mse <= (0); end end end wire sec_l_flag = ((mse == 10'd9) && (delay_1ms == 1'b1)); // 1s延時完成標志位 //****************************************************************************** // 秒計數實現 //****************************************************************************** // 秒個位數計數 always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) sec_l <= 0; else begin if(sec_l_flag == 1'b1) begin if(sec_l < 4'd9) sec_l <= sec_l + 1'b1; else sec_l <= 0; end end end wire sec_h_flag = ((sec_l == 4'd9) && (sec_l_flag == 1'b1)); // 秒個位數進位標志位 // 秒十位數計數 always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) sec_h <= 0; else begin if(sec_h_flag == 1'b1) begin if(sec_h < 3'd5) sec_h <= sec_h + 1'b1; else sec_h <= 0; end end end wire min_l_flag = ((sec_h == 3'd5) && (sec_h_flag == 1'b1)); // 秒十位數進位標志位 //****************************************************************************** // 分計數實現 //****************************************************************************** // 分個位數計數 always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) min_l <= 0; else begin if(min_l_flag == 1'b1) begin if(min_l < 4'd9) min_l <= min_l + 1'b1; else min_l <= 0; end end end wire min_h_flag = ((min_l == 4'd9) && (min_l_flag == 1'b1)); // 分個位數進位標志位 // 分十位數計數 always@(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) min_h <= 0; else begin if(min_h_flag == 1'b1) begin if(min_h < 3'd5) min_h <= min_h + 1'b1; else min_h <= 0; end end end //****************************************************************************** endmodule //*********************************************文件結束*****************************************************
display.v:
/**********************************************版權申明************************************************* ** **--------------------------------------------文件信息-------------------------------------------------- ** 文件名: display.v ** 創建者: CrazyBird ** 創建日期: 2015-7-26 ** 版本號: v1.0 ** 功能描述: 編碼顯示 ** ********************************************************************************************************/ // synopsys translate_off `timescale 1 ns / 1 ps // synopsys translate_on module display( rst_n, clk, min_h, min_l, sec_h, sec_l, display_flag, seg, sel ); //****************************************************************************** // 端口定義 //****************************************************************************** input rst_n; // 全局復位,低電平有效 input clk; // 全局時鐘,50MHz input [2:0] min_h; // 分的十位數 input [3:0] min_l; // 分的個位數 input [2:0] sec_h; // 秒的十位數 input [3:0] sec_l; // 秒的個位數 input display_flag; // 數碼管動態顯示標志位 output reg [7:0] seg; // 編碼后的數碼管輸出 output reg [3:0] sel; // 數碼管的位選 //****************************************************************************** // 編碼函數 //****************************************************************************** function [7:0] seg_data; input [3:0] din; // 待編碼數據 input dp; // 決定數碼管點號是否點亮,1為點亮 begin case(din) 4'd0 : seg_data = {7'b1111110,dp}; 4'd1 : seg_data = {7'b0110000,dp}; 4'd2 : seg_data = {7'b1101101,dp}; 4'd3 : seg_data = {7'b1111001,dp}; 4'd4 : seg_data = {7'b0110011,dp}; 4'd5 : seg_data = {7'b1011011,dp}; 4'd6 : seg_data = {7'b1011111,dp}; 4'd7 : seg_data = {7'b1110000,dp}; 4'd8 : seg_data = {7'b1111111,dp}; 4'd9 : seg_data = {7'b1111011,dp}; endcase end endfunction //****************************************************************************** // 數碼管動態顯示的計數器 //****************************************************************************** reg [1:0] cnt; // 由于只有四個數碼管,故只需兩位 always @(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) cnt <= (0); else if(display_flag == 1'b1) cnt <= cnt + 1'b1; else cnt <= cnt; end //****************************************************************************** // 編碼輸出 //****************************************************************************** always @(posedge clk or negedge rst_n) begin if(rst_n == 1'b0) begin seg <= (0); sel <= (0); end else begin case(cnt) 2'b00 : // 顯示秒個位數 begin seg <= seg_data(sec_l,1'b0); sel <= 4'b0001; end 2'b01 : // 顯示秒十位數 begin seg <= seg_data({1'b0,sec_h},1'b0); sel <= 4'b0010; end 2'b10 : // 顯示分個位數 begin seg <= seg_data(min_l,1'b1); sel <= 4'b0100; end 2'b11 : // 顯示分十位數 begin seg <= seg_data({1'b0,min_h},1'b0); sel <= 4'b1000; end endcase end end //****************************************************************************** endmodule //*********************************************文件結束*****************************************************
電子鐘的modelsim仿真結果如圖3~圖8所示。其中,為了減少仿真時間以及可以在較短時間內驗證功能的正確性,將設計中的延時參數改小。
圖3

圖4
圖5
圖6
圖7
圖8
最后,經過綜合、實現、生成bit流文件以及下載到板子上,便可以在數碼管上看到電子鐘的效果。