基於FPGA的數字時鐘

實現功能:

1.可以實現24小時的計時,初始值可以根據程序進行更改;

2.Reset值進行復位,復位後顯示00 00 00;

3.在59分50~59分54秒,LED燈會以2Hz的頻率閃爍,在59分55~59分59秒,LED燈會以5Hz的頻率閃爍;

首先先來介紹一下硬件,這裏我們選用的是正點原子的FPGA開發板。

FPGA主控芯片:Cyclone IV E - EP4CE6F17C8

時鐘:50MHz。

數碼管硬件電路如下:

LEDSEG_A,LEDSEG_B,LEDSEG_C,LEDSEG_D,LEDSEG_E,LEDSEG_F,LEDSEG_G,LEDSEG_DOT,

是數碼管的段選信號。

SEL0_T,SEL1_T,SEL2_T,SEL3_T,SEL4_T,SEL5_T是數碼管的位選信號,選擇的是哪一位。這裏爲了增加I/O口的驅動能力,我們加了一個PNP管,所以當爲低電平時,選擇的是當前位。

軟件模塊圖如下:

這裏我先將輸入的50MHz時鐘通過一個鎖相環,讓其輸出50MHz以獲得更加穩定的時鐘。

count模塊的作用是計數,其主要代碼如下:

module count(
		input clk,
		input reset,
		output [3:0] count_miao_ge,
		output [2:0] count_miao_shi,
		output [3:0] count_fen_ge,
		output [2:0] count_fen_shi,
		output [3:0] count_shi_ge,
		output [1:0] count_shi_shi
);

reg [31:0] cnt;
reg [3:0] count_miao_ge_reg;
reg [2:0] count_miao_shi_reg;
reg [3:0] count_fen_ge_reg;
reg [2:0] count_fen_shi_reg;
reg [3:0] count_shi_ge_reg;
reg [1:0] count_shi_shi_reg;

assign count_miao_ge = count_miao_ge_reg;  //秒的個位
assign count_miao_shi = count_miao_shi_reg;//秒的十位
assign count_fen_ge = count_fen_ge_reg;//分的個位
assign count_fen_shi = count_fen_shi_reg;//分的十位
assign count_shi_ge = count_shi_ge_reg;//時的個位
assign count_shi_shi = count_shi_shi_reg;//時的十位

always@(posedge clk or negedge reset)
		begin
				if(!reset)
					begin
							cnt <= 32'd0;
							count_miao_ge_reg <= 4'd0;
							count_miao_shi_reg <= 3'd4;
							count_fen_ge_reg <= 4'd9;
							count_fen_shi_reg <= 3'd5;
							count_shi_ge_reg <= 4'd0;
							count_shi_shi_reg <= 2'd0;
					end	
					//當計滿一秒並且秒的個位小於9時,秒的個位加一
					else if(cnt == 32'd5000_0000 && count_miao_ge_reg<4'd9)
						begin
							count_miao_ge_reg <= count_miao_ge_reg + 4'd1;
							cnt <= 32'd0;
						end
					//當計滿一秒並且秒的個位等於9時,秒的十位加一,秒的個位清零
					else if(cnt == 32'd5000_0000 && count_miao_ge_reg==4'd9 && count_miao_shi_reg<3'd5)
						begin
							count_miao_shi_reg <= count_miao_shi_reg + 3'd1;
							count_miao_ge_reg <= 4'd0;
							cnt <= 32'd0;
						end
					//當計滿一秒,秒到達59並且分的個位小於9時,分的個位加1	
					else if(cnt == 32'd5000_0000 && count_miao_ge_reg==4'd9 && count_miao_shi_reg==3'd5 && count_fen_ge_reg<4'd9)
						begin
							count_fen_ge_reg <= count_fen_ge_reg + 4'd1;	
							count_miao_shi_reg <= 3'd0;
							count_miao_ge_reg <= 4'd0;
							cnt <= 32'd0;
						end
					//當計滿一秒,秒到達59並且分的個位等於9時,分的十位加1	
					else if(cnt == 32'd5000_0000 && count_miao_ge_reg==4'd9 && count_miao_shi_reg==3'd5 && count_fen_ge_reg==4'd9 && count_fen_shi_reg < 3'd5)
						begin
						   count_fen_shi_reg <= count_fen_shi_reg + 3'd1;
							count_fen_ge_reg <= 4'd0;
							count_miao_shi_reg <= 3'd0;
							count_miao_ge_reg <= 4'd0;
							cnt <= 32'd0;
						end
						//當計滿一秒,秒到達59,分等於59時,時的個位小於<9,時的十位小於2時,時的個位加一	
					else if(cnt == 32'd5000_0000 && count_miao_ge_reg==4'd9 && count_miao_shi_reg==3'd5 && count_fen_ge_reg==4'd9 && count_fen_shi_reg ==3'd5 && count_shi_ge_reg < 4'd9 && count_shi_shi < 2'd2)
						begin
							count_shi_ge_reg <= count_shi_ge_reg + 4'd1;
							count_fen_shi_reg <= 3'd0;
							count_fen_ge_reg <= 4'd0;
							count_miao_shi_reg <= 3'd0;
							count_miao_ge_reg <= 4'd0;
							cnt <= 32'd0;
						end
						//當計滿一秒,秒到達59,分等於59時,時的個位等於9時,時的十位<2,時的十位加一	
					else if(cnt == 32'd5000_0000 && count_miao_ge_reg==4'd9 && count_miao_shi_reg==3'd5 && count_fen_ge_reg==4'd9 && count_fen_shi_reg ==3'd5 && count_shi_ge_reg == 4'd9 && count_shi_shi < 2'd2)
						begin
							count_shi_shi_reg <= count_shi_shi + 2'd1;
						   count_shi_ge_reg <= 4'd0;
							count_fen_shi_reg <= 3'd0;
							count_fen_ge_reg <= 4'd0;
							count_miao_shi_reg <= 3'd0;
							count_miao_ge_reg <= 4'd0;
							cnt <= 32'd0;
						end
						//當計滿一秒,秒到達59,分等於59時,時的個位小於3時,時的十位等於2,時的個位加一
					else if(cnt == 32'd5000_0000 && count_miao_ge_reg==4'd9 && count_miao_shi_reg==3'd5 && count_fen_ge_reg==4'd9 && count_fen_shi_reg ==3'd5 && count_shi_ge_reg < 4'd3 && count_shi_shi == 2'd2)
						begin
							count_shi_ge_reg <= count_shi_ge_reg + 4'd1;
							count_fen_shi_reg <= 3'd0;
							count_fen_ge_reg <= 4'd0;
							count_miao_shi_reg <= 3'd0;
							count_miao_ge_reg <= 4'd0;
							cnt <= 32'd0;
						end
					//當計滿一秒,秒到達59,分等於59時,時等於23,所有清零
					else if(cnt == 32'd5000_0000 && count_miao_ge_reg==4'd9 && count_miao_shi_reg==3'd5 && count_fen_ge_reg==4'd9 && count_fen_shi_reg ==3'd5 && count_shi_ge_reg == 4'd3 && count_shi_shi == 2'd2)
						begin
							count_shi_shi_reg <= 2'd0;
							count_shi_ge_reg <= 4'd0;
							count_fen_shi_reg <= 3'd0;
							count_fen_ge_reg <= 4'd0;
							count_miao_shi_reg <= 3'd0;
							count_miao_ge_reg <= 4'd0;
							cnt <= 32'd0;
						end
					else
						begin
							cnt <= cnt + 32'd1;
						end
		end
endmodule

decoding模塊是譯碼模塊,就是將每個位轉換成相應的數碼管的段選。

這裏我們是共陽級連接,所以當我們的I/O口輸出低電平時有效。

這裏0~9對應的碼分別是

0:0100_0000

1:0111_1001

2:0010_0100

3:0011_0000

4:0001_1001

5:0001_0010

6:0000_0010

7:0111_1000

8:0000_0000

9:0001_0000

其代碼如下:

module decoding(
		input clk,
		input reset,
		input [3:0] count_miao_ge,
		input [2:0] count_miao_shi,
		input [3:0] count_fen_ge,
		input [2:0] count_fen_shi,
		input [3:0] count_shi_ge,
		input [1:0] count_shi_shi,
		output reg [7:0] code_miao_ge,
		output reg [7:0] code_miao_shi,
		output reg [7:0] code_fen_ge,
		output reg [7:0] code_fen_shi,
		output reg [7:0] code_shi_ge,
		output reg [7:0] code_shi_shi
);

always@(posedge clk or negedge reset)
		begin
			if(!reset)
				code_miao_ge <= 8'b1111_1111;
			else
					begin
						case(count_miao_ge)
							4'd0:code_miao_ge <= 8'b1100_0000;
							4'd1:code_miao_ge <= 8'b1111_1001;
							4'd2:code_miao_ge <= 8'b1010_0100;
							4'd3:code_miao_ge <= 8'b1011_0000;
							4'd4:code_miao_ge <= 8'b1001_1001;
							4'd5:code_miao_ge <= 8'b1001_0010;
							4'd6:code_miao_ge <= 8'b1000_0010;
							4'd7:code_miao_ge <= 8'b1111_1000;
							4'd8:code_miao_ge <= 8'b1000_0000;
							4'd9:code_miao_ge <= 8'b1001_0000;
							default:code_miao_ge <= 8'b1011_1111;
						endcase
					end
				
		end

always@(posedge clk or negedge reset)
		begin
			if(!reset)
				code_miao_shi <= 8'b1111_1111;
			else
					begin
						case(count_miao_shi)
							3'd0:code_miao_shi <= 8'b1100_0000;
							3'd1:code_miao_shi <= 8'b1111_1001;
							3'd2:code_miao_shi <= 8'b1010_0100;
							3'd3:code_miao_shi <= 8'b1011_0000;
							3'd4:code_miao_shi <= 8'b1001_1001;
							3'd5:code_miao_shi <= 8'b1001_0010;
							default:code_miao_shi <= 8'b1011_1111;
						endcase
					end
				
		end
		
always@(posedge clk or negedge reset)
		begin
			if(!reset)
				code_fen_ge <= 8'b1111_1111;
			else
					begin
						case(count_fen_ge)
							4'd0:code_fen_ge <= 8'b0100_0000;
							4'd1:code_fen_ge <= 8'b0111_1001;
							4'd2:code_fen_ge <= 8'b0010_0100;
							4'd3:code_fen_ge <= 8'b0011_0000;
							4'd4:code_fen_ge <= 8'b0001_1001;
							4'd5:code_fen_ge <= 8'b0001_0010;
							4'd6:code_fen_ge <= 8'b0000_0010;
							4'd7:code_fen_ge <= 8'b0111_1000;
							4'd8:code_fen_ge <= 8'b0000_0000;
							4'd9:code_fen_ge <= 8'b0001_0000;
							default:code_fen_ge <= 8'b1011_1111;
						endcase
					end
				
		end

always@(posedge clk or negedge reset)
		begin
			if(!reset)
				code_fen_shi <= 8'b1111_1111;
		   else
					begin
						case(count_fen_shi)
							3'd0:code_fen_shi <= 8'b1100_0000;
							3'd1:code_fen_shi <= 8'b1111_1001;
							3'd2:code_fen_shi <= 8'b1010_0100;
							3'd3:code_fen_shi <= 8'b1011_0000;
							3'd4:code_fen_shi <= 8'b1001_1001;
							3'd5:code_fen_shi <= 8'b1001_0010;
							default:code_fen_shi <= 8'b1011_1111;
						endcase
				end
		
	  end

	  
always@(posedge clk or negedge reset)
		begin
			if(!reset)
				code_shi_ge <= 8'b1111_1111;
			else
					begin
						case(count_shi_ge)
							4'd0:code_shi_ge <= 8'b0100_0000;
							4'd1:code_shi_ge <= 8'b0111_1001;
							4'd2:code_shi_ge <= 8'b0010_0100;
							4'd3:code_shi_ge <= 8'b0011_0000;
							4'd4:code_shi_ge <= 8'b0001_1001;
							4'd5:code_shi_ge <= 8'b0001_0010;
							4'd6:code_shi_ge <= 8'b0000_0010;
							4'd7:code_shi_ge <= 8'b0111_1000;
							4'd8:code_shi_ge <= 8'b0000_0000;
							4'd9:code_shi_ge <= 8'b0001_0000;
							default:code_shi_ge <= 8'b1011_1111;
						endcase
					end
				
		end
		
always@(posedge clk or negedge reset)
		begin
			if(!reset)
				code_shi_shi <= 8'b1111_1111;
		   else
					begin
						case(count_shi_shi)
							2'd0:code_shi_shi <= 8'b1100_0000;
							2'd1:code_shi_shi <= 8'b1111_1001;
							2'd2:code_shi_shi <= 8'b1010_0100;
							default:code_shi_shi <= 8'b1011_1111;
						endcase
				end
		
	  end
endmodule

sel_show模塊是位選模塊,這裏我們選用1000Hz的時鐘並且用簡單的狀態機來進行位選。

其代碼如下:

module sel_show(
		input clk,
		input reset,
		input [7:0] code_miao_ge,
		input [7:0] code_miao_shi,
		input [7:0] code_fen_ge,
		input [7:0] code_fen_shi,
		input [7:0] code_shi_ge,
		input [7:0] code_shi_shi,
		output reg [7:0] led,
		output reg [5:0] sel
);

reg [15:0] cnt;
reg flag;
//產生1000Hz的時鐘
always@(posedge clk or negedge reset)
		begin
			if(!reset)
					begin
						cnt <=16'd0;
						flag <= 1'b0;
					end
				
			else if(cnt == 16'd500_00)
					begin
					   cnt <= 16'd0;
						flag <= 1'b1;
					end
			else
					begin
						cnt <= cnt + 16'd1;
						flag <= 1'b0;
					end
		end

reg [2:0] state;
parameter start = 3'd0; //開始狀態
parameter sel_1 = 3'd1; //位選1
parameter sel_2 = 3'd2; //位選2
parameter sel_3 = 3'd3; //位選3
parameter sel_4 = 3'd4; //位選4
parameter sel_5 = 3'd5; //位選5
parameter sel_6 = 3'd6; //位選6
parameter ending = 3'd7;//結束
always@(posedge clk or negedge reset)
		begin
			if(!reset)
				begin
					state <= 3'd0;
					sel <= 6'b000_000;
				end
			else
			case(state)
				start:
					begin
						if(flag == 1'b1)
							state <= sel_1;
						else
							state <= state;
					end
				sel_1:
					begin
						if(flag == 1'b1)
							begin
								state <= sel_2;
								sel <= 6'b111_110;
								led <= code_miao_ge;
							end
						else
							state <= state;
					end
				sel_2:
					begin
						if(flag == 1'b1)
							begin
								state <= sel_3;
								sel <= 6'b111_101;
								led <= code_miao_shi;
							end
						else
							state <= state;
					end
				sel_3:
					begin
						if(flag == 1'b1)
							begin
								state <= sel_4;
								sel <= 6'b111_011;
								led <= code_fen_ge;
							end
						else
							state <= state;
					end
				sel_4:
					begin
						if(flag == 1'b1)
							begin
								state <= sel_5;
								sel <= 6'b110_111;
								led <= code_fen_shi;
							end
						else
							state <= state;
					end
				sel_5:
					begin
						if(flag == 1'b1)
							begin
								state <= sel_6;
								sel <= 6'b101_111;
								led <= code_shi_ge;
							end
						else
							state <= state;
					end
				sel_6:
					begin
						if(flag == 1'b1)
							begin
								state <= ending;
								sel <= 6'b011_111;
								led <= code_shi_shi;
							end
						else
							state <= state;
					end
				ending:
					state <= start;
				default:
					state <= start;
			endcase
		end
endmodule
		

led_flash模塊就是控制led燈閃爍的,這裏我們選擇用計數的方式產生2Hz和5Hz的時鐘。

其代碼如下:

module led_flash(
		input clk,
		input reset,
	        input [3:0] count_miao_ge,
		input [2:0] count_miao_shi,
		input [3:0] count_fen_ge,
		input [2:0] count_fen_shi,
		output reg Led
);
reg [31:0] cnt_2Hz;
reg [31:0] cnt_5Hz;
reg clk_2Hz;
reg clk_5Hz;
parameter flash_2Hz = 32'd1250_0000;
parameter flash_5Hz = 32'd5000_000;
//產生2Hz的時鐘5000_0000/2/2 = 1250_0000
always@(posedge clk or negedge reset)
		begin
				if(!reset)
					begin
						cnt_2Hz <= 32'd0;
					end
				else if(cnt_2Hz == flash_2Hz)
					begin
						clk_2Hz <= ~clk_2Hz;
						cnt_2Hz <= 32'd0;
					end
				else
					cnt_2Hz <= cnt_2Hz + 32'd1;
		end

//產生5Hz的時鐘5000_0000/5/2 = 500_0000
always@(posedge clk or negedge reset)
		begin
				if(!reset)
					begin
						cnt_5Hz <= 32'd0;
					end
				else if(cnt_5Hz == flash_5Hz)
					begin
						clk_5Hz <= ~clk_5Hz;
						cnt_5Hz <= 32'd0;
					end
				else
					cnt_5Hz <= cnt_5Hz + 32'd1;
		end		
	
//判斷相應的範圍並且選取相應的時鐘。
always@(posedge clk or negedge reset)
		begin
			if(!reset)
				begin
					Led <= 1'b0;
				end
			else if(count_fen_shi == 3'd5 && count_fen_ge == 4'd9 && count_miao_shi == 3'd5 && count_miao_ge >= 4'd0 && count_miao_ge < 4'd5)
					Led <= clk_2Hz;
			else if(count_fen_shi == 3'd5 && count_fen_ge == 4'd9 && count_miao_shi == 3'd5 && count_miao_ge >= 4'd5 && count_miao_ge <= 4'd9)
					Led <= clk_5Hz;
			else
					Led <= 1'b0;
		end

endmodule

接下來我們用SignalTap進行簡單的仿真:

可以看出進行正常的計數。

最後的效果圖如下:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章