模六十计数器(二)


前言

时隔一年,重新拾起Verilog,对之前写的模六十计数器进行修改,使其尽量符合编写规范,用电路的思想写程序,解决遗留的计数器进位问题,并使用模块化编程增强可读性和扩展性。


一、设计思路

模六十计数器顶层模块Mo60:相当于60s计时器
计数分频模块Div_cnt:将系统时钟转为1Hz输入计数模块
计数模块Count:两个计数器级联,通过设置同步置零信号完成60进制
扫描模块Scan:将系统时钟转为2x100Hz,对两个数码管引脚扫描
译码模块Convert:组合逻辑,将计数转为数码管引脚电位
具体原理见上一篇

二、Verilog文件

1、顶层模块

/*
模六十计数器顶层模块Mo60:相当于60s计时器
计数分频模块Div_cnt:将系统时钟转为1Hz输入计数模块
计数模块Count:两个计数器级联,通过设置进位信号完成60进位
扫描模块Scan:将系统时钟转为2x100Hz,对两个数码管引脚扫描
译码模块Convert:组合逻辑,将计数转为数码管引脚电位
*/
module Mo60(
    input clk,//系统时钟50MHz
    input rst_n,//异步复位按钮
    output [3:0] loc,//被选中数码管的位置
    output [7:0] pin//被选中数码管的八个引脚
    );
//计数部分
    wire clk_cnt;//计数时钟
    wire co1, co0;//进位信号
    wire [3:0] n1, n0;//十位与个位数据
    Div_cnt U0(.clk(clk), .rst_n(rst_n), .clk_cnt(clk_cnt));
    Count U1(.clk(clk_cnt), .rst_n(rst_n), .en(1), .set(co0), .cnt(n0));
    Count U2(.clk(clk_cnt), .rst_n(rst_n), .en(co0), .set(co1), .cnt(n1));
    assign co0 = (n0 == 9);
    assign co1 = (n1 == 5)&&(n0 == 9);//可实现模任意两位数
//扫描部分
    Scan U3(.clk(clk), .loc(loc));
//译码部分
    reg [3:0] num;//待译码数字
    always @(loc, n1, n0) begin
        case (loc)
            4'b1101: num = n1;
            4'b1110: num = n0;
            default: num = 4'bxxxx;//避免锁存
        endcase
    end
    Convert U4(.num(num), .pin(pin));
endmodule

2、分频模块

/*
计数器分频模块
系统时钟50MHz
仿真时输出1MHz
下载时输出1Hz
上升沿触发计数
CNT_END=50M/(2x计数频率)
*/
module Div_cnt (
    input clk,
    input rst_n,
    output reg clk_cnt);
    reg [24:0] counter;
    parameter [24:0] CNT_END = 24;//仿真时计数25
    //parameter [24:0] CNT_END = 24 999 999;//下载时计数25M
    always @(posedge clk, negedge rst_n) begin
        if (!rst_n) begin
            counter <= 0;
            clk_cnt <= 1;//上升沿触发计数
        end
        else if (counter == CNT_END) begin
            counter <= 0;
            clk_cnt <= !clk_cnt;
        end
        else counter <= counter + 1;
    end
endmodule

3、计数模块

/*
计数模块
16进制计数器
同步置零在外部电路完成
*/
module Count(
    input clk,//时钟
    input rst_n,//异步复位
    input en,//使能
    input set,//同步置0
    output reg [3:0] cnt);//计数值
    always @(posedge clk, negedge rst_n) begin
        if(!rst_n) cnt <= 0;
        else if(set) cnt <= 0;
        else if(en) cnt <= cnt + 1;
    end
endmodul

4、扫描模块

/*
扫描模块
扫描分频,系统时钟50MHz
仿真时5MHz(每计一次数扫描5轮)
下载时100Hz(扫描一轮100Hz)
数码管选择,为0表示被选中
电平触发扫描
CNT_END=50M/(扫描数x扫描频率)
*/
module Scan(
    input clk,
    output reg [3:0] loc);
    //扫描分频
    reg [17:0] counter = 0;//最多计到249999
    reg flag = 0;//选择标志
    parameter [17:0] CNT_END = 4;//仿真时计数5
    //parameter [17:0] CNT_END = 249 999;//下载时计数250 000
    always @(posedge clk) begin
        if (counter == CNT_END) begin 
            counter <= 0;
            flag <= ~flag;
        end
        else counter <= counter + 1;
    end
    //数码管选择
    always @(flag) begin
        case(flag)
            0: loc = 4'b1110;
            1: loc = 4'b1101;
            default loc = 4'bxxxx;
        endcase
    end
endmodule

5、译码模块

/*
译码模块
将取到的数转换为数码管对应的电平
组合逻辑
*/
module Convert(
    input [3:0] num,
    output reg [7:0] pin);
    always @(num) begin
	    case(num)
			0: pin = 8'b00000011;//最后一位表示小数点
			1: pin = 8'b10011111;
			2: pin = 8'b00100101;
			3: pin = 8'b00001101;
			4: pin = 8'b10011001;
			5: pin = 8'b01001001;
			6: pin = 8'b01000001;
			7: pin = 8'b00011111;
			8: pin = 8'b00000001;
			9: pin = 8'b00001001;
			default: pin = 8'b01100001;//其他状态显示E
		endcase
	end
endmodule

三、测试文件

/*
测试文件
*/
`timescale 1ns/1ns
module Test_mo60;
    reg clk;
    reg rst_n;
    wire [3:0] loc;
    wire [7:0] pin;
    parameter PERIOD = 20;//时钟周期20ns
    Mo60 u_Mo60 (
        .clk(clk),
        .rst_n(rst_n),
        .loc(loc),
        .pin(pin));
    initial begin
        clk = 0;
        rst_n = 1;
    end
    always #10 clk = ~clk;
    initial begin
        #5000 rst_n = 0;
        #5000 rst_n = 1;
        #90000 rst_n = 0;
        #5000 rst_n = 1; 
    end
endmodule

四、仿真波形

本次修改只对程序进行了简单的仿真而未在FPGA中进行验证,不排除实际使用时可能会出现问题。
1、整体波形
整体波形2、进位部分波形
进位部分波形


总结

永远不知道学的东西什么时候会用上,这几天抽空把之前写的EDA数字钟再改一下。

猜你喜欢

转载自blog.csdn.net/qq_53715621/article/details/127351157