原创 Verilog频率计(低频)

2010-5-17 23:45 2561 6 6 分类: FPGA/CPLD

  小弟最近弄了个频率计,主要测量工频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

PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
6
关闭 站长推荐上一条 /3 下一条