目录

VHDL设计 秒表

VHDL课程作业,设计一个带存储功能的秒表。

演示视频:https://www.bilibili.com/video/av79725799/

秒表由以下模块组成:

  1. 按键消抖 xd
  2. 分频器 clock_gen
  3. 计数器 cnt
  4. 译码器 dec
  5. 串行扫描 scan
  6. 寄存器 reg
  7. 蜂鸣器 ling
  8. 顶层秒表 miaobiao

连接示意图,画的很垃圾😑

https://raw.githubusercontent.com/wanakiki/photo/master/vhdl/20200101215905.png

引脚分配图:

https://raw.githubusercontent.com/wanakiki/photo/master/vhdl/20200101220658.jpg

顶层模块

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;