时隔一年,重新拾起Verilog,对之前写的模六十计数器进行修改,使其尽量符合编写规范,用电路的思想写程序,解决遗留的计数器进位问题,并使用模块化编程增强可读性和扩展性。
模六十计数器顶层模块Mo60:相当于60s计时器
计数分频模块Div_cnt:将系统时钟转为1Hz输入计数模块
计数模块Count:两个计数器级联,通过设置同步置零信号完成60进制
扫描模块Scan:将系统时钟转为2x100Hz,对两个数码管引脚扫描
译码模块Convert:组合逻辑,将计数转为数码管引脚电位
具体原理见上一篇
/*
模六十计数器顶层模块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
/*
计数器分频模块
系统时钟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
/*
计数模块
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
/*
扫描模块
扫描分频,系统时钟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
/*
译码模块
将取到的数转换为数码管对应的电平
组合逻辑
*/
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数字钟再改一下。