实现功能:
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进行简单的仿真:
可以看出进行正常的计数。
最后的效果图如下: