Combination Lock

From Hamsterworks Wiki!

Jump to: navigation, search

This FPGA Project was completed in May 2017

After looking how to do a "push the right buttons in the right order" project on Arduino I decided to try a 4-digit combination lock for the , using the buttons for input and the seven segment display.

The code that lights LED0 can be found in lock_unlock.vhd

A short video of it in action is at https://youtu.be/1kWDc_u6D_s

Contents

Source

combo_lock.vhd

The top level structural module - debounce the four buttons, detect when the buttons are pressed, process the button presses and update the seven segment display.

-------------------------------------------------
-- combo_lock.vhd - a 4-digit combination lock
--
-- Author: Mike Field <hamster@snap.net.nz 
--
----------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity combo_lock is
    Port ( clk        : in  STD_LOGIC;
           btn_up     : in  STD_LOGIC;
           btn_down   : in  STD_LOGIC;
           btn_left   : in  STD_LOGIC;
           btn_right  : in  STD_LOGIC;
           segments   : out STD_LOGIC_VECTOR (6 downto 0);
           seg_select : out STD_LOGIC_VECTOR (3 downto 0);
           led0       : out STD_LOGIC);
end combo_lock;

architecture Behavioral of combo_lock is
    component debounce is
    Port ( clk : in STD_LOGIC;
           i   : in STD_LOGIC;
           o   : out STD_LOGIC);
    end component;

    signal debounced_up    : std_logic := '0';
    signal debounced_down  : std_logic := '0';
    signal debounced_left  : std_logic := '0';
    signal debounced_right : std_logic := '0';

    component edge_detect is
    Port ( clk : in STD_LOGIC;
           i : in STD_LOGIC;
           o : out STD_LOGIC);
    end component;

    signal up_pressed    : std_logic := '0';
    signal down_pressed  : std_logic := '0';
    signal left_pressed  : std_logic := '0';
    signal right_pressed : std_logic := '0';
    signal up_down_pressed : std_logic := '0';
    component one_of_four_counter is
    Port ( clk : in STD_LOGIC;
           up_down_pressed : in STD_LOGIC;
           select_prev : in STD_LOGIC;
           select_next : in STD_LOGIC;
           active : out STD_LOGIC_VECTOR (3 downto 0));
    end component;

    signal active_digit : STD_LOGIC_VECTOR (3 downto 0) := (others => '0');    

    component up_down_counter is
    Port ( clk : in STD_LOGIC;
           clock_enable : in STD_LOGIC;
           up : in STD_LOGIC;
           down : in STD_LOGIC;
           digit : out STD_LOGIC_VECTOR (3 downto 0));
    end component;

    signal digit0 : STD_LOGIC_VECTOR (3 downto 0) := (others => '0');
    signal digit1 : STD_LOGIC_VECTOR (3 downto 0) := (others => '0');
    signal digit2 : STD_LOGIC_VECTOR (3 downto 0) := (others => '0');
    signal digit3 : STD_LOGIC_VECTOR (3 downto 0) := (others => '0');

    component lock_unlock is
    Port ( clk : in STD_LOGIC;
           digit0 : in STD_LOGIC_VECTOR (3 downto 0);
           digit1 : in STD_LOGIC_VECTOR (3 downto 0);
           digit2 : in STD_LOGIC_VECTOR (3 downto 0);
           digit3 : in STD_LOGIC_VECTOR (3 downto 0);
           unlock : out STD_LOGIC);
    end component;

    component seven_seg is
        Port ( clk : in STD_LOGIC;
               segments   : out STD_LOGIC_VECTOR (6 downto 0);
               seg_select : out STD_LOGIC_VECTOR (3 downto 0);
               digit0     : in STD_LOGIC_VECTOR (3 downto 0);
               blink0     : in STD_LOGIC;
               digit1     : in STD_LOGIC_VECTOR (3 downto 0);
               blink1     : in STD_LOGIC;
               digit2     : in STD_LOGIC_VECTOR (3 downto 0);
               blink2     : in STD_LOGIC;
               digit3     : in STD_LOGIC_VECTOR (3 downto 0);
               blink3     : in STD_LOGIC);
    end component;

begin

debounce_down:  debounce port map ( clk => clk,  i => btn_down,  o => debounced_down);
debounce_up:    debounce port map ( clk => clk,  i => btn_up,    o => debounced_up);
debounce_left:  debounce port map ( clk => clk,  i => btn_left,  o => debounced_left);
debounce_right: debounce port map ( clk => clk,  i => btn_right, o => debounced_right);

edge_detect_down:  edge_detect port map ( clk => clk, i => debounced_down,  o => down_pressed);
edge_detect_up:    edge_detect port map ( clk => clk, i => debounced_up,    o => up_pressed);
edge_detect_left:  edge_detect port map ( clk => clk, i => debounced_left,  o => left_pressed);
edge_detect_right: edge_detect port map ( clk => clk, i => debounced_right, o => right_pressed);

    up_down_pressed <= up_pressed or down_pressed;

i_one_of_four_counter: one_of_four_counter Port map ( 
    clk => clk,
    up_down_pressed => up_down_pressed,
    select_prev     => left_pressed,
    select_next     => right_pressed,
    active          => active_digit);

up_down_counter0: up_down_counter port map (
    clk => clk,
    clock_enable => active_digit(0),
    up           => up_pressed,
    down         => down_pressed,
    digit        => digit0);

up_down_counter1: up_down_counter port map (
    clk => clk,
    clock_enable => active_digit(1),
    up           => up_pressed,
    down         => down_pressed,
    digit        => digit1);

up_down_counter2: up_down_counter port map (
    clk => clk,
    clock_enable => active_digit(2),
    up           => up_pressed,
    down         => down_pressed,
    digit        => digit2);

up_down_counter3: up_down_counter port map (
    clk => clk,
    clock_enable => active_digit(3),
    up           => up_pressed,
    down         => down_pressed,
    digit        => digit3);

i_lock_unlock: lock_unlock Port map ( 
    clk    => clk,
    digit0 => digit0,
    digit1 => digit1,
    digit2 => digit2,
    digit3 => digit3,
    unlock => led0);

i_seven_seg: seven_seg Port map ( 
        clk        => clk,
        digit0     => digit0,
        blink0     => active_digit(0),
        digit1     => digit1,
        blink1     => active_digit(1),
        digit2     => digit2,
        blink2     => active_digit(2),
        digit3     => digit3,
        blink3     => active_digit(3),
        segments   => segments,
        seg_select => seg_select);

end Behavioral;

seven_seg.vhd

Drive the seven segment display

---------------------------------------------------------
-- seven_seg.vhd - Seven Segment driver 
--
-- Author: Mike Field <hamster@snap.net.nz 
--
---------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity seven_seg is
    Port ( clk : in STD_LOGIC;
           segments   : out STD_LOGIC_VECTOR (6 downto 0);
           seg_select : out STD_LOGIC_VECTOR (3 downto 0);
           digit0     : in STD_LOGIC_VECTOR (3 downto 0);
           blink0     : in STD_LOGIC;
           digit1     : in STD_LOGIC_VECTOR (3 downto 0);
           blink1     : in STD_LOGIC;
           digit2     : in STD_LOGIC_VECTOR (3 downto 0);
           blink2     : in STD_LOGIC;
           digit3     : in STD_LOGIC_VECTOR (3 downto 0);
           blink3     : in STD_LOGIC);
end seven_seg;

architecture Behavioral of seven_seg is
    signal digit_count : unsigned(25 downto 0);
    signal to_decode : std_logic_vector(3 downto 0);
begin
                                    -- GFEDCBA
    with to_decode select segments <= "1000000" when "0000", -- Zero
                                      "1111001" when "0001", -- One
                                      "0100100" when "0010", -- Two
                                      "0110000" when "0011", -- Three
                                      "0011001" when "0100", -- Four
                                      "0010010" when "0101", -- Five
                                      "0000010" when "0110", -- Six
                                      "1111000" when "0111", -- Seven
                                      "0000000" when "1000", -- Eight
                                      "0011000" when "1001", -- Nine
                                      "0001000" when "1010", -- A
                                      "0000011" when "1011", -- B
                                      "1000110" when "1100", -- C
                                      "0100001" when "1101", -- D
                                      "0000110" when "1110", -- E
                                      "0001110" when others; -- F
                                 
process(clk)
    begin
        if rising_edge(clk) then
            seg_select <= "1111";
            case digit_count(digit_count'high-6 downto digit_count'high-7) is
                when "00" =>
                    seg_select(0) <= blink0 AND digit_count(digit_count'high);
                    to_decode  <= digit0;                    
                when "01" =>
                    seg_select(1) <= blink1 AND digit_count(digit_count'high);
                    to_decode  <= digit1;                    
                when "10" =>
                    seg_select(2) <= blink2 AND digit_count(digit_count'high);
                    to_decode  <= digit2;                    
                when others =>
                    seg_select(3) <= blink3 AND digit_count(digit_count'high);
                    to_decode  <= digit3;                    
            end case;
            digit_count <= digit_count + 1;
        end if;
    end process;
end Behavioral;

lock_unlock.vhd

Assert 'unlock' when the correct digits are present.

---------------------------------------------------------
-- lock_unlock.vhd - detect when a four digit code has been
--                   entered 
--
-- Author: Mike Field <hamster@snap.net.nz 
--
---------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity lock_unlock is
    Port ( clk : in STD_LOGIC;
           digit0 : in STD_LOGIC_VECTOR (3 downto 0);
           digit1 : in STD_LOGIC_VECTOR (3 downto 0);
           digit2 : in STD_LOGIC_VECTOR (3 downto 0);
           digit3 : in STD_LOGIC_VECTOR (3 downto 0);
           unlock : out STD_LOGIC);
end lock_unlock;

architecture Behavioral of lock_unlock is

begin

process(clk) 
    begin
        if rising_edge(clk) then
            if digit0 = x"1" and digit1 = x"2" and digit2 = x"3" and digit3 = x"4" then
                unlock <= '1';
            else
                unlock <= '0';
            end if;
        end if;
    end process;
end Behavioral;

up_down_counter.vhd

Count up or down when clock_enable is asserted.

---------------------------------------------------------
-- up_down_counter.vhd - an up/down counter 
--
-- Author: Mike Field <hamster@snap.net.nz 
--
---------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity up_down_counter is
    Port ( clk          : in  STD_LOGIC;
           clock_enable : in  STD_LOGIC;
           up           : in  STD_LOGIC;
           down         : in  STD_LOGIC;
           digit        : out STD_LOGIC_VECTOR (3 downto 0));
end up_down_counter;

architecture Behavioral of up_down_counter is
    signal count : unsigned(3 downto 0) := (others => '0');
begin
    digit <= std_logic_vector(count);
    
process(clk) 
    begin
        if rising_edge(clk) then
          if clock_enable = '1' then
             if up = '1' and down = '1' then
                NULL;
             elsif up = '1' then 
                count <= count + 1;
             elsif down = '1' then 
                count <= count - 1;
             end if;
          end if;
       end if;
    end process;    
end Behavioral;

one_of_four_counter.vhd

Select one of four output signals

---------------------------------------------------------
-- one_of_four_counter.vhd - allow a used to select on of 
--                           four output signals.
--                           
--
-- Author: Mike Field <hamster@snap.net.nz 
--
---------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity one_of_four_counter is
    Port ( clk : in STD_LOGIC;
           up_down_pressed : in STD_LOGIC;
           select_prev     : in STD_LOGIC;
           select_next     : in STD_LOGIC;
           active : out STD_LOGIC_VECTOR (3 downto 0));
end one_of_four_counter;

architecture Behavioral of one_of_four_counter is
    signal state : std_logic_vector(3 downto 0) := "1000";
    signal counter : unsigned(29 downto 0) := (others => '1');

begin
    with counter(counter'high) select active <= state when '0',
                                                (others => '0') when others;
process(clk)
    begin
        if rising_edge(clk) then
            if select_prev = '1' and select_next = '0' then
                state <= state(2 downto 0) & state(3);
                counter <= (others => '0');
            elsif select_prev = '0' and select_next = '1' then
                state <= state(0) & state(3 downto 1);
                counter <= (others => '0');
            elsif counter(counter'high) = '0' then
                counter <= counter + 1;
            else
               if up_down_pressed = '1' then
                    counter <= (others => '0');
                end if;
            end if;
        end if;
    end process;

end Behavioral;

edge_detect.vhd

---------------------------------------------------------
-- edge_detect.vhd - look for the rising edge on a signal
--
-- Author: Mike Field <hamster@snap.net.nz 
--
---------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity edge_detect is
    Port ( clk : in STD_LOGIC;
           i : in STD_LOGIC;
           o : out STD_LOGIC);
end edge_detect;

architecture Behavioral of edge_detect is
    signal last : std_logic := '0';
begin

process(clk)
    begin 
        if rising_edge(clk) then        
            if i = '1' and last = '0' then
                o <= '1';
            else
                o <= '0';
            end if;
            last <= i;
        end if;
    end process;
end Behavioral;

debounce.vhd

Generic button debouncer

-------------------------------------------------
-- debounce.vhd - a button debouncer
--
-- Author: Mike Field <hamster@snap.net.nz 
--
----------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity debounce is
    Port ( clk : in STD_LOGIC;
           i : in STD_LOGIC;
           o : out STD_LOGIC);
end debounce;

architecture Behavioral of debounce is
    signal counter : unsigned(20 downto 0) := (others => '0');
    signal almost_synced : std_logic := '0';
    signal synced        : std_logic := '0';
    
begin

process(clk)
    begin
        if rising_edge(clk) then
            if synced = '0' then
                if counter(counter 'high) = '1' then
                   -- Count down if the button is lifted
                   counter <= counter  - 1;
                else 
                   -- snap down to zero once the MSB clears
                   counter <= (others => '0');
                end if;
            else
                if counter(counter'high) = '0' then
                    -- Count up if the button is pressed
                    counter <= counter + 1;
                else 
                    -- snap up to all ones once the MSB is set
                    counter <= (others => '1');
                end if;
           end if;
           o <= std_logic(counter(counter'high));
           
           synced        <= almost_synced;
           almost_synced <= i;         
        end if;
    end process;
end Behavioral;

basys3.xcd

Constraints for the Basys-3 board

set_property PACKAGE_PIN W5 [get_ports clk]	
set_property IOSTANDARD LVCMOS33 [get_ports clk]
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports clk]

#7 segment display
set_property PACKAGE_PIN W7 [get_ports {segments[0]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {segments[0]}]
set_property PACKAGE_PIN W6 [get_ports {segments[1]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {segments[1]}]
set_property PACKAGE_PIN U8 [get_ports {segments[2]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {segments[2]}]
set_property PACKAGE_PIN V8 [get_ports {segments[3]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {segments[3]}]
set_property PACKAGE_PIN U5 [get_ports {segments[4]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {segments[4]}]
set_property PACKAGE_PIN V5 [get_ports {segments[5]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {segments[5]}]
set_property PACKAGE_PIN U7 [get_ports {segments[6]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {segments[6]}]

set_property PACKAGE_PIN U2 [get_ports {seg_select[0]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {seg_select[0]}]
set_property PACKAGE_PIN U4 [get_ports {seg_select[1]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {seg_select[1]}]
set_property PACKAGE_PIN V4 [get_ports {seg_select[2]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {seg_select[2]}]
set_property PACKAGE_PIN W4 [get_ports {seg_select[3]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {seg_select[3]}]

##Buttons
set_property PACKAGE_PIN T18 [get_ports btn_up]						
	set_property IOSTANDARD LVCMOS33 [get_ports btn_up]
set_property PACKAGE_PIN W19 [get_ports btn_left]						
	set_property IOSTANDARD LVCMOS33 [get_ports btn_left]
set_property PACKAGE_PIN T17 [get_ports btn_right]						
	set_property IOSTANDARD LVCMOS33 [get_ports btn_right]
set_property PACKAGE_PIN U17 [get_ports btn_down]						
	set_property IOSTANDARD LVCMOS33 [get_ports btn_down]

set_property PACKAGE_PIN U16     [get_ports {led0}]					
set_property IOSTANDARD LVCMOS33 [get_ports {led0}]

Personal tools