El bus de circuito Interintegrado (I2C) es un mecanismo de comunicaciones serie a nivel de chip que opera sobre solo dos cables. Algunos desarrolladores pronuncian el nombre del bus eye-two-see, otros eye-squared-see, pero ambos se refieren a la misma cosa. Ideado por Philips a principios de la década de 1980, I2C se estableció para estandarizar las comunicaciones entre los chips de la compañía, pero desde entonces se ha convertido en un estándar de facto soportado por muchos dispositivos microcontroladores desde placas Arduino hasta Raspberry Pi y, por supuesto, imps.,
el Bus físico
I2C comprende dos cables. Una línea I2C transmite datos, la otra las señales de reloj que sincronizan la conversación entre dispositivos. La línea de datos se llama «SDA», la línea de reloj SCL’.
normalmente, tanto SDA como SCL están conectados a una línea eléctrica de 3.3 o 5V a través de una sola resistencia ‘pull-up’, una en cada línea. Esto es necesario porque las conexiones SDA y SCL de los dispositivos son líneas de ‘drenaje abierto’: pueden forzar el voltaje en la línea a 0V, o ‘bajo’, pero no pueden elevarlo a 3.3 V, o ‘alto’., Alta y baja son las representaciones eléctricas de los 1s y 0s que son los componentes fundamentales de la información digital. La adición de estas dos resistencias — y el bus solo necesita dos, sin importar cuántos dispositivos estén conectados a él — asegura que el voltaje aumente de nuevo a 3.3 V sin un cortocircuito.
I2C insiste en que los dispositivos tienen líneas de drenaje abiertas para garantizar que las corrientes altas que dañan los componentes no puedan fluir cuando dos dispositivos intentan señalar simultáneamente.
en la mayoría de los casos, se espera que usted mismo agregue estas resistencias, pero algunos dispositivos, típicamente aquellos que operan a 3.,3V, inclúyalos para ser compatibles con dispositivos que suministran 5V.recuerde, solo necesita un par de resistencias pull-up por bus, por lo que puede ser necesario quitar las resistencias pull-up conectadas a otros dispositivos en el bus. Aunque el imp tiene resistencias pull-up internas propias, estas son demasiado débiles para ser útiles para I2C y por lo tanto se desactivan automáticamente cuando sus pines se configuran para manejar señales I2C.
Controladores Y Periféricos
El bus I2C separa los dispositivos en los ‘controladores’ y ‘periféricos’., Solo un dispositivo puede enviar pulsos de tiempo en la línea SCL a la vez, y ese es el elegido para ser el controlador. Todos los demás sincronizan sus tiempos con el controlador, y por lo tanto se consideran periféricos. El controlador — típicamente el imp-y sus periféricos pueden transmitir y recibir datos, pero solo el controlador puede decirle a un periférico cuándo transmitir datos de vuelta.
direccionamiento
para que un dispositivo I2C se comunique con otro de forma individual, ambos dispositivos deben ser identificables de forma única. Esta identidad es la dirección I2C del dispositivo., Las direcciones I2C son generalmente números de 7 bits, por lo que un bus puede comprender hasta 127 dispositivos en total. Un byte comprende ocho bits; el bit extra se usa para indicar si la señal está siendo enviada por el controlador al periférico — a ‘write’ — o en la otra dirección — a ‘read’. Este octavo bit es en realidad bit cero en el byte de dirección enviado al autobús. La dirección de 7 bits se coloca en los bits del uno al siete del byte de dirección.
de acuerdo con este formato, la API imp toma direcciones I2C como un valor de 8 bits., Los proveedores de dispositivos generalmente dan las direcciones de sus productos como un número de 7 bits, por lo que es necesario convertir la dirección de siete bits a ocho. Esto se logra con el operador <<
de Squirrel, que mueve los valores de bits individuales de un número a la izquierda. Este proceso es el equivalente de multiplicar por dos. En el Código, este proceso se ve así:
local sevenBitAddress = 0x39;local eightBitAddress = sevenBitAddress << 1;
Squirrel establece automáticamente el bit cero al valor correcto definido por I2C: 0 para una operación de escritura, 1 para una lectura., Ahora está listo para usar uno de los métodos I2C del imp para escribir datos en el bus:
i2c.write(eightBitAddress, dataInStringForm);
la dirección de 7 bits del dispositivo con el que desea que su imp se comunique será suministrada por el fabricante del componente y aparecerá en la hoja de datos del dispositivo. Puede no ser fijo, sino seleccionado de un rango de direcciones de acuerdo con la tensión aplicada a otro de los pines de los dispositivos., Por ejemplo, un sensor de luz TAOS TSL2561 tiene tres direcciones posibles: 0x29
, 0x49
o 0x39
, dependiendo de si su pin ADDR está fijo en 0V, 3.3 V o ‘flotante’ entre los dos. Decimos que el valor es flotante porque no ha sido seleccionado activamente para operar a un voltaje específico; podría ser cualquier cosa de 0 a 3.3 V inclusive.
señalización
El controlador del bus I2C utiliza la dirección de 7 bits de un periférico para identificar el componente con el que desea hablar., De hecho, la señalización es más compleja que eso, pero afortunadamente todos los detalles son manejados por el imp, por lo que solo necesita proporcionar la dirección como un valor de 8 bits.
si está escribiendo en el periférico, también debe proporcionar los datos para escribir, lo que a menudo incluye valores de registro que indican al periférico qué hacer con los datos. La documentación de la API imp se refiere a estos registros como ‘sub-direcciones’:
i2c.write(eightBitAddress, dataString);
write() requiere datos en forma de cadena. En consecuencia, es posible que necesite convertir el valor almacenado en otros tipos de variables en forma de cadena.,
Leer información de un dispositivo puede requerir un comando para decirle al dispositivo qué datos obtener. Decirle al imp que lea datos del bus I2C también implica proporcionar un tercer parámetro, el número de bytes que espera recibir:
i2c.read(eightBitAddress, controlDataString, numberOfBytesToRead);
detrás de estas operaciones están las señales eléctricas del I2C, aplicadas a la línea SDA sincronizadas con los pulsos de tiempo aplicados a la línea SCL. Escribir en el bus implica un marcador de inicio: soltar SDA a 0V mientras SCL es 3.3 V. cambiar el voltaje SDA cuando el voltaje del SCL es alto define los marcadores de inicio y parada., Si el voltaje SDA no cambia mientras el SCL es alto, los dispositivos I2C saben que se envían datos, en lugar de marcadores.
SDA ahora va alto o bajo para enviar cada bit del byte de dirección: la dirección del dispositivo de 7 bits seguida por el bit de lectura / escritura. Los bits del byte se envían a la izquierda-el ‘bit más significativo’ – primero, con SDA yendo alto si el valor del bit es 1 o bajo si es cero. El periférico objetivo ahora bajará el SDA para señalar el acuse de recibo de los datos, y luego saldrá ocho bits de información o datos de control, seguidos de más datos si es necesario., Hay una pausa de confirmación ‘ack’ de un solo pulso en SDA entre cada ocho bits enviados, cronometrado a un noveno pulso SCL. Si el periférico no acusa recibo de esta manera, el controlador detectará que el SDA se ha mantenido alto y señalará un error.,
cuando los datos fluyen desde el periférico al controlador, este último igualmente reconoce la recepción de ocho bits tirando de SDA bajo en el noveno pulso SCL a menos que este sea el byte final de un lote, en cuyo caso el controlador no tira de SDA bajo — Hace una señal ‘nak’, o ‘no acuse de recibo’ — para que el periférico sepa que ha terminado.
Cuando hemos terminado, SDA va alto como un marcador de parada.,
El inicio de la transmisión se indica por SDA cayendo de alto a bajo voltaje (el rectángulo de la izquierda),
stop por el reverso (el rectángulo de la derecha). El SCL debe ser alto cuando esto ocurre
Timing
La velocidad de reloj estándar para las comunicaciones I2C es de 100KHz — 100,000 pulsos SCL por segundo. Es posible ir más rápido, hasta 400kHz. Algunos dispositivos pueden no ser capaces de soportar esta velocidad; compruebe la hoja de datos que acompaña al dispositivo que desea conectar a su imp., Sin embargo, muchos dispositivos lentos usan una técnica llamada ‘estiramiento de reloj’ para forzar a los dispositivos más rápidos a trabajar a su velocidad. El imp soporta dispositivos que hacen uso de esta técnica. Esencialmente, mantienen el SCL bajo mientras buscan los datos que desea que envíen al imp. El imp detecta esto, libera la línea SCL y espera hasta que SCL vuelva a subir antes de Continuar.
sin embargo, es posible que deba reducir la velocidad de I2C usted mismo si las características eléctricas de su configuración ralentizan la velocidad de la transición entre 0V y 3.3 V, llamado «Tiempo de subida»., Esto a menudo es causado por cables largos, que aumentan la capacitancia del circuito. Para que los dispositivos detecten con éxito la transmisión de cada bit, el bus necesita funcionar más lentamente. La corrupción de datos o los resultados inesperados son las pistas que debe buscar. Reduzca la velocidad del bus I2C hasta que los datos se lean correctamente.
la API imp actualmente proporciona cuatro valores de reloj predefinidos: 10, 50 100 y 400KHZ., Se seleccionan pasando una constante al método de configuración de I2C como parámetro:
i2c.configure(speedConstant);
donde el valor de speedConstant es uno de
- CLOCK_SPEED_10_KHZ
- CLOCK_SPEED_50_KHz
- CLOCK_SPEED_100_KHZ
- clock_speed_400_khz
configurar un imp para I2C
el objeto i2c en las líneas de ejemplo de código dadas anteriormente no es proporcionado directamente por el imp, sino elegido por usted de acuerdo con cuál de los pines de su IMP elegido usará para las comunicaciones I2C., Cada tipo de imp tiene múltiples buses I2C, todos disponibles en la puesta en marcha. Echa un vistazo a la pin mux para el tipo de imp que está utilizando para ver qué objetos I2C están disponibles para usted. Aquí, asumiremos que está utilizando un imp001. Los dos buses I2C del imp001 están en los pines 1 y 2, y los pines 8 y 9, respectivamente instanciados como propiedades i2c12 e i2c89 del objeto de hardware cuando se inicia el imp. Los pines 1 y 8 se asignan a SCL, 2 y 9 a SDA.,
es común hacer referencia a su elección con una variable global:
i2c <- hardware.i2c12;i2c.configure(CLOCK_SPEED_100_KHZ);
código de ejemplo
el siguiente código funciona con el sensor de luz visible e infrarroja TAOS TSL2561, un dispositivo de 3.3 V que utiliza I2C para comunicarse con su microcontrolador host. La hoja de datos del chip se puede descargar desde el Sitio Web de Adafruit. Adafruit vende el chip en una placa de arranque de bajo costo que incluye resistencias pull-up adecuadas en el pasador de alimentación, VCC. Esto significa que está listo para conectarse directamente a los pines I2C de un imp.,
placa de desbloqueo TSL2561 de Adafruit
Nota Después de escribir este artículo, Adafruit actualizó su placa de sensor TSL2561. La nueva versión funciona con el código actual.
Aquí está el código, para el agente y luego el dispositivo:
Qué hace el Código
el código del agente responde a una solicitud HTTP entrante notificando al dispositivo que requiere una lectura. Para simplificar, la lectura se muestra simplemente en el registro.
el código del dispositivo lee el sensor y calcula un valor de lux de acuerdo con las matemáticas establecidas en la hoja de datos TSL2561., La primera parte del programa establece constantes para los registros de claves y configuraciones del TSL2561, incluyendo sus opciones de dirección I2C. Otras constantes se utilizan en el proceso de conversión de luminosidad.
cableando el imp
en el punto de inicio del programa, el código se alias uno de los pines I2C del imp establecido como una variable global, configura la velocidad del bus a 100kHz y luego desplaza la dirección I2C del TSL2561 un bit a la izquierda para que esté listo para ser utilizado con las funciones imp I2C. Luego configura el sensor., Solo para comprobar que esto ha funcionado, leemos el registro de control: el valor de retorno debe ser 3 si el TSL2561 acaba de iniciarse, o 51 si el dispositivo ya se ha encendido.
a continuación, el código establece la resolución ADC del sensor a través del registro de temporización del TSL2561, y luego establece la ganancia de señal en alta.
finalmente, le decimos al imp cómo responder a "sense"
notificaciones del agente. Esto llama a una función que lee los dos sensores de luz del TSL2561, cuyos valores digitales se almacenan en los cuatro registros ADC de 8 bits del chip., Los dos primeros dan una lectura óptica e infrarroja combinada de 16 bits, el segundo par un valor de solo IR de 16 bits. Las funciones readSensorAdc0 () y readSensorAdc1 () convierten los valores individuales del registro en números de 16 bits multiplicando el byte más significativo por 256 y agregando el valor del byte menos significativo. La multiplicación se realiza cambiando los bits del número de ocho bits a la izquierda ocho lugares con el operador de Squirrel <<.,
el código proporciona ambas lecturas a una tercera función para calcular una luminosidad final, el valor ‘lux’.
lectura adicional
- Errores de I2C-cómo depurar problemas de lectura y escritura de I2C