I2Cバスで選択したスレーブ応答を変更できるモジュールを設計しようとしています。これは元のバス構成です(わかりやすくするためにプルアップと電源接続は示していません)。
このバスには2つのデバイスしかなく、100kHzしかありません。コントローラーMCU(I2Cマスター)およびRFIDカードリーダー(I2Cスレーブ)NXP PN512。コントローラーのファームウェアを変更したり、I2Cバスのトランザクションを変更したりできません。良い点は、コントローラーが2種類のトランザクションのみを送信することです。
Master (Write Register) - <s><address+W><register number><data><p>
Master (Read Register) - <s><address+W><register number><p><s><address+R><data><p>
私がしたいことは、マスターレジスタの読み取り中に選択したデータバイトを自分のバイトで置き換えることです。MCUが読み取りたいレジスタ番号をUART(921.6kbaud)経由でPCに送信できます。C / C ++またはPythonで処理できます。値を置き換える必要のあるレジスター番号を受け取ったら、偽のバイトをデバイスに送り返すことができます。コントローラーに送り返して、元のカードの応答を置き換えます。
Arduino Nanoを試し、その後クロックストレッチを使用してCPLDを試しました。MCUコントローラーに面したATmega328ハードウェアI2Cは、前の停止サイクルの5usより前に開始シーケンスが生成されることがあったため、追いつくことができませんでした。したがって、時々、AVRは読み取りトランザクションをNAKしていました。CPLDは、バスストレッチがMCUで無効になっていることが判明した停止/開始速度を処理できました。
マスターレジスタの読み取りは、その後に読み取りが続くと確信しているので、1バイトの書き込みを検出することで、読み取りを「予測」できるというアイデアを思いつきました。次のリードサイクルアドレス書き込み中に、スレーブからバイトを取り込むのに十分な時間があったようです。それはうまくいきませんでした。バストランザクションは最初(約最初の5秒間)は正常に見えましたが、コントローラーはバス上のすべての通信を停止し、タグの読み取りと直接通信していないことを検出したかのように処理していました。
カードリーダーは、マスターへの割り込みを生成することもできます。IRQは、タイマーまたはイベントベースです。この問題は、バスに本来導入されていた遅延が原因であると考えています。私は間違っていたかもしれませんが、別の「ゼロ遅延」設計を思いつきました。
私が考えているのは、SDAラインを切断し、SCLラインをマスターとスレーブの間に接続したままにすることだけです。このようにして、データラインのバイトをどちらの方向でも置き換えることができます。バスサイクルに基づいてSDAラインの方向を制御する必要があるため、設計がより複雑であることがわかりました。バストランザクションを処理し、16進バイトをUART経由でコンピューターに送信するVHDLコードを次に示します。コンピューターからのバイトの受信はまだ実装されていません。
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity I2C_Sniffer is
port (
clk : in std_logic;
scl_master : in std_logic;
sda_master : inout std_logic;
sda_slave : inout std_logic;
tx : out std_logic
);
end entity I2C_Sniffer;
architecture arch of I2C_Sniffer is
signal clkDiv: std_logic_vector(7 downto 0) := (others => '0');
type I2C_STATE is (I2C_IDLE, I2C_MASTER_WRITE, I2C_SLAVE_ACK, I2C_MASTER_READ, I2C_MASTER_ACK);
signal i2cState: I2C_STATE := I2C_IDLE;
type I2C_BUS_DIR is (MASTER_TO_SLAVE, SLAVE_TO_MASTER);
signal i2cBusDir: I2C_BUS_DIR := MASTER_TO_SLAVE;
signal i2cRxData: std_logic_vector(7 downto 0);
signal i2cCntr: integer range 0 to 8 := 0;
signal i2cAddr: std_logic := '1';
signal i2cCmd: std_logic := '0';
signal scl_d: std_logic := '1';
signal scl: std_logic := '1';
signal sda_d: std_logic := '1';
signal sda: std_logic := '1';
--Strobes for SCL edges and Start/Stop bits
signal start_strobe : std_logic := '0';
signal stop_strobe : std_logic := '0';
signal scl_rising_strobe : std_logic := '0';
signal scl_falling_strobe : std_logic := '0';
type UART_STATE is (UART_IDLE, UART_START, UART_DATA, UART_STOP);
signal uartState: UART_STATE := UART_IDLE;
signal uartTxRdy: std_logic := '0';
signal uartTxData: std_logic_vector(7 downto 0);
signal uartCntr: integer range 0 to 8 := 0;
begin
CLK_DIV: process (clk)
begin
if rising_edge(clk) then
clkDiv <= std_logic_vector(unsigned(clkDiv) + 1);
end if;
end process;
I2C_STROBES: process (clk)
begin
if rising_edge(clk) then
--Pipelined SDA and SCL signals
scl_d <= scl_master;
scl <= scl_d;
scl_rising_strobe <= '0';
if scl = '0' and scl_d = '1' then
scl_rising_strobe <= '1';
end if;
scl_falling_strobe <= '0';
if scl = '1' and scl_d = '0' then
scl_falling_strobe <= '1';
end if;
if i2cBusDir = MASTER_TO_SLAVE then
sda_d <= sda_master;
sda <= sda_d;
else
sda_d <= sda_slave;
sda <= sda_d;
end if;
start_strobe <= '0';
if sda_d = '0' and sda = '1' and scl = '1' and scl_d = '1' then
start_strobe <= '1';
end if;
stop_strobe <= '0';
if sda_d = '1' and sda = '0' and scl = '1' and scl_d = '1' then
stop_strobe <= '1';
end if;
end if;
end process;
BUS_DIR: process(sda_master, sda_slave, i2cBusDir)
begin
if i2cBusDir = MASTER_TO_SLAVE then
sda_slave <= sda_master;
sda_master <= 'Z';
else
sda_master <= sda_slave;
sda_slave <= 'Z';
end if;
end process;
I2C: process(clk)
begin
if rising_edge(clk) then
uartTxRdy <= '0';
case i2cState is
when I2C_IDLE =>
i2cBusDir <= MASTER_TO_SLAVE;
if start_strobe = '1' then
i2cAddr <= '1';
i2cCntr <= 0;
i2cState <= I2C_MASTER_WRITE;
end if;
-- Master Write (Address/Data)
when I2C_MASTER_WRITE =>
i2cBusDir <= MASTER_TO_SLAVE;
if stop_strobe = '1' then
i2cState <= I2C_IDLE;
uartTxData <= "00001010";
uartTxRdy <= '1';
end if;
if scl_rising_strobe = '1' then
if i2cCntr <= 7 then
i2cRxData(7 - i2cCntr) <= sda;
i2cCntr <= i2cCntr + 1;
end if;
end if;
if i2cCntr = 4 then
case i2cRxData(7 downto 4) is
when "0000" => uartTxData <= "00110000"; --0
when "0001" => uartTxData <= "00110001"; --1
when "0010" => uartTxData <= "00110010"; --2
when "0011" => uartTxData <= "00110011"; --3
when "0100" => uartTxData <= "00110100"; --4
when "0101" => uartTxData <= "00110101"; --5
when "0110" => uartTxData <= "00110110"; --6
when "0111" => uartTxData <= "00110111"; --7
when "1000" => uartTxData <= "00111000"; --8
when "1001" => uartTxData <= "00111001"; --9
when "1010" => uartTxData <= "01000001"; --A
when "1011" => uartTxData <= "01000010"; --B
when "1100" => uartTxData <= "01000011"; --C
when "1101" => uartTxData <= "01000100"; --D
when "1110" => uartTxData <= "01000101"; --E
when "1111" => uartTxData <= "01000110"; --F
when others => uartTxData <= "00111111"; --?
end case;
uartTxRdy <= '1';
end if;
if i2cCntr = 8 then
case i2cRxData(3 downto 0) is
when "0000" => uartTxData <= "00110000"; --0
when "0001" => uartTxData <= "00110001"; --1
when "0010" => uartTxData <= "00110010"; --2
when "0011" => uartTxData <= "00110011"; --3
when "0100" => uartTxData <= "00110100"; --4
when "0101" => uartTxData <= "00110101"; --5
when "0110" => uartTxData <= "00110110"; --6
when "0111" => uartTxData <= "00110111"; --7
when "1000" => uartTxData <= "00111000"; --8
when "1001" => uartTxData <= "00111001"; --9
when "1010" => uartTxData <= "01000001"; --A
when "1011" => uartTxData <= "01000010"; --B
when "1100" => uartTxData <= "01000011"; --C
when "1101" => uartTxData <= "01000100"; --D
when "1110" => uartTxData <= "01000101"; --E
when "1111" => uartTxData <= "01000110"; --F
when others => uartTxData <= "00111111"; --?
end case;
uartTxRdy <= '1';
end if;
if i2cCntr = 8 then
if scl_falling_strobe = '1' then
i2cState <= I2C_SLAVE_ACK;
if i2cAddr = '1' then
i2cCmd <= i2cRxData(0);
i2cAddr <= '0';
end if;
end if;
end if;
when I2C_SLAVE_ACK =>
i2cBusDir <= SLAVE_TO_MASTER;
if scl_falling_strobe = '1' then
i2cCntr <= 0;
if i2cCmd = '0' then
i2cState <= I2C_MASTER_WRITE;
else
i2cState <= I2C_MASTER_READ;
end if;
end if;
when I2C_MASTER_READ =>
i2cBusDir <= SLAVE_TO_MASTER;
if stop_strobe = '1' then
i2cState <= I2C_IDLE;
uartTxData <= "00001010";
uartTxRdy <= '1';
end if;
if scl_rising_strobe = '1' then
if i2cCntr <= 7 then
i2cRxData(7 - i2cCntr) <= sda;
i2cCntr <= i2cCntr + 1;
end if;
end if;
if i2cCntr = 4 then
case i2cRxData(7 downto 4) is
when "0000" => uartTxData <= "00110000"; --0
when "0001" => uartTxData <= "00110001"; --1
when "0010" => uartTxData <= "00110010"; --2
when "0011" => uartTxData <= "00110011"; --3
when "0100" => uartTxData <= "00110100"; --4
when "0101" => uartTxData <= "00110101"; --5
when "0110" => uartTxData <= "00110110"; --6
when "0111" => uartTxData <= "00110111"; --7
when "1000" => uartTxData <= "00111000"; --8
when "1001" => uartTxData <= "00111001"; --9
when "1010" => uartTxData <= "01000001"; --A
when "1011" => uartTxData <= "01000010"; --B
when "1100" => uartTxData <= "01000011"; --C
when "1101" => uartTxData <= "01000100"; --D
when "1110" => uartTxData <= "01000101"; --E
when "1111" => uartTxData <= "01000110"; --F
when others => uartTxData <= "00111111"; --?
end case;
uartTxRdy <= '1';
end if;
if i2cCntr = 8 then
case i2cRxData(3 downto 0) is
when "0000" => uartTxData <= "00110000"; --0
when "0001" => uartTxData <= "00110001"; --1
when "0010" => uartTxData <= "00110010"; --2
when "0011" => uartTxData <= "00110011"; --3
when "0100" => uartTxData <= "00110100"; --4
when "0101" => uartTxData <= "00110101"; --5
when "0110" => uartTxData <= "00110110"; --6
when "0111" => uartTxData <= "00110111"; --7
when "1000" => uartTxData <= "00111000"; --8
when "1001" => uartTxData <= "00111001"; --9
when "1010" => uartTxData <= "01000001"; --A
when "1011" => uartTxData <= "01000010"; --B
when "1100" => uartTxData <= "01000011"; --C
when "1101" => uartTxData <= "01000100"; --D
when "1110" => uartTxData <= "01000101"; --E
when "1111" => uartTxData <= "01000110"; --F
when others => uartTxData <= "00111111"; --?
end case;
uartTxRdy <= '1';
end if;
if i2cCntr = 8 and scl_falling_strobe = '1' then
i2cState <= I2C_MASTER_ACK;
end if;
when I2C_MASTER_ACK =>
i2cBusDir <= MASTER_TO_SLAVE;
if scl_falling_strobe = '1' then
i2cCntr <= 0;
end if;
if stop_strobe = '1' then
i2cState <= I2C_IDLE;
uartTxData <= "00001010"; -- \n
uartTxRdy <= '1';
end if;
end case;
end if;
end process;
UART: process (clk, clkDiv(1), uartTxRdy)
begin
if rising_edge(clk) then
case uartState is
when UART_IDLE =>
if uartTxRdy = '1' then
uartState <= UART_START;
end if;
when UART_START =>
if clkDiv(1 downto 0) = "00" then
tx <= '0';
uartState <= UART_DATA;
uartCntr <= 0;
end if;
when UART_DATA =>
if clkDiv(1 downto 0) = "00" then
if uartCntr <= 7 then
uartCntr <= uartCntr + 1;
tx <= uartTxData(uartCntr);
else
tx <= '1';
uartState <= UART_STOP;
end if;
end if;
when UART_STOP =>
if clkDiv(1 downto 0) = "00" then
tx <= '1';
uartState <= UART_IDLE;
end if;
end case;
end if;
end process;
end architecture arch;
以下は、SDAラインを制御するCPLDでキャプチャされたバス変換です。
レジスタ書き込み:
レジスター読み取り:
バスの方向が変わると、いくつかの不具合が見られます。これは、CPLDがバスの方向を変更するタイミングと、カードリーダーがACKを生成するタイミングの違いが原因です。ACKレベルは、SCLの立ち上がりエッジで安定しているようです。私が知る限り、それで十分です。
このことを行うと、コントローラーは、数秒以内にバスのアクティビティを中断するスプリットバスの場合と同じように動作します。私はまた、そのMCUをモックしてバストラフィックを生成するArduinoをテストしました。Arduinoも時々フリーズするようです。したがって、VHDLステートマシンで何らかの問題が発生し、状況によっては1つの状態から抜け出せなくなる可能性があります。何か案は?
There's only 2 devices on this bus running at 100kHz
、それからThe hardware I2C was a slave and a bit banged I2C was a master on the card reader bus at 1Mbps
。なぜ2つのバスがあるのですか?なぜ高速バスが必要なのですか?初期設計のスケッチを提供し、質問を明確にしてください。