【Verilog】频率计再设计

曾在2020年7月使用VHDL在FPGA上实现了频率计,但是那时尚未了解状态机,设计方法不够系统,现在重新用Verilog语言进行再设计
\quad 功能需求:使用计数方法进行频率测量,主要分为三个量程(0-100Hz,100-999Hz,1k-10kHz),并且具有溢出提示,小数点、单位显示和锁存功能,自动根据输入选择量程并显示
\quad 基本原理:测频率最简单的方法就是计数法,例如在1s内进行输入波计数,1s闸门脉冲后锁存的数及时频率(计数器未溢出的情况下)。此外,由于本设计是三量程,在1000计数器小于100时,改为在10s内进行计数,增加准确度。而在计数器第一次超量程时,对输入脉冲进行10分频再计数。此外,如果待测频率过低,还可以使用测周法(但在本项目中不使用)。
\quad 状态图在这里插入图片描述 \quad 值得一提的是,我们在计数结束阶段进行锁存,在显示结束阶段进行计数器清零,这样可以保证在清零之前数据已经完成了锁存

\quad 结合控制模块的状态图,我们不难根据这个理念设计系统框图

\quad 控制模块框图在这里插入图片描述
\quad 实现代码如下:
\quad 外层:

module sys_controller(
input RESET,
input CP,
input [9:0] CNT,
input FULL,
input GATE,
output [1:0]STATE,
output [1:0] RANGE,
output OF,
output LOCK,
output RECOUNT);
reg OVER;
wire CLR;
reg recount;
always@(posedge CLR,negedge CP)begin//***atuomatically goto 0 CLR->recount
if(CP==0) recount<=1'b0;
else recount<=1'b1;
end
always@(negedge GATE,posedge recount)begin
if(recount) OVER<=1'b0;
else OVER<=1'b1;
end
sys_ctrl controller_center(.RESET(RESET),
.CP(CP),
.CNT(CNT),
.FULL(FULL),
.OVER(OVER),
.STATE(STATE),
.RANGE(RANGE),
.OF(OF),
.LOCK(LOCK),
.RECOUNT(CLR));
assign RECOUNT=recount;
endmodule

\quad 内层:

//sys_ctrl for frequency detect FD_module
module sys_ctrl#(parameter S_count=2'b00,S_display=2'b01,S_start=2'b10,L=2'b00,M=2'b01,H=2'b10)
(
input RESET,
input CP,
input [9:0] CNT,
input FULL,
input OVER,
output [1:0]STATE,
output [1:0] RANGE,
output reg OF,
output reg LOCK,
output reg RECOUNT//计数器清零信号
);
reg  [1:0] current_state;
reg [1:0] next_state;
reg  [1:0]range;
always@(posedge CP, negedge RESET)begin
    if(~RESET)begin//复位操作
        current_state<=S_start;
    end
    else begin//cp触发
        current_state<=next_state;
    end
end
always@(posedge CP)begin
        case(current_state) 
            S_start:begin
                    next_state<=S_count;
                    range<=M;
                    RECOUNT<=1'b0;
                    OF<=1'b0;
                    LOCK<=1'b0;
                    end
            S_count:begin
            if(OVER==1'b1)begin//如果计数器结束计数,结果可用
            case(range)
            L:begin
                if(FULL==0)//低量程不超
                    begin
                    next_state<=S_display;
                    OF<=1'b0;
                    LOCK<=1'b1;//用于上升沿刷新数据 
                    end
                else 
                    begin//低量程超
                    next_state<=S_count;
                    range<=M;
                    RECOUNT<=1'b1;//上升沿清零计数器
                    OF<=1'b0;
                    LOCK<=1'b0;
                    end
            end
            M:begin
                if(FULL==0&&CNT>=100)begin
                    next_state<=S_display;
                    OF<=1'b0;
                    LOCK<=1'b1;//用于上升沿刷新数据                     
                end
                else if(FULL==0&&CNT<100)begin
                    next_state<=S_count;
                    range<=L;
                    RECOUNT<=1'b1;
                    OF<=1'b0;
                    LOCK<=1'b0;
                end 
                else if(FULL==1)begin
                    next_state<=S_count;
                    range<=H;
                    RECOUNT<=1'b1;
                    OF<=1'b0;
                    LOCK<=1'b0;
                end
            end
            H:begin
                if(FULL==0&&CNT>=100)begin
                    next_state<=S_display;
                    OF<=1'b0;
                    LOCK<=1'b1;//用于上升沿刷新数据                     
                end
                else if(FULL==0&&CNT<100)begin
                    next_state<=S_count;
                    range<=M;
                    RECOUNT<=1'b1;
                    OF<=1'b0;
                    LOCK<=1'b0;
                end 
                else if(FULL==1)begin
                    next_state<=S_display;
                    OF<=1'b1;
                    LOCK<=1'b1;//用于上升沿刷新数据                    
                end
            end
            endcase
            end
            end
            S_display:begin
                next_state<=S_count;
                RECOUNT<=1'b1;
            end
        endcase
end
assign STATE=current_state;
assign RANGE=range;
endmodule

\quad RTL框图如下:
在这里插入图片描述
以上是频率计设计核心内容,我们再控制模块的基础上加上计数器和锁存器,进行合理的时序调整,可以得到以下这个部分功能框图,这个框图所代表的模块可以用来检验控制模块的是否能够正常工作
在这里插入图片描述 \quad 代码如下:

//a combie of CNT1K and sys_controller
module part1(
input F,
input GATE,
input CP,
input RESET,
output reg [9:0] CNT_disp,
output [1:0]STATE,
output [1:0] RANGE,
output OF,
output LOCK,
output RECOUNT
);
wire of,lock,recount,full;
wire [9:0] cnt;
CNT1K cnt_inst(.F_IN(F&GATE),
.CLR(recount),
.CNT(cnt),
.FULL(full));
sys_controller conrtrl_inst(.CP(CP),
.CNT(cnt),
.FULL(full),
.GATE(GATE),
.RESET(RESET),
.STATE(STATE),
.RANGE(RANGE),
.OF(of),
.LOCK(lock),
.RECOUNT(recount));
always@(posedge lock)begin
CNT_disp<=of?10'd999:cnt;
end
assign OF=of;
assign LOCK=lock;
assign RECOUNT=recount;
endmodule

\quad RTL框图如下:
在这里插入图片描述 \quad 需要提出,CP是对FPGA自带时钟的中等分频,而GATE是对FPGA自带时钟的高分频,一般为1s,在低量程时为10s。CP的引入与状态机一样,大大简化了本次设计,留出了许多设计余量,大大减少了系统脉冲冲突竞争冒险的可能性。
\quad part1验证
\quad 低频输入:结果为16.0HZ
在这里插入图片描述
\quad 高频输入:结果为3.20kHz
在这里插入图片描述
\quad 此外这里有个设计上比较巧妙的一点,代码如下

always@(posedge CLR,negedge CP)begin//***atuomatically goto 0 CLR->recount
if(CP==0) recount<=1'b0;
else recount<=1'b1;
end

\quad 这段代码把CP作为下降沿异步清零,把CLR上升沿作为触发,可以实现CLR信号提前下降,防止干扰后续模块。


\quad 其中,如果是高频时,可以将输入频率进行10分频。(简单模拟为闸门缩短为10分之一)如果是低频时,可以延长闸门时间到10倍(这里没有做)。这些功能可以由part1的输出量程信号RANGE控制协控制器进行闸门产生和输入信号分频选择,比较简单,作为part2,这里时间原因暂不说明。


\quad 至于显示与译码模块,我们可以使用扫描显示的方法,利用计数器和数据选择器,用计数器遍历数据选择器输入,使得数据选择器循环输出各个端口数据,在视觉上实现动态扫描显示。其次,需要将CNT_disp10位信号进行除法操作,分离出其三个数据位,然后译码为8端数码管的7位(小数点位先留着),最后一位是单位数码管,其数据来自RANGE量程信息,输出为H或者K。小数点(即每个数码管的第8位)也由RANGE确定。需要注意,CNT_disp之前已经经过latch锁存,而在这里小数点位和单位位需要与之前的CLOCK信号进行同步锁存,保证显示的一致性。这些都比较简单,作为part3,暂时也先不实现。

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值