用状态机实现的4x4键扫,当有键按下时产生一个按键中断,在中断中再读按键的键值
LIBRARY IEEE; USE IEEE.STD_LOGIC_1164.ALL; USE IEEE.STD_LOGIC_UNSIGNED.ALL; USE IEEE.STD_LOGIC_ARITH.ALL;
ENTITY KeyBoard IS PORT( rstn : IN STD_LOGIC; --reset signal, active low clk : IN STD_LOGIC; --clock signal
scan : OUT STD_LOGIC_VECTOR(3 DOWNTO 0); -- retn : IN STD_LOGIC_VECTOR(3 DOWNTO 0); --
keyokn : OUT STD_LOGIC; keycsn : IN STD_LOGIC;
keycod : INOUT STD_LOGIC_VECTOR(3 DOWNTO 0) --key value bus ); END KeyBoard;
ARCHITECTURE Behavior OF KeyBoard IS
SIGNAL setf : STD_LOGIC; SIGNAL keyoktmp: STD_LOGIC;
SIGNAL s_cnt : STD_LOGIC_VECTOR(1 DOWNTO 0);
SIGNAL keytmp : STD_LOGIC_VECTOR(3 DOWNTO 0);
SIGNAL keycode : STD_LOGIC_VECTOR(3 DOWNTO 0);
SIGNAL keysta : STD_LOGIC_VECTOR(3 DOWNTO 0); SIGNAL codtmp : STD_LOGIC_VECTOR(5 DOWNTO 0);
BEGIN codtmp <= s_cnt & keytmp; PROCESS(rstn, clk) VARIABLE mscnt : INTEGER RANGE 0 TO 99999;--set delay time BEGIN IF clk'EVENT AND clk = '1' THEN IF rstn = '0' THEN s_cnt <= "00"; keysta <= "0000"; setf <= '0'; ELSE CASE keysta IS WHEN "0000" => --idle state s_cnt <= "11"; keysta <= "0001"; setf <= '0'; WHEN "0001" => --shift state s_cnt <= s_cnt + 1; keysta <= "0011"; setf <= '0'; WHEN "0011" => --check out any key pressed IF retn /= "1111" THEN keysta <= "0010"; ELSE keysta <= "0001"; END IF; s_cnt <= s_cnt; mscnt := 0; keytmp <= retn; setf <= '0'; WHEN "0010" => --delay enough time IF mscnt >= 99999 THEN keysta <= "0110"; mscnt := 0; ELSE keysta <= "0010"; mscnt := mscnt + 1; END IF; setf <= '0'; WHEN "0110" => --check out whether that key changed IF retn = "1111" THEN --if the key released keysta <= "0001"; ELSIF keytmp /= retn THEN --if the key changed keysta <= "0001"; ELSE keysta <= "0111"; --the key is a valid key END IF; setf <= '0'; mscnt := 0; WHEN "0111" => --wait for key release IF retn = "1111" THEN --normally released keysta <= "0101"; ELSE keysta <= "0111"; END IF; mscnt := 0; setf <= '0'; WHEN "0101" => --delay enough time IF mscnt >= 99999 THEN keysta <= "0100"; mscnt := 0; ELSE keysta <= "0101"; mscnt := mscnt + 1; END IF; setf <= '0'; WHEN "0100" => IF retn = "1111" THEN --normally released keysta <= "1100"; ELSE keysta <= "0001"; END IF; mscnt := 0; setf <='0'; WHEN "1100" => --key decode keysta <= "1101"; setf <='0'; CASE codtmp IS WHEN "001110" => keycode <= CONV_STD_LOGIC_VECTOR(1,4); ;中间部分译码程序略 WHEN OTHERS => keycode <= "1111"; END CASE; WHEN "1101" => keysta <= "0001"; setf <= '1'; WHEN OTHERS => keysta <= "0000"; setf <= '0'; END CASE; END IF; END IF; END PROCESS;
PROCESS(s_cnt) BEGIN CASE s_cnt IS WHEN "00" => scan <= "1110"; WHEN "01" => scan <= "1101"; WHEN "10" => scan <= "1011"; WHEN OTHERS =>scan <= "0111"; END CASE; END PROCESS;
PROCESS(rstn, setf, keycsn) BEGIN IF rstn = '0' OR keycsn = '0' THEN keyoktmp <= '1';复位后清中断标志,或者在片选无效时也清中断产生标志 ELSIF setf = '1' THEN keyoktmp <= '0';有键按下时,中断标志为0 ELSE keyoktmp <= keyoktmp; END IF; END PROCESS;
keyokn <= keyoktmp;有键按下时,产生一个下降沿,触发外部中断
keycod <= keycode WHEN keycsn = '0' ELSE (OTHERS => 'Z');读键值 END Behavior;
|