MITM auf I2C-Bus

9

Ich habe versucht, ein Modul zu entwerfen, mit dem ich ausgewählte Slave-Antworten auf einem I2C-Bus ändern kann. Hier ist die ursprüngliche Buskonfiguration (die Klimmzüge und Stromanschlüsse sind aus Gründen der Übersichtlichkeit nicht dargestellt:

Geben Sie hier die Bildbeschreibung ein Es gibt nur 2 Geräte auf diesem Bus und es ist nur 100kHz. Eine Controller-MCU (I2C-Master) und der RFID-Kartenleser (I2C-Slave) NXP PN512. Ich kann die Controller-Firmware oder die I2C-Bustransaktionen nicht ändern. Der gute Teil ist, dass der Controller nur zwei Arten von Transaktionen sendet:

Master (Write Register) - <s><address+W><register number><data><p> Master (Read Register) - <s><address+W><register number><p><s><address+R><data><p>

Ich möchte ausgewählte Datenbytes während des Lesens des Master-Registers durch meine eigenen Bytes ersetzen. Ich kann die Registernummern, die die MCU lesen möchte, über UART (921,6 kBaud) an meinen PC senden. Ich kann sie dort in C / C ++ oder Python verarbeiten. Wenn ich die Registernummer erhalte, deren Wert ersetzt werden muss, kann ich ein gefälschtes Byte an mein Gerät zurücksenden, das sich darum kümmert, es an den Controller zurückzusenden, wobei die ursprüngliche Kartenantwort ersetzt wird.

Zuerst habe ich den I2C-Bus in zwei Busse aufgeteilt: Geben Sie hier die Bildbeschreibung ein

Ich habe Arduino Nano und später eine CPLD mit Clock Stretching ausprobiert. Die dem MCU-Controller zugewandte ATmega328-Hardware I2C konnte nicht mithalten, da die Startsequenz manchmal früher als 5us nach dem vorherigen Stoppzyklus generiert wurde. Ab und zu machte der AVR eine Lesetransaktion. Die CPLD konnte mit der Stopp- / Startgeschwindigkeit umgehen, bei der sich herausstellte, dass die Busdehnung in der MCU deaktiviert war.

Ich hatte die Idee, dass ich das Lesen des Master-Registers durch Erkennen eines einzelnen Byte-Schreibvorgangs "vorhersagen" kann, da ich sicher bin, dass darauf ein Lesevorgang folgt. Es scheint, dass ich während des folgenden Lesezyklus-Adressschreibens genug Zeit hatte, um das Byte vom Slave einzubringen. Das hat nicht ganz funktioniert. Die Bustransaktionen schienen am Anfang in Ordnung zu sein (ca. die ersten 5 Sekunden), aber dann stellte der Controller die gesamte Kommunikation auf dem Bus ein, als ob er feststellte, dass er nicht direkt mit dem Lesen von Tags spricht.

Der Kartenleser kann auch Interrupts für den Master erzeugen. Die IRQs basieren auf Timern oder Ereignissen. Ich schrieb das Problem der Verzögerung zu, die ich von Natur aus im Bus eingeführt hatte. Ich hätte mich vielleicht geirrt, aber ich habe mir ein anderes "Zero-Delay" -Design ausgedacht. Geben Sie hier die Bildbeschreibung ein

Die Idee ist, dass ich nur die SDA-Leitung unterbrechen und die SCL-Leitung zwischen Master und Slave angeschlossen lassen kann. Auf diese Weise kann ich immer noch Bytes auf der Datenleitung in beide Richtungen ersetzen. Das Design erwies sich als komplizierter, da ich die SDA-Leitungsrichtung basierend auf dem Buszyklus steuern muss. Hier ist der VHDL-Code, der die Bustransaktionen verarbeitet und Hex-Bytes über UART an den Computer sendet. Das Empfangen von Bytes vom Computer ist noch nicht implementiert:

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;

Unten sind die Busübergänge aufgeführt, die mit der CPLD erfasst wurden, die die SDA-Leitung steuert.

Registrieren schreiben:

Geben Sie hier die Bildbeschreibung ein

Register lesen:

Geben Sie hier die Bildbeschreibung ein

Sie können einige Störungen sehen, wenn sich die Busrichtung ändert. Dies wird durch die zeitlichen Unterschiede zwischen der CPLD, die die Busrichtung ändert, und dem Kartenleser verursacht, der eine ACK erzeugt. Der ACK-Pegel scheint an der ansteigenden Flanke der SCL stabil zu sein. Soweit ich weiß, ist das alles was du brauchst.

Mit dieser Funktion verhält sich der Controller genauso wie mit den geteilten Bussen, die die Busaktivität innerhalb weniger Sekunden unterbrechen. Ich teste auch das Arduino, das diese MCU verspottet und Busverkehr für mich erzeugt, und es sieht so aus, als ob Arduino auch ab und zu einfriert. Ich glaube, ich habe ein Problem mit der VHDL-Zustandsmaschine, bei der ich unter bestimmten Umständen in einem Zustand ohne Ausweg stecken bleibe. Irgendwelche Ideen?

Alexxx
quelle
Ihre Frage ist mir sowieso nicht sehr klar. Zuerst sagst du There's only 2 devices on this bus running at 100kHzund dann The hardware I2C was a slave and a bit banged I2C was a master on the card reader bus at 1Mbps. Warum gibt es zwei Busse? Warum braucht man den Hochgeschwindigkeitsbus? Stellen Sie eine Skizze Ihres ursprünglichen Entwurfs bereit und versuchen Sie, Ihre Frage zu klären.
TisteAndii
Ja entschuldigung. Der ursprüngliche Bus verfügt nur über den Controller (i2c-Master) und den RFID-Karten- / Tag-Leser (i2c-Slave). Daher muss ich mich nicht wirklich um die I2C-Adressierung kümmern, da sie Punkt-zu-Punkt ist (jedes vom Master gesendete Paket ist für diesen einen Slave). Mein erster Ansatz bestand darin, den Bus in zwei Busse aufzuteilen und auf der Controllerseite als i2c-Slave und auf der Seite des RFID-Lesegeräts als Master zu fungieren.
Alexxx
Der RIFD-Leser kann schnellere Geschwindigkeiten (1 MHz oder sogar schneller) erreichen, daher dachte ich, ich könnte dies tun, damit ich den Bus (Bus-Stretching) auf der Controllerseite nicht zu lange halte, während ich Daten vom RFID-Leser lese registrieren. Außerdem habe ich beim nächsten Lesezyklus nur wenig Zeit, um das Byte vom RIFD-Lesegerät zu lesen und an die Steuerung zurückzusenden, ohne dass sich der Bus ausdehnt, wenn ich einen einzelnen Byte-Schreibvorgang erkenne.
Alexxx
Mit Busdehnung meine ich die I2C-Taktdehnung, bei der der Slave die SCL-Leitung niedrig hält, um den Master wissen zu lassen, dass die Daten noch nicht bereit sind. Wenn der Slave bereit ist, gibt er die SCL-Leitung frei und der Master liest weiterhin die vom Slave gesendeten Bits.
Alexxx
1
Am besten bearbeiten Sie stattdessen Ihre Frage. Sie haben immer noch nicht erklärt, warum 2 Busse benötigt werden. Wenn Sie nur Daten vom Kartenleser mit I2C lesen müssen, schließen Sie sie dann einfach an denselben Bus an und lassen Sie Ihre MCU daraus lesen. Das Dehnen der Uhr ist nur auf der Seite des Sklaven sinnvoll, normalerweise, wenn es langsam ist, auf einen Master zu reagieren. Sie können nichts dagegen tun, wenn der Slave nicht bereit ist. 100-400 kHz reichen normalerweise für die meisten Anwendungen aus. Der einzige Grund, warum Sie möglicherweise schnellere Geschwindigkeiten wünschen, wenn Sie zeitkritische Vorgänge für die von Ihnen gelesenen oder ähnlichen Daten ausführen müssen.
TisteAndii

Antworten:

6

Ich denke, dass der Versuch, Cutsey-Hacks wie Sie zu versuchen, um Ärger bittet, mit genau den Symptomen, auf die Sie stoßen. Sie versuchen im Grunde zu betrügen und hoffen, dass Sie nicht erwischt werden.

Das einzige, was Sie Ihrer Beschreibung nach nicht ausprobiert haben, ist eine vollständige Emulation dieser Kartenleser-Sache. Sie haben nicht wirklich erklärt, was genau es tut und wie kompliziert es ist, aber nach dem zu urteilen, was der Meister sendet, ist es nicht so kompliziert.

Verwenden Sie einen Mikrocontroller mit Hardware-IIC-Slave-Fähigkeit. Das ist mit dem Master verbunden. Die Firmware emuliert den Kartenleser. Da der Master nur eine Folge von Registern liest, kommuniziert der andere Teil der Firmware vollständig asynchron mit dem Kartenleser, um Informationen von ihm abzurufen und zu steuern. Dies bedeutet auch, dass die Reset- und IRQ-Leitungen ebenfalls getrennt sind.

Wenn es richtig gemacht wird, muss dies funktionieren, da kein Betrug stattfindet. Der Kartenleser sieht einen Controller, der ihm Befehle sendet und genau liest, wie er verwendet werden soll. Dies beinhaltet die Reaktion auf IRQ-Ereignisse.

Der Master glaubt, dass er direkt mit einem echten Kartenleser spricht, da Sie alle seine Vorgänge genau wie das Original emulieren, einschließlich des Zurücksetzens und des IRQ-Verhaltens.

Dies mag nach mehr Arbeit klingen als ein schnelles und schmutziges Stören eines anderen Bytes auf den Bus-Hack, aber wie Sie festgestellt haben, ist dies nicht so schnell und kann immer zu Zeitproblemen führen. Bei einer vollständigen Emulation werden alle zeitlichen Einschränkungen aufgehoben. Wenn Ihre Emulation noch nicht etwas erreicht hat, was der Kartenleser getan hat, verhält sie sich gegenüber dem Master so, als wäre es noch nicht geschehen. Sie tun im Grunde so, als wäre nichts Neues passiert, bis Ihre Emulation bereit ist, in allen Aspekten auf das Ereignis zu reagieren.

Dies bedeutet, dass Sie wirklich zwei asynchrone Teile der Firmware haben: Die IIC-Emulation des Lesegeräts, die dem Master präsentiert wird, und einen vollständigen Kartenlesertreiber, mit dem Sie den gesamten Status in Ihrem internen Speicher erhalten können.

Da Sie nicht schummeln, muss dies funktionieren, wenn es richtig gemacht wird. Das einzige Problem auf Systemebene besteht darin, dass der Master Kartenleseraktionen etwas länger sieht und verursacht als das vorhandene System. Für einen "Kartenleser" klingt dies nicht nach einer großen Sache, und wenn man bedenkt, dass diese Verzögerung im schlimmsten Fall 10 Sekunden Millisekunden betragen würde. Es sollte auf menschlicher Zeitskala sicherlich nicht wahrnehmbar sein.

Beachten Sie, dass die Kommunikation zwischen Ihrem Emulator und dem Kartenleser nicht auf die derzeit verwendeten 100 kbit / s beschränkt ist. Sie sollten dies so schnell ausführen, wie es der Kartenleser und Ihre Hardware zulassen. Auf diesem Link bist du schließlich der Meister, also besitzt du die Uhr. Auch bei einer ordnungsgemäßen Firmware-Architektur und asynchronen Aufgaben sollte dies keine Rolle spielen. In der Tat wird Ihr Treiber wahrscheinlich häufiger kommunizieren und mehr Daten vom Kartenleser erhalten, als der Master von Ihrem Emulator erhält.

Olin Lathrop
quelle
Danke für die Antwort. Ich hatte genau das im Sinn, als ich das zum ersten Mal ansah. Ich habe die Idee schnell aufgegeben, da dies ziemlich kompliziert zu sein scheint. Wenn die MCU nur die Register schreiben und lesen würde, wäre dies einfach, aber der Leser kommuniziert mit einer RFID, die über ein eigenes Protokoll verfügt (Multi-Byte-Befehle und -Antworten). Darüber hinaus richtet die MCU einige Flags für den IRQ im Reader ein und liest Statuen zurück. Daher schien es viel einfacher zu sein, nur ein paar Bytes anzuvisieren und den Rest dem Leser zu überlassen.
Alexxx
Wenn ich den gesamten Bus in zwei Busse aufteile, kann ich tatsächlich schneller mit dem Kartenleser sprechen. Bei dem neuesten Design, bei dem ich nur die SDA-Leitung abschneide, muss ich mich jedoch an das von der MCU auf der SCL-Leitung bereitgestellte Timing halten, das 100 kHz beträgt.
Alexxx
0

Ich würde vorschlagen, dass Sie mit einem Arduino Nano als MITM auf dem richtigen Weg waren, obwohl ich denke, dass es mit zwei am besten wäre.

Geben Sie hier die Bildbeschreibung ein

Der NXP-PN512 wird mit einer Taktrate von 3,4 MHz betrieben, daher würde ich vorschlagen, dass Sie für die rechte MCU, die mit dem Reader spricht, etwas in der Größenordnung von 1,5 bis 2 MHz verwenden können.
Da die linke MCU auf 100 kHz eingestellt ist, können Sie Transaktionsbytes (Adresse / Register-WR), sobald Sie sie erkannt haben, über einen 8-Bit-Parallelbus (oder noch breiter) zwischen den MCUs kopieren und die Befehle an den Leser senden weniger als eine Uhrzeit auf dem langsamen I2C-Kanal. Gleichermaßen wird das Empfangen eines Bytes vom Lesegerät in weniger als einer Taktzeit auf dem langsamen Bus erreicht, was ausreichend Zeit zum Einrichten des Antwortbytes gibt.

Ich gehe hier davon aus, dass Sie möglicherweise tatsächlich mehrere Bytes als NFC-ID übersetzen müssen und nicht nur eine einzelne Byte-für-Byte-Konvertierung (was weniger Zeit erfordert).

Das Hauptproblem, das ich dann sehen würde, ist, dass das Timing noch kritischer wird, wenn Sie mehrere Bytes zum / vom PC serialisieren müssen, um Ihre Änderungen abzubilden. Wenn es eine Möglichkeit gäbe, Ihren Mapping-Änderungsalgorithmus / Ihre Mapping-Änderungstabelle in die linke MCU zu integrieren, wäre dies ein besserer Ansatz, obwohl das Lösen eines Multi-Bye-Identifier-Mappings immer noch die größte Herausforderung darstellt.

Wenn ich falsch liege und Sie nur ein einzelnes Kartenidentifizierungsbyte zuordnen müssen, funktioniert dies möglicherweise.

Haben Sie bei Ihren frühen Tests mit dem Arduino sichergestellt, dass alle Interrupts ausgeschaltet sind (zumindest wird nur TWI verwendet)? Wenn Sie dies nicht getan haben, hat dies möglicherweise Ihr Timing beeinträchtigt.

Jack Creasey
quelle
1
Ich verstehe nicht, warum zwei separate Mikros notwendig sind. Viele Mikros können zwei IIC-Busse gleichzeitig bedienen. Sie brauchen wirklich nur Hardware, um der Slave zu sein, obwohl die Verwendung von Hardware auch als Master praktisch sein kann. Die Kommunikation zwischen zwei Mikros scheint unnötig komplex und langsam zu sein, verglichen mit zwei Aufgaben, die im selben Mikro ausgeführt werden. Ich sehe das Problem, das zwei Mikros lösen, nicht.
Olin Lathrop
@Olin Lathrop. Es vereinfacht die Softwareentwicklung. macht das Debuggen viel einfacher usw. usw. Es ist so, als ob Autos Hunderte von Mikroprozessoren enthalten, anstatt einen (einfacher, wie Sie vielleicht vorschlagen) großen Multiprozessor-Prozessor. Ich habe absolut kein Problem damit, mehrere MCUs zu verwenden, bei denen die Kosten meist unter denen von Einzelfunktionslogik-Chips liegen und die Funktionalität einfacher zu definieren und zu entwickeln ist. In diesem Fall gibt es im ATMega328 nur einen TWI-Interrupt-Vektor, sodass es schwieriger ist, zwei I2C-Kanäle zu unterstützen. ..Aber es ist sicherlich eine persönliche Wahl.
Jack Creasey
In diesem Fall erhöhen mehrere Prozessoren die Komplexität und erfordern zusätzliche Kommunikation. Sie müssen für IIC keine Interrupts oder Hardware verwenden, wenn Sie der Busmaster sind. Es gibt jedoch viele Prozessoren, die zwei IIC-Busse unabhängig voneinander in der Hardware verarbeiten können. Wenn der ATMega dies nicht kann und Sie zwei Hardware-IIC verwenden möchten, verwenden Sie keinen ATMega.
Olin Lathrop
@Olin Lathrop. Lassen Sie uns zustimmen, nicht zuzustimmen. IMO-Bit-Bashing über 100 kHz ist kein Starter. Das Problem für das OP besteht darin, dass die Kosten für die Serialisierung von Daten, die an einen PC gesendet werden müssen, um den Zuordnungsalgorithmus auszuführen, mit Zeitproblemen behaftet sind.
Jack Creasey
Danke für die Antwort und die Kommentare. ATMega328 / Arduino kann aufgrund des MCU I2C-Timings nicht auf der MCU-Seite verwendet werden. Diese MCU kann nach einem vorherigen Stopp eine Startsequenz schneller als 4,7 us erzeugen. Siehe Tabelle 32-10 in den ATMega328-Datenblättern (Parameter tBUF). Was geschah, war, dass Arduino alle i2c-Lesevorgänge nach einem i2c-Schreibvorgang NACKTE. Es ist anscheinend ein bekanntes Problem. Ich habe irgendwo online Informationen dazu gefunden, nachdem ich alle meine Haare darüber gezogen hatte. Deshalb bin ich zur CPLD gewechselt.
Alexxx