10月份学了仅仅20多天的FPGA,后来学长和我说先搁一段时间,以后用到再现学,于是耽搁了整整2个月,中间画板子调电路去了。(所以评论我博客的人问的问题,我真的不会回答)
最近终于要用到FPGA了,要做一个频率计,20多天的经验果然不够,写了两天代码,数码管死活全是0。测频率的原理是懂的,但是怎么都不会输出。咨询了很多搞电设的同学,都在忙期末考试没时间理我。功夫不负有心人,我竟然在CSDN资源上找到了一个能用的代码,哈哈哈,于是我直接把代码理解了就OK。写这篇博文,希望能够手把手教大家怎么样用FPGA做一个简单的频率计且用数码管输出。(以帮助像自己一样的菜鸟初学者顺利飞出林子变得不那么菜)
首先,实现的原理如逻辑门所示,信号通过输入,产生频率的计数,将数值送到数码管,进行6位数据的处理,最后再进行译码显示出来。(我原本自己写的数码管程序太naive了,原来需要这么复杂的驱动,我还是太菜了)
No.1测频率的fre_test.v
module fre_test(
clk,
rst_n,
clk_in,
fre
);//时钟、复位信号、待测的输入信号、输出的频率值
input clk,rst_n,clk_in;
output [19:0] fre;
reg [19:0] fre;
reg [30:0] count1, count2;
reg clk_1hz, en;
wire load, clr;
//分频
always @ ( posedge clk or negedge rst_n )
begin
if( !rst_n )
begin
count1<=0;
clk_1hz<=0;
end
else
begin
if ( count1<25000000-1 ) //晶振是50M,因此计数到49999999
begin
count1 <= count1 + 1 ;
clk_1hz <= 1; //产生频率为1hz的时钟
end
else if ( count1 <50000000-1 )
begin
count1 <= count1 + 1;
clk_1hz <= 0;
end
else
begin
count1 <= 0;
clk_1hz <=0;
end
end
end
//测量频率值
always @ ( posedge clk_1hz or negedge rst_n ) //当检测到clk_1hz的上升沿时
begin
if ( !rst_n)
en <= 0;
else
begin
en <= ~en; //取反,en为高电平的时间为1s end
end
assign load = ~en ; //load的信号永远和使能信号en是反的
assign clr = load&&(~clk_1hz) ; //当load和clk_1hz同时为1时清零信号clr有效
always @ ( posedge clk_in or posedge clr )
begin
if ( clr)
count2 <= 0; //清零信号时,计数器count2为0
else
begin
if ( en ) //使能信号时,开始计数
count2 <= count2 + 1; //当检测到方波信号的上升沿时则计数,计数的值则为信号的频率值
else
count2 <= count2;
end
end
always @ ( posedge load or negedge rst_n) //输出频率load为上升沿时输出
begin
if(!rst_n)
fre <= 0;
else
begin
if(count2 < 900000) //低于900000输出正常位
fre <= count2 ;
else if(count2 < 10000000) //小于10000000时位数不够,因此需要去掉一位
fre <= count2 / 10;
else
fre <= count2 / 100; //去掉两位
end
end
endmodule
在这里给大家理一下clk_1hz、en、load、clr信号之间的关系。
可以看出,en的频率为1秒,load的目的是为更新和为clr清零信号作准备的。从时序图可以简单看出来就不详细解释了。
其次,之所以取900000之后就换位而不是1000000的目的在于提醒大家位数变大了。
接着是wei_test
`timescale 1ns / 1ps
//
//
module wei_test(clk,rst,hex,Million,HunThousand,TenThousand,Thousand,Hundred,Ten,One);
input clk;
input rst;
input [19:0] hex;
output reg [3:0] Million; //百万位
reg [19:0] d5;
output reg [3:0] HunThousand; //十万位
reg [19:0] d6;
output reg [3:0] TenThousand; //万位
reg [19:0] d1;
output reg [3:0] Thousand; //千位
reg [16:0] d2;
output reg [3:0] Hundred; //百位
reg [16:0] d3;
output reg [3:0] Ten; //十位
reg [3:0] d4;
output reg [3:0] One;
initial begin //初始化使各个位都变为0
Million=4'h0;
d5=20'h00000;
TenThousand=4'h0;
d6=20'h00000;
TenThousand=4'h0;
d1=20'h00000;
Thousand=4'h0;
d2=16'h0000;
Hundred=4'h0;
d3=16'h0000;
d4=4'h0;
end
//百万位
always@(posedge clk) begin
if(!rst) begin //同步复位
Million<=4'h0;
d5<=20'h00000;
end else begin
if(hex>999999) begin
Million<=4'd1;
d5<=hex-20'd1000000;
end else begin
Million<=4'd0;
d5<=hex;
end
end
end
//十万位
always@(posedge clk) begin
if(!rst) begin //同步复位
HunThousand<=4'h0;
d6<=20'h00000;
end else begin
if(d5>899999) begin
HunThousand<=4'd9;
d6<=d5-20'd900000;
end else if(d5>799999) begin
HunThousand<=4'd8;
d6<=d5-20'd800000;
end else if(d5>699999) begin
HunThousand<=4'd7;
d6<=d5-20'd700000;
end else if(d5>599999) begin
HunThousand<=4'd6;
d6<=d5-20'd600000;
end else if(d5>499999) begin
HunThousand<=4'd5;
d6<=d5-20'd500000;
end else if(d5>399999) begin
HunThousand<=4'd4;
d6<=d5-20'd400000;
end else if(d5>299999) begin
HunThousand<=4'd3;
d6<=d5-20'd300000;
end else if(d5>199999) begin
HunThousand<=4'd2;
d6<=d5-20'd200000;
end else if(d5>99999) begin
HunThousand<=4'd1;
d6<=d5-20'd100000;
end else begin
HunThousand<=4'd0;
d6<=d5;
end
end
end
//万位
always@(posedge clk) begin
if(!rst) begin //同步复位
TenThousand<=4'h0;
d1<=20'h00000;
end else begin
if(d6>89999) begin
TenThousand<=4'd9;
d1<=d6-20'd90000;
end else if(d6>79999) begin
TenThousand<=4'd8;
d1<=d6-20'd80000;
end else if(d6>69999) begin
TenThousand<=4'd7;
d1<=d6-20'd70000;
end else if(d6>59999) begin
TenThousand<=4'd6;
d1<=d6-20'd60000;
end else if(d6>49999) begin
TenThousand<=4'd5;
d1<=d6-20'd50000;
end else if(d6>39999) begin
TenThousand<=4'd4;
d1<=d6-20'd40000;
end else if(d6>29999) begin
TenThousand<=4'd3;
d1<=d6-20'd30000;
end else if(d6>19999) begin
TenThousand<=4'd2;
d1<=d6-20'd20000;
end else if(d6>9999) begin
TenThousand<=4'd1;
d1<=d6-20'd10000;
end else begin
TenThousand<=4'd0;
d1<=d6;
end
end
end
//千位
always@(posedge clk) begin
if(!rst) begin
Thousand<=4'h0;
d2<=16'h0000;
end else begin
if(d1>8999) begin
Thousand<={4'd9};
d2<=d1-16'd9000;
end else if(d1>7999) begin
Thousand<={4'd8};
d2<=d1-16'd8000;
end else if(d1>6999) begin
Thousand<={4'd7};
d2<=d1-16'd7000;
end else if(d1>5999) begin
Thousand<={4'd6};
d2<=d1-16'd6000;
end else if(d1>4999) begin
Thousand<={4'd5};
d2<=d1-16'd5000;
end else if(d1>3999) begin
Thousand<={4'd4};
d2<=d1-16'd4000;
end else if(d1>2999) begin
Thousand<={4'd3};
d2<=d1-16'd3000;
end else if(d1>1999) begin
Thousand<={4'd2};
d2<=d1-16'd2000;
end else if(d1>999) begin
Thousand<={4'd1};
d2<=d1-16'd1000;
end else begin
Thousand<={4'd0};
d2<=d1;
end
end
end
//百位
always@(posedge clk) begin
if(!rst) begin
Hundred<=4'h0;
d3<=16'h0000;
end else begin
if(d2>899) begin
Hundred<={4'd9};
d3<=d2-16'd900;
end else if(d2>799) begin
Hundred<={4'd8};
d3<=d2-16'd800;
end else if(d2>699) begin
Hundred<={4'd7};
d3<=d2-16'd700;
end else if(d2>599) begin
Hundred<={4'd6};
d3<=d2-16'd600;
end else if(d2>499) begin
Hundred<={4'd5};
d3<=d2-16'd500;
end else if(d2>399) begin
Hundred<={4'd4};
d3<=d2-16'd400;
end else if(d2>299) begin
Hundred<={4'd3};
d3<=d2-16'd300;
end else if(d2>199) begin
Hundred<={4'd2};
d3<=d2-16'd200;
end else if(d2>99) begin
Hundred<={4'd1};
d3<=d2-16'd100;
end else begin
Hundred<={4'd0};
d3<=d2;
end
end
end
//十位
always@(posedge clk) begin
if(!rst) begin
Ten<=4'h0;
d4<=4'h0;
end else begin
if(d3>89) begin
Ten<={4'd9};
d4<=d3-16'd90;
end else if(d3>79) begin
Ten<={4'd8};
d4<=d3-16'd80;
end else if(d3>69) begin
Ten<={4'd7};
d4<=d3-16'd70;
end else if(d3>59) begin
Ten<={4'd6};
d4<=d3-16'd60;
end else if(d3>49) begin
Ten<={4'd5};
d4<=d3-16'd50;
end else if(d3>39) begin
Ten<={4'd4};
d4<=d3-16'd40;
end else if(d3>29) begin
Ten<={4'd3};
d4<=d3-16'd30;
end else if(d3>19) begin
Ten<={4'd2};
d4<=d3-16'd20;
end else if(d3>9) begin
Ten<={4'd1};
d4<=d3-16'd10;
end else begin
Ten<={4'd0};
d4<=d3;
end
end
end
//个位
always@(posedge clk) begin
if(!rst) begin
One<=4'd0;
end else begin
One<={d4[3:0]};
end
end
endmodule
这段代码是用来显示六位数码管的。先判断hex即频率值有没有超过999999(前面已经处理过,如果超过,则百万就有数值,否则可以正常显示),将值先送给d5寄存器,此时一个一个进行比较。当在899999~999999之间,则为9,一次类推下去,一位一位地赋值,同时减掉最高位赋给下一位。得到十万位、万位、千位、百位、十位和个位。
最后则是对数码管进行编码
module smg(
clk,
Million,
HunThousand,
TenThousand,
Thousand,
Hundred,
Ten,
One,
sel,
data
);
input clk;
input [3:0]Million,HunThousand,TenThousand,Thousand,Hundred,Ten,One;
output reg [7:0] data;
output reg [7:0] sel;
reg [3:0] cnt,data_dis;
reg [20:0] m;
always@(posedge clk)
begin
m<=m+1;
end
always@(m[13])
begin
case(m[15:13])
0: begin
data_dis<=4'b0000;
sel<=8'b1111_1110;
end
1: begin
data_dis<=Million;
sel<=8'b1111_1101;
end
2: begin
data_dis<=HunThousand;
sel<=8'b1111_1011;
end
3: begin
data_dis<=TenThousand;
sel<=8'b1111_0111;
end
4: begin
data_dis<=Thousand;
sel<=8'b1110_1111;
end
5: begin
data_dis<=Hundred;
sel<=8'b1101_1111;
end
6: begin
data_dis<=Ten;
sel<=8'b1011_1111;
end
7: begin
data_dis<=One;
sel<=8'b0111_1111;
end
default:begin
data<=8'bz;
sel<=8'bz;
end
endcase
end
always@(data_dis)
begin
case(data_dis) //七段译码
4'h0:data = 8'hc0; //显示0
4'h1:data = 8'hf9; //显示1
4'h2:data = 8'ha4; //显示2
4'h3:data = 8'hb0; //显示3
4'h4:data = 8'h99; //显示4
4'h5:data = 8'h92; //显示5
4'h6:data = 8'h82; //显示6
4'h7:data = 8'hf8; //显示7
4'h8:data = 8'h80; //显示8
4'h9:data = 8'h90; //显示9
default data = 8'hxx;
endcase
end
endmodule
这里对数码管进行扫描操作时,定义了一个21位的寄存器,一直在计数,检测第16位和14位中间三位进行0~7之间8个数的判断,由此扫描显示数码管。
数码管的编码我相信每一块板子的教学视频都有讲解,就不过多解释了。
最后感谢这位分享资源的兄dei,提供一下网址:https://download.csdn.net/download/qq_33836552/9881856
这个资源显示的数值是两倍的频率,因为使能信号的频率为2hz,后面修正了一下,代码就是我上面写的。
最后定义一下管脚,下载到FPGA里,用信号发生器检验一下。希望大家成功。
-------------------------------分割线-------------------------------------------
我没有找到关于学FPGA的qq群,所以我自己建了一个群,期待和大家一起交流。