Tôi đã cố gắng thiết kế một mô-đun cho phép tôi sửa đổi các phản hồi nô lệ được chọn trên xe buýt I2C. Đây là cấu hình bus gốc (kết nối pull-up và power không được hiển thị cho rõ ràng:
Chỉ có 2 thiết bị trên xe buýt này và nó chỉ có 100kHz. Bộ điều khiển MCU (I2C master) và đầu đọc thẻ RFID (nô lệ I2C) NXP PN512. Tôi không thể sửa đổi phần sụn của bộ điều khiển hoặc thay đổi các giao dịch bus I2C. Phần tốt là Bộ điều khiển chỉ gửi 2 loại giao dịch:
Master (Write Register) - <s><address+W><register number><data><p>
Master (Read Register) - <s><address+W><register number><p><s><address+R><data><p>
Những gì tôi muốn làm là thay thế các byte dữ liệu được chọn trong quá trình đăng ký Master được đọc bằng các byte của riêng tôi. Tôi có thể gửi số đăng ký mà MCU muốn đọc cho PC của tôi qua UART (921.6kbaud). Tôi có thể xử lý chúng trong C / C ++ hoặc Python ở đó. Khi tôi nhận được số đăng ký có giá trị cần thay thế, tôi có thể gửi lại byte giả cho thiết bị của mình và sẽ gửi lại cho bộ điều khiển thay thế phản hồi thẻ gốc.
Lúc đầu, tôi chia xe buýt I2C thành hai xe buýt:
Tôi đã thử Arduino Nano và sau đó là CPLD bằng cách kéo dài đồng hồ. Phần cứng I2C của ATmega328 đối mặt với bộ điều khiển MCU không thể theo kịp vì đôi khi trình tự bắt đầu được tạo ra sớm hơn 5us sau chu kỳ dừng trước đó. Vì vậy, cứ thỉnh thoảng thì AVR lại là một giao dịch đọc. CPLD có thể xử lý tốc độ dừng / khởi động, hóa ra việc kéo dài xe buýt đã bị vô hiệu hóa trong MCU.
Tôi đã nảy ra một ý tưởng rằng tôi có thể "dự đoán" thanh ghi chính được đọc bằng cách phát hiện một byte ghi đơn vì tôi chắc chắn rằng nó được theo sau bởi một lần đọc. Có vẻ như tôi đã có đủ thời gian trong quá trình ghi địa chỉ chu trình đọc sau đây để mang byte từ nô lệ. Điều đó đã không hoàn toàn làm việc. Các giao dịch xe buýt ban đầu có vẻ ổn (khoảng 5 giây đầu tiên) nhưng sau đó bộ điều khiển đã ngừng tất cả các thông tin liên lạc trên xe buýt như thể nó phát hiện ra rằng nó không trực tiếp nói chuyện với thẻ đọc.
Đầu đọc thẻ cũng có thể tạo ra các ngắt cho chủ. IRQ là một bộ đếm thời gian hoặc sự kiện dựa trên. Tôi gán cho vấn đề về sự chậm trễ mà tôi đã giới thiệu trên xe buýt. Tôi có thể đã sai nhưng tôi đã đưa ra một thiết kế "không trễ" khác.
Ý tưởng là tôi chỉ có thể phá vỡ dòng SDA và để dòng SCL được kết nối giữa chủ và nô lệ. Bằng cách này, tôi vẫn có thể thay thế các byte trên dòng dữ liệu theo một trong hai hướng. Thiết kế tỏ ra phức tạp hơn khi tôi phải điều khiển hướng đường SDA dựa trên chu kỳ xe buýt. Dưới đây là mã VHDL xử lý các giao dịch xe buýt và gửi các byte hex qua UART đến máy tính. Nhận byte từ máy tính chưa được thực hiện:
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;
Dưới đây là các chuyển đổi xe buýt được chụp bằng CPLD kiểm soát đường SDA.
Đăng ký viết:
Đăng ký đọc:
Bạn có thể thấy một vài trục trặc khi hướng xe buýt thay đổi. Điều đó gây ra bởi sự khác biệt về thời gian giữa CPLD thay đổi hướng xe buýt và đầu đọc thẻ tạo ra ACK. Mức ACK dường như ổn định trên cạnh tăng của SCL. Theo như tôi biết đó là tất cả những gì bạn cần.
Với điều này, bộ điều khiển hoạt động theo cách tương tự như với các xe buýt phân chia đình chỉ bất kỳ hoạt động xe buýt nào trong vòng vài giây. Tôi cũng kiểm tra rằng Arduino chế tạo MCU đó và tạo ra lưu lượng xe buýt cho tôi và có vẻ như Arduino cũng đóng băng mọi lúc mọi nơi. Vì vậy, tôi đoán rằng tôi có thể có một số loại vấn đề với máy trạng thái VHDL trong đó trong một số điều kiện tôi bị kẹt trong một trạng thái không có lối thoát. Có ý kiến gì không?
There's only 2 devices on this bus running at 100kHz
và sau đóThe hardware I2C was a slave and a bit banged I2C was a master on the card reader bus at 1Mbps
. Tại sao có hai xe buýt? Tại sao cần xe buýt tốc độ cao? Cung cấp một bản phác thảo thiết kế ban đầu của bạn và cố gắng làm rõ câu hỏi của bạn.