状态机实现的键扫,程序如下:
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啊,不应该出现上面的现象啊?
请大侠们帮忙看看程序,谢谢!
如果可以的话,回邮箱更好,因我本人实在忙,上论坛时间少
chenye_cau@163.com
谢谢 |