0
\$\begingroup\$

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.

\$\endgroup\$
0
0
\$\begingroup\$

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 on rising_edge(PS2_CLK2), but also making assignment when NOT rising_edge(PS2_CLK2) which isn't really valid. I believe that is the source of your error. The idea of setting depressed to 0 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 check rising_edge() and also NOT rising_edge(), your depressed 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, and depressed 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 and PS2_CLK signals before using. I.e.:

    signal PS2_CLK2,PS2_CLK3,PS3_CLK4,PS2_DAT2,PS2_DAT3, detected_fall : std_logic;

    ...

    input_signals : process (clk) begin 
      if rising_edge (clk) then 
        -- get data
        PS2_DAT2 <= PS2_DAT;  --PS2_DAT2 is NOT properly synchronized. Don't use.
        PS2_DAT3 <= PS2_DAT2; --PS2_DAT3 is properly synchronized. OK to use.
        PS2_CLK2 <= PS2_CLK;  --PS2_CLK2 is NOT properly synchronized. Don't use.
        PS2_CLK3 <= PS2_CLK2; --PS2_CLK3 is properly synchronized. OK to use.
        PS2_CLK4 <= PS2_CLK3; --PS2_CLK4 is properly synchronized. OK to use.
      end if;
    end process;

    ...

    detected_fall <= (NOT PS2_CLK3) AND PS2_CLK4; --Use only properly synchronized signals

    ...

    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_DAT3; --Use only properly synchronized signals
        end if;
      end if;
    end process;

By the way, for generic (rising or falling) edge detect, you would do:

    PS2_CLK_EDGE <= PS2_CLK4 XOR PS2_CLK3;
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.