Verilog数字系统设计——数字时钟(带暂停和任意位置位)

功能说明

本文实现一个采用同步计数,具有暂停以及用11位控制字进行时分秒置位功能的24h数字时钟。

问题分析及模块实现

数字时钟由时分秒三部分组成,为此需要设计分频器、模24的计数器和模60的计数器,此外为了在多位共阴极数码管上显示,还需要设计数码管的驱动电路,最后,为实现全置位的功能,设计了11位控制字来对复位进行控制。

模24计数器

一般模24计数器只需要5位二进制状态码即可实现,但考虑到数字时钟应用的特殊性,即需要两个数码管来显示,其中每一个数码管的显示信号均需要4位二进制数译码得到。因此,这里采用8位二进制状态码来设计,高4位为十位,低4位为个位。

//8421BCD码计数器,模24
module counter24(clk, rst_n, en, dout,set,set_flag);
 
input clk, rst_n, en;
input set_flag;
input[7:0] set;
output[7:0] dout;
reg[7:0] dout;
 
always@(posedge clk or negedge rst_n)          
begin
	if(!rst_n)       //复位信号有效时,置位
		if(set_flag)
			dout <= set[7:0];
		else
			dout<=dout;
	else if(en == 1'b0)   //计数使能无效时,输出不变
		dout <= dout;
	else if( (dout[7:4] == 4'b0010)&&(dout[3:0] == 4'b0011) )  //计数达到23时,输出清零
		dout <= 8'b00000000;
	else if(dout[3:0] == 4'b1001)       //低位达到9时,低位清零,高位加1
	begin
		dout[3:0] <= 4'b0000;
		dout[7:4] <= dout[7:4] + 1'b1;
	end
	else                     //上述情况都没有发生,则高位不变,低位加1
	begin
		dout[7:4] <= dout[7:4];
		dout[3:0] <= dout[3:0] + 1'b1;
	end
end
endmodule

值得说明的是这里为了后续暂停、进位以及自定义置位等功能的实现,在每个计数器中都加入了使能信号en、置位标识set_flag、置位信号set输入和进位信号co输出。

模60计数器

这里所设计的模60计数器由一个模6计数器和一个模10计数器组成,其中模6计数器的4位状态输出表示十位,模10计数器的4位状态输出表示个位,两者组合产生模60计数器的8位输出。

模6计数器
//模6计数器模块
module counter6(clk, rst_n, en, dout, co,set,set_flag);

input clk, rst_n, en;
input set_flag;
input [3:0]set;
output[3:0] dout;
reg [3:0] dout;
output co;

always@(posedge clk or negedge rst_n)
begin
   if(!rst_n)
   	if(set_flag)
   		dout <= set[3:0];
   	else
   		dout<=dout;        //系统置位
   else if(en)
   	if(dout == 4'b0101)     //计数值达到5时,计数器清零
   		dout <= 4'b0000;
   	else
   		dout <= dout + 1'b1; //否则,计数器加1
   else
   	dout <= dout;

end

assign co = dout[0]&dout[2];  //当计数达到5(4'b1001)时,进位为1,计数值为其他,都没有进位

endmodule
模10计数器
//模10计数器模块
module counter10(clk, rst_n, en, dout, co,set,set_flag);

input clk, rst_n, en;
input set_flag;
input[3:0] set;
output[3:0] dout;
reg [3:0] dout;
output co;

always@(posedge clk or negedge rst_n)
begin
   if(!rst_n )
   	if(set_flag)
   		dout <= set[3:0];
   	else
   		dout<=dout;        //系统复位,计数器置为8(展示进位)
   else if(en)
   	if(dout == 4'b1001)     //计数值达到9时,计数器清零
   		dout <= 4'b0000;
   	else
   		dout <= dout + 1'b1; //否则,计数器加1
   else
   	dout <= dout;

end

assign co = dout[0]&dout[3];  //当计数达到5(4'b1001)时,进位为1,计数值为其他,都没有进位

endmodule
模60计数器
//模60计数器的Verilog HDL设计
module counter60(clk, rst_n, en, dout, co,set,set_flag);

input clk, rst_n, en;
input set_flag;
input[7:0] set;
output[7:0] dout;
output co;
wire co10_1, co10, co6;
wire[3:0] dout10, dout6;

counter10 inst_counter10(.clk(clk), .rst_n(rst_n), .en(en), .dout(dout10), .co(co10_1),.set(set[3:0]),.set_flag(set_flag)); //模10计数器的进位为co10_1
and u3(co10,en,co10_1); //co10_1与en的与为co10
counter6 inst_counter6(.clk(clk), .rst_n(rst_n), .en(co10), .dout(dout6), .co(co6),.set(set[7:4]),.set_flag(set_flag)); //co10_1与en的与为co10,作为模6计数器的使能信号
and u4(co, co10, co6); //模6计数器的进位和模6的使能信号co10的与作为模60计数器的进位

assign dout = {dout6,dout10}; //模60计数器的输出,高位为模6计数器的输出,低位为模10计数器的输出,读法是8421BCD码读法

endmodule

此处说明一点,当两个计数器连接在一起的时候,低位计数器的进位信号与低位计数器的使能信号做与运算得到高位计数器的使能信号。此时clk信号都是统一直接连接在各计数器的clk端上的,故所实现的计数器是同步计数器。

数码管驱动电路

由于数码管的显示频率与数字时钟频率不同,故在其内部需要实现一个分频,来产生数码管的片选信号。实现的原理是将一个18位二进制计数器的高3位状态译码,作为片选。值得一提的是这里有6个数码管,而高3位可以对8个数码管进行片选,所以在计数过程中还应该实现一个状态的跳转。

//数码管显示模块
module digital_tube_display(
   input clk,
   input rst_n,
   input [3:0] hex0, //第一个数码管显示的数字
   input [3:0] hex1,
   input [3:0] hex2,
   input [3:0] hex3,
   input [3:0] hex4,
   input [3:0] hex5,
   output reg [5:0] an,   //数码管选择
   output reg [7:0] sseg  //共阴极数码管信号
   );
   reg [3:0] hex_in; //进入的数字
   localparam N = 17;//数码管分频
   reg [N-1:0] regN; 
   
   
   always@(posedge clk)
   begin
   		regN <= regN + 1'b1;
   		if (regN[N-1:N-3] == 3'b110) regN[N-1:N-3] <= 3'b000;//跳过110,111的情况
   end

   always@ *
   begin
   	case(regN[N-1:N-3])//使用分频信号的高三位作为数码管选择信号
   	3'b000:begin
   		an = 6'b111110; //选中第1个数码管
   		hex_in = hex0; //数码管显示的数字由hex_in控制,显示hex0输入的数字;
   		sseg[7] =0;
   	end
   	3'b001:begin
   		an = 6'b111101; //选中第2个数码管,以此类推
   		hex_in = hex1;
   		sseg[7] =0;
   	end
   	3'b010:begin
   		an = 6'b111011;
   		hex_in = hex2;
   		sseg[7] =1;//这里需要设置小数点
   	end
   	3'b011: begin
   		an = 6'b110111;
   		hex_in = hex3;
   		sseg[7] =0;
   	end
   	3'b100: begin
   		an = 6'b101111;
   		hex_in = hex4;
   		sseg[7] =1;//这里需要设置小数点
   	end
   	3'b101: begin
   		an = 6'b011111;
   		hex_in = hex5;
   		sseg[7] =0;
   	end
   	default:begin
   		an = 6'b111111;
   		hex_in = 0;
   		sseg[7] =0;
   	end
   	endcase
   end
   always@ (*)
   begin
   	case(hex_in)
   		4'h0: sseg[6:0] = 7'b1111110; //共阴极数码管
   		4'h1: sseg[6:0] = 7'b0110000;
   		4'h2: sseg[6:0] = 7'b1101101;
   		4'h3: sseg[6:0] = 7'b1111001;
   		4'h4: sseg[6:0] = 7'b0110011;
   		4'h5: sseg[6:0] = 7'b1011011;
   		4'h6: sseg[6:0] = 7'b1011111;
   		4'h7: sseg[6:0] = 7'b1110000;
   		4'h8: sseg[6:0] = 7'b1111111;
   		4'h9: sseg[6:0] = 7'b1111011;
   		4'ha: sseg[6:0] = 7'b1110111;
   		4'hb: sseg[6:0] = 7'b0011111;
   		4'hc: sseg[6:0] = 7'b1001110;
   		4'hd: sseg[6:0] = 7'b0111101;
   		4'he: sseg[6:0] = 7'b1001111;
   		4'hf: sseg[6:0] = 7'b1000111;
   	endcase
   end
   
   

endmodule

11位控制位说明

D10D9D8D7D6D5D4D3D2D1D0
hourminsecH3H2H1H0L3L2L1L0

hour:小时置位使能信号,=1时对小时置位,=0时不对小时置位。
min:分钟置位使能信号,=1时对分钟置位,=0时不对分钟置位。
sec:秒置位使能信号,=1时对秒置位,=0时不对秒置位。
H3~0:高4位置位信号。
L3~0:低4位置位信号。

注意当hour、min、sec同时为1时,时分秒会同时置为HL上表示的数,可以用来同步置0.

分频器

//分频
module demultiplication(clk,clk_out);
input clk;
output clk_out;
reg clk_out;
localparam Num=22;
reg [Num-1:0] count;
always @ (posedge clk) begin
   count <= count + 1'b1;
end

always @ (posedge clk) begin
   if(count == 1)begin
   	clk_out <= ~clk_out;
   end
   else begin
   	clk_out<=clk_out;
   end
end
endmodule

数字时钟

//数字时钟
module digital_clock(
	input clk,
	input rst_n,
	input en,
	input[10:0] setting,
	output [7:0] hour,
	output [7:0] min,
	output [7:0] sec
	);
 
//用两个模60计数器作为分秒信号
//用一个模24计数器作为小时信号
//计数器之间进位用co信号与使能信号与来得到
wire co_sec1,co_sec,co_min,co_min1;
 
counter60 inst_sec(.clk(clk), .rst_n(rst_n), .en(en), .dout(sec), .co(co_sec1),.set_flag(setting[8]),.set(setting[7:0]));
and inst_and_sec(co_sec,en,co_sec1);

counter60 inst_min(.clk(clk), .rst_n(rst_n), .en(co_sec), .dout(min), .co(co_min1),.set_flag(setting[9]),.set(setting[7:0]));
and inst_and_min(co_min,co_sec,co_min1);


counter24 inst_hour(.clk(clk), .rst_n(rst_n), .en(co_min), .dout(hour),.set_flag(setting[10]),.set(setting[7:0]));
 
endmodule

其中,en为使能,为0时时钟暂停。setting为11位控制位输入。当rst_n有一个负脉冲时根据控制位信息对时钟置位,为1时时钟正常工作。

顶层文件实现

module TOP(
   input clk,
   input rst_n,
   input en,
    input[10:0] setting, //11位控制位
    //S10为小时置位使能
    //S9为分钟置位使能
    //S8为秒置位使能
    //S7~S4为十位数字
    //S3~S0为个位数字
   output  [5:0] an,   //数码管选择
   output  [7:0] sseg  //数码管显示

   );  
   	
  wire [7:0] hour;
   wire [7:0] min;
   wire [7:0] sec;
   wire clk_p;//clk_p为数字时钟所使用的时钟分频信号
   
   //时钟分频
  demultiplication inst_demultiplication(
   .clk(clk),
   .clk_out(clk_p)
   );
   
   digital_clock inst_digital_clock(
   	.clk(clk_p),
   	.rst_n(rst_n),
   	.en(en),
   	.setting(setting),
   	.hour(hour),
   	.min(min),
   	.sec(sec)
   	);

    wire [3:0] hex0; //第一个数码管显示的数字,以此类推
   wire [3:0] hex1;
   wire [3:0] hex2;
   wire [3:0] hex3;
   wire [3:0] hex4;
   wire [3:0] hex5;

   //sec 
   assign hex0 = sec[3:0];
   assign hex1 = sec[7:4];

   //min 
   assign hex2 = min[3:0];
   assign hex3 = min[7:4];

   //hour 
   assign hex4 = hour[3:0];
   assign hex5 = hour[7:4];



   //数码管显示,注意数码管和时钟使用的是不同的分频时钟信号
   digital_tube_display inst_digital_tube_display(
   	.clk(clk),
   	.rst_n(rst_n),
   	.hex0(hex0),
   	.hex1(hex1),
   	.hex2(hex2),
   	.hex3(hex3),
   	.hex4(hex4),
   	.hex5(hex5),
   	.an(an),
   	.sseg(sseg)
   	);

endmodule

代码中clk为系统自带时钟。此处的分频是写死的,可以根据实际需要去改分频器和驱动电路里面的localparam。频率太快会使得显示不正常,注意。

写在后面

学校数电实验总设计的代码,顺便放了上来,写代码的过程中参考了网上一些大佬的代码和思路,某些功能的实现可能会比较相似,给有需要的同学做一个参考吧。

不点个赞吗?
要工程文件可以评论留邮箱

  • 203
    点赞
  • 528
    收藏
    觉得还不错? 一键收藏
  • 225
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 225
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值