1、设计任务及要求:

设计任务:设计一台能显示时、分、秒的数字钟。
具体要求如下:
(1) 由实验箱上的时钟信号经分频产生秒脉冲;
(2) 计时计数器用 24 进制计时电路;
(3) 可手动校时,能分别进行时、分的校正;
(4) 整点报时;
设计要求:
(1) 采用 VHDL 语言描述系统功能,并在 QUARTUS II 工具软件中进行仿真,下 载到 EDA 实验箱进行验证。
(2) 编写设计报告,要求包括方案选择、程序代码清单、调试过程、测试结果 及心得体会。

2、设计原理

在这里插入图片描述
该系统由振荡器、分频器、“时、分、秒”计数器、译码器及显示器、校时 电路、整点报时电路等组成。石英晶体振荡器和分频器产生整个系统的时基信号, 它直接决定计时系统的精度。“秒计数器”采用六十进制计数器,每累计 60 秒向 “分计数器”进位;“分计数器”采用六十进制计数器,每累计 60 分向“时计数 器”进位;“时计数器”采用二十四进制计数器,按照“24 翻 1”规律计数。“时、 分、秒”计数器的输出经译码器送显示器显示。校时电路用来当计时出现误差时 对“时、分、秒”进行校对调整。整点报时电路是根据计时系统的输出状态产生 一脉冲信号,然后去触发音频发生器实现报时。

3、方案设计

3.1、 系统RTL图设计
1.分频模块
分频模块的输入信号为实验箱上的时钟信号,输出信号为秒计数器脉冲、按键扫描时钟、数码管扫描时钟。
2.按键处理模块
按键处理模块的输入信号为实验箱上的按键和分频模块输出的按键扫描时钟,输出信号为延时消抖之后的按键信号。
3.计数器模块
计数器模块分为秒计数器、分计数器和时计数器,秒计数器的输入信号为分频模块输出的秒脉冲,分计数器的输入信号为秒进位信号和按键信号,时计数器的输入信号为分进位信号和按键信号,计数器模块输出时、分、秒的个位和十位的BCD码。
4.整点报时模块
整点报时模块的输入信号为秒钟和分钟的BCD码,输出分蜂鸣器控制信号。系统RTL图如下图所示。
在这里插入图片描述
3.2 代码编写与调试
1.分频模块
对系统的时钟进行分频,设置不同长度的计数值,当系统时钟CLK有变化时计数器开始计数,当计数到某个值时输出一个信号,计数值不同输出信号的周期也就不同,从而实现了对系统时钟进行不同的分频,产生不同频率的信号。
2.按键处理模块
按键按下弹起电平会有一小段毛刺,可能会引起电路误操作,所以要对按键进行消抖处理使变为干净的矩形信号。抖动时间一般为10ms,若采样时间为10ms则噪声最多采样到一次。对于两级D触发器,Q1和Q2之间有一个时间延迟效果。每一次时钟上升沿就是对信号采样一次,Q1保存的是最新一次的采样,Q2保存的是上一次的采样,Q1和Q2之间有一个时间延迟,时间为时钟周期。
对于噪声信号只能采样到一次,Q1|Q2的值一定为1。对于持续时间大于时钟周期的信号可以采样到两次,Q1的值和Q2的值相同,Q1&Q2为信号电平。
3.计数器模块
时钟计数要具有时分秒的显示,且时分秒均需两位数码管计数,因此共需6个数码管。其中,小时采用24进制的BCD计数模块,分和秒采用60进制的BCD计数模块。秒计数器的输入信号为分频模块输出的秒脉冲,秒计数模块的进位信号可作为分计数模块的输入信号,分计数模块的进位信号可作为时计数模块的输入信号。
因此,秒计数模块可利用60进制计数器,通过分频器的输入实现从00到59的循环计数;分计数模块可利用60进制计数器,通过秒的进位信号的输入可实现从00到59的循环计数;小时计数模块可利用24进制计数器,通过分钟的进位信号的输入可实现从00到23的循环计数。
4.整点报时模块
对于整点报时模块,当分钟计数至59时来一个时钟脉冲则产生一个进位信号,分钟计数到00,此时产生报警信号。在该模块我们将蜂鸣器换成了LED灯,产生1秒的灯光闪烁,在产生一个进位信号分和秒计数到00是灯亮,在秒计数到01时灯灭,通过LED灯的亮灭表明是否是整点。

1、主程序:

-----------------------------------------------------
  --主程序 CLOCK.vhdl
----------------------------------------------------- 
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

ENTITY CLOCK IS
PORT(					CLK_IN,KEY_MIN,KEY_HOUR : IN STD_LOGIC;  -- 1Hz时钟信号输入
						--SEL_OUT : OUT STD_LOGIC_VECTOR(5 DOWNTO 0);
						--SEG_OUT : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
						SPEAKER_OUT : OUT STD_LOGIC;
					   C_H_SHI , C_H_GE , C_M_SHI , C_M_GE , C_S_SHI , C_S_GE : OUT STD_LOGIC_VECTOR(3 DOWNTO 0)
						);
END CLOCK;

ARCHITECTURE BHV OF CLOCK IS
   -- 调用秒钟计数模块声明
	COMPONENT SEC_BCD_COUNT  
	PORT(clk_to_sec: IN STD_LOGIC;
                            DATOUT_SHI,DATOUT_GE : OUT STD_LOGIC_VECTOR(3 DOWNTO 0);
									 C: OUT STD_LOGIC);
	END COMPONENT;
	-- 调用分钟计数模块声明
		COMPONENT MIN_BCD_COUNT  
	PORT(clk_to_min, ADD_MIN: IN STD_LOGIC;
                            DATOUT_SHI,DATOUT_GE : OUT STD_LOGIC_VECTOR(3 DOWNTO 0);
									 C: OUT STD_LOGIC);
	END COMPONENT;
	-- 调用小时计数模块声明
	COMPONENT HOUR_BCD_COUNT  
	PORT(clk_to_hour, ADD_HOUR: IN STD_LOGIC;
                            DATOUT_SHI,DATOUT_GE : OUT STD_LOGIC_VECTOR(3 DOWNTO 0)
									 );
	END COMPONENT;
	
	COMPONENT xiaodou   --按键消抖
	PORT(
			key: IN STD_LOGIC;
			clk_i:IN STD_LOGIC;
			DLY_OUT:OUT STD_LOGIC);
	END COMPONENT;
	

	
	COMPONENT DEV
	PORT(				  CLK_20MHz : IN STD_LOGIC;
					CLK_1Hz,  CLK_250Hz : OUT STD_LOGIC);
	END COMPONENT;
	
	COMPONENT buzz  --整点报时
	PORT(CLK: IN STD_LOGIC;
		M_SHI,M_GE,S_SHI,S_GE:IN STD_LOGIC_VECTOR(3 DOWNTO 0); 
		speaker:OUT STD_LOGIC);
	END COMPONENT;
	
	SIGNAL S_CO, M_CO : STD_LOGIC;  -- 计数进位
	SIGNAL S_M_SHI,S_M_GE,S_S_SHI,S_S_GE,S_H_SHI,S_H_GE : STD_LOGIC_VECTOR(3 DOWNTO 0);
	SIGNAL S_SET_MIN,S_SET_HOUR : STD_LOGIC;
	
	SIGNAL CLK_BUZZ, CLK_SCAN, CLK_SEC : STD_LOGIC;
	--SIGNAL N_BUZZ : STD_LOGIC_VECTOR(9 DOWNTO 0):="0000000010";  --蜂鸣器分频系数
	--SIGNAL N_KEY : STD_LOGIC_VECTOR(9 DOWNTO 0):="0000000100";   --按键扫描分频系数
	--SIGNAL N_LED_DISP : STD_LOGIC_VECTOR(9 DOWNTO 0):="0000001000";	--数码管扫描分频系数
	--SIGNAL N_SEC : STD_LOGIC_VECTOR(9 DOWNTO 0):="0000010000";	--1HZ分频系数
	
	BEGIN
		 --u_buzz_clk : ClkDiv PORT MAP(CLK_IN,N_BUZZ,CLK_BUZZ);
		 --u_key_clk : ClkDiv PORT MAP(CLK_IN,N_KEY,CLK_KEY);
		-- u_led_disp_clk : ClkDiv PORT MAP(CLK_IN,N_LED_DISP,CLK_LED_DISP);
		 --u_sec_clk :  ClkDiv PORT MAP(CLK_IN,N_SEC,CLK_SEC);
		 u_div : DEV PORT MAP(CLK_IN, CLK_SEC,  CLK_SCAN);
		 u_xiaodou_m : xiaodou PORT MAP(KEY_MIN, CLK_SCAN, S_SET_MIN);
		 u_xiaodou_h : xiaodou PORT MAP(KEY_HOUR, CLK_SCAN, S_SET_HOUR);
	    u_clock_sec : SEC_BCD_COUNT PORT MAP(CLK_SEC, S_S_SHI,S_S_GE,S_CO);  -- 秒钟
		 u_clock_min : MIN_BCD_COUNT PORT MAP(S_CO, S_SET_MIN, 
		 S_M_SHI,S_M_GE,M_CO);  -- 分钟
		 u_clock_hour : HOUR_BCD_COUNT  PORT MAP(M_CO, S_SET_HOUR, S_H_SHI,S_H_GE);  -- 小时
		 
		 --u_led_disp : LED_DISP PORT MAP(CLK_SCAN, S_H_SHI, S_H_GE, S_M_SHI, S_M_GE, S_S_SHI, S_S_GE, SEL_OUT, SEG_OUT);
		 u_buzz : buzz PORT MAP( CLK_SCAN,S_M_SHI, S_M_GE, S_S_SHI, S_S_GE,SPEAKER_OUT);
       --u4 : buzz   PORT MAP(buzz_CLK,S_M_SHI, S_M_GE,S_S_SHI,S_S_GE,speaker_out );  -- 小时   
		C_H_SHI<=S_H_SHI;C_H_GE<=S_H_GE;C_M_SHI<=S_M_SHI;C_M_GE<=S_M_GE;C_S_SHI<=S_S_SHI;C_S_GE<=S_S_GE;
END BHV;

2、报时模块:

-----------------------------------------------------
  --报时模块 buzz.vhdl
----------------------------------------------------- 
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

ENTITY buzz IS
PORT(
	 CLK : IN STD_LOGIC;
	 M_SHI,M_GE,S_SHI,S_GE:IN STD_LOGIC_VECTOR(3 DOWNTO 0);  
	 speaker:OUT STD_LOGIC);
END;

ARCHITECTURE BAV OF buzz IS
--SIGNAL EN:STD_LOGIC;
SIGNAL min1,min0,sec1,sec0:STD_LOGIC_VECTOR(3 DOWNTO 0);

BEGIN

	PROCESS(M_SHI,M_GE,S_SHI,S_GE)
	BEGIN
		IF(M_SHI = "0101" AND M_GE = "1001" AND S_SHI = "0101" AND S_GE = "1001") THEN
			speaker <= CLK;
		ELSIF(M_SHI = "0000" AND M_GE = "0000" AND S_SHI = "0000" AND S_GE = "0001") THEN
			speaker <= '0';

		END IF;
	END PROCESS;
END;

3、分频模块:

-----------------------------------------------------
  -- 分频模块 dev.vhdl
-----------------------------------------------------
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

ENTITY DEV IS
PORT(				  CLK_20MHz : IN STD_LOGIC;
					CLK_1Hz,  CLK_250Hz : OUT STD_LOGIC);
END;

ARCHITECTURE BHV OF DEV IS
SIGNAL Q1 : INTEGER RANGE 0 TO 49999999;
SIGNAL Q2 : INTEGER RANGE 0 TO 199999;
SIGNAL Q3 : INTEGER RANGE 0 TO 9999999;
BEGIN
	PROCESS(CLK_20MHz) BEGIN
		IF CLK_20MHz'EVENT AND CLK_20MHz = '1' THEN
		   -- 1Hz
			IF Q1 < 10000000		THEN CLK_1Hz <= '0'; Q1 <= Q1 + 1;
			ELSIF Q1 < 19999999		THEN CLK_1Hz <= '1'; Q1 <= Q1 + 1;
			ELSE Q1 <= 0;
			END IF;
			-- 250Hz
			IF Q2 < 40000		THEN CLK_250Hz <= '0'; Q2 <= Q2 + 1;
			ELSIF Q2 < 79999		THEN CLK_250Hz <= '1'; Q2 <= Q2 + 1; 
			ELSE Q2 <= 0;
			END IF;
		END IF;
	END PROCESS;
END;

4、秒计时模块:

-----------------------------------------------------
 -- 秒计时模块 sec_bcd_count.vhdl
-----------------------------------------------------
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY SEC_BCD_COUNT IS
PORT(clk_to_sec : IN STD_LOGIC;
DATOUT_SHI,DATOUT_GE :OUT STD_LOGIC_VECTOR(3 DOWNTO 0);
			C: OUT STD_LOGIC);
END SEC_BCD_COUNT;
ARCHITECTURE BHV OF SEC_BCD_COUNT IS
	SIGNAL COUNT_SHI, COUNT_GE : STD_LOGIC_VECTOR(3 DOWNTO 0);
	BEGIN 
	PROCESS(clk_to_sec)  	BEGIN
		
			IF clk_to_sec'EVENT AND clk_to_sec = '1'    THEN
				IF COUNT_SHI = "0101" AND COUNT_GE = "1001"		THEN COUNT_SHI <= "0000"; COUNT_GE <= "0000";C<='1';  -- 
				ELSIF COUNT_GE < "1001"		THEN COUNT_GE <= COUNT_GE + 1;C<='0';
				ELSE COUNT_GE <= "0000"; COUNT_SHI <= COUNT_SHI + 1;C<='0';
				END IF;
			END IF;
	DATOUT_GE <=  COUNT_GE;  
	DATOUT_SHI <= COUNT_SHI;
	END PROCESS;
END BHV;

5、分计数模块:

-----------------------------------------------------
 -- 分计数模块 min_bcd_count.vhdl
-----------------------------------------------------
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

ENTITY MIN_BCD_COUNT IS
PORT(clk_to_min, ADD_MIN: IN STD_LOGIC;
     DATOUT_SHI,DATOUT_GE :OUT STD_LOGIC_VECTOR(3 DOWNTO 0);
		  C: OUT STD_LOGIC);
END MIN_BCD_COUNT;

ARCHITECTURE BHV OF MIN_BCD_COUNT IS
	SIGNAL COUNT_SHI, COUNT_GE : STD_LOGIC_VECTOR(3 DOWNTO 0);
	SIGNAL CLK : STD_LOGIC;
	BEGIN 
	CLK <= clk_to_min OR (NOT ADD_MIN);
	PROCESS(CLK)  	BEGIN
IF CLK'EVENT AND CLK = '1'    THEN
			IF COUNT_SHI = "0101" AND COUNT_GE = "1001"		THEN COUNT_SHI <= "0000"; COUNT_GE <= "0000";C<='1';  浂
				ELSIF COUNT_GE < "1001"		THEN COUNT_GE <= COUNT_GE + 1;C<='0';
				ELSE COUNT_GE <= "0000"; COUNT_SHI <= COUNT_SHI + 1;C<='0';
				END IF;
			END IF;
	DATOUT_GE <=  COUNT_GE; 
	DATOUT_SHI <= COUNT_SHI;
	END PROCESS;
END BHV;

6、小时计数模块:

-----------------------------------------------------
 -- 小时计数模块 hour_bcd_count.vhdl
-----------------------------------------------------
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;

ENTITY HOUR_BCD_COUNT IS
PORT(clk_to_hour, ADD_HOUR: IN STD_LOGIC;
                            DATOUT_SHI,DATOUT_GE : OUT STD_LOGIC_VECTOR(3 DOWNTO 0));
END HOUR_BCD_COUNT;
ARCHITECTURE BHV OF HOUR_BCD_COUNT IS
	SIGNAL COUNT_SHI, COUNT_GE : STD_LOGIC_VECTOR(3 DOWNTO 0);
	SIGNAL CLK : STD_LOGIC;
BEGIN 
	CLK <= clk_to_hour OR (NOT ADD_HOUR);
	PROCESS(CLK)  	BEGIN
		
			IF CLK'EVENT AND CLK = '1'    THEN
			
				IF COUNT_SHI = "0010" AND COUNT_GE = "0011"		THEN COUNT_SHI <= "0000"; COUNT_GE <= "0000";  -- 24进制溢出清零
				ELSIF COUNT_GE < "1001"		THEN COUNT_GE <= COUNT_GE + 1;
				ELSE COUNT_GE <= "0000"; COUNT_SHI <= COUNT_SHI + 1;
				END IF;
			END IF;
		
	DATOUT_GE <=  COUNT_GE;  -- 数据输出
	DATOUT_SHI <= COUNT_SHI;
	END PROCESS;
END BHV;

7、按键消抖模块:

-----------------------------------------------------
 -- 按键消抖模块 xiaodou.vhdl
-----------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_UNSIGNED.all;
entity xiaodou is	
	port(
	key: IN STD_LOGIC;
	clk_i:IN STD_LOGIC;
	DLY_OUT:OUT STD_LOGIC);
end xiaodou;
architecture rtl of xiaodou is
signal D1,D2,S:STD_LOGIC;
begin
	process(clk_i)
	begin
		if (clk_i'EVENT and clk_i='1')then
			D1<=key;
			D2<=D1;
		end if;
	end process;	
	S<=D1 or D2;	
	DLY_OUT<=S;
end rtl;

4、系统时序仿真与分析

4.1、时序仿真的步骤与方法
(1)确认Quartus II中的仿真工具是否指向Modelsim所在路径
(2)打开波形仿真器
(3)设置波形仿真时间区域
(4)波形文件存盘
(5)将工程CLOCK的端口信号节点选入波形编辑器中
(6)设置激励信号波形
(7)启动仿真器
(8)观察仿真结果

4.2、仿真波形与分析
(1)秒时序仿真如图:
在这里插入图片描述
由上图可知:一个时钟脉冲,秒模块输出就加一,刚好十进制计满6次,当秒模块输出达到60,产生一个大小为1的进位,并立刻清零。重新等待时钟脉冲,重复十进制计数6次,产生进位。符合设计要求。

(2)分时序仿真如图:
在这里插入图片描述

由上图可知:一个时钟脉冲,分模块输出就加一,刚好十进制计满6次,当分模块输出达到60,产生一个大小为1的进位,并立刻清零。重新等待时钟脉冲,重复十进制计数6次。同时,设置了一个分校正模块,可以对分模块进行设置。从仿真波形来看,符合设计要求。

(3)时时序仿真如图:
在这里插入图片描述

由上图可知:一个时钟脉冲,时模块输出就加一,刚好两个十进制和一个四进制一共计满24次,当时模块输出达到24,不会产生进位,但会等待来自分模块的下一个进位,若此时输入进位信号,时模块立刻清零。重新等待时钟脉冲,重复计满24次。同时,设置了一个时校正模块,可以对时模块进行设置。从仿真波形来看,符合设计要求。

(4)按键消抖时序仿真如图:
在这里插入图片描述
由上图可知,虽然按键输入的信号波形产生了抖动,但是经过按键消抖模块,输出被延时,同时消除了抖动,使得系统更加稳定。符合设计要求。

(5)整点报时时序仿真如图:
在这里插入图片描述
由上图可知,当分模块显示59,并且秒模块也显示59时,此时不会整点报时,继续输入时钟脉冲,当分模块变为00,秒模块变为00,此时刚好是整点,当秒模块从00变为01之前,启动报时。符合设计要求。

(6)总的时序仿真如图所示:
在这里插入图片描述
由上图可知,这是将所有模块一起运行信号放在一起观测的时序仿真图,容易发现所有模块一起工作时,信号与单独仿真时具有一样的功能,因此,符合设计要求。

5、硬件下载与测试

本次我们使用的硬件是Cyclone Ⅲ系列的EP3C10E144C8芯片,Cyclone Ⅲ是一款65nm低成本FPGA,利用TSMC的65nm低功耗(LP)工艺,Cyclone III FPGA提供丰富的逻辑、存储器和DSP功能,功耗更低。在可编程逻辑发展历史中,Cyclone III FPGA比其他低成本FPGA系列能够支持实现更多的应用。

5.1、引脚设置如下:

在这里插入图片描述

5.2、实验测试结果:

在这里插入图片描述

项目中的VHDL程序工程文件已经放在下面公众号里面,可以关注公众号:Kevin的学习站,输入关键字:“FPGA 数字电子时钟”,就可以免费获取啦!

在这里插入图片描述

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐