FPGA可以实现多种多样的时序电路,用Verilog语言可以描述任意进制的计数器。本文描述了一个一百进制的计数器,由两个十进制计数器级联而成。每个十进制计数器可以送显至一位数码管。
资源链接:
https://download.csdn.net/download/benchuspx/11222728
先例化一个十进制计数器:
module counter10(rst_n,clkin,t,up_down,D,c);
input rst_n,clkin,t,up_down;
output [3:0]D;//输出的bcd码的计数,只有个位
output c; //进位或借位
reg [3:0]D; //D为四位二进制输出
reg c;
always@(posedge clkin or negedge rst_n)
begin
if(~rst_n) begin D<=0;end
else if(t==1)
begin
if(up_down==1)
begin
if(D==4'd8) begin D<=4'd9;c<=1; end
else if(D>=4'd9) begin D<=4'd0;c<=0; end
else begin D<=D+1;c<=0; end
end
if(up_down==0)
begin
if(D==4'd1) begin D<=4'd0;c<=1; end
else if(D==4'd0) begin D<=4'd9;c<=0; end
else begin D<=D-1;c<=0; end
end
end
end
endmodule
然后设计一个按键防抖模块:
因为FPGA板上自带晶振clk是50MHz很大,我们只需要每隔1ms读取一下按键的值clkin输出到clkin_ok里就能有效防抖:
module anti_shake(clk,clkin,clkin_ok);
input clk,clkin;
output clkin_ok;
reg clkin_ok;
reg [19:0]count;
always@(posedge clk) //count 降频计时,约20ms计满一次
begin
count<=count+1;
end
always@(posedge clk)
begin
clkin_ok <= (count==20'h0)? clkin : clkin_ok; //count 每过一个周期检测一次clkin
end
endmodule
主程序:两个十进制级联并送显数码管:
module counter100(rst_n,clk,clkin,t,up_down,seg,sel,c);
input rst_n,clk,clkin,t,up_down;
output [7:0]seg; //段选的送显数值的显示码(带小数点8位)低有效
output [5:0]sel; //位选信号(6个数码管)低有效
output c;
reg [7:0]seg;
reg [5:0]sel;
wire c; //计满输出或者回零输出
wire [3:0]D0; //D0为四位二进制bcd码输出,个位 //用函数的时候函数的输出变量必须是wire形式的
wire [3:0]D1; //D1为四位二进制bcd码输出,十位
wire c1; //个位的进位
wire c2; //十位的进位
reg [15:0]count; //降频计时,数50000个clk 之后count就回到0,过了1ms
reg [3:0]status; //步骤生成器(6步)
reg [3:0]seg_data;//段选的送显数值
wire clkin_ok;
anti_shake anti_shake1(.clk(clk),.clkin(clkin),.clkin_ok(clkin_ok));//防抖
counter10 counter10_0(.rst_n(rst_n),.clkin(clkin_ok),.t(t),.up_down(up_down),.D(D0),.c(c1)); //个位计数
counter10 counter10_1(.rst_n(rst_n),.clkin(clkin_ok),.t(c1),.up_down(up_down),.D(D1),.c(c2)); //十位计数
assign c=c1&c2;
/*当个位和十位都为9,都产生进位时,百位才产生进位!但这里的设计不好,应该说c2
本身就应该含有t2(c1),但那样做不出来。使能t的判断有问题?下一个沿来的时候,低位的数字变了,t也要变,但我们还是用t变之前的t来判断是否进位。*/
always@(posedge clk) //步骤发生器,1ms 为一步
/*count为模50000计数器,count 每到50000就过了1ms,这时status 加1,status是模6计数器。每数50000个上升沿status 就增1;*/
begin
if(~rst_n) begin count<=0;status<=0;end
else
begin
if(count==16'd50000)
begin
count<=0;
if(status==4'd5) status<=0;
else status<=status+1;
end
else count<=count+1;
end
end
always@(posedge clk) //每一个步骤的段选和位选(动态扫描),位选低有效
begin
case(status)
4'd0: begin seg_data<=D0; sel<=6'b111110;end //第1个数码管的数值
4'd1: begin seg_data<=D1; sel<=6'b111101;end //第2个数码管的数值
4'd2: begin seg_data<=0; sel<=6'b111011;end //第3个数码管的数值
4'd3: begin seg_data<=0; sel<=6'b110111;end //第4个数码管的数值
4'd4: begin seg_data<=0; sel<=6'b101111;end //第5个数码管的数值
4'd5: begin seg_data<=0; sel<=6'b011111;end //第6个数码管的数值
default: begin seg_data<=11; sel<=6'b000000;end //每个数码管的g段都亮
endcase
end
always@(posedge clk) //bcd数码管译码器,每段低有效
begin
case(seg_data)
4'd0: seg<=8'hc0 ;
4'd1: seg<=8'hf9 ;
4'd2: seg<=8'ha4 ;
4'd3: seg<=8'hb0 ;
4'd4: seg<=8'h99 ;
4'd5: seg<=8'h92 ;
4'd6: seg<=8'h82 ;
4'd7: seg<=8'hf8 ;
4'd8: seg<=8'h80 ;
4'd9: seg<=8'h90 ;
default:seg<=8'hbf; //每个数码管的g段都亮
endcase
end
endmodule
这里的数码管有6个,我只用了前两个显示数值,其他的都显示0.
通过Quartus下载到FPGA板里就能计数了(数码管显示按键按下的次数);
资源链接:
https://download.csdn.net/download/benchuspx/11222728