Verilog HDL 设计一个电子钟

基于 Verilog HDL 设计电子钟,能够进行正常计时,时间调整,时间复位的模式选择。


文章目录

  • Verilog HDL 设计一个电子钟
  • 设计思路
  • 复位设计
  • 计时设计
  • 调整设计
  • 关键代码
  • 源代码
  • 设置引脚


设计思路

电子时钟分为主模块(time)和数码管模块(smg):
主模块用来控制时、分、秒的逻辑,数码管模块用来显示时间;计时实际就是进行计数,间隔为1秒;
60秒:分成个位和十位实现,个位:0-9,到9向十位进1;十位:0-6,到6向分钟进1;
60分:分成个位和十位实现,个位:0-9,到9向十位进1;十位:0-6,到6向小时进1;
24时:直接设计0-24,reg[5:0] xy(6位的二进制数),但分成个位、十位进行数码管显示,个位取小时除以10取余,十位取小时除以10取整。

复位设计

用硬件上的开关(SW1)实现时间复位,就是实现电子时钟全部清零,并且不再改变(一直为0),除非关闭时间复位。

计时设计

首先进行时间复位,再关闭时间复位,从0进行正常计时。

调整设计

只调整分钟和小时,用硬件上的开关控制(SW2,SW3),依然是1秒进行计数。
需要再引入两个变量:xn,xn1;当时间调整开关打开时,让xn或xn1进行单独计时,同时再赋值给分钟或小时会显示的变量,调分钟时调的是分的个位。

关键代码

//1S定时时钟产生
reg [31:0]	count;
reg clk_1s; //0,1 一秒钟
always @(posedge clk)  
	if(count==24_000_000-1)
	begin
		count <= count+1;
		clk_1s <= 0;
	end
	else if(count<48_000_000-1)
		count <= count+1;
	else 
	begin
		count <= 0;
		clk_1s <= 1;		
	end

源代码

主模块

//
//主模块time
//
module time( 
input SW1,//控制复位
input SW2,//控制调分钟
input SW3,//控制调小时
output  [7:0] sel,
output  [7:0] seg,
input clkin//时钟
);		

wire clk;
pll4 pll4_inst0(
	.inclk0(clkin),//12Mhz
	.c0(clk)//48Mhz
);

//1S定时时钟产生	
reg [31:0]	count;
reg clk_1s;
always @(posedge clk)  //always语句相当于一个死循环
	if(count==24_000_000-1)
	begin
		count <= count+1;
		clk_1s <= 0;
	end
	else if(count<48_000_000-1)
		count <= count+1;
	else 
	begin
		count <= 0;
		clk_1s <= 1;		
	end
		
reg [3:0] dout1,dout2,dout3,dout4,dout5,dout6;//时、分、秒的个位和十位
reg co_1;//秒的个位向十位的进位
reg co_2;//秒的十位向分的个位的进位
reg co_3;//分的个位向十位的进位
reg co_4;//分的十位向时的进位
	
//秒的个位实现		
always@(posedge clk_1s,negedge SW1)
begin
	if(!SW1)     //如果SW1关闭时钟复位
 		dout1<=0;
 	else		//SW1打开进行计时(0-9)
 		begin
   			if(dout1<9)
				begin
					dout1 <= dout1 +1;	//进行加1操作
	  				co_1<=0;  			//此时进位数为0;
				end
			else   						//秒的个位为9时
				begin
	  				dout1 <= 0;	 	//重新赋值为0
	  				co_1<=1;        //此时进位数为1
	 			end
		end
end	
		
//秒的十位的实现
always @(posedge co_1,negedge SW1)
begin
	if(!SW1)
		dout2<=0;
	else
		begin
   			if(dout2<5)
				begin
	  				dout2 <= dout2 +1;	
	  				co_2<=0;
				end
			else 
				begin
	  				dout2 <= 0;	 	
	  				co_2<=1;
	 			end
 		end
end 


//分的个位的实现
always @(posedge co_2,negedge SW1,posedge SW2)
begin
	if(!SW1)
 		dout3<=0;
 	else        //SW1打开才执行以下代码
 		begin 
 			if(SW2)  //SW2打开执行以下代码,即调分钟时
	  			begin
	    			if(xn<=9)  
	      				begin
	        				dout3<=xn;  //这时直接赋值
	        				co_3<=0;    //进位为0
	      				end
	    			else 
	    				begin
	        				dout3<=0;  //xn为10时,分的个位重新赋值为0
	        				co_3<=1;  //进位为1;
	      				end
	  			end
    		else   //SW2关闭执行以下代码,即不调分,正常计时
				begin
	    			if(dout3<9)
	     				begin
	     					dout3 <= dout3 +1;	
	     					co_3<=0;
	     				end
	   				else 
	    				begin
	      					dout3 <= 0;	 	
	      					co_3<=1;
	    				end
       			end	
  		end
 end


//如果要调分钟,引入变量xn(0-9)
reg [3:0] xn;
always @(posedge clk_1s,negedge SW1,negedge SW2)
begin
	if(!SW1)  //如果SW1关闭时钟复位
  		xn<=0;
	else      //SW1打开才执行以下代码
  		begin
			if(!SW2)//如果SW2关闭,即不调分钟时:xn不变
	 			xn<=xn;
			else    //SW2打开
	 			begin
	    			if(xn<=9) 
	      				xn <= xn +1;//此时xn进行计时
	    			else 
	      				xn <= 0;//xn为十时再重新赋值为0	 	
	   			end	
   		end
end
	
 
 //分的十位的实现
always @(posedge co_3,negedge SW1)
begin
	if(!SW1)
 		dout4<=0;
 	else
 		begin
   			if(dout4<5)
				begin
	 				dout4 <= dout4 +1;	
	  				co_4<=0;
				end
			else 
				begin
	  				dout4 <= 0;	 	
	  				co_4<=1;
	 			end
 		end	
 end
	
//时的实现
reg[5:0] xy;
reg x;      //是否进行调小时操作的标志
always @(posedge co_4,negedge SW1,posedge SW3)
begin
	if(!SW1)
 		dout5<=0;
 	else   //SW1打开执行以下代码
 		begin
   			if(SW3)  //SW3打开时执行下面代码,即要调时
				begin
	  				dout5<=xn1%10; //将xn1除以10取余赋值给时的个位
	  				dout6<=xn1/10;//将xn1除以10取整赋值给时的十位
	  				x<=1;   //此时x为1标志进行了调时
				end
   			else //SW3关闭执行以下代码
	  			begin
	    			if(x==1) //调时后
		   				begin
			 				x<=0;  //这时将x赋值为0
			  				if(xn1<23)
			    				xy<=xn1+1;//将这时的xy赋值为调时后的xn1再加1
			  				else
			    				xy<=0;//xn1==23时,xy赋值为0
						end
					else //调时后x又变为0,正常计时
		   				begin
			  				if(xy<23)
	          					xy<=xy+1;
		        			else
	          					xy<=0;
						end
       			dout5<=xy%10;//将xy除以10取余赋值给时的个位
		 		dout6<=xy/10;//将xy除以10取整赋值给时的十位
      			end
		end	
  end

//如果要调时,引入变量xn1(0-24)
reg [5:0] xn1;
always @(posedge clk_1s,negedge SW1,negedge SW3)
begin
	if(!SW1)
  		xn1<=0;
	else
  		begin
			if(!SW3)
	  			xn1<=xn1;
			else
	  			begin
	    			if(xn1<=22)
	      				xn1 <= xn1 +1;
	    			else 
	      				xn1 <= 0;	 	
	  			 end	
   		end
end

//调用数码管模块
smg smg_inst
(
	.clk(clk) ,	// input  clkin
	.d1(d1) ,	// input [4:0] d1
	.d2(d2) ,	// input [4:0] d2
	.d3(d3) ,	// input [4:0] d3
	.d4(d4) ,	// input [4:0] d4
	.d5(d5) , 	// input [4:0] d5
	.d6(d6) ,	// input [4:0] d6
	.d7(d7) ,	// input [4:0] d7
	.d8(d8) ,	// input [4:0] d8
	.sel(sel) ,	// output [7:0] sel
	.seg(seg) 	// output [7:0] seg
);


reg [3:0] d1, d2, d3, d4;//定义数码管显示 
reg [3:0] d5, d6, d7, d8;//定义数码管显示
always @(posedge clk)
begin
		//显示赋值
		d8 <= dout1; //秒的个位
		d7 <= dout2; //秒的十位 
		d5 <= dout3; //分的个位
		d4 <= dout4; //分的十位 
		d2 <= dout5; //时的个位
		d1 <= dout6; //时的十位 
end 				
		
endmodule

数码管模块

//
//8位数码管显示控制模块smg
//

module smg(clk, d1, d2, d3, d4, d5, d6, d7, d8, sel, seg);
input clk;
output reg [7:0] sel;
output reg [7:0] seg;	//a~g,dp
input [3:0] d1, d2, d3, d4; //d[7]-dp, d[6:0]-ASCII
input [3:0] d5, d6, d7, d8; //d[7]-dp, d[6:0]-ASCII 

//扫描频率:50Hz
parameter update_interval = 48000000 / 400 - 1; 
reg [4:0] dat; 
reg [2:0] cursel;
integer selcnt;

//扫描计数,选择位
always @(posedge clk)
begin
	selcnt <= selcnt + 1;
	if (selcnt == update_interval)
		begin
			selcnt <= 0;
			cursel <= cursel + 1;
		end
end
 
//切换扫描位选线和数据
always @(posedge clk)
begin
	case (cursel)
		3'b000: begin dat = d1; sel = ~8'b00000001; end
		3'b001: begin dat = d2; sel = ~8'b00000010;end
		3'b010: begin dat = d3; sel = ~8'b00000100; end
		3'b011: begin dat = d4; sel = ~8'b00001000; end
		3'b100: begin dat = d5; sel = ~8'b00010000; end
		3'b101: begin dat = d6; sel = ~8'b00100000;end
		3'b110: begin dat = d7; sel = ~8'b01000000; end
		3'b111: begin dat = d8; sel = ~8'b10000000; end
	endcase
end

//更新段码
always @(posedge clk)
begin
	seg[0] <= dat[4]; //dp
	case (dat[3:0])
		4'h00 	: seg[7:1] <= ~7'b0000001;	//0
		4'h01 	: seg[7:1] <= ~7'b1001111;	//1
		4'h02 	: seg[7:1] <= ~7'b0010010;	//2
		4'h03 	: seg[7:1] <= ~7'b0000110;	//3
		4'h04 	: seg[7:1] <= ~7'b1001100;	//4
		4'h05 	: seg[7:1] <= ~7'b0100100;	//5
		4'h06 	: seg[7:1] <= ~7'b0100000;	//6
		4'h07 	: seg[7:1] <= ~7'b0001111;	//7
		4'h08 	: seg[7:1] <= ~7'b0000000;	//8
		4'h09 	: seg[7:1] <= ~7'b0000100;	//9 
		4'h0a 	: seg[7:1] <= ~7'b0001000;	//a 
		4'h0b 	: seg[7:1] <= ~7'b1100000;	//b 
		4'h0c 	: seg[7:1] <= ~7'b1110010;	//c 
		4'h0d 	: seg[7:1] <= ~7'b1000010;	//d 
		4'h0e 	: seg[7:1] <= ~7'b0110000;	//e 
		4'h0f 	: seg[7:1] <= ~7'b0111000;	//f 
		default  : seg[7:1] <= ~7'b0110000; 	//E-rror
	endcase
end
	
endmodule

设置引脚

源代码需要配置引脚才能运行。

systemverilog时钟模块_编程语言