我一直在尝试设计一个模块,该模块将允许我修改I2C总线上的选定从属响应。这是原始总线配置(为清楚起见,未显示上拉和电源连接:
该总线上只有2个设备,并且只有100kHz。控制器MCU(I2C主设备)和RFID读卡器(I2C从设备)NXP PN512。我无法修改控制器固件或更改I2C总线事务。好消息是控制器仅发送两种交易类型:
Master (Write Register) - <s><address+W><register number><data><p>
Master (Read Register) - <s><address+W><register number><p><s><address+R><data><p>
我想做的是用我自己的字节替换在主寄存器读取期间选择的数据字节。我可以通过UART(921.6kbaud)将MCU希望读取的寄存器号发送到我的PC。我可以在那里用C / C ++或Python处理它们。当我收到需要替换其值的寄存器号时,可以将假字节发送回我的设备,它将负责将其发送回控制器,以替换原始卡的响应。
我尝试了Arduino Nano,后来尝试了使用时钟扩展的CPLD。面对MCU控制器的ATmega328硬件I2C不能跟上,因为有时启动序列的产生早于上一个停止周期后的5us。因此,AVR时不时要进行NAK读取事务。CPLD可以处理停止/启动速度,结果证明在MCU中禁用了总线扩展。
我想出了一个可以通过检测单字节写入来“预测”主寄存器读取的想法,因为我确信它后面会进行读取。在下一个读周期地址写入期间,似乎有足够的时间从从设备引入字节。那不是很有效。总线事务在开始时(大约前5秒)似乎还不错,但随后控制器停止了总线上的所有通信,就好像它检测到不是在直接与标签读取对话一样。
读卡器还可以产生对主机的中断。IRQ是基于计时器或事件的。我将问题归因于本来是在总线上引入的延迟。我可能错了,但我想出了另一种“零延迟”设计。
我的想法是,我只能断开SDA线,而使SCL线保持连接在主机和从机之间。这样,我仍然可以在任一方向上替换数据线上的字节。由于我必须根据总线周期来控制SDA线的方向,因此设计变得更加复杂。这是处理总线事务并通过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;
以下是CPLD控制SDA线捕获的总线转换。
注册写:
注册阅读:
当总线方向改变时,您会看到一些故障。这是由于CPLD更改总线方向与读卡器生成ACK之间的时序差异引起的。ACK电平在SCL的上升沿似乎稳定。据我所知,这就是您所需要的。
有了这个东西,控制器的行为就与拆分总线在几秒钟内暂停任何总线活动的方式相同。我还测试了可以模拟我的MCU并为我生成总线流量的Arduino,而且Arduino有时也会冻结。所以我想我在VHDL状态机上可能会遇到某种问题,在某些情况下,我陷入了一种状态而没有出路。有任何想法吗?
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
。为什么有两辆公共汽车?为什么需要高速巴士?提供初始设计的草图,并尝试阐明您的问题。