状态机实现的键扫,程序如下:
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';
ELSE
keyoktmp <= keyoktmp;
END IF;
END PROCESS;
keyokn <= keyoktmp;
keycod <= keycode WHEN keycsn = '0' ELSE (OTHERS => 'Z');
END Behavior;
其中keyokn接到单片机外部中断线上,配置为下降沿中断(或低电平中断)
可是发现一个奇怪问题:第一次按键后能正常响应,第二次按键后程序就死机了,好像是第二次按键以后,键盘中断标志没有清除。
可是从程序来看,当按键中断产生后,有把keyoktmp恢复为1啊,不应该出现上面的现象啊?
请大侠们帮忙看看程序,谢谢!
如果可以的话,回邮箱更好,因我本人实在忙,上论坛时间少
谢谢
欢迎光临 电子技术论坛_中国专业的电子工程师学习交流社区-中电网技术论坛 (http://bbs.eccn.com/) | Powered by Discuz! 7.0.0 |