VHDL课程作业,设计一个带存储功能的秒表。
演示视频:https://www.bilibili.com/video/av79725799/
秒表由以下模块组成:
- 按键消抖 xd
- 分频器 clock_gen
- 计数器 cnt
- 译码器 dec
- 串行扫描 scan
- 寄存器 reg
- 蜂鸣器 ling
- 顶层秒表 miaobiao
连接示意图,画的很垃圾😑
引脚分配图:
顶层模块
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity miaobiao is
port(
clk, k1, k2, k3: in std_logic;
selector: out std_logic_vector(3 downto 0);
num: out std_logic_vector(7 downto 0);
ling_out: out std_logic
);
end entity miaobiao;
architecture rtl of miaobiao is
component clock_gen is
port(
clk : IN STD_LOGIC;
clk_us : OUT STD_LOGIC;
clk_ms : OUT STD_LOGIC;
clk_10ms : OUT STD_LOGIC;
clk_100ms : OUT STD_LOGIC;
clk_500ms : OUT STD_LOGIC;
clk_second : OUT STD_LOGIC
);
end component;
component cnt is
port(
reset, clk: in std_logic;
carry: out std_logic;
nums: out std_logic_vector(3 downto 0)
);
end component cnt;
component cnt6 is
port(
reset, clk: in std_logic;
carry: out std_logic;
nums: out std_logic_vector(3 downto 0)
);
end component cnt6;
component dec is
port(
nums : in std_logic_vector(3 downto 0);
res : out std_logic_vector(7 downto 0)
);
end component dec;
component dec_node is
port(
nums : in std_logic_vector(3 downto 0);
res : out std_logic_vector(7 downto 0)
);
end component dec_node;
component ling is
port(
enable, clk1, clk2: in std_logic;
clk_out : out std_logic
);
end component;
component reg is
port(
clk, clear, enable, st:in std_logic;
data: in std_logic_vector(15 downto 0);
l_st: out std_logic;
out_num0, out_num1, out_num2, out_num3: out std_logic_vector(3 downto 0)
);
end component reg;
component scan is
port(
num0, num1, num2, num3: in std_logic_vector(7 downto 0);
clk, clk_l, st: in std_logic;
selector : out std_logic_vector(3 downto 0);
nums: out std_logic_vector(7 downto 0)
);
end component scan;
component xd is
generic(n:integer:=30);
port(clk,k1:in std_logic;
y :out std_logic);
end component xd;
--===================================================
signal vcc, st, clear: std_logic;
signal k1_xd, k2_xd, k3_xd, clk_10ms, clk_l, clk_xd, clk_cnt: std_logic;
signal c0, c1, c2, c3: std_logic; --cnt
signal num0, num1, num2, num3: std_logic_vector(3 downto 0);
signal nums:std_logic_vector(15 downto 0); --reg
signal di_num0, di_num1, di_num2, di_num3: std_logic_vector(3 downto 0); --decoder
signal sc_num0, sc_num1, sc_num2, sc_num3: std_logic_vector(7 downto 0); --scan
signal l_st:std_logic;
signal push: std_logic;
begin
vcc <= '1';
u0: clock_gen port map(clk=>clk, clk_10ms=>clk_cnt, clk_us=>open, clk_ms=>clk_xd, clk_100ms=>clk_l, clk_500ms=>open, clk_second=>open);
u1: xd port map(clk=>clk_xd, k1=>k1, y=>k1_xd);
u2: xd port map(clk=>clk_xd, k1=>k2, y=>k2_xd);
u3: xd port map(clk=>clk_xd, k1=>k3, y=>k3_xd);
c0 <= clk_cnt and st;
clear <= st or k2_xd;
u4: cnt port map(reset=>clear, clk=>c0, carry=>c1, nums=>num0);
u5: cnt port map(reset=>clear, clk=>c1, carry=>c2, nums=>num1);
u6: cnt port map(reset=>clear, clk=>c2, carry=>c3, nums=>num2);
u7: cnt6 port map(reset=>clear, clk=>c3, carry=>open, nums=>num3);
nums<= num3 & num2 & num1 & num0;
u8: reg port map(clk=>k3_xd, clear=>clear, enable=>vcc, st=>st, data=>nums, l_st=>l_st,out_num0=>di_num0, out_num1=>di_num1, out_num2=>di_num2, out_num3=>di_num3);
u9: dec port map(nums=>di_num0, res=>sc_num0);
u10: dec port map(nums=>di_num1, res=>sc_num1);
u11: dec_node port map(nums=>di_num2, res=>sc_num2);
u12: dec port map(nums=>di_num3, res=>sc_num3);
u13: scan port map(num0=>sc_num0, num1=>sc_num1, num2=>sc_num2, num3=>sc_num3, clk=>clk_xd, clk_l=>clk_l, st=>l_st, selector=>selector, nums=>num);
push <= k1_xd and k2_xd and k3_xd;
u14: ling port map(enable=> push, clk1=>clk_l, clk2=>clk_xd, clk_out=>ling_out);
--zhuangtaikongzhi
process(k1_xd)
begin
if k1_xd'event and k1_xd = '0' then
if st = '0' then
st <= '1';
else
st <= '0';
end if;
end if;
end process;
end architecture rtl;
消抖模块
通常情况下,消抖时间为20ms。以下代码同样由老师提供,时钟输入为1ms。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity xd is
generic(n:integer:=30);
port(clk,k1:in std_logic;
y :out std_logic);
end xd;
architecture a of xd is
begin
process(clk)
variable sum:integer range 1 to n;
begin
if clk'event and clk = '0' then
if k1='0' then
if sum=n then
sum:=sum;
else
sum:=sum+1;
end if;
if sum=n then
y<='0';
else
y<='1';
end if;
else
sum:=1;
y<='1';
end if;
end if;
end process;
end;
分频器模块
由老师直接提供,能够产生的频率见引脚定义处。
LIBRARY ieee;
USE ieee.std_logic_1164.all;
USE ieee.std_logic_unsigned.all;
USE ieee.STD_LOGIC_ARITH.all;
------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------
ENTITY clock_gen IS
PORT(
clk : IN STD_LOGIC;
clk_us : OUT STD_LOGIC;
clk_ms : OUT STD_LOGIC;
clk_10ms : OUT STD_LOGIC;
clk_100ms : OUT STD_LOGIC;
clk_500ms : OUT STD_LOGIC;
clk_second : OUT STD_LOGIC
);
END clock_gen;
------------------------------------------------------------------------------------------
------------------------------------------------------------------------------------------
ARCHITECTURE behav OF clock_gen IS
CONSTANT shake_constant :integer:=10; -- shake wait for shake_constant us
signal q_1us :integer range 0 to 50; --us��Ƶϵ��
signal q_1ms :integer range 0 to 1000; --ms��Ƶϵ��
signal q_10ms:integer range 0 to 10; --10ms��Ƶϵ��
signal q_100ms :integer range 0 to 10; --100ms��Ƶϵ��
signal q_500ms:integer range 0 to 5; --500ms��Ƶϵ��
signal q_1s :integer range 0 to 1; --����Ƶϵ��
SIGNAL sig_second : STD_LOGIC;
SIGNAL sig_500ms : STD_LOGIC;
SIGNAL sig_100ms : STD_LOGIC;
SIGNAL sig_ms : STD_LOGIC;
SIGNAL sig_10ms : STD_LOGIC;
SIGNAL sig_us : STD_LOGIC;
-------------------------------------------------------------------------------------------
begin
output_clock:process(clk)
begin
if clk'event and clk='1' then
clk_us <= sig_us;
clk_ms <= sig_ms;
clk_10ms <= sig_10ms;clk_100ms <= sig_100ms;
clk_second <= sig_second;
end if;
end process;
-------------------------------------------------------------------------------------------
----**************��Ƶ�� 1 us clock*******************
u_second:process(clk)
begin
if clk'event and clk='1' then
if q_1us < 49 then
q_1us <= q_1us+1;
sig_us <= '0';
else
q_1us <= 0;
sig_us <= '1';
end if;
end if;
end process;
-------------------------------------------------------------------------------------------
----**************��Ƶ�� 1 ms clock*******************
m_second:process(sig_us)
begin
if sig_us'event and sig_us='1' then
if q_1ms < 999 then
q_1ms <= q_1ms+1;
sig_ms <= '0';
else
q_1ms <= 0;
sig_ms <= '1';
end if;
end if;
end process;
-------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
----**************��Ƶ�� 10 ms clock*******************
m_second10:process(sig_ms)
begin
if sig_ms'event and sig_ms='1' then
if q_10ms < 9 then
q_10ms <= q_10ms+1;
sig_10ms <= '0';
else
q_10ms <= 0;
sig_10ms <= '1';
end if;
end if;
end process;
-------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
----**************��Ƶ�� 100 ms clock*******************
m_second100:process(sig_10ms)
begin
if sig_10ms'event and sig_10ms='1' then
if q_100ms < 9 then
q_100ms <= q_100ms+1;
sig_100ms <= '0';
else
q_100ms <= 0;
sig_100ms <= '1';
end if;
end if;
end process;
-------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
----**************��Ƶ�� 500 ms clock*******************
m_second500:process(sig_100ms)
begin
if sig_100ms'event and sig_100ms='1' then
if q_500ms < 4 then
q_500ms <= q_500ms+1;
sig_500ms <= '0';
else
q_500ms <= 0;
sig_500ms <= '1';
end if;
end if;
end process;
-------------------------------------------------------------------------
----**************��Ƶ�� 1 second clock*******************
second:process(sig_500ms)
begin
if sig_500ms'event and sig_500ms='1' then
if q_1s < 1 then
q_1s <= q_1s+1;
sig_second <= '0';
else
q_1s <= 0;
sig_second <= '1';
end if;
end if;
end process;
-------------------------------------------------------------------------
END behav;
------------------------------------------------------------------------------------------
计数器模块
十进制计数器,带有复位端、进位端。在实现秒表时,需要将最后一个计数器改为六进制。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity cnt is
port(
reset, clk: in std_logic;
carry: out std_logic;
nums: out std_logic_vector(3 downto 0)
);
end entity cnt;
architecture rtl of cnt is
begin
process(clk, reset)
variable temp : std_logic_vector(3 downto 0);
begin
if reset = '0' then
temp := (others => '0');
elsif clk'event and clk = '1' then
if temp >= "1001" then
temp := (others => '0');
carry <= '1';
else
temp := temp + 1;
carry <= '0';
end if;
end if;
nums <= temp;
end process;
end architecture rtl;
译码器模块
输入为四位二进制数,输出为对应的七段译码管数组。
library ieee;
use ieee.std_logic_1164.all;
entity dec is
port(
nums : in std_logic_vector(3 downto 0);
res : out std_logic_vector(7 downto 0)
);
end entity dec;
architecture rtl of dec is
begin
process(nums)
begin
case nums is
when "0000" => res <= B"1100_0000";
when "0001" => res <= B"1111_1001";
when "0010" => res <= B"1010_0100";
when "0011" => res <= B"1011_0000";
when "0100" => res <= B"1001_1001";
when "0101" => res <= B"1001_0010";
when "0110" => res <= B"1000_0011";
when "0111" => res <= B"1111_1000";
when "1000" => res <= B"1000_0000";
when "1001" => res <= B"1001_1000";
when others => res <= B"1111_1111";
end case;
end process;
end architecture rtl;
串行扫描模块
实现串行扫描功能,当st为零时会进入闪烁状态。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity scan is
port(
num0, num1, num2, num3: in std_logic_vector(7 downto 0);
clk, clk_l, st: in std_logic;
selector : out std_logic_vector(3 downto 0);
nums: out std_logic_vector(7 downto 0)
);
end entity scan;
architecture rtl of scan is
signal flag :std_logic;
begin
process(clk_l)
begin
if clk_l'event and clk_l = '1' then
if flag = '1' and st = '0' then
flag <= '0';
else
flag <= '1';
end if;
end if;
end process;
process(clk)
variable cur : std_logic_vector(1 downto 0);
begin
if flag = '1' then
if clk'event and clk = '1' then
case cur is
when "00" =>
selector <= "1110";
nums <= num0;
when "01" =>
selector <= "1101";
nums <= num1;
when "10" =>
selector <= "1011";
nums <= num2;
when others =>
selector <= "0111";
nums <= num3;
end case;
cur := cur + 1;
end if;
else
selector <= "1111";
end if;
end process;
end architecture rtl;
寄存器模块
当秒表处于计时状态时,按下寄存器控制按键会将秒表当前的时间保存到计数器当中。当秒表处于暂停状态时,按下寄存器控制按键会将寄存器保存的数据逐个显示,在显示寄存器内部数据时,要求串行扫描闪烁。
寄存器的输入为四个计数器当前的状态,内部能够根据秒表当下工作状态选择对应的数据交给译码器,设计相对复杂。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity reg is
port(
clk, clear, enable, st:in std_logic;
data: in std_logic_vector(15 downto 0);
l_st: out std_logic;
out_num0, out_num1, out_num2, out_num3: out std_logic_vector(3 downto 0)
);
end entity reg;
architecture rtl of reg is
signal num0, num1, num2:std_logic_vector(15 downto 0);
begin
process(clk, clear)
variable status: integer range 0 to 3;
begin
if clear = '0' then
num0 <= (others => '0');
num1 <= (others => '0');
num2 <= (others => '0');
status := 0;
elsif enable = '1' then
if(clk'event and clk='1') then
if st = '1' then
num2 <= num1;
num1 <= num0;
num0 <= data;
else
if status >= 3 then
status := 0;
else
status := status + 1;
end if;
end if;
end if;
end if;
if st = '1' then
status := 0;
end if;
case status is
when 0 =>
out_num3 <= data(15 downto 12);
out_num2 <= data(11 downto 8);
out_num1 <= data(7 downto 4);
out_num0 <= data(3 downto 0);
l_st <= '1';
when 1 =>
out_num3 <= num0(15 downto 12);
out_num2 <= num0(11 downto 8);
out_num1 <= num0(7 downto 4);
out_num0 <= num0(3 downto 0);
l_st <= '0';
when 2 =>
out_num3 <= num1(15 downto 12);
out_num2 <= num1(11 downto 8);
out_num1 <= num1(7 downto 4);
out_num0 <= num1(3 downto 0);
l_st <= '0';
when 3 =>
out_num3 <= num2(15 downto 12);
out_num2 <= num2(11 downto 8);
out_num1 <= num2(7 downto 4);
out_num0 <= num2(3 downto 0);
l_st <= '0';
when others=>
out_num3 <= data(15 downto 12);
out_num2 <= data(11 downto 8);
out_num1 <= data(7 downto 4);
out_num0 <= data(3 downto 0);
l_st <= '1';
end case;
end process;
end rtl;
蜂鸣器
蜂鸣器的工作方式是当按键按下时鸣叫,当按键松开后,鸣叫会持续0.2秒。在我的设计当中,蜂鸣器使用了两个时钟输入,clk1用于控制蜂鸣器的发声频率,clk2(100ms)用来进行按键松开后的计时。
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
entity ling is
port(
enable, clk1, clk2: in std_logic;
clk_out : out std_logic
);
end entity;
architecture rtl of ling is
signal flag: std_logic;
begin
process(clk2, flag)
begin
if flag = '1' then
clk_out <= clk2;
end if;
end process;
process(clk1,enable, clk2)
variable num: integer range 0 to 2000;
begin
if enable = '0' then
flag <= '1';
num := 0;
elsif clk1'event and clk1 = '1' then
if flag = '1' then
if num >= 2 then
flag <= '0';
else
num := num + 1;
end if;
end if;
end if;
end process;
end architecture rtl;