MITM en el bus I2C


9

He estado tratando de diseñar un módulo que me permita modificar las respuestas esclavas seleccionadas en un bus I2C. Aquí está la configuración original del bus (los pull-ups y las conexiones de alimentación no se muestran para mayor claridad:

ingrese la descripción de la imagen aquí Solo hay 2 dispositivos en este bus y solo tiene 100 kHz. Un controlador MCU (maestro I2C) y el lector de tarjetas RFID (esclavo I2C) NXP PN512. No puedo modificar el firmware del controlador o cambiar las transacciones del bus I2C. Lo bueno es que el controlador envía solo 2 tipos de transacciones:

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

Lo que quiero hacer es reemplazar los bytes de datos seleccionados durante la lectura del registro maestro con mis propios bytes. Puedo enviar los números de registro que el MCU quiere leer a mi PC a través de UART (921.6kbaudios). Puedo procesarlos en C / C ++ o Python allí. Cuando recibo el número de registro cuyo valor necesita ser reemplazado, puedo enviar un byte falso de regreso a mi dispositivo y se encargará de enviarlo de regreso al controlador reemplazando la respuesta original de la tarjeta.

Al principio dividí el bus I2C en dos buses: ingrese la descripción de la imagen aquí

Probé Arduino Nano y luego un CPLD usando el estiramiento del reloj. El hardware I2C de ATmega328 que enfrenta el controlador MCU no pudo seguir el ritmo ya que a veces la secuencia de inicio se generó antes de 5us después del ciclo de parada anterior. Entonces, de vez en cuando, el AVR estaba haciendo una transacción de lectura. El CPLD podía manejar la velocidad de parada / arranque, resultó que el estiramiento del bus estaba desactivado en la MCU.

Se me ocurrió una idea de que puedo "predecir" la lectura del registro maestro al detectar una escritura de un solo byte, ya que estoy seguro de que es seguida por una lectura. Parece que tuve suficiente tiempo durante el siguiente ciclo de lectura de escritura de dirección para traer el byte del esclavo. Eso no funcionó del todo. Las transacciones del bus parecían estar bien al principio (aproximadamente los primeros 5 segundos), pero luego el controlador interrumpió todas las comunicaciones en el bus como si detectara que no está hablando directamente con la lectura de etiqueta.

El lector de tarjetas también puede generar interrupciones al maestro. Las IRQ son un temporizador o evento basado. Atribuí el problema a la demora que introduje inherentemente en el autobús. Puede que me haya equivocado, pero se me ocurrió otro diseño de "retraso cero". ingrese la descripción de la imagen aquí

La idea es que solo puedo romper la línea SDA y dejar la línea SCL conectada entre el maestro y el esclavo. De esta manera, todavía puedo reemplazar bytes en la línea de datos en cualquier dirección. El diseño resultó ser más complicado ya que tengo que controlar la dirección de la línea SDA en función del ciclo del bus. Aquí está el código VHDL que maneja las transacciones del bus y envía bytes hexadecimales sobre UART a la computadora. La recepción de bytes desde la computadora aún no está implementada:

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;

A continuación se muestran las transiciones de bus capturadas con el CPLD que controla la línea SDA.

Registrarse escribir:

ingrese la descripción de la imagen aquí

Registrarse leer:

ingrese la descripción de la imagen aquí

Puede ver algunos problemas técnicos cuando cambia la dirección del autobús. Esto se debe a las diferencias de tiempo entre el CPLD que cambia la dirección del bus y el lector de tarjetas que genera un ACK. El nivel de ACK parece ser estable en el borde ascendente de la SCL. Por lo que sé, eso es todo lo que necesitas.

Con esto en su lugar, el controlador se comporta de la misma manera que con los buses divididos que suspenden cualquier actividad del bus en unos pocos segundos. También pruebo que w Arduino se burla de esa MCU y genera tráfico de autobús para mí y parece que Arduino también se congela de vez en cuando. Así que supongo que puedo tener algún tipo de problema con la máquina de estado VHDL donde, en algunas condiciones, me quedo atascado en un estado sin salida. ¿Algunas ideas?


Tu pregunta no es muy clara, para mí de todos modos. Primero dices There's only 2 devices on this bus running at 100kHzy luego The hardware I2C was a slave and a bit banged I2C was a master on the card reader bus at 1Mbps. ¿Por qué hay dos autobuses? ¿Por qué la necesidad del autobús de alta velocidad? Proporcione un boceto de su diseño inicial e intente aclarar su pregunta.
SoreDakeNoKoto

Si lo siento El bus original solo tiene el controlador (maestro i2c) y el lector de tarjetas / etiquetas RFID (esclavo i2c). Por lo tanto, realmente no tengo que preocuparme por el direccionamiento I2C, ya que es punto a punto (cada paquete enviado por el maestro es para este esclavo). Mi primer enfoque fue dividir el bus en 2 buses y actuar como esclavo i2c en el lado del controlador y maestro en el lado del lector RFID.
Alexxx

El lector RIFD es capaz de velocidades más rápidas (1MHz o incluso más rápido), así que pensé que podría hacerlo mientras lo usamos para no mantener el bus (estiramiento del bus) en el lado del controlador durante demasiado tiempo mientras leo datos del lector RFID Registrarse. Además, sin que el bus se estire cuando detecto una escritura de un solo byte, solo tengo poco tiempo durante el siguiente ciclo de lectura para leer el byte del lector RIFD y enviarlo de regreso al controlador.
Alexxx

Por estiramiento de bus me refiero al reloj I2C que se extiende donde el esclavo mantiene baja la línea SCL para que el maestro sepa que aún no está listo para recibir los datos. Cuando el esclavo está listo, libera la línea SCL y el maestro continúa leyendo los bits enviados por el esclavo.
Alexxx

1
Es mejor si edita su pregunta en su lugar. Aún no ha explicado por qué se necesitan 2 autobuses. Si todo lo que necesita es leer datos del lector de tarjetas que emplean I2C, ¿por qué no simplemente conectarlos en el mismo bus y hacer que su MCU lea de él? El estiramiento del reloj solo tiene sentido en el lado del esclavo, generalmente cuando es lento para responder a un maestro. No hay nada que puedas hacer al respecto si el esclavo no está listo. 100-400kHz suele ser suficiente para la mayoría de las aplicaciones; La única razón por la que podría desear velocidades más rápidas si debe realizar una operación urgente en los datos que ha leído o similar.
SoreDakeNoKoto

Respuestas:


6

Creo que intentar hackear cutsey como lo has estado haciendo es pedir problemas, con exactamente el tipo de síntomas que estás teniendo. Básicamente estás tratando de hacer trampa y espero que no te atrapen.

Lo único que no ha probado, según su descripción, es una emulación completa de este lector de tarjetas. Realmente no has explicado qué hace exactamente y qué tan complicado es, pero a juzgar por lo que el maestro está enviando no es tan complicado.

Utilice un microcontrolador con capacidad de esclavo IIC de hardware. Eso está conectado con el maestro. El firmware emula el lector de tarjetas. Como lo único que lee el maestro es una secuencia de registros, la otra parte del firmware se comunica de forma completamente asíncrona con el lector de tarjetas para obtener información y controlarla. Esto también significa que las líneas de reinicio e IRQ también están separadas.

Si se hace bien, esto tiene que funcionar, ya que no hay trampas. El lector de tarjetas ve un controlador que le envía comandos y hace lecturas exactamente de la forma en que se utilizaron. Esto incluye responder a eventos IRQ.

El maestro cree que está hablando directamente con un lector de tarjetas real porque emula todas sus operaciones como si fueran reales, incluido el comportamiento de reinicio e IRQ.

Esto puede parecer más trabajo que algo rápido y sucio al atascar un byte diferente en el truco del autobús, pero como descubrió, eso no es tan rápido y siempre puede tener algunos problemas de sincronización. Con una emulación completa, se eliminan todas las restricciones de tiempo. Si su emulación aún no ha alcanzado algo que el lector de tarjetas haya hecho, entonces actúa ante el maestro como si aún no hubiera sucedido. Básicamente finges que no ha sucedido nada nuevo hasta que tu emulación esté lista para responder al evento en todos los aspectos.

Esto significa que realmente tiene dos partes asincrónicas del firmware: la emulación IIC del lector presentada al maestro y un controlador de lector de tarjetas completo que le permite mantener vivo todo su estado en su memoria interna.

Como no estás haciendo trampa, esto tiene que funcionar si se hace bien. El único problema a nivel del sistema es que habrá un retraso en la vista maestra y causará acciones del lector de tarjetas que el sistema existente. Esto no suena como un gran problema para un "lector de tarjetas", y teniendo en cuenta este retraso probablemente sería de 10 segundos en el peor de los casos. Ciertamente no debería ser notable en una escala de tiempo humana.

Tenga en cuenta que la comunicación entre su emulador y el lector de tarjetas no se limita a los 100 kbits / s utilizados actualmente. Debe ejecutarlo tan rápido como lo permita el lector de tarjetas y su hardware. Después de todo, en ese enlace serás el maestro, así que eres el dueño del reloj. Nuevamente, con una arquitectura de firmware adecuada y tareas asincrónicas, esto no debería importar. De hecho, su controlador probablemente se comunicará con más frecuencia y obtendrá más datos del lector de tarjetas que el maestro de su emulador.


Gracias por la respuesta. Exactamente tenía algo en mente cuando dije por primera vez mirar eso. Rápidamente abandoné la idea, ya que parece ser bastante complicado. Si la MCU solo escribiera y leyera los registros, sería fácil, pero el lector se comunica con una RFID que tiene su propio protocolo (comandos y respuestas de varios bytes). Además de eso, el MCU está configurando algunas banderas para el IRQ en el lector y leyendo las estatuas. Por lo tanto, parecía que sería mucho más fácil apuntar solo a unos pocos bytes y dejar el resto al lector.
Alexxx

Además, si divido todo el bus en 2 buses, puedo hablar con el lector de tarjetas a velocidades más rápidas. Sin embargo, con el último diseño en el que corté la línea SDA solo tengo que apegarme a la sincronización proporcionada por el MCU en la línea SCL que es de 100KHz.
Alexxx

0

Sugeriría que estabas en el camino correcto con un Arduino Nano como MITM, aunque creo que sería mejor con dos.

ingrese la descripción de la imagen aquí

El NXP-PN512 funcionará a una velocidad de reloj de 3.4 Mhz, por lo que le sugiero que use algo del orden de 1.5 - 2 MHz para la MCU de la derecha que habla con el Lector.
Como la MCU de la izquierda está configurada a 100 kHz, una vez que haya reconocido los bytes de transacción (dirección / registro-WR) puede copiarla en un bus paralelo de 8 bits (o incluso más ancho) entre las MCU y enviar los comandos al lector en menos de una hora en el canal I2C de baja velocidad. Igualmente, se recibe un byte del lector en menos de un tiempo de reloj en el bus lento, dando tiempo suficiente para configurar el byte de respuesta.

Supongo que es posible que deba traducir varios bytes como una ID NFC y no solo una conversión byte a byte (que requiere menos tiempo).

El problema principal que vería entonces es que si necesita serializar múltiples bytes hacia / desde la PC para mapear sus cambios, el tiempo se vuelve aún más crítico. Si hubiera una manera de construir su algoritmo / tabla de cambio de mapeo en la MCU de la izquierda, eso parecería un mejor enfoque, aunque resolver un mapeo de identificador de bye sigue siendo el mayor desafío.

Si me equivoco y solo necesita asignar un solo byte identificador de tarjeta, entonces esto podría funcionar.

En sus primeras pruebas con el Arduino, ¿se aseguró de que todas las interrupciones estuvieran apagadas (al menos solo TWI en uso)? Si no lo hizo, entonces esto puede haber alterado su tiempo.


1
No veo por qué son necesarios dos micros separados. Muchos micros pueden manejar dos buses IIC simultáneamente. Realmente solo necesitas hardware para ser el esclavo, aunque usar hardware puede ser conveniente incluso cuando eres el maestro. La comunicación entre dos micros parece innecesariamente compleja y lenta, en comparación con dos tareas que se ejecutan en el mismo micro. No veo el problema que resuelven dos micros.
Olin Lathrop

@Olin Lathrop. Simplifica el desarrollo de software. hace que la depuración sea mucho más simple, etc. Es como por qué los autos tienen cientos de microprocesadores en lugar de uno (más simple, como podría sugerir), un procesador multiproceso grande. No tengo absolutamente ningún problema al usar múltiples MCU donde el costo es mayormente inferior al de los chips lógicos de función única y la funcionalidad es más fácil de definir y desarrollar. En este caso, solo hay un vector de interrupción TWI en el ATMega328, por lo que es más difícil admitir dos canales de I2C. .. Pero sin duda es una elección personal.
Jack Creasey

En este caso, los procesadores múltiples agregan complejidad e introducen la necesidad de una comunicación adicional. No necesita usar interrupciones o hardware para IIC cuando es el maestro del bus. Sin embargo, hay muchos procesadores que pueden manejar dos buses IIC independientemente en hardware. Si el ATMega no puede, y desea usar dos IIC de hardware, entonces no use un ATMega.
Olin Lathrop

@Olin Lathrop. Acordemos estar en desacuerdo. IMO bit bashing por encima de cien kHz es un no arrancador. El problema para el OP es que el costo de serializar datos para enviar a una PC para hacer el algoritmo de mapeo está plagado de problemas de tiempo.
Jack Creasey

Gracias por la respuesta y comentarios. ATMega328 / Arduino no se puede usar en el lado MCU debido a la sincronización MCU I2C. Esta MCU es capaz de generar una secuencia de inicio más rápido que 4.7us después de una parada previa. Mire la tabla 32-10 en las hojas de datos ATMega328 (parámetro tBUF). Lo que estaba sucediendo era que Arduino NACKEaba todas las lecturas de i2c que seguían a una escritura de i2c. Aparentemente es un problema conocido. Encontré información sobre eso en algún lugar en línea después de que me puse todo el pelo sobre esto. Es por eso que me cambié al CPLD.
Alexxx
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.