He estado intentando diseñar un módulo que me permita modificar las respuestas de esclavo seleccionadas en un bus I2C. Aquí está la configuración del bus original (los pull-ups y las conexiones de alimentación no se muestran para mayor claridad:
Solohay2dispositivosenestebusysolo100kHz.UncontroladorMCU(maestroI2C)yellectordetarjetasRFID(esclavoI2C)NXPPN512.NopuedomodificarelfirmwaredelcontroladorocambiarlastransaccionesdelbusI2C.LobuenoesqueelControladorsoloenvía2tiposdetransacciones:
Master(WriteRegister)-<s><address+W><registernumber><data><p>Master(ReadRegister)-<s><address+W><registernumber><p><s><address+R><data><p>
Loquequierohaceresreemplazarlosbytesdedatosseleccionadosdurantelalecturadelregistromaestroconmispropiosbytes.PuedoenviarlosnúmerosderegistroqueelMCUquiereleeramiPCatravésdeUART(921.6kbaud).PuedoprocesarlosenC/C++oPythonallí.Cuandoreciboelnúmeroderegistrocuyovalornecesitaserreemplazado,puedoenviarunbytefalsodevueltaamidispositivoyseencargarádedevolverloalcontroladorquereemplazalarespuestadelatarjetaoriginal.
Alprincipio,dividíelbusI2Cendosbuses:
ProbéArduinoNanoyluegounCPLDusandoelestiramientodelreloj.ElhardwareI2CdeATmega328orientadohaciaelcontroladorMCUnopudomantenersealdía,yaqueaveceslasecuenciadeiniciosegeneróantesde5usdespuésdelciclodedetenciónanterior.AsíquedevezencuandoelAVRNAKeraunatransaccióndelectura.ElCPLDpodíamanejarlavelocidaddeparada/inicioyresultóqueelestiramientodelbusestabadeshabilitadoenlaMCU.
Semeocurrióunaideadequepuedo"predecir" la lectura del Registro maestro detectando 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 ingresar 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 suspendió todas las comunicaciones en el bus como si detectara que no está hablando directamente con la etiqueta leída.
El lector de tarjetas también puede generar interrupciones en el maestro. Las IRQ son un temporizador o un evento basado. Atribuí el problema a la demora que estaba introduciendo intrínsecamente en el autobús. Puede que me haya equivocado, pero se me ocurrió otro diseño de "retraso cero".
LaideaesquesolopuedoromperlalíneaSDAydejarlalíneaSCLconectadaentreelmaestroyelesclavo.Deestamaneratodavíapuedoreemplazarbytesenlalíneadedatosencualquierdirección.EldiseñodemostrósermáscomplicadoyaquetengoquecontrolarladireccióndelalíneaSDAenfuncióndelciclodelbus.AquíestáelcódigoVHDLquemanejalastransaccionesdelbusyenvíabyteshexadecimalesatravésdeUARTalacomputadora.Larecepcióndebytesdelacomputadoraaúnnoestáimplementada:
libraryieee;useieee.std_logic_1164.all;useieee.numeric_std.all;entityI2C_Snifferisport(clk:instd_logic;scl_master:instd_logic;sda_master:inoutstd_logic;sda_slave:inoutstd_logic;tx:outstd_logic);endentityI2C_Sniffer;architecturearchofI2C_SnifferissignalclkDiv:std_logic_vector(7downto0):=(others=>'0');typeI2C_STATEis(I2C_IDLE,I2C_MASTER_WRITE,I2C_SLAVE_ACK,I2C_MASTER_READ,I2C_MASTER_ACK);signali2cState:I2C_STATE:=I2C_IDLE;typeI2C_BUS_DIRis(MASTER_TO_SLAVE,SLAVE_TO_MASTER);signali2cBusDir:I2C_BUS_DIR:=MASTER_TO_SLAVE;signali2cRxData:std_logic_vector(7downto0);signali2cCntr:integerrange0to8:=0;signali2cAddr:std_logic:='1';signali2cCmd:std_logic:='0';signalscl_d:std_logic:='1';signalscl:std_logic:='1';signalsda_d:std_logic:='1';signalsda:std_logic:='1';--StrobesforSCLedgesandStart/Stopbitssignalstart_strobe:std_logic:='0';signalstop_strobe:std_logic:='0';signalscl_rising_strobe:std_logic:='0';signalscl_falling_strobe:std_logic:='0';typeUART_STATEis(UART_IDLE,UART_START,UART_DATA,UART_STOP);signaluartState:UART_STATE:=UART_IDLE;signaluartTxRdy:std_logic:='0';signaluartTxData:std_logic_vector(7downto0);signaluartCntr:integerrange0to8:=0;beginCLK_DIV:process(clk)beginifrising_edge(clk)thenclkDiv<=std_logic_vector(unsigned(clkDiv)+1);endif;endprocess;I2C_STROBES:process(clk)beginifrising_edge(clk)then--PipelinedSDAandSCLsignalsscl_d<=scl_master;scl<=scl_d;scl_rising_strobe<='0';ifscl='0'andscl_d='1'thenscl_rising_strobe<='1';endif;scl_falling_strobe<='0';ifscl='1'andscl_d='0'thenscl_falling_strobe<='1';endif;ifi2cBusDir=MASTER_TO_SLAVEthensda_d<=sda_master;sda<=sda_d;elsesda_d<=sda_slave;sda<=sda_d;endif;start_strobe<='0';ifsda_d='0'andsda='1'andscl='1'andscl_d='1'thenstart_strobe<='1';endif;stop_strobe<='0';ifsda_d='1'andsda='0'andscl='1'andscl_d='1'thenstop_strobe<='1';endif;endif;endprocess;BUS_DIR:process(sda_master,sda_slave,i2cBusDir)beginifi2cBusDir=MASTER_TO_SLAVEthensda_slave<=sda_master;sda_master<='Z';elsesda_master<=sda_slave;sda_slave<='Z';endif;endprocess;I2C:process(clk)beginifrising_edge(clk)thenuartTxRdy<='0';casei2cStateiswhenI2C_IDLE=>i2cBusDir<=MASTER_TO_SLAVE;ifstart_strobe='1'theni2cAddr<='1';i2cCntr<=0;i2cState<=I2C_MASTER_WRITE;endif;--MasterWrite(Address/Data)whenI2C_MASTER_WRITE=>i2cBusDir<=MASTER_TO_SLAVE;ifstop_strobe='1'theni2cState<=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 transacciones de bus capturadas con el CPLD que controla la línea SDA.
Registro de escritura:
Registroleído:
Puedes ver algunos fallos cuando cambia la dirección del bus. Esto se debe a las diferencias en el 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 flanco ascendente de la SCL. Por lo que sé, eso es todo lo que necesitas.
Con esta cosa en su lugar, el controlador se comporta de la misma manera que con los buses divididos suspendiendo cualquier actividad del bus en unos pocos segundos. También pruebo que w Arduino se burla de MCU y genera tráfico de bus 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 estados VHDL donde, en algunas condiciones, me quedo atascado en un estado sin salida. ¿Alguna idea?