¿Cuándo libera y vuelve a adquirir el control sobre SDA en torno a un i2c Ack?

Tengo un comportamiento extraño con la biblioteca Wire de Energia, así que pensé que intentaría hacer i2c yo mismo.

Estoy escribiendo mi propia implementación (bit-bang) si i2c y lo mantengo bastante simple. Por ahora, solo quiero detectar un dispositivo i2c en particular. Aquí está mi algoritmo básico.

  1. Inicie el mensaje conduciendo SDA de Alto a Bajo mientras SCL se mantiene alto.
  2. Envíe una dirección de 7 bits un bit a la vez llevando SDA a alto o bajo mientras SCL está bajo y luego cambiando SCL a alto para que el dispositivo esclavo lea cada bit.
  3. Envía 1 bit de escritura o lectura.
  4. Reciba Ack del esclavo.
  5. Termine el mensaje conduciendo SDA de Bajo a Alto mientras SCL se mantiene alto.

Sé que lo anterior puede ser un poco simplificado, pero solo quería poner las cosas en contexto. Tengo problemas con el paso 4 y conozco el momento particular en el que liberar y volver a adquirir el control de la línea SDA durante un Ack.

Entonces, después de que el último bit de lectura/escritura se haya establecido en SDA y el SCL establecido en ALTO, necesito saber cuál es el orden de las operaciones para obtener un Ack del esclavo.

He probado todas las combinaciones que se me ocurren, pero parece que no funciona correctamente. Parece que debería hacer lo siguiente:

setMode(SDA, INPUT);                //Release the SDA line to the slave device
digitalWrite(SCL,LOW);              //Pulse the SCL line
digitalWrite(SCL,HIGH);
int ack = digitalRead(SDA) == LOW;  //Read from the SDA line
setMode(SDA, OUTPUT);               //Seize control of the SDA line

La secuencia anterior no parece estar funcionando. Mi analizador digital no reconoce mis comunicaciones como debería.

Cualquier corrección en mis suposiciones o ajustes al código anterior sería de gran ayuda.

Para mayor aclaración:

Estoy usando un entorno de desarrollo llamado 'Energia'. Es una bifurcación de Arduino y se utiliza para 'simplificar' el proceso de programación de los procesadores TI MSP430x. De todos modos, no parece tener ningún concepto de modo 'OpenCollector'.

Es Entrada, Salida o Input_Pullup. Me pregunto si Input_Pullup no es su versión del modo OpenCollector. Lo intentaré... Hay una discusión interesante sobre Input, Output, Input_Pullup aquí:

Constantes de energía

No "libera el control de SDA". Ambos pines deben ser de colector abierto, lo que significa que su dispositivo solo funciona bajo. En lugar de conducir alto, simplemente pasa a Z alto, lo que permite que las resistencias pull-up eleven la línea, a menos que otro dispositivo maneje bajo (el esclavo envía ACK). Parece que necesitas leer sobre I2C.
Si, por casualidad, no puede hacer un colector abierto, la alternativa es establecer el estado de salida del pin en bajo y luego cambiar los modos de pin entre salida para 0 y entrada para 1.
¿A qué te refieres con "parece que no funciona"? Necesitas mostrar una traza de osciloscopio.
Entonces supongo que digitalWrite(SDA, LOW); le dice a mi aplicación que quiero que el pin esté bajo cuando lo estoy controlando. pinMode (SDA, OUTPUT) ¿estoy tomando el control del pin (forzándolo a bajo), y pinMode (SDA, INPUT) estoy liberando el control del pin (permitiendo ir a ALTO a través de las resistencias pull-up)? Le daré una oportunidad. Gracias DoxyLover!!! ¡Pon tu sugerencia como respuesta y si funciona, la marcaré como LA respuesta correcta!

Respuestas (2)

La mayor parte de su problema probablemente se deba al hecho de que está configurando estas líneas altas en lugar de dejarlas flotar. SDA y SCL aumentan porque hay una resistencia que los sube a +V, no porque estén altos. Mucha gente no se da cuenta de que la línea del reloj también está diseñada para funcionar de esta manera.

Hay algo más en la forma en que describe su lógica que me hace pensar que mientras ve la transición de SCL a bajo como el evento que bloquea los datos, parece pensar que luego regresa a SCL a alto. Te animo a pensar en el evento del reloj más así:

  • configurar los datos
  • levanta el reloj
  • esperar un tiempo de bit apropiado
  • bajar el reloj

De la forma en que lo estás haciendo, puedes meterte en problemas de la siguiente manera:

  • escribes SDA, digamos que es bajo.
  • subes el reloj, por lo que SCL ahora está alto
  • lanzas SDA porque quieres leerlo.
  • SDA aumenta si no hay ACK presente

¡Sorpresa! Acabas de crear una condición de STOP sin darte cuenta.

Sí, eso es exactamente lo que está sucediendo, según mi analizador digital... Llego al bit ACK y luego veo que se detiene y se inicia.
Si ninguno de los esclavos en un bus I2C usa el estiramiento del reloj, no hay realmente ninguna desventaja en que el maestro controle el reloj activamente en ambas direcciones. Por supuesto, si los esclavos usan la extensión del reloj, el maestro no solo necesitará usar un pull-up pasivo al soltar el reloj, sino que también tendrá que esperar a que el reloj suba cada vez que se suelte, y tendrá en cuenta el hecho de que los dispositivos esclavos están No es necesario poner datos válidos en SDA hasta que lancen SCL.
Aquellos que manejan SCL tienden a "aprender" que siempre pueden hacerlo, especialmente si solo usan EEPROM, sensores de temperatura y cualquier cosa sin un microcontrolador en su interior. ¡Entonces, un día, descubren lo contrario! Entonces, me gusta recordarle a la gente cuál era la intención original.

Cada vez que se transmite un bit, el escritor activa SDA (o no) inmediatamente después de que SCL baja, y el lector lee el estado de SDA cuando SCL aumenta. (El nivel alto de SCL puede ser retrasado por el esclavo con el alargamiento del reloj; debe esperar a que SCL realmente suba).

Su código debe pulsar SCL durante el tiempo suficiente para que el esclavo tenga suficiente tiempo para establecer el valor (al menos 4,7 µs).

Cambiar SDA al modo de salida podría bajarlo si ese fuera el último valor anterior. Esto no está permitido mientras el SCL está alto (a menos que realmente quiera hacer un inicio repetido).

Muy bien. Gracias por ese consejo/información.