VHDL:EPM570 IIC和按键

来源:互联网 发布:小岛秀夫 知乎 编辑:程序博客网 时间:2024/06/04 21:15

EPM570扫描4X2键盘,通过IIC发送到LPC2141,然后发送到电脑,EPM570为主机端,LPC2141为从机

以下为CPLD的源代码:

--/*******************************************************************
-- *
-- *
-- *    AUTHOR:
-- *
-- *    HISTORY:
-- *
-- *******************************************************************/

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.std_logic_unsigned.ALL;

ENTITY KeyIIC IS
PORT (clk,reset : IN std_logic;
   KeyIn  : IN std_logic_vector(3 downto 0);
   led  : OUT std_logic_vector(3 downto 0);
   KeyOut : OUT std_logic_vector(1 downto 0);
   SCL  : OUT std_logic;
   SDA  : OUT std_logic);
END KeyIIC;


ARCHITECTURE behave OF KeyIIC IS
 SIGNAL led_reg  : std_logic_vector(3 downto 0);
 SIGNAL KeyOut_reg : std_logic_vector(1 downto 0);
 SIGNAL Key_reg  : std_logic_vector(7 downto 0);
 SIGNAL KeyIn_reg : std_logic_vector(3 downto 0);
 SIGNAL clk_iic  : std_logic;
 SIGNAL SDA_reg  : std_logic;
 SIGNAL clk_iic_div : std_logic_vector(2 downto 0);
 SIGNAL count  : std_logic_vector(3 downto 0);
 SIGNAL clk_key  : std_logic;
 SIGNAL key_down_up : std_logic_vector(1 downto 0);
 CONSTANT CLK_DIV_RANGE  :integer := 128;
 CONSTANT CLK_DIV_C   :integer := 10;
 CONSTANT CLK_DIV_IIC   :integer := 50;
 CONSTANT CLK_KEY_RANGE  :integer := 31;
 CONSTANT CLK_KEY_C   :integer := 10;
 TYPE state_key is (scan_state,key_state,keydown_state);
 TYPE state_iic is (idle,start,addr,data,stop,ack);
 SIGNAL keystate,nextstate_key :state_key;
 SIGNAL iicstate,nextstate_iic :state_iic;
 
BEGIN

 KeyOut <= KeyOut_reg;
 clk_out_gen : PROCESS (clk,reset)    --generate the scan clk
 VARIABLE clk_div : INTEGER RANGE 0 to CLK_DIV_RANGE;
 BEGIN
  if(reset ='0') then
   clk_div := 0;
  elsif(rising_edge(clk))then
   if(clk_div = CLK_DIV_IIC)then    --10000000/16
    clk_div := 0;
    clk_iic <= not clk_iic;
   else
    clk_div := clk_div + 1;
   end if;
  end if;
 END PROCESS;
 
 PROCESS (clk_iic,reset)    --generate the scan clk
 VARIABLE clk_div : INTEGER RANGE 0 to CLK_DIV_RANGE;
 BEGIN
  if(reset ='0') then
   clk_div := 0;
  elsif(rising_edge(clk_iic))then
   if(clk_div = CLK_DIV_C)then    --10000000/16
    clk_div := 0;
    clk_key <= not clk_key;
   else
    clk_div := clk_div + 1;
   end if;
  end if;
 END PROCESS;
 
 process(clk_iic,reset)      --rising edge change the iic state
 begin
  if(reset = '0') then
   iicstate <= idle;
  elsif(rising_edge(clk_iic)) then
   iicstate <= nextstate_iic;
  end if;
 END PROCESS;
 
 process(clk_iic,reset)      --scan the keyboard
 begin
  if(reset = '0') then
   key_down_up <= "00";
  elsif(rising_edge(clk_iic))then
   if(keystate = key_state and nextstate_key = keydown_state)then
    key_down_up <= "01";    --key down
   elsif(keystate = keydown_state and nextstate_key = scan_state)then
    key_down_up <= "10";   --key up
   else
    key_down_up <= "00";
   end if;
  end if;
 END PROCESS;

 process(clk_iic,reset)      --scan the keyboard
 begin
  if(reset = '0') then
   Key_reg(7 downto 6) <= "ZZ";
  elsif(rising_edge(clk_iic))then
   if(key_down_up /= "00") then    --key down
    Key_reg(7 downto 6) <= key_down_up;
   end if;
  end if;
 END PROCESS;

 process(clk_iic,reset)      --rising edge change the iic state
 VARIABLE sign :integer range 0 to 1;
 begin
  if(reset = '0') then
   nextstate_iic <= idle;
   SDA_reg <= '1';
  elsif(rising_edge(clk_iic)) then
   case iicstate is --start,data,stop
   when idle =>
    sign := 0;
    clk_iic_div <= "100";
    SDA_reg <= '1';
    if(key_down_up /= "00") then
     nextstate_iic <= start;
    end if;
   when start =>
    clk_iic_div <= clk_iic_div + 1;
    if(clk_iic_div = "111")then
     nextstate_iic <= addr;
     count <= "0000";
    elsif(clk_iic_div = "101")then
     SDA_reg <= '0';
    end if;
   when addr =>
    clk_iic_div <= clk_iic_div + 1;
    if(clk_iic_div = "001")then
     count <= count +1;
     case count is
     when "0000" =>
      SDA_reg <= '1';
     when "0001" =>
      SDA_reg <= '1';
     when "0010" =>
      SDA_reg <= '0';
     when "0011" =>
      SDA_reg <= '0';
     when "0100" =>
      SDA_reg <= '0';
     when "0101" =>
      SDA_reg <= '0';
     when "0110" =>
      SDA_reg <= '1';
     when "0111" =>
      SDA_reg <= '0';
     when others =>
      SDA_reg <= '1';
     end case;
    end if;
    if(clk_iic_div = "111" and count = "1000")then
     nextstate_iic <= ack;
    end if;
   when ack =>
    SDA_reg <= '1';
    clk_iic_div <= clk_iic_div + 1;
    if(clk_iic_div = "111")then
     if(sign = 0) then
      nextstate_iic <= data;
     else
      nextstate_iic <= stop;
     end if;
     count <= "0000";
    end if;
   when data =>
    sign := 1;
    clk_iic_div <= clk_iic_div + 1;
    if(clk_iic_div = "001")then
     count <= count +1;
     case count is
     when "0000" =>
      SDA_reg <= Key_reg(7);
     when "0001" =>
      SDA_reg <= Key_reg(6);
     when "0010" =>
      SDA_reg <= Key_reg(5);
     when "0011" =>
      SDA_reg <= Key_reg(4);
     when "0100" =>
      SDA_reg <= Key_reg(3);
     when "0101" =>
      SDA_reg <= Key_reg(2);
     when "0110" =>
      SDA_reg <= Key_reg(1);
     when "0111" =>
      SDA_reg <= Key_reg(0);
     when others =>
      SDA_reg <= '1';
     end case;
    end if;
    if(clk_iic_div = "111" and count = "1000")then
     nextstate_iic <= ack;
    end if;
   when stop =>
--    SDA_reg <= '1';
    clk_iic_div <= clk_iic_div + 1;
    if(clk_iic_div = "101")then
     SDA_reg <= '1';
     nextstate_iic <= idle;
    elsif(clk_iic_div = "001")then
     SDA_reg <= '0';
    end if;
   end case;
  end if;
 END PROCESS;

 SDA <= '0' when SDA_reg = '0' else 'Z'; 
 SCL <= '0' when clk_iic_div(2) = '0' else 'Z';
 led <= not led_reg;
 process(clk_iic,reset)      --show the key number pressed
 begin           --on leds
  if(rising_edge(clk_iic)) then
  case (Key_reg(5 downto 0)) is
  when "100111" =>
   led_reg <= "0001";
  when "101011" =>
   led_reg <= "0010";
  when "101101" =>
   led_reg <= "0011";
  when "101110" =>
   led_reg <= "0100";
  when "010111" =>
   led_reg <= "0101";
  when "011011" =>
   led_reg <= "0110";
  when "011101" =>
   led_reg <= "0111";
  when "011110" =>
   led_reg <= "1000";
  when others =>
   led_reg <= "0000";
  end case;
  end if;
 END PROCESS;
 
 process(clk_key,reset)      --rising edge change the key state
 begin
  if(reset = '0') then
   keystate <= scan_state;
  elsif(rising_edge(clk_key)) then
   keystate <= nextstate_key;
  end if;
 END PROCESS;

 process(clk_key,reset)      --scan the keyboard
 VARIABLE times : INTEGER RANGE 0 to 127;
 begin
  if(reset = '0') then
   nextstate_key <= scan_state;
   KeyOut_reg <= "01";
  elsif(rising_edge(clk_key)) then
   case keystate is
   when scan_state =>
    KeyOut_reg <= not KeyOut_reg;
    if(KeyIn /= "1111") then   --if any key pressed change the keystate
     KeyIn_reg <= KeyIn;
     nextstate_key <= key_state;
     times := 0;
    end if;
   when key_state =>
    times := times + 1;
    if(KeyIn_reg /= KeyIn)then    
     nextstate_key <= scan_state;
    elsif(times = 100) then     --if key pressed 10ms show the key
     Key_reg(5 downto 0) <= KeyOut_reg & KeyIn_reg;
     nextstate_key <= keydown_state;
    end if;
   when keydown_state =>
    if(KeyIn_reg /= KeyIn) then
     nextstate_key <= scan_state;
    end if;
   end case;
  end if;
 end process;
 
END behave;

原创粉丝点击