一、设计任务与要求
设计一个数字时钟系统,该系统应包含准确的计时、直观的显示、方便的校时和复位功能。采用EDA自顶向下的设计方法,在Quartus II环境中,结合原理图和VHDL文本混合输入,完成数字时钟系统的整体设计、各功能模块的程序编写、仿真验证,并在FPGA实验开发板上实现。系统需实现时、分、秒的计时,通过6个LED七段数码管显示时间,支持通过按键进行小时和分钟的单独调整,以及通过按键进行系统复位。
二、数字钟系统设计分析
在数字钟系统设计中,我们采用了自顶向下的方法,将系统划分为多个子模块以实现所需功能。主要模块设计概述如下:
1、分频器模块:用于降低时钟频率,生成1Hz的时钟信号和1KHz的刷新信号,分别用于时间计量和数码管刷新。
2、信号分配模块:负责将1Hz时钟信号和复位信号分发至秒、分、时计时器,确保同步计时和复位。
3、计时器模块:采用24进制和60进制计数器分别记录小时、分钟和秒数,通过进位信号实现连续计时。
4、数据选择器与消抖模块:选择时间调整模式,并使用D触发器型电路消除按键抖动,确保输入信号的稳定性。
5、数码管译码器模块:将计时器的二进制输出转换为数码管可显示的编码,并行输出到显示模块。
6、显示与提醒模块:通过1KHz刷新信号驱动数码管动态显示时间,同时实现闹钟提醒功能,当时间达到预设值时触发蜂鸣器。
7、模块整合:通过原理图设计连接各模块,优化信号传输路径,形成完整的数字钟系统。
三、管脚配置及程序下载硬件验证
1、管脚配置
在FPGA设计流程中,管脚配置是确保设计正确映射到物理硬件的关键步骤。针对选用的FPGA开发板型号(EP4CE10F17C8),进行了精确的管脚分配。
(1)了解开发板资源:查阅FPGA开发板的用户手册文档,详细了解了开发板上的各种资源。根据数字钟系统的设计需求,我们规划了所需使用的IO引脚,包括用于连接数码管显示的时间显示引脚、用于接收按键输入的引脚等。
(2)Quartus II环境下的管脚分配:在明确资源规划后,打开了Quartus II软件,在项目中进行了管脚配置。通过软件提供的图形化界面,逐一将VHDL代码中定义的信号与实际硬件的引脚进行对应。
2、程序下载硬件验证
在完成管脚配置后,将VHDL代码编译生成的二进制文件(如“MARQUEE.sof”)通过FPGA开发板的下载工具(如USB-Blaster)下载到FPGA开发板中,进行硬件验证。
(1)时间显示验证:在正常运行状态下,观察数字钟是否能够准确无误地显示当前的时间,包括小时、分钟和秒数。通过长时间运行和多次观察,验证了时间显示的准确性,确保每个时间单位的更新都是准确无误的。
(2)复位功能验证:按下“复位”键,数字钟迅速且完全地重置为初始状态。这时所有时间单位(小时、分钟、秒)都归零。当松开“复位”键时,系统从初始时间开始计时。
(3)闹钟功能验证:在闹钟功能验证环节,设定了特定的时间(如“00:00:00”和“12:00:00”)作为触发蜂鸣器响起的条件。当达到这些特定时间时,观察到蜂鸣器按预期响起2秒,从而验证了闹钟功能的实现。
(4)时间调分功能验证:按下“调分”键。当按下“调分”键时,数字钟的分钟显示将按照预设的1Hz速度递增。当松开“调分”键,时间又按照正常时间开始进行计时。
(5)时间调时功能验证:按下“调时”键。当按下“调时”键时,数字钟的小时显示将按照预设的1Hz速度递增。当松开“调时”键,时间又按照正常时间开始进行计时。
三、代码与原理图连接
1、CLOCKDIVIDER:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY CLOCKDIVIDER IS
PORT (
CLK_IN : IN STD_LOGIC;
CLK_1KHZ : OUT STD_LOGIC;
CLK_1HZ : OUT STD_LOGIC
);
END CLOCKDIVIDER;
ARCHITECTURE BEHAVIORAL OF CLOCKDIVIDER IS
SIGNAL COUNT_1KHZ : INTEGER RANGE 0 TO 24999 := 0;
SIGNAL COUNT_1HZ : INTEGER RANGE 0 TO 24999999 := 0;
SIGNAL TEMP_1KHZ : STD_LOGIC := '0';
SIGNAL TEMP_1HZ : STD_LOGIC := '0';
BEGIN
PROCESS(CLK_IN)
BEGIN
IF CLK_IN = '1' AND CLK_IN'EVENT THEN
IF COUNT_1KHZ = 24999 THEN
COUNT_1KHZ <= 0;
TEMP_1KHZ <= NOT TEMP_1KHZ;
ELSE
COUNT_1KHZ <= COUNT_1KHZ + 1;
END IF;
END IF;
END PROCESS;
CLK_1KHZ <= TEMP_1KHZ;
PROCESS(CLK_IN)
BEGIN
IF CLK_IN = '1' AND CLK_IN'EVENT THEN
IF COUNT_1HZ = 24999999 THEN
COUNT_1HZ <= 0;
TEMP_1HZ <= NOT TEMP_1HZ;
ELSE
COUNT_1HZ <= COUNT_1HZ + 1;
END IF;
END IF;
END PROCESS;
CLK_1HZ <= TEMP_1HZ;
END BEHAVIORAL;
2、MUX_TWO_ONE:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY MUX_TWO_ONE IS
PORT(
KEY : IN STD_LOGIC; -- 选择信号
CLK_1HZ : IN STD_LOGIC; -- 1HZ时钟输入
CLK_QOUT : IN STD_LOGIC; -- 时钟输入
CLK_OUT : OUT STD_LOGIC -- 输出时钟
);
END MUX_TWO_ONE;
ARCHITECTURE MUX OF MUX_TWO_ONE IS
SIGNAL D1 : STD_LOGIC:='1';
SIGNAL D2 : STD_LOGIC:='1';
SIGNAL D3 : STD_LOGIC:='1';
SIGNAL OUT_KEY : STD_LOGIC;
BEGIN
P1: PROCESS(CLK_1HZ)
BEGIN
IF CLK_1HZ'EVENT AND CLK_1HZ = '1' THEN
D1<=KEY;
D2<=D1;
D3<=D2;
END IF;
OUT_KEY<=D1 OR D2 OR D3;
END PROCESS P1;
P2:PROCESS(OUT_KEY)
BEGIN
IF OUT_KEY = '0' THEN
CLK_OUT <= CLK_1HZ;
ELSE
CLK_OUT <= CLK_QOUT;
END IF;
END PROCESS P2;
END MUX;
3、SPLITTER_ONE_THREE:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY SPLITTER_ONE_THREE IS
PORT(
INPUT : IN STD_LOGIC; -- 输入
OUTPUTONE : OUT STD_LOGIC; -- 输出1
OUTPUTTWO : OUT STD_LOGIC; -- 输出2
OUTPUTTHREE : OUT STD_LOGIC -- 输出3
);
END SPLITTER_ONE_THREE;
ARCHITECTURE SPLITTER OF SPLITTER_ONE_THREE IS
BEGIN
PROCESS(INPUT)
BEGIN
IF INPUT='1' THEN
OUTPUTONE<='1';
OUTPUTTWO<='1';
OUTPUTTHREE<='1';
ELSE
OUTPUTONE<='0';
OUTPUTTWO<='0';
OUTPUTTHREE<='0';
END IF;
END PROCESS;
END SPLITTER;
4、clock_display_24:
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity clock_display_24 is
Port (
seconds : in std_logic_vector(4 downto 0); -- 输入的秒数(0-59)
seg_ten : out std_logic_vector(6 downto 0); -- 十位数码管段
seg_one : out std_logic_vector(6 downto 0)); -- 个位数码管段
end clock_display_24;
architecture Behavioral of clock_display_24 is
-- 七段数码管译码函数
function seven_seg_decode(num : integer range 0 to 9) return std_logic_vector is
variable seg : std_logic_vector(6 downto 0);
begin
case num is
when 0 => seg := "1000000"; -- A到G(DP省略)
when 1 => seg := "1111001";
when 2 => seg := "0100100";
when 3 => seg := "0110000";
when 4 => seg := "0011001";
when 5 => seg := "0010010";
when 6 => seg := "0000010";
when 7 => seg := "1111000";
when 8 => seg := "0000000";
when 9 => seg := "0010000";
when others => seg := "1111111"; -- 错误指示
end case;
return seg;
end function;
-- 声明变量来存储十位和个位的值
signal ten_val, one_val : integer range 0 to 9;
begin
process(seconds)
begin
-- 计算十位
ten_val <= to_integer(unsigned(seconds)) / 10;
-- 调用译码函数并更新输出
seg_ten <= seven_seg_decode(ten_val);
end process;
process(seconds)
begin
-- 计算个位
one_val <= to_integer(unsigned(seconds)) mod 10;
-- 调用译码函数并更新输出
seg_one <= seven_seg_decode(one_val);
end process;
end Behavioral;
5、CLOCK_24:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY CLOCK_24 IS
PORT(
CLK : IN STD_LOGIC; -- 时钟输入
RST : IN STD_LOGIC; -- 重置输入
CLK_M : OUT STD_LOGIC_VECTOR(4 DOWNTO 0) -- 当前时间
);
END CLOCK_24;
ARCHITECTURE CLOCK2 OF CLOCK_24 IS
SIGNAL TEM_CLK_M : STD_LOGIC_VECTOR(4 DOWNTO 0) := (OTHERS => '0');
BEGIN
PROCESS(CLK, RST)
BEGIN
IF RST = '0' THEN
TEM_CLK_M <= (OTHERS => '0');
ELSIF CLK'EVENT AND CLK = '1' THEN
IF TEM_CLK_M = "11000" THEN
TEM_CLK_M <= (OTHERS => '0');
ELSE
TEM_CLK_M <= TEM_CLK_M + 1;
END IF;
END IF;
END PROCESS;
CLK_M <= TEM_CLK_M;
END CLOCK2;
6、CLOCK_60:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY CLOCK_60 IS
PORT(
CLK : IN STD_LOGIC; -- 时钟输入
RST : IN STD_LOGIC; -- 重置输入
QOUT : OUT STD_LOGIC; -- 进位输出信号
CLK_M : OUT STD_LOGIC_VECTOR(5 DOWNTO 0) -- 当前时间
);
END CLOCK_60;
ARCHITECTURE CLOCK1 OF CLOCK_60 IS
SIGNAL TEM_CLK_M : STD_LOGIC_VECTOR(5 DOWNTO 0) := (OTHERS => '0');
BEGIN
PROCESS(CLK, RST)
BEGIN
IF RST = '0' THEN
TEM_CLK_M <= (OTHERS => '0');
QOUT <= '0';
ELSIF CLK'EVENT AND CLK = '1' THEN
IF TEM_CLK_M = "111011" THEN
QOUT <= '1';
TEM_CLK_M <= (OTHERS => '0');
ELSE
TEM_CLK_M <= TEM_CLK_M + 1;
QOUT <= '0';
END IF;
END IF;
END PROCESS;
CLK_M <= TEM_CLK_M;
END CLOCK1;
7、clock_display :
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity clock_display is
Port (
seconds : in std_logic_vector(5 downto 0); -- 输入的秒数(0-59)
seg_ten : out std_logic_vector(6 downto 0); -- 十位数码管段
seg_one : out std_logic_vector(6 downto 0)); -- 个位数码管段
end clock_display;
architecture Behavioral of clock_display is
-- 七段数码管译码函数
function seven_seg_decode(num : integer range 0 to 9) return std_logic_vector is
variable seg : std_logic_vector(6 downto 0);
begin
case num is
when 0 => seg := "1000000"; -- A到G(DP省略)
when 1 => seg := "1111001";
when 2 => seg := "0100100";
when 3 => seg := "0110000";
when 4 => seg := "0011001";
when 5 => seg := "0010010";
when 6 => seg := "0000010";
when 7 => seg := "1111000";
when 8 => seg := "0000000";
when 9 => seg := "0010000";
when others => seg := "1111111"; -- 错误指示
end case;
return seg;
end function;
-- 声明变量来存储十位和个位的值
signal ten_val, one_val : integer range 0 to 9;
begin
process(seconds)
begin
-- 计算十位
ten_val <= to_integer(unsigned(seconds)) / 10;
-- 调用译码函数并更新输出
seg_ten <= seven_seg_decode(ten_val);
end process;
process(seconds)
begin
-- 计算个位
one_val <= to_integer(unsigned(seconds)) mod 10;
-- 调用译码函数并更新输出
seg_one <= seven_seg_decode(one_val);
end process;
end Behavioral;
8、TUBE:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_UNSIGNED.ALL;
ENTITY TUBE IS
PORT (
CLK : IN STD_LOGIC;
TUBE_SIX : IN STD_LOGIC_VECTOR(6 DOWNTO 0);
TUBE_FIVE : IN STD_LOGIC_VECTOR(6 DOWNTO 0);
TUBE_FOUR : IN STD_LOGIC_VECTOR(6 DOWNTO 0);
TUBE_THREE : IN STD_LOGIC_VECTOR(6 DOWNTO 0);
TUBE_TWO : IN STD_LOGIC_VECTOR(6 DOWNTO 0);
TUBE_ONE : IN STD_LOGIC_VECTOR(6 DOWNTO 0);
TUBE_NOW : OUT STD_LOGIC_VECTOR(6 DOWNTO 0);
TIME_CLOCK : OUT STD_LOGIC;
EN : OUT STD_LOGIC_VECTOR(5 DOWNTO 0)
);
END TUBE;
ARCHITECTURE ONE OF TUBE IS
SIGNAL TEM_EN : STD_LOGIC_VECTOR(5 DOWNTO 0) := "011111";
SIGNAL TEM_TIME_CLOCK : STD_LOGIC := '1';
SIGNAL FLAG : STD_LOGIC := '0';
SIGNAL TEM_TUBE_SIX : STD_LOGIC_VECTOR(6 DOWNTO 0) := "0000000";
SIGNAL TEM_TUBE_FIVE : STD_LOGIC_VECTOR(6 DOWNTO 0) := "0000000";
SIGNAL TEM_TUBE_FOUR : STD_LOGIC_VECTOR(6 DOWNTO 0) := "0000000";
SIGNAL TEM_TUBE_THREE : STD_LOGIC_VECTOR(6 DOWNTO 0) := "0000000";
SIGNAL TEM_TUBE_TWO : STD_LOGIC_VECTOR(6 DOWNTO 0) := "0000000";
SIGNAL TEM_TUBE_ONE : STD_LOGIC_VECTOR(6 DOWNTO 0) := "0000000";
BEGIN
P1: PROCESS(CLK)
BEGIN
IF CLK'EVENT AND CLK = '1' THEN
TEM_EN <= TEM_EN(4 DOWNTO 0) & TEM_EN(5);
EN <= TEM_EN;
END IF;
END PROCESS P1;
P2: PROCESS(TEM_EN, TUBE_ONE, TUBE_TWO, TUBE_THREE, TUBE_FOUR, TUBE_FIVE, TUBE_SIX)
BEGIN
CASE TEM_EN IS
WHEN "111101" => TUBE_NOW <= TUBE_ONE;
WHEN "111011" => TUBE_NOW <= TUBE_TWO;
WHEN "110111" => TUBE_NOW <= TUBE_THREE;
WHEN "101111" => TUBE_NOW <= TUBE_FOUR;
WHEN "011111" => TUBE_NOW <= TUBE_FIVE;
WHEN "111110" => TUBE_NOW <= TUBE_SIX;
WHEN OTHERS => TUBE_NOW <= (OTHERS => '0');
END CASE;
END PROCESS P2;
P3: PROCESS(CLK)
VARIABLE COUNTER : INTEGER RANGE 0 TO 1000;
BEGIN
TEM_TUBE_SIX <= TUBE_SIX;
TEM_TUBE_FIVE <= TUBE_FIVE;
TEM_TUBE_FOUR <= TUBE_FOUR;
TEM_TUBE_THREE <= TUBE_THREE;
TEM_TUBE_TWO <= TUBE_TWO;
TEM_TUBE_ONE <= TUBE_ONE;
IF (
TEM_TUBE_ONE = "1000000" AND TEM_TUBE_TWO = "1000000" AND TEM_TUBE_THREE = "1000000" AND TEM_TUBE_FOUR = "1000000" AND TEM_TUBE_FIVE = "0100100" AND TEM_TUBE_SIX = "1111001"
) THEN
COUNTER := 0;
TEM_TIME_CLOCK <= '0';
FLAG <= '1';
ELSIF (
TEM_TUBE_ONE = "1000000" AND TEM_TUBE_TWO = "1000000" AND TEM_TUBE_THREE = "1000000" AND TEM_TUBE_FOUR = "1000000" AND TEM_TUBE_FIVE = "1000000" AND TEM_TUBE_SIX = "1000000"
) THEN
COUNTER := 0;
TEM_TIME_CLOCK <= '0';
FLAG <= '1';
ELSIF FLAG = '1' THEN
IF CLK'EVENT AND CLK = '1' THEN
COUNTER := COUNTER + 1;
END IF;
IF COUNTER = 1000 THEN
TEM_TIME_CLOCK <= '1';
FLAG <= '0';
END IF;
END IF;
END PROCESS P3;
TIME_CLOCK <= TEM_TIME_CLOCK;
END ONE;
9、原理图连接: