Is it possible to detect a keypress on a PS/2 keyboard connected to an FPGA, using VHDL, with only PS/2 clock signal?
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity Keyboard_Drive is
Port ( PS2_DAT, PS2_CLK, clk, rstn : in std_logic;
end entity;
architecture rtl of Keyboard_Drive is
signal PS2_CLK2, PS2_CLK2_old, PS2_DAT2, detected_fall : std_logic;
signal shiftreg: std_logic_vector (9 downto 0);
signal pressed: std_logic := '0';
begin
input_signals : process (clk) begin
if rising_edge (clk) then
-- get data
PS2_DAT2 <= PS2_DAT;
PS2_CLK2 <= PS2_CLK;
PS2_CLK2_old <= PS2_CLK2;
end if;
end process;
detected_fall <= (NOT PS2_CLK2) AND PS2_CLK2_old;
Key: process (clk, rstn) begin
if rstn = '0' then
shiftreg <= (others => '0');
elsif rising_edge (clk) then
-- assign shift
if detected_fall = '1' then
shiftreg (8 downto 0) <= shiftreg (9 downto 1);
shiftreg (9) <= PS2_DAT2;
end if;
end if;
end process;
I was considering to add this process (code under) to try to detect only when PS2_CLK rises, because I read that PS2_CLK is constantly high (PS2_CLK=1) when they keyboard is not in use.
sound : process (clk) is
begin
if rising_edge(PS2_CLK2) then
pressed <= '1';
else
pressed <= '0';
end if;
end process;
But this gives an error ('couldn't implement registers for assignments on this clock edge').
I have tried to read and understand bouncing and de-bouncing and how to take it into consideration. I have tried many solutions and many hours. It felt as a simple problem in the beginning but it never gets solved.
The pressed signal has to be a steady '1' when being pressed since the signal will be used for producing sound when being pressed.
Thanks in advance.
Best Answer
There's a lot of good PS/2 info here. This does mention using debounce. I'm not sure how important that is, as the keyboard will likely have some amount of debounce before sending a code via PS/2 for you to process in VHDL.
What you are trying to do in the
sound
process is making assignment onrising_edge(PS2_CLK2)
, but also making assignment when NOTrising_edge(PS2_CLK2)
which isn't really valid. I believe that is the source of your error. The idea of settingdepressed
to0
any other time than a rising edge wouldn't really work anyway, as you said you want a steady '1' when pressed. If this circuit existed which could checkrising_edge()
and also NOTrising_edge()
, yourdepressed
signal would never be steady, as it would get set only at the instant of a rising edge and never any other time.The PS/2 clock will toggle multiple times while a code is sent. One approach would be having a timer that resets on any PS/2 clock edge. While the timer is greater than zero,
depressed
is asserted. If a PS/2 clock edge is detected, the timer is reset to the max time for a high or low PS/2 clock pulse. The Digikey link I gave says the PS/2 clock period is between 60-100us, so you would set your timer value to 50us or 1/2 the max clock period you might have. If you don't detect an edge in this amount of time, a key is no longer being pressed, anddepressed
gets deasserted.A final note, since your PS/2 signals are in a separate clock domain to your system clock, the signals need to be double-registered to mitigate metastability. This means you need an additional register on your
PS2_DAT
andPS2_CLK
signals before using. I.e.:By the way, for generic (rising or falling) edge detect, you would do: