一、設計需求
設計一個可以顯示分、秒的電子鐘模塊并在紅色颶風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流文件以及下載到板子上,便可以在數碼管上看到電子鐘的效果。