34 votos

¿Está rota la coincidencia de direcciones I2C del MCP23017?

Estoy luchando por conseguir que un maestro I2C se comunique de forma fiable con un esclavo, porque también hay un expansor de puertos MCP23017 en el mismo bus. El MCP23017 parece detectar - ¡y ACK! - su dirección, incluso cuando ese patrón de bits aparece casualmente en medio de las conversaciones de otros dispositivos. Cuando hace el ACK espurio, aplasta el bit de datos actual a '0', lo que corrompe el mensaje recibido.

No encuentro ninguna errata publicada al respecto. ¿Esto es real o he metido la pata?

Esto parece muy serio. Estoy descubriendo que hay ciertos mensajes que simplemente no pueden ser enviados correctamente a un esclavo diferente, porque parecen válidos pero contienen bits cero espurios.

Pruebas:

En un experimento, acabamos de conectar un Arduino al MCP23017 usando I2C, y escribimos algo de código para hacer saltar el I2C como si fuera a hablar con un dispositivo diferente (inexistente). A mitad de camino, el MCP23017 salta y corrompe las comunicaciones con su ACK espurio.

Notas:

  • Para el experimento, el bus I2C se implementa simplemente usando los pines digitales 4 (SDA) y 5 (SCL) del Arduino, con los pull-ups internos del Arduino. No es perfecto, lo sé, pero habla felizmente con el MCP23017 cuando lo probamos.
  • Añadimos una resistencia de 10k al SDA del MCP23017, para que cuando interfiera con el bus, sea una señal visiblemente más débil en el osciloscopio.
  • Hicimos un experimento de control para asegurarnos de que la configuración podía hablar correctamente con el MCP23017, y así fue, todo bien. Luego modificamos el código para fingir que hablaba con un dispositivo diferente, y el MCP23017 se entrometió con su ACK espurio a pesar de no estar dirigido.
  • Es un MCP23017 bastante antiguo, de 2005.

Aquí está el código - puedes ponerlo en un archivo .ino y subirlo. Añade pequeños retrasos para que las trazas de alcance sean inteligibles.

enum I2cResult {
  OK,
  WRONG_BIT,
  NACK,
  WRONG_BIT_NACK
};

void sdaLow(){
  pinMode(4, OUTPUT);
  digitalWrite(4, LOW); // just make sure that no matter what pinMode does, we get it right...
}

void sdaHigh(){
  pinMode(4, INPUT_PULLUP);
}

bool isSdaHigh(){
  return digitalRead(4);
}

void sclLow(){
  pinMode(5, OUTPUT);
  digitalWrite(5, LOW);
}

void sclHigh(){
  pinMode(5, INPUT_PULLUP);
}

bool isSclHigh(){
  return digitalRead(5);
}

void startBit(){
  delayMicroseconds(10);
  if(isSdaHigh()){
    sclHigh();
    delayMicroseconds(20);
    sdaLow();
    delayMicroseconds(20);
    sclLow();
  } else {
    sclLow();
    delayMicroseconds(20);
    sdaHigh();
    delayMicroseconds(20);
    sclHigh();
    delayMicroseconds(20);
    sdaLow();
    delayMicroseconds(20);
    sclLow();
  }
  delayMicroseconds(10);
}

void stopBit(){
  delayMicroseconds(10);
  if(isSdaHigh()){
    sclLow();
    delayMicroseconds(20);
    sdaLow();
    delayMicroseconds(20);
    sclHigh();
    delayMicroseconds(20);
    sdaHigh();
    delayMicroseconds(20);
  } else {
    sclHigh();
    delayMicroseconds(20);
    sdaHigh();
    delayMicroseconds(20);
  }
  delayMicroseconds(20);
}

//Sends a 1 or 0 based on level - false gives 0, true gives 1.
//returns true if the level on the pin matched the written level
bool sendBit(bool level){
  if (level){
    sdaHigh();
  } else {
    sdaLow();
  }
  delayMicroseconds(25);
  sclHigh();
  delayMicroseconds(25);
  bool v = isSdaHigh();
  delayMicroseconds(25);
  sclLow();
  delayMicroseconds(25);
  return v == level;
}

I2cResult sendByte(uint8_t b){
  I2cResult result = OK;
  for(uint8_t i = 0; i < 8; i++){
    if (i == 4){
      delayMicroseconds(50); // adds space on scope for readability
    }
    bool v = (b & (1<<(7-i)));
    if(!sendBit(v)){
      result = WRONG_BIT;
    }
  }
  delayMicroseconds(50); // adds space on scope for readability
  if(sendBit(true)){
    if(result == WRONG_BIT){
      result = WRONG_BIT_NACK;
    }else{
      result = NACK;
    }
  }
  return result;
}

//This sends a byte and acks it (simulating a slave receiving the data).
I2cResult sendByteAck(uint8_t b) {
  I2cResult result = OK;
  for (uint8_t i = 0; i < 8; i++) {
    if (i == 4){
      delayMicroseconds(50); // adds space on scope for readability
    }
    bool v = (b & (1<<(7-i)));
    if (!sendBit(v)){
      result = WRONG_BIT;
    }
  }
  delayMicroseconds(50); // adds space on scope for readability
  sendBit(false);
  return result;
}

void setup() {
  Serial.begin(9600);
  Serial.println("hello!");
  digitalWrite(4, LOW);
  digitalWrite(5, LOW);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  delay(100);

}

// The MCP23017 is wired to use address 0100 000.
// We then pretend to talk to another device - on address 1100 001, and send it the byte 1000 0001
// This triggers the bug, as, including ack bits, the bus has the following data:
//  start|Address|ACK|  Data |Ack|stop
//     S 1100 0010 0 1000 0001 0  P
//                 +---++---+^

// with the data the expander sees highlighted (+--+ for the address nibbles, ^ for the ack)
// the expander then writes 0 over the 1 at the end.

void loop() {
  startBit();
  delayMicroseconds(50);
  sendByteAck(0xC2);
  delayMicroseconds(150); // adds space on scope for readability
  Serial.println(sendByteAck(0x81));
  delayMicroseconds(50);
  stopBit();
  delay(10000);
}

Aquí está el hardware, en una protoboard: breadboard with Arduino Nano and MCP23017

Aquí están las trazas de alcance que muestran el problema - amarillo es el reloj, azul es los datos. Esta imagen muestra el mensaje completo de dos bytes: Scope trace showing whole message, with spurious ACK

Esta imagen de abajo amplía el segundo byte. Observe el '0' debilitado en la línea de datos azul, visible una vez que el Arduino deja de afirmar SDA=0, donde el MCP23017 está afirmando su ACK después de aparentemente ver un fantasma 0100 000W. Scope trace zoomed in on bad second byte

49voto

will hart Puntos 96

Es un problema conocido con esa primera revisión del chip.

Las revisiones más recientes están arregladas. Las mías están fechadas 0522 (supongo que la semana 22 de 2005, que es más o menos cuando los recibimos, ¡en los tiempos en que se podían comprar fichas con entrega en el mismo año! 0543 en adelante. Bastante mala suerte, en realidad. Espero que haya una moraleja en alguna parte.

Para más detalles, consulte: Hoja de erratas de Microchip

Texto específico relevante:

Todos los problemas enumerados aquí se abordarán en futuras revisiones del silicio MCP23017.

  1. Módulo: Módulo I2C™

En las revisiones de silicio A0 y anteriores:
El I2C puede detectar su dirección de esclavo (OPCODE) en el momento equivocado en una transferencia de datos y reconocer (ACK) su OPCODE percibido. Durante las operaciones normales, el MCP23017 espera que el byte que sigue inmediatamente a un bit de inicio sea un OPCODE.

Cuando el dispositivo no es direccionado, debe permanecer en silencio y no interferir con el bus. Sin embargo, el dispositivo sigue monitorizando el bus y comprueba si hay una coincidencia de dirección cada 8 bits y acusa recibo (ACK) si se detecta una coincidencia.

Mientras que el dispositivo comprueba una coincidencia cada 8 bits, cada transferencia de bytes de datos en el bus tiene una longitud de 9 bits, lo que hace que la rutina de coincidencia del dispositivo se desfase con el bus. Por lo tanto, el falso ACK podría ocurrir en el campo de datos así como en el campo ack.

Trabajar en torno a

El problema se ha solucionado y ya no aparece en la revisión A1 del silicio. Consulte el Apéndice B: "Historial de revisiones del silicio" para determinar cómo identificar las revisiones del silicio.

Mientras no haya otros dispositivos en el bus, o se conozcan los datos del bus (y no causen una falsa coincidencia), el problema no aparecerá.

Se puede utilizar una solución de hardware que desactive la entrada de reloj al MCP23017 cuando no esté direccionado.

Códigos de fecha que corresponden a este tema:

- El código de fecha 0542 y anteriores tienen el problema.

- El código de fecha 0543 y posteriores no tienen el problema.

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X