FPGA学习——基于Verilog实现的多功能时钟

FPGA基于Verilog实现的多功能时钟

时钟实现的功能:
1.数码管显示时间
2.有计时功能
3.可实现定点报时

多功能时钟共两种工作状态:
1.正常的时钟显示时间
2.计时状态

由于部分原因,本例中调节时间的功能并没有添加,程序大体功能完整,可能出现少许BUG,可以做个参考,望知。
实现资源:

  1. FPGA开发板一块,包括: 六位八段数码管(用于显示时间)、蜂鸣器(用于定点报时)、 四位按键输入
  2. 开发板采用50Mhz晶振产生时钟

变量定义

一共有5个输入,包括四个按键及一个时钟输入
3个输出,包括位、段数码管及蜂鸣器

module multfun_clock(clk,key1,key2,key3,key0,beep,dxuan, wxuan,count,divclk,wei_cnt,disp_state );
input   clk;
input   key1;
input   key2;
input   key3;
input   key0;

output reg [7:0] dxuan;
output reg [5:0] wxuan;
output reg beep;	
 reg [25:0] count;
 reg divclk; //分频产生1次/秒的脉冲用于时钟计数
 reg [4:0] cnth,cnthh;
 reg [5:0] cntd,cntdd;
 reg [5:0] cnts,cntss;
 reg [1:0] kstate; 
//******************数码管数值定义*********************************//
parameter disp0=8'b1100_0000;
parameter disp1=8'b1111_1001;
parameter disp2=8'b1010_0100;
parameter disp3=8'b1011_0000;
parameter disp4=8'b1001_1001;
parameter disp5=8'b1001_0010;
parameter disp6=8'b1000_0010;
parameter disp7=8'b1111_1000;
parameter disp8=8'b1000_0000;
parameter disp9=8'b1001_0000;

按键的消抖

利用脉冲的递送产生的上升沿来控制时钟状态转换

reg  low_g;
reg  low_g_r;
reg  d1;
reg  [19:0]  cnt;
 always @ (posedge clk )
 if (cnt == 20'hfffff)  low_g <= key3;
 else cnt <= cnt + 1'b1;
 
always @ (posedge clk  )
  low_g_r <= low_g;
  
wire  led_ctr = low_g_r & (~low_g);

always@(posedge clk)
 begin
   if(led_ctr==0)  //按键3用来切换时钟、计数器与调节时钟状态
	 begin
	  if(kstate>2'b10)
	    kstate<=2'b00;
	  else 
	    kstate<=kstate+2'b01;
	 end
	else kstate<=kstate;
 end

分频

该always语句用于产生时钟所用脉冲,将50Mhz脉冲分频,计数2500_0000次即为1秒钟时间。50Mhz脉冲每个脉冲为20ns,1秒钟需要5000_0000个脉冲,那么产生的分频信号应该计数至2500_0000就翻转一次

always@(posedge clk)   //时钟分频
  begin
    if(count<=26'd2500_0000)  count<=count+26'd1; 
	 else    
	      begin
	      divclk=~divclk;
			count<=26'd0;
			end  	
  end

时钟模块

无论当前多功能时钟计数状态为什么,时钟模块永远处于工作状态,按秒、分、时依次进位

always@(posedge divclk)
  begin
    if(cnts<=6'd58)      
       cnts=cnts+6'd1;
	 else 
	   begin            //计时60秒,秒计数清零,分计数加1
	    cnts<=6'd0;
		  if(cntd<=6'd58)
		    cntd<=cntd+6'd1;
		  else
		    begin        //计数60分,分计数清零,时计数加1
		     cntd<=6'd0;
			  if(cnth<=5'd22)
			    cnth<=cnth+5'd1;
			  else 
			    cnth<=5'd0;  //计数24时,时计数清零
			 end
		end
  end

计时模块

本例中计时最大限度设为2小时,可清零

 always@(posedge divclk)
  if(kstate==2'b01)  begin//切换到计时
	     if(key0==1'b0)  //开关0清零
		      begin
		       cntss<=0;
				 cntdd<=0;
				 cnthh<=0;
			   end
	    else if(key1==1'b0) begin  //复位端无效
		           if(cntss<=6'd59)  cntss=cntss+6'd1;
	              else begin            //计时60秒,秒计数清零,分计数加1
	                       cntss<=6'd0;
		                    if(cntdd<=6'd59) cntdd<=cntdd+6'd1;
		                    else begin        //计数60分,分计数清零,时计数加1
		                         cntdd<=0;
			                      if(cnthh<=5'd31) cnthh<=cnthh+5'd1;
			                      else cnthh<=5'd0; 
					                end  //计数2小时,时计数清零   
		                 end
	                       end
		 else begin
		      cntss<=cntss;
				cntdd<=cntdd;
				cnthh<=cnthh; 
				end		
end

时间显示模块

cnt_symin用于选择八段数码管的输入显示

  case(cnt_symin)

显示分为时钟显示和计时显示,根据状态不同进行切换

 if(kstate==2'b01) begin ……
 else begin ……

下面为该模块所有代码:

reg [15:0] wei_cnt;
reg [ 2:0] disp_state;
reg [ 7:0] cnt_symin;
reg [ 7:0] cnt_symout;

always@(posedge clk)  //位数码管切换
 begin
   if(wei_cnt<=16'd5_0000) begin
	   disp_state<=disp_state; 
		wei_cnt<=wei_cnt+12'd1; end
	else begin
	  wei_cnt<=16'd0;
	  if(disp_state==3'd5) disp_state<=3'd0;
	  else disp_state<=disp_state+3'd1;
	  end
 end
  
always@(posedge clk)   //段数码管切换
 begin
  case(cnt_symin)
    1:dxuan<=disp1;
	 2:dxuan<=disp2;
	 3:dxuan<=disp3;
	 4:dxuan<=disp4;
	 5:dxuan<=disp5;
	 6:dxuan<=disp6;
	 7:dxuan<=disp7;
	 8:dxuan<=disp8;
	 9:dxuan<=disp9;
	 0:dxuan<=disp0;
	 default:dxuan<=disp0;
	 endcase
 end

always@(posedge clk)
  begin
   if(kstate==2'b01) begin
	   case(disp_state)
		0:begin wxuan<=6'b011_111;cnt_symin<=cnthh/10; end
		1:begin wxuan<=6'b101_111;cnt_symin<=cnthh%10; end
	   2:begin wxuan<=6'b110_111;cnt_symin<=cntdd/10; end
		3:begin wxuan<=6'b111_011;cnt_symin<=cntdd%10; end
	   4:begin wxuan<=6'b111_101;cnt_symin<=cntss/10; end
		5:begin wxuan<=6'b111_110;cnt_symin<=cntss%10; end
	   default:wxuan<=6'b111_111;
	   endcase	
	   end
   else begin
	     case(disp_state)
		0:begin wxuan<=6'b011_111;cnt_symin<=cnth/10; end
		1:begin wxuan<=6'b101_111;cnt_symin<=cnth%10; end
	   2:begin wxuan<=6'b110_111;cnt_symin<=cntd/10; end
		3:begin wxuan<=6'b111_011;cnt_symin<=cntd%10; end
	   4:begin wxuan<=6'b111_101;cnt_symin<=cnts/10; end
		5:begin wxuan<=6'b111_110;cnt_symin<=cnts%10; end
	   default:wxuan<=6'b111_111;
	   endcase	
	   end 
  end

定点提醒

这个模块因为某些原因并没有实现功能,可做参考

reg [15:0] beep_cnt;
always@(posedge clk)
 begin
   if(cnts==6'd58) begin         //59秒时开始提醒
      if(beep_cnt==16'd1000) begin        //如果计时等于0.01秒,则停止蜂鸣
		    beep<=0;
			 beep_cnt<=0; end
		else begin
		   beep<=~beep;           //否则一直蜂鸣直到计时结束
			beep_cnt<=beep_cnt+16'd1;
			  end
		end
	else  beep=0;
 end

【注】:个人学习记录,如有错误,望不吝赐教
邮箱:[email protected]

猜你喜欢

转载自blog.csdn.net/zsisinterested/article/details/115116992