用状态机实现的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;
状态机内部不可以跑计数器的
WHEN "0001" => --shift state
s_cnt <= s_cnt + 1;--计数器哦
keysta <= "0011";
setf <= '0';
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) | Powered by Discuz! 7.0.0 |