You need a rising edge detector. This is done by generating a delayed input that is 1 clock cycle later than the actual input.
For example:
____ ____ ____ ____ ____ ____ ____ ____
CLock ___/ \___/ \___/ \___/ \___/ \___/ \___/ \___/ \
_______________________________
Input ___/ \____________________________________
_______________________________
Input_z ____________/ \___________________________
^ You want to detect this point, where the signals are not the
same. Input has gone high, but because Input_z is delayed, it
isn't high yet
The delayed input is generated as follows:
gen_input_z : process(clk,rst)
begin
if (rst = '1') then
Input_z <= '0';
elsif (rising_edge(clk)) then
Input_z <= Input;
end if;
end process
Now you want to detect your rising edge:
gen_edge_det : process(clk,rst)
begin
if (rst = '1') then
Edge_detect <= '0';
elsif (rising_edge(clk)) then
if (Input = '1' and Input_z = '0') then
Edge_detect <= '1';
else
Edge_detect <= '0';
end if;
end if;
end process
But now our edge detect is only one clock cycle:
____ ____ ____ ____ ____ ____ ____ ____
CLock ___/ \___/ \___/ \___/ \___/ \___/ \___/ \___/ \
_______________________________
Input ___/ \____________________________________
_______________________________
Input_z ____________/ \___________________________
________
Edge_det ____________/ \__________________________________________________
To modify that, add in a counter which only makes edge detect fall after a certain number of clock cycles:
-- Port Declarations
signal clk : in std_logic;
signal rst : in std_logic;
signal input : in std_logic;
-- Signal declarations
signal input_z : std_logic;
signal edge_detect : std_logic;
signal counter : unsigned(31 downto 0); -- include numeric_std for this
gen_edge_det : process(clk,rst)
begin
if (rst = '1') then
Edge_detect <= '0';
counter <= '0';
input_z <= '0';
elsif (rising_edge(clk)) then
input_z <= input;
if (Input = '1' and Input_z = '0') then
Edge_detect <= '1';
counter <= (others => '0');
elsif (counter < 2) then -- we want an edge detect of 2 clock cycles
Edge_detect <= '1';
counter <= counter + "1"; -- declare counter as unsigned.
else
Edge_detect <= '0';
end if;
end if;
end process
Now it's doing what we want:
____ ____ ____ ____ ____ ____ ____ ____
CLock ___/ \___/ \___/ \___/ \___/ \___/ \___/ \___/ \
_______________________________
Input ___/ \____________________________________
_______________________________
Input_z ____________/ \___________________________
_____________
Edge_det ____________/ \_____________________________________________
The short answer:
Invert your logic. Drive the column select lines with open-drain (or open-collector) logic where the selected column is pulled low and the un-selected columns are floating. When you look at a row, a key-press will be detected by a '0'. Un-pressed keys will be detected by a '1'.
Now the details:
As EEIngenuity points out, when you press 2 buttons in the same row, it results in a short-circuit between their corresponding columns. This (and other problems involving multiple key presses) is usually overcome in a keyboard matrix by adding a diode in series with each switch.
Since adding diodes is not an option for you, you will need to float the outputs of your inactive column selects to avoid trying to drive them to the opposite polarity as your active column select. This is done using open-drain logic. If your column selects are tied directly to a CPLD or FPGA, you should be able to accomplish this in your VHDL code.
The photo in your question shows that you have a pull-up resistor on each column and each row. The pull-ups on the columns are unnecessary, but will not hurt anything. The pull-ups on each row will assure a high condition unless pulled low by the open-drain driver on the column selects (through a closed switch).
I have had to make some assumptions about your circuit since you have not provided a complete schematic or your VHDL code. You say
when no key is pressed, which is the condition pin4pin6pin7pin2 = "0000"
yet from the photo you provide, pull-up resistors are shown. This implies that you already have a logic inversion somewhere, possibly in your VHDL code or (less likely) inverters between your rows and your logic device (CPLD or FPGA).
Edit:
Per your comment, you are using negative logic in your descriptions: "0000" indicates all four pins are high, etc. That being the case, assuming the column selects and row signals go directly from connector 2 on your schematic to the FPGA, just follow my directions above by using open-drain logic for the column select outputs in your FPGA.
I am not a VHDL expert, but I found this from Xilinx:
Infer the open drain buffer by using the following code:
VHDL:
dout <= 'Z' when din='1' else '0';
Also note in your schematic, all of the LEDs are shown wired backwards. The anodes go to the current-limiting resistors and the cathodes go to the signal lines. The LEDs light when the signal lines are pulled low.
Best Answer
I'm not surprised it's not working. Now, I don't know the slightest thing about VHDL, but I do know about programming in general.
Firstly: INDENT YOUR CODE With no indents the code is all but unreadable. Here is your code indented properly:
Now you can see one major flaw in your code at a glance.
C being 1 will only be tested if B is 1, which itself will only be tested if A is 1. The
if
statements shouldn't be nested like that.Secondly, (again, I don't know VHDL) is how A B and C are being assigned. In a classic programming language you would need to assign the incoming values to A B and C every time you want to test them, otherwise you won't ever see any changes. This may not be the case in VHDL, but it's something to look more closely at.