本实验设计一个能够显示时、分、秒钟的数字时钟,时间在7段数码管上显示。通过开发板上的按键调整数字时钟的时间,分别用四个键来控制分、时的增减。各按键及数码管功能要求如表12-1所示。

表12-1 按键及数码管功能要求

数码管L13、L14秒钟显示reset时钟复位
数码管L12,L11分钟显示S3,S7小时加减
数码管L10,L9小时显示S6,S10分钟加减

按下S1,则时钟复位;L13,L14显示秒;按下键盘区的S3键,L12即分钟数增1,同时数码管L7和L8显示S3所在的行列值。同样,按下S7为分钟数减1,S6为小时数增1,数码管L10上的数字相应增1,S10为小时数减1。



本实验的原理较为简单(图12 1),设计一个时钟产生模块,产生周期为1s的计时时钟和周期为4ms键盘扫描时钟。周期为1s的时钟触发“秒”计时,计时到59发生分钟的进位,并触发分钟的计时,同样分钟计时到59发生小时的进位,并触发小时的计时。通过键盘上的某些键可以手动设置小时和分钟,时间和键值都会在数码管上显示。键盘键值的识别和数码管的显示参看《键盘扫锚及数码管实验》。 zzcbook_digitalclock.png

图12-1 数字时钟原理示意图

3.1 总体架构

程序由以下模块组成(图 12 2):
1. 顶层模块Digitalclk将各子模块实例化并将各模块连接起来。 2. clkgen模块产生扫描键盘所要求的周期为4ms的脉冲信号和计数需要的周期为1秒钟的脉冲信号。
3. scan和前面的键盘扫描与数码管显示实验的一样,负责键盘的扫描识别。
4. scan模块扫描到的键盘列值和行值送给Timeset模块。Timeset模块用来识别手动时间设置的模式,如按下键盘S3,则表示小时加1。
5. Time模块是本程序的核心模块,实现一个支持时分秒的时钟。
6. data2disp模块和dispscan也与前面的键盘扫描与数码管显示实验的一样。这里用到六个data2_disp模块, disp1,disp0显示秒针;disp3,disp2显示分钟;disp5,disp4显示小时。

图12-2 程序总体框图

图12-2 程序总体框图

3.2 Digital_clk模块(digital_clk.v)

Digital_clk模块为顶层模块,输入输出接口如下:

 
input	clk,reset;			//时钟和复位信号。
input 	[3:0]kr;			//扫描键盘行输入。	
output 	[3:0]kc;			//扫描键盘列输出。					
output	[7:0]HEX_sel;		//数码管显示选通信号,每一位选通一个数码管。
output	[7:0]HEX_seg;		//数码管译码显示的内容。

3.3 one_sec模块(one_sec.v)

One_sec产生以1s为周期的时钟和以4ms为周期的键盘扫描时钟,输入输出接口如下:

 
input	clk, reset;				//时钟和复位信号。
output 	one_sec_clk;		//周期为1s的时钟输出
output	scan_clk;		    //周期为4ms的键盘扫描时钟输出

3.3 Time_set模块(Time_set.v)

Time_set实现对时钟小时和分钟的手动设置,输入输出接口如下:

 
	input [3:0] add_r;         //按键行地址
	input [3:0] add_c;         //按键列地址
	output [3:0] set;          //手动手动设置时间信号	
Time_set是一个组合逻辑模块,通过按键值来决定时间的调整方式。定义set来指示是何种调整方式,set有四种取值,如下列代码所示:
always @(r_reg or c_reg)
		begin
			if(r_reg == 4'b0001)
				begin
					if(c_reg == 4'b0100)		//按下S3分钟加1
						set = 4'b0010;
					else if(c_reg == 4'b0001)	//按下S6小时加1
						set = 4'b0001;
					else
						set = 4'b0000;
					end
			else if(r_reg == 4'b0010)
				begin
					if(c_reg == 4'b0100)		//按下S7分钟减1
						set = 4'b1000;
					else if(c_reg == 4'b0001)	//按下S10小时减1
						set = 4'b0100;
					else
						set = 4'b0000;
					end
			else
					set = 4'b0000;	
			end
endmodule

3.5 Time模块(Time.v)

Time模块控制数字时钟的“时”、“分”及“秒”,模块输入输出接口如下:

 
input	clk, reset;			//时钟和复位信号。
input	en_sec;				//输入周期为1s的时钟	。
input 	[3:0]set;			//手动时间设置控制输入。
output 	[7:0]sec;			//输出秒。
output 	[7:0]minu;			//输出分钟。
output 	[7:0]hour;			//输出小时。

以下以小时位的增减来说明如何控制“时”“分”“秒”。
当键盘有控制键按下时表示需要手动调节分钟和小时,如set=0001,说明输入的按键是S6,则en_h_manu赋值为1。

 
if(keyout_en)   //键盘输入使能信号为高时,set输入有效
    case(set)//手动控制分钟与小时的增减		
        4'b0001: begin	
             en_m_manu <= 0;	en_h_manu <= 1;	de_m <= 0; de_h <= 0;
        end//手动小时增1
        //四个信号的含义为:
        // en_h_manu为高时表示小时手动调整,de_h为高时表示减1,为低时表示加1。
        // en_m_manu为高时表示分钟手动调整,de_m为高时表示减1,为底时表示加1。
	4’b0010: begin	
            en_m_manu <= 1;	en_h_manu <= 0;	de_m <= 0; de_h <= 0;
        end//手动分钟增1。
        4'b0100:  begin	
            en_m_manu <= 0;	en_h_manu <= 0;	de_m <= 0; de_h <= 1;
        end//手动小时减1。
	4'b1000: begin	
            en_m_manu <= 0;	en_h_manu <= 0;	de_m <= 1; de_h <= 0;
        end//手动分钟减1。
        default:  begin	
            en_m_manu <= 0;	en_h_manu <= 0; de_m <= 0; de_h <= 0;
        end  
    endcase

当分钟位计时到59时,产生一个小时位自动增加变量enhauto置为1。
信号enmmanu和enmauto分别控制手动和自动分钟调整,定义信号enm表示分钟需要调整:
assign en
m = enmmanu | en_m_auto;

下列代码判断分钟是否需要向小时进位,如果分钟需要调整且分钟计数值到59,则需要向小时进位:

 
always @(en_m or COUNTERm)   // COUNTERm表示分钟计数。
    begin
        en_h_auto <= 0;
	if(en_m)     //分钟需要调整。
		begin
	        if(COUNTERm == 6'b11_1011)//计数到59。
		    en_h_auto <= 1; //en_h_auto表示小时需要自动调整。
		else
		    en_h_auto <= 0;
		end
		else
		     en_h_auto <= 0;
    end

信号enh 表示小时是否需要增加,enh = enhmanu | enhauto。即无论是用按键手动控制还是分钟自动产生进位信号,小时数都要增加1。小时是以24为循环所以当计数到23时,小时计数COUNTERh置为0。当按键产生小时减少信号de_h时,小时数COUNTERh减1:

 
always@( posedge clk or  negedge reset) begin: calculate_hourur
    if (!reset)
        COUNTERh <= 5'b0;
    else begin 
        if(en_h) begin
            if(COUNTERh == 5'b1_0111)//计数到23
                COUNTERh <= 5'b0;
	    else
		COUNTERh <= COUNTERh+1'b1;小时加1
	  end
        else if(de_h)          //小时位减1信号
	    begin
		if(COUNTERh == 5'b0_0000)//当小时位为0时,减1
			COUNTERh <= 5'b1_0111;//小时位变为23
		 else 
			COUNTERh <= COUNTERh-1'b1;
            end
	end					
    end

因为是用两位十进制来显示小时数,所以需要把二进制数COUNTERh转换成十进制表示。hour[7:0]的前四位为十位,后四位为个位。

assign   hour[7:4] = COUNTERh/10;//小时十位数
assign   hour[3:0] = COUNTERh%10; //小时个位数

(5)scan模块,data2disp模块和disp_scan与键盘扫描与数码管显示实验中的一样,请参考键盘扫描实验。



图12-3 增大小时值

图12-3 增大小时值

在图12 3中的黄色线处:set=0001,此时增加小时,所以置enhmanu为高,enh是enhmanu和enhauto逻辑“或”的结果,所以此时enh也为高电平,使hour的值加1;
红色线处:set=0010,此时增加分钟,所以置enmmanu为高,enm是enmmanu和enmauto或的结果,所以此时enm也为高电平,使minu的值加1。

图12-4 减小分钟值

图12-4 减小分钟值

在图12-4中的紫色线处:set=1000,此时减小小时的值,所以置de_h为高,使hour的减1,从01变到00;

图12-5 减小分钟值

图12-5 减小分钟值

图 12 5是在没有调整时间情况下,在sec=59且ensec=1时置enmauto为高电平,使分钟加1;在minu=59且enm=1时置en_h_auto为高电平,使小时加1。
在上面三个图中sec、minu、hour的显示格式都是16进制形式。



文件名功能
DigitalClk.v|主程序| |onesec.v产生1s周期时钟和键盘扫描时钟
clock.v数字钟
scan.v键盘扫描
dispscan.v|数码管扫描| |data2_disp.v数码管译码



演示设备要求:核心板,扩展板。

演示方法:下载后,可以看到数码管L14 由零开始计数,L13,L14为秒数显示,按下键盘区的S3键,可以观察到L12即分钟数增1,同时数码管L7L8显示14,即S3所在的行列值。同样,按下S7键为分钟数减1,S6键为小时数增1,数码管L10上的数字会相应增1,S10键为小时数减1。所按键的行列值都会在L7L8上显示。核心板Reset键为复位键。