2

作为项目的一部分,我正在尝试在我的项目中实现一个非常基本的 I2C 模块(不是自己编写的)。I2C 模块使用数据线作为输入端口。我正在尝试验证包含 6 位地址 +“读取”位的数据“01010001”。我生成了以下添加代码的比特流,我试图用示波器测量端口的输出。我可以验证 SCL 上的时钟,但数据信号始终保持为零。我创建了一个包含时钟分频器的顶层模块,为 i2c 模块和 i2c 模块提供时钟。我将在下面添加两个 VHDL 文件。我的问题是:代码中是否有我无法弄清楚的错误,还是我改变了测量端口的方式?

I2C 模块:

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
--use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx leaf cells in this code.
--library UNISIM;
--use UNISIM.VComponents.all;

entity i2c_neuer_ansatz is
    Port
        (   CLK_800kHz_IN, RESET : in  STD_LOGIC;
            SCL : out  STD_LOGIC;
            SDA :inout  STD_LOGIC
        );
end i2c_neuer_ansatz;

architecture i2c_neuer_ansatz_arch of i2c_neuer_ansatz is
signal STATE: STD_LOGIC_VECTOR (7 DOWNTO 0);
    signal SDAINT: STD_LOGIC;
    signal BITCOUNT: integer range 0 to 8;
constant SLAVEADD_READ: STD_LOGIC_VECTOR (7 DOWNTO 0):= "01010001"; 
---- I2C slave address + read
constant SLAVEADD_WRITE: STD_LOGIC_VECTOR (7 DOWNTO 0):= "01010000"; 
---- I2C slave address + write

signal DATA_IN: STD_LOGIC_VECTOR (7 DOWNTO 0):= "00000000"; 
---- Data to be written to slave

signal DATA_OUT: STD_LOGIC_VECTOR (7 DOWNTO 0):= "00000000"; 
---- Data to be read from slave

--  ---- for 100 kHz clock
--  constant max200k: integer:= 250;
--  signal clocktick200k: integer range 0 to max200k;
    signal CLK_800kHz: STD_LOGIC;
---- CLK_200KHz is a 200 kHz clock and SCL is a 100kHz clock for i2c


begin
---- SDA line is open drain, therefore to set SDA to '1' we need to output a 'Z' value
    SDA <= 'Z' when SDAINT = '1' else '0';

---- process for implementing FSM for the I2C master controller
    CLK_800kHz <= CLK_800kHz_IN;
Output: process (CLK_800kHz, RESET)
    begin
        if (RESET = '0') then 
            ---- idle condition, both SDA and SCL = 1
            SCL <= '1';
            SDAINT <= '1';
            State <= x"00";         ---- next state

--      elsif (CLK_800kHz'event and CLK_800kHz = '1')then
        elsif rising_edge(CLK_800kHz) then
            case STATE is 

when x"00" =>
            ---- when idle, both SDA and SCL = 1
                SCL <= '1';
                SDAINT <= '1';
                state <= x"01";     ---- next state

            when x"01" =>
            ---- send start condition SDA goes from '1' to '0' with SCL = '1'
                SCL <= '1';
                SDAINT <= '0';
                BITCOUNT <= 8;      ---- starting bit count
                state <= x"40";     ---- next state

            when x"02" =>
            ---- send seven bit address of the slave followed by write/read bit 
                SCL <= '0';
                SDAINT <= SLAVEADD_WRITE (BITCOUNT - 1);
---- sending the seven bit address and write bits on SDA line
                state <= x"03";     ---- next state

            when x"03" =>
                SCL <= '1';
                if (BITCOUNT)> 0 then 
---- if BITOCUNT > 0, then there are more bits to be sent.  Therefore, go to state x"02" 
                    BITCOUNT <= BITCOUNT - 1;
                    State <= x"02"; ---- next state                     
                else 
---- If BITCOUNT = 0, then all bits have been sent. Go to state x"12" and Set the value of BITCOUNT to 8
                    BITCOUNT <= 8;
                    State <= x"12"; ---- next state
                end if;

---- address and write bits have been sent and now get acknowledgement from slave
            when x"12" =>
                SCL <= '0';
                state <= x"13";     ---- next state

            when x"13" =>
                SCL <= '1';
                if SDA = '1' then 
---- if no acknowledgement from slave, go to the idle state. The designer can create an error state if desired and send the FSM there. 
                    state <= x"00"; ---- next state
                else
---- if there is acknowledgement from slave, go to state x"40" for reading data to the slave
                    State <= x"40"; ---- next state
                end if;

            ---- writing data to slave
            when x"30" =>
                SCL <= '0';
---- sending the data to be written on the SDA line
                SDAINT <= DATA_IN (BITCOUNT - 1);
                state <= x"31";     ---- next state

            when x"31" =>
                SCL <= '1';
                if (BITCOUNT)> 0 then 
---- if BITOCUNT > 0, then there are more bits to be sent.  Therefore, go to state x"30"
                    BITCOUNT <= BITCOUNT - 1;
                    state <= x"30"; ---- next state
                else
---- If BITCOUNT = 0, then all bits have been sent. Go to state x"32" 
                    state <= x"32"; ---- next state
                    end if;

            ----get acknowledgement from slave
            when x"32" =>
                SCL <= '0';
                state <= x"33";     ---- next state

            when x"33" =>
                BITCOUNT <= 8;
                SCL <= '1';
                if SDA = '1' then 
                    state <= x"00"; ---- next state
                else
                    SDAINT <= '0';
                    state <= x"34"; ---- next state
                end if;

            when x"34" =>
                SCL <= '0';
-- SDA starts at 0 to prepare for the 0 to 1 transition
                SDAINT <= '0';  
                state <= x"42";     ---- next state

            ---- read from slave
            when x"40" =>
            ---- send seven bit address of the slave followed by read bit 
                SCL <= '0';
                SDAINT <= SLAVEADD_READ (BITCOUNT - 1);
                state <= x"41"; ---- next state

            when x"41" =>
                SCL <= '1';
                if (BITCOUNT)> 0 then 
                    BITCOUNT <= BITCOUNT - 1;
                    state <= x"40"; ---- next state
                else
                    BITCOUNT <= 8;
                    state <= x"50";
            end if;

            ---- get acknowledgement from slave
            when x"50" =>
                BITCOUNT <= 8;
                SCL<= '0';                                          
                state <= x"51";     ---- next state
            when x"51" =>
                SCL <= '1';
                if SDA = '1' then 
                    state <= x"00";
                else
                    BITCOUNT <= 8;
                    state <= x"52";
                end if;

            when x"52" =>
                SCL <= '0';
                state <= x"53";

            when x"53" =>
                SCL <= '1';
                DATA_OUT(bitcount-1) <= SDA;    
                if (bitcount - 1) > 0 then
                    bitcount <= bitcount - 1;
                    state <= x"54";
                else
                    bitcount <= 8;
                    state <= x"00";
                end if;

            when others =>
                state <= x"00";

            end case;   
            end if;         

    end process;    

------ process for generating 200 kHz clock from 50 MHz input clock

--Clk200kHz: process

--  begin
--      wait until CLK_50MHz'event and CLK_50MHz = '1';

--          if clocktick200k < max200k then 
--              clocktick200k <= clocktick200k + 1;
--          else
--              clocktick200k <= 0;
--          end if;

--          if clocktick200k < max200k/2 then 
--              CLK_200KHz <= '0';
--          else
--              CLK_200KHz <= '1';
--          end if;

--  end process;



end i2c_neuer_ansatz_arch;

该模块由以下代码组成:

entity i2cNew_clk is
    Port
        (
            MAIN_CLK_IN     :   in std_logic;
            MAIN_RESET_I2C  :   in std_logic;
            MAIN_RESET_CLK  :   in std_logic;

            MAIN_SDA        :   inout std_logic;
            MAIN_SCL        :   out std_logic;
            MAIN_CLK_TEST   :   out std_logic
        );
end i2cNew_clk;

architecture i2cNew_clk_arch of i2cNew_clk is
    component i2c_neuer_ansatz is
        Port
        (   CLK_800kHz_IN, RESET : in  STD_LOGIC;
            SCL : out  STD_LOGIC;
            SDA :inout  STD_LOGIC
        );
    end component i2c_neuer_ansatz;

    signal sign_SCL :   std_logic;
    signal sign_SDA :   std_logic;
    signal sign_RESET   :   std_logic;


    component clk_gen is
    Generic
        (
            sysclk_generic      :   natural := 125000000;   --250MHz vom Board
            clk_out_generic     :   natural := 1000000;     --1MHz für General
            i2c_clk_generic     :   natural := 800000       --800kHz für I2C
        );
    Port
        (
            SYSCLK_IN_CLK_GEN   :   in std_logic;
            RST_IN_CLK_GEN      :   in std_logic;

            CLK_THRU                : out std_logic;
            CLK_OUT_GENERAL_CLK_GEN : out std_logic;
            CLK_OUT_I2C_CLK_GEN     : out std_logic
        );
    end component clk_gen;

    signal sign_SYSCLK_IN_CLK_GEN   :   std_logic;
    signal sign_RST_IN_CLK_GEN      :   std_logic;
    signal sign_CLK_THRU    :   std_logic;
    signal sign_CLK_OUT_GENERAL_CLK_GEN :   std_logic;
    signal sign_CLK_OUT_I2C_CLK_GEN     :   std_logic;
begin

    sign_RESET <= MAIN_RESET_I2C;
    sign_RST_IN_CLK_GEN <= MAIN_RESET_CLK;  
    sign_SYSCLK_IN_CLK_GEN  <=  MAIN_CLK_IN;
    MAIN_SDA    <=  sign_SDA;
    MAIN_SCL    <=  sign_SCL;
    MAIN_CLK_TEST <= sign_CLK_OUT_I2C_CLK_GEN;

    i2c_neuer_ansatz_inst : i2c_neuer_ansatz
        port map
            (
                CLK_800kHz_IN   =>  sign_CLK_OUT_I2C_CLK_GEN,
                RESET           =>  sign_RESET,
                SCL             =>  sign_SCL,
                SDA             =>  sign_SDA                
            );

    clk_gen_inst    :   clk_gen
        port map
            (
                SYSCLK_IN_CLK_GEN    =>  sign_SYSCLK_IN_CLK_GEN,
                RST_IN_CLK_GEN      =>  sign_RST_IN_CLK_GEN,
                CLK_THRU            =>  sign_CLK_THRU,
                CLK_OUT_GENERAL_CLK_GEN => sign_CLK_OUT_GENERAL_CLK_GEN,
                CLK_OUT_I2C_CLK_GEN =>  sign_CLK_OUT_I2C_CLK_GEN                
            );


end i2cNew_clk_arch;

编辑:更新了 i2c 模块的代码。我评论了写给奴隶的整个部分


library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity i2c_neuer_ansatz is
    Port
        (   CLK_800kHz_IN, RESET : in  STD_LOGIC;
            SCL : out  STD_LOGIC;
            SDA :inout  STD_LOGIC
        );
end i2c_neuer_ansatz;

architecture i2c_neuer_ansatz_arch of i2c_neuer_ansatz is
signal STATE: STD_LOGIC_VECTOR (7 DOWNTO 0);
--  signal SDAINT: STD_LOGIC;
    signal BITCOUNT: integer range 0 to 8;
constant SLAVEADD_READ: STD_LOGIC_VECTOR (7 DOWNTO 0):= "01010001"; 
---- I2C slave address + read
constant SLAVEADD_WRITE: STD_LOGIC_VECTOR (7 DOWNTO 0):= "01010000"; 
---- I2C slave address + write

signal DATA_IN: STD_LOGIC_VECTOR (7 DOWNTO 0):= "00000000"; 
---- Data to be written to slave

signal DATA_OUT: STD_LOGIC_VECTOR (7 DOWNTO 0):= "00000000"; 
---- Data to be read from slave

--  ---- for 100 kHz clock
--  constant max200k: integer:= 250;
--  signal clocktick200k: integer range 0 to max200k;
    signal CLK_800kHz: STD_LOGIC;
---- CLK_200KHz is a 200 kHz clock and SCL is a 100kHz clock for i2c

    signal SDA_OUT :   std_logic;
    signal SDA_IN  :   std_logic;
    signal SDA_OE  :   std_logic;
begin
------ SDA line is open drain, therefore to set SDA to '1' we need to output a 'Z' value
--  SDA <= 'Z' when SDAINT = '1' else '0';
    SDA <= SDA_OUT when SDA_OE = '1' else 'Z';
    SDA_IN <= SDA when SDA_OE = '0' else '0';

---- process for implementing FSM for the I2C master controller
    CLK_800kHz <= CLK_800kHz_IN;
Output: process (CLK_800kHz, RESET)
    begin
        if (RESET = '0') then 
            ---- idle condition, both SDA and SCL = 1
            SCL <= '1';
            SDA_OUT <= '1';
            SDA_OE <= '1';
            State <= x"00";         ---- next state

--      elsif (CLK_800kHz'event and CLK_800kHz = '1')then
        elsif rising_edge(CLK_800kHz) then
            case STATE is 

when x"00" =>
            ---- when idle, both SDA and SCL = 1
                SCL <= '1';
                SDA_OE <= '1';
                SDA_OUT <= '1';
                state <= x"01";     ---- next state

            when x"01" =>
            ---- send start condition SDA goes from '1' to '0' with SCL = '1'
                SCL <= '1';
                SDA_OE <= '1';
                SDA_OUT <= '0';
                BITCOUNT <= 8;      ---- starting bit count
                state <= x"40";     ---- next state

--          when x"02" =>
--          ---- send seven bit address of the slave followed by write/read bit 
--              SCL <= '0';
--              SDAINT <= SLAVEADD_WRITE (BITCOUNT - 1);
------ sending the seven bit address and write bits on SDA line
--              state <= x"03";     ---- next state

--          when x"03" =>
--              SCL <= '1';
--              if (BITCOUNT)> 0 then 
------ if BITOCUNT > 0, then there are more bits to be sent.  Therefore, go to state x"02" 
--                  BITCOUNT <= BITCOUNT - 1;
--                  State <= x"02"; ---- next state                     
--                else 
------ If BITCOUNT = 0, then all bits have been sent. Go to state x"12" and Set the value of BITCOUNT to 8
--                    BITCOUNT <= 8;
--                  State <= x"12"; ---- next state
--              end if;

------ address and write bits have been sent and now get acknowledgement from slave
--          when x"12" =>
--              SCL <= '0';
--              state <= x"13";     ---- next state

--          when x"13" =>
--              SCL <= '1';
--              if SDA = '1' then 
------ if no acknowledgement from slave, go to the idle state. The designer can create an error state if desired and send the FSM there. 
--                  state <= x"00"; ---- next state
--              else
------ if there is acknowledgement from slave, go to state x"40" for reading data to the slave
--                  State <= x"40"; ---- next state
--              end if;

--          ---- writing data to slave
--          when x"30" =>
--              SCL <= '0';
------ sending the data to be written on the SDA line
--              SDAINT <= DATA_IN (BITCOUNT - 1);
--              state <= x"31";     ---- next state

--          when x"31" =>
--              SCL <= '1';
--              if (BITCOUNT)> 0 then 
------ if BITOCUNT > 0, then there are more bits to be sent.  Therefore, go to state x"30"
--                  BITCOUNT <= BITCOUNT - 1;
--                  state <= x"30"; ---- next state
--              else
------ If BITCOUNT = 0, then all bits have been sent. Go to state x"32" 
--                  state <= x"32"; ---- next state
--                  end if;

--          ----get acknowledgement from slave
--          when x"32" =>
--              SCL <= '0';
--              state <= x"33";     ---- next state

--          when x"33" =>
--              BITCOUNT <= 8;
--              SCL <= '1';
--              if SDA = '1' then 
--                  state <= x"00"; ---- next state
--              else
--                  SDAINT <= '0';
--                  state <= x"34"; ---- next state
--              end if;

--          when x"34" =>
--              SCL <= '0';
---- SDA starts at 0 to prepare for the 0 to 1 transition
--              SDAINT <= '0';  
--              state <= x"42";     ---- next state

            ---- read from slave
            when x"40" =>
            ---- send seven bit address of the slave followed by read bit 
                SCL <= '0';
                SDA_OE <= '1';
                SDA_OUT <= SLAVEADD_READ (BITCOUNT - 1);
                state <= x"41"; ---- next state

            when x"41" =>
                SCL <= '1';
                if (BITCOUNT)> 0 then 
                    BITCOUNT <= BITCOUNT - 1;
                    state <= x"40"; ---- next state
                else
                    BITCOUNT <= 8;
                    state <= x"50";
            end if;

            ---- get acknowledgement from slave
            when x"50" =>
                BITCOUNT <= 8;
                SDA_OE <= '0';
                SCL<= '0';                                          
                state <= x"51";     ---- next state
            when x"51" =>
                SCL <= '1';
                SDA_OE <= '0';
                if SDA = '1' then 
                    state <= x"00";
                else
                    BITCOUNT <= 8;
                    state <= x"52";
                end if;

            when x"52" =>
                SDA_OE <= '0';
                SCL <= '0';
                state <= x"53";

            when x"53" =>
                SCL <= '1';
                SDA_OE <= '0';
                DATA_OUT(bitcount-1) <= SDA;    
                if (bitcount - 1) > 0 then
                    bitcount <= bitcount - 1;
                    state <= x"54";
                else
                    bitcount <= 8;
                    state <= x"00";
                end if;

            when others =>
                state <= x"00";

            end case;   
            end if;         

    end process;    

------ process for generating 200 kHz clock from 50 MHz input clock

--Clk200kHz: process

--  begin
--      wait until CLK_50MHz'event and CLK_50MHz = '1';

--          if clocktick200k < max200k then 
--              clocktick200k <= clocktick200k + 1;
--          else
--              clocktick200k <= 0;
--          end if;

--          if clocktick200k < max200k/2 then 
--              CLK_200KHz <= '0';
--          else
--              CLK_200KHz <= '1';
--          end if;

--  end process;



end i2c_neuer_ansatz_arch;

4

1 回答 1

2

你的三态管理不好。

您的主要问题在这一行,我认为评论是错误的:

---- SDA line is open drain, therefore to set SDA to '1' we need to output a 'Z' value
    SDA <= 'Z' when SDAINT = '1' else '0';

为了在 VHDL 中管理INOUT,您的模块i2c_neuer_ansatz中应该有三个内部信号:

  • 一种用于输入行为:SDA_IN
  • 一种用于输出行为:SDA_OUT(您称其为 SDAINT)
  • 一种在输入和输出模式之间进行管理:SDA_OE(代表输出使能)

这是你应该写的那一行,而不是我引用的那一行:

SDA <= SDA_OUT when SDA_OE = '1' else 'Z';

您可以使用这一行来处理输入模式:

SDA_IN <= SDA when SDA_OE = '0' else '0';

但是这条第二行不是必需的,您也可以直接使用 SDA 作为代码中的输入(您已经这样做了)。

在您的代码中,您只需添加输出启用管理。

于 2019-07-01T12:26:26.767 回答