小弟最近弄了个频率计,主要测量工频50HZ的。代码附在下边。
下面的代码,功能实现了,通过了modelsim仿真,实际测试也成功。还是存在一些问题,不是功能上的,而是实现方法上了,我总感觉我的这个设计肯定不是最好的,有一些除法,还有十进制书转化成BCD码的问题还须要很大的优化。但是小弟,刚学FPGA,一些问题也搞不清楚,。就发在网上,分享一下,也让大家指正一下。
//// Description 测量频率(工频50HZ 13~ 59.9HZ)
// Create Date: 2010- 05 -17
// Engineer: 张书腾
// Module Name: frequency
////nate:该设计只是适用于50HZ左右的频率测量,采用的是测周发
module frequency(clk, rst_n, fre,sm_bit, segment);
input clk; //50M系统时钟
input rst_n; //异步复位端
input fre;//频率输入端
output [3:0] sm_bit;//数码管位码
output[7:0] segment; //数码管位码
reg [3:0] sm_bit;//数码管位码寄存器
reg [7:0] segment;//数码管位码寄存器
////////////////////标准信号 F="100K",测周///////////////////////////
reg [9:0]count;//100K时钟分频计数器
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
count <= 1'b0;
else if(count< 10'd499)
count <= count+1'b1;
else
count <= 1'b0;
end
reg clk_100K;//100K时钟输出寄存器
always @ (posedge clk or negedge rst_n) begin
if(!rst_n)
clk_100K <= 1'b0;
else if(count<=249) //时钟的占空比为50%
clk_100K <= 1'b0;
else clk_100K <= 1'b1;
end
/////////////////////////////////////////////
//待测频率进行2分频,2分频后,就可以只计二分频后信号的高电平或低电平
//就可以了,如果不2分频,只记高电平或低电平的话,信号的占空比不同会导致结果不准确
reg fre_2;
always @ (posedge fre or negedge rst_n) begin
if(!rst_n)
fre_2 <= 1'b0;
else if(fre)
fre_2 <= ~fre_2;//2分频
end
//////////////测周计数模块,计2分频后的待测频的高电平或低电平///////////////////////
//相当于在待测频率的整个周期内计数
//////////////////////////////////////////////////////
reg [12:0] cnt;//计数寄存器//由于位宽的限制,最大计数值8192,所以测低频(低于14HZ时会出错)
always @ (posedge clk_100K or negedge rst_n) begin
if(!rst_n) begin
cnt <= 13'd0;
end
else if(!fre_2) //低电平计数
cnt <= cnt + 1'b1;
else begin
cnt <= 13'd0;//不为低电平时清零
end
end
////////锁存信号,上升沿有效//////////////////////////
reg load;
always @ (posedge fre_2 or negedge rst_n) begin
if(!rst_n) begin
load <= 1'b0;
end
else load <= ~load;//产生上升沿
end
//////////////////////////////////
reg [12:0] cnt_r;//存储锁存的值
always @ (posedge load)//load上升沿锁存
begin
cnt_r <= cnt;//将计数值锁存
end
/////////////////////////数据处理单/////////////////////
////////////////////////////////////////////////////////
reg [23:0] temp; //暂存数据
//(将一个十进制的四位数各个位取出),fre_r 中的数为实际频率是100倍,是为了后面的计算方便
always @ (posedge clk or negedge rst_n)
if(!rst_n) begin
fre_r <= 13'd0;
end
else begin
temp <= (24'd10000000)/cnt_r;//fre_r=(1/(10us * cnt_r ))*100实际频率扩大100倍
//采用了一个除法运算符,不清楚效率如何,但是能工作
fre_r <= temp[12:0]; //取出低位消除警告
//////////////////////////////////////
end
//////////////////////////////////
reg [12:0] fre_r , temp1, temp2,temp3;//fre_r , temp1, temp2,temp3用于计算
reg [3:0] count1,count2,count3,count4;//数码管要显示的每位十进制数
reg [3:0] state;///状态机的状态寄存器
///////////////////////状态机编码//////////////////
parameter shiwei = 4'b0001, //十位
gewei = 4'b0010,//个位
shifenwei = 4'b0100,//十分位
baifenwei = 4'b1000;//百分位
always @ (posedge clk or negedge rst_n) ////状态机,取出一个四位数的各个位的值,用比较法
if(!rst_n) begin
state <= shiwei;
count1 <= 4'd0;
count2 <= 4'd0;
count3 <= 4'd0;
count4 <= 4'd0;
end
else begin
case(state)
shiwei: begin
state <= gewei; /////////////// //十位 //用比较发对照出每位的十进制数
if((fre_r>=5000)&&(fre_r<6000)) begin count1 <=4'd5; temp1<=fre_r-13'd5000; end
else if((fre_r>=4000)&&(fre_r<5000)) begin count1 <=4'd4; temp1<=fre_r-13'd4000; end
else if((fre_r>=3000)&&(fre_r<4000)) begin count1 <=4'd3; temp1<=fre_r-13'd3000; end
else if((fre_r>=2000)&&(fre_r<3000)) begin count1 <=2; temp1<=fre_r-13'd2000; end
else if((fre_r>=1000)&&(fre_r<2000)) begin count1 <=1; temp1<=fre_r-13'd1000; end
else if(fre_r<1000) begin count1 <=0; temp1<=fre_r; end
else begin count1 <=5; temp1<=fre_r-13'd5000;end
end
gewei: begin
state <= shifenwei; ////////////////////////个位 //用比较发对照出每位的十进制数
if(temp1>=900) begin count2 <=9; temp2<=temp1-13'd900; end
else if((temp1>=800)&&(temp1<900)) begin count2 <=8; temp2<=temp1-13'd800; end
else if((temp1>=700)&&(temp1<800)) begin count2 <=7; temp2<=temp1-13'd700; end
else if((temp1>=600)&&(temp1<700)) begin count2 <=6; temp2<=temp1-13'd600; end
else if((temp1>=500)&&(temp1<600)) begin count2 <=5; temp2<=temp1-13'd500; end
else if((temp1>=400)&&(temp1<500)) begin count2 <=4; temp2<=temp1-13'd400; end
else if((temp1>=300)&&(temp1<400)) begin count2 <=3; temp2<=temp1-13'd300; end
else if((temp1>=200)&&(temp1<300)) begin count2 <=2; temp2<=temp1-13'd200; end
else if((temp1>=100)&&(temp1<200)) begin count2 <=1; temp2<=temp1-13'd100; end
else if(temp1<100) begin count2 <=0; temp2<=temp1; end
else begin count2 <=0; temp2<=temp1-13'd900; end
end
shifenwei: begin
state <= baifenwei;///////////////////////////十分位 //用比较发对照出每位的十进制数
if(temp2>=90) begin count3 <=9; temp3<=temp2-13'd90; end
else if((temp2>=80)&&(temp2<90)) begin count3 <=8; temp3<=temp2-13'd80; end
else if((temp2>=70)&&(temp2<80)) begin count3 <=7; temp3<=temp2-13'd70; end
else if((temp2>=60)&&(temp2<70)) begin count3 <=6; temp3<=temp2-13'd60; end
else if((temp2>=50)&&(temp2<60)) begin count3 <=5; temp3<=temp2-13'd50; end
else if((temp2>=40)&&(temp2<50)) begin count3 <=4; temp3<=temp2-13'd40; end
else if((temp2>=30)&&(temp2<40)) begin count3 <=3; temp3<=temp2-13'd30; end
else if((temp2>=20)&&(temp2<30)) begin count3 <=2; temp3<=temp2-13'd20; end
else if((temp2>=10)&&(temp2<20)) begin count3 <=1; temp3<=temp2-13'd10; end
else if(temp2<10) begin count3 <=0; temp3<=temp2; end
else begin count3 <=0; temp3<=temp2-13'd90; end
end
baifenwei: begin
state <= shiwei;/////////////////百分位///////////////////////////////////
count4 <= temp3[3:0];
end
default: begin
state <= shiwei;
end
endcase
end
/////////////////////////////////////////////
///////////////////显示模块 ///////////////
reg [18:0] count_1ms; //用来产生数码管动态显示的位变化的驱动时钟
always @ (posedge clk or negedge rst_n)
if(!rst_n) begin
count_1ms <= 19'd0;
end
else begin
count_1ms <= count_1ms + 1'b1;
end
//////////数码管动态扫描的位产生单元
always @ (posedge clk or negedge rst_n)
if(!rst_n)
sm_bit=4'b0000;
else begin
case(count_1ms[18:16]) //数码管动态显示的位扫描 //基本上是每一位亮1ms
3'b000: sm_bit=4'b1110;
3'b001: sm_bit=4'b1101;
3'b010: sm_bit=4'b1011;
3'b011: sm_bit=4'b0111;
default: sm_bit=4'b1111;
endcase
end
always @ (posedge clk or negedge rst_n)
if(!rst_n) begin
segment =8'h03;
end
else begin
case(sm_bit)
4'b1110: segment=convent(count1); //十位
4'b1101: segment=convent(count2)-8'd1;///个位 //显示小数点
4'b1011: segment=convent(count3); //十分位
4'b0111: segment=convent(count4); //百分位
default: segment=8'b1111_1111;
endcase
end
function [7:0] convent;//将自然数转化成共阳数码管的函数
input [3:0] bcd; //输入为自然数
case(bcd)
4'd0: convent="8"'h03; //数码管段码,a为最高位,dp为最低位
4'd1: convent="8"'h9f;
4'd2: convent="8"'h25;
4'd3: convent="8"'h0d;
4'd4: convent="8"'h99;
4'd5: convent="8"'h49;
4'd6: convent="8"'h41;
4'd7: convent="8"'h1f;
4'd8: convent="8"'h01;
4'd9: convent="8"'h09;
default: convent = 8'hff;
endcase
endfunction
///////////////////////////////////////////////////////////
endmodule
文章评论(0条评论)
登录后参与讨论