Los sensores utilizan protocolos maestro-esclavo para interactuar con microcontroladores y microcomputadoras. Entre muchos protocolos esclavo-maestro diferentes, los protocolos I2C y SPI son los protocolos de comunicación en serie comunes que se encuentran ampliamente en dispositivos integrados. Ambos protocolos permiten que el microcontrolador/microordenador asuma el papel de maestro y permiten la interfaz de varios sensores y bloques integrados en un bus común. Este tutorial analizará la implementación del protocolo I2C en MicroPython y explorará cómo los sensores interactúan con ESP8266 y ESP32 utilizando el bus I2C. Obtenga más información sobre el protocolo I2C antes de continuar con este tutorial.
Módulo de máquina
El módulo de la máquina MicroPython es responsable de administrar los recursos básicos de hardware de los puertos admitidos. El módulo incluye clases para controlar entrada/salida digital, controlar señales de salida de dispositivos externos, modulación de ancho de pulso, conversión analógica a digital, control de periféricos ADC, UART, SPI, I2C, I2S, temporizador, RTC, temporizador Watchdog y gestión de tarjetas SD. Tiene clase I2C para gestionar el bus I2C de los puertos compatibles.
Clase I2C
I2C es un protocolo de dos hilos. Utiliza dos líneas: una línea de datos (SDA) y una línea de reloj (SCL). MicroPython implementa hardware I2C y software I2C. El hardware I2C utiliza el periférico I2C subyacente y el bus de puerto compatible para la comunicación en serie de acuerdo con el protocolo I2C. Dado que los periféricos de hardware I2C están vinculados a pines específicos en un puerto, la implementación del hardware I2C solo se puede realizar en esos pines específicos.
El hardware I2C se implementa en la clase I2C del módulo de máquina. La clase I2C se importa a un script MicroPython mediante la siguiente declaración.
de importación de máquinas I2C
Después de importar la clase I2C, necesita crear una instancia de un objeto I2C. Una función constructora hace esto. La función constructora tiene el siguiente prototipo.
máquina de clase.I2C(id, *, scl, sda, frecuencia=400000)
La función constructora puede recibir cuatro argumentos: id, scl, sda y freq. La identificación es el identificador del periférico I2C específico. Si un puerto tiene varios periféricos I2C, es importante pasar el ID de I2C. La identificación puede ser un número entero, una cadena o una tupla. Esto depende del puerto específico. scl y sda son los pines utilizados para el reloj I2C y los datos I2C respectivamente. Si los pines SCL y SDA se pueden cambiar en un puerto, estos argumentos se pueden pasar para asignar pines a los periféricos I2C disponibles. Si scl y sda no se pasan como argumentos, los pines predeterminados se asignarán a I2C SCL y SDA respectivamente. Los pines SDA y SCL se pueden fijar a muchos puertos y no se pueden cambiar. freq define la frecuencia máxima para el reloj I2C. A continuación se muestran algunos ejemplos válidos del constructor I2C.
i2c = I2C(0)
i2c = I2C(1, scl=Pin(5), sda=Pin(4), frecuencia=400000)
i2c = I2C(scl=Pin(5), sda=Pin(4), frecuencia=100000)
La clase I2C incluye los siguientes métodos para configurar y administrar la comunicación de datos.
I2C.init : este método se utiliza para inicializar el bus I2C. Se aplica a un objeto I2C, que ya tiene una instancia con una identificación I2c. El método init puede tomar sda, scl y freq como argumentos.
I2C.deinit : este método apaga el bus I2C. Sólo está disponible en el puerto WiPy.
I2C.scan : este método escanea todas las direcciones I2C entre 0x08 y 0x77 y devuelve una lista de direcciones que responden. Este método es útil para enumerar dispositivos I2C conectados. Los dispositivos se reconocen por sus direcciones I2C.
I2C.readfrom(addr, nbytes, stop=True) : este método lee nbytes de la dirección I2C. Se generará una condición de parada al finalizar la operación de lectura si la parada es verdadera. Una llamada a este método devuelve un objeto de tipo byte, que debe almacenarse en una variable.
I2C.readfrom_into(addr, buf, stop=True) : este método lee el búfer I2C de la dirección I2C. El método no devuelve nada, pero se guardan todos los bytes disponibles en el bus I2C para lectura. La condición de parada se crea después de leer en el búfer si la parada es verdadera.
I2C.writeto(addr, buf, stop=True) : este método escribe bytes del búfer en la dirección I2c. Devuelve el número de confirmaciones recibidas. Si no se recibe confirmación después de enviar un byte, los bytes restantes no se enviarán. Si stop es verdadero, la condición de parada se genera después de enviar los bytes.
I2C.writevto(address, vector, stop = True) : este método envía bytes almacenados en un vector a la dirección I2C. Un vector es una lista o tupla de objetos con un protocolo de búfer. Con este método, se pueden enviar varios objetos a una dirección I2c determinada con una sola llamada. El método devuelve el número de confirmaciones recibidas. El método envía la dirección una vez y luego los objetos de bytes se envían secuencialmente. Los bytes y objetos restantes no se enviarán si no se recibe acuse de recibo después de enviar un byte. Si stop es verdadero, la condición de parada se genera después de enviar el vector.
I2C.start : este método genera una condición de inicio en el bus I2C. SDA se pone BAJO en la condición inicial mientras que SCL se pone ALTO.
I2C.stop : este método genera una condición de parada en el bus I2C. El SDA se pone ALTO en la condición de parada mientras que el SCL se pone a BAJO.
I2C.readinto(buf, nack=True) : este método se utiliza para leer bytes del bus y almacenarlos en el búfer. El número de bytes leídos es igual al tamaño del búfer. Una vez recibidos todos los bytes, se enviará la confirmación; de lo contrario, no se enviará ninguna confirmación mientras nack esté establecido en verdadero.
I2C.write(buf) : este método se utiliza para escribir bytes del búfer en el bus I2C. El método devuelve el número de confirmaciones recibidas. Es igual a la cantidad de bytes escritos exitosamente en el bus. Después de enviar cada byte, se recibe la confirmación. Si no se recibe confirmación, los bytes restantes no se escribirán.
I2C.readfrom_mem(addr, memaddr, nbytes, *, addrsize=8) : este método se utiliza para leer bytes de la dirección I2C comenzando desde la dirección de memoria memadr . El tamaño de la dirección especifica el tamaño de la dirección de memoria en bits. El número de bytes leídos es igual a nbytes . El método devuelve un objeto de bytes.
I2C.readfrom_mem_into(addr, memaddr, buf, *, addrsize=8) : este método se utiliza para leer bytes en el búfer de la dirección I2C comenzando desde la dirección de memoria memadr . El tamaño de la dirección especifica el tamaño de la dirección de memoria en bits. El número de bytes leídos es igual al tamaño del búfer.
I2C.writeto_mem(addr, memaddr, buf, *, addrsize=8) : este método se utiliza para escribir un búfer en la dirección I2C comenzando desde la dirección de memoria memadr . El método no devuelve nada. El tamaño de la dirección especifica el tamaño de la dirección de memoria en bits.
Software I2C en MicroPython
El software I2C se implementa en MicroPython utilizando la clase SoftI2C. La clase SoftI2C se importa a un script MicroPython mediante la siguiente declaración.
Importación de máquina SoftI2C
Después de importar la clase SoftI2C, es necesario crear una instancia de un objeto I2C. Una función constructora hace esto. La función constructora tiene el siguiente prototipo.
máquina de clase.SoftI2C (scl, sda, frecuencia = 400000)
Todos los métodos disponibles en la clase I2C también están disponibles en la clase SoftI2C tal cual. El software I2C se implementa mediante bits. Se puede generar en cualquier GPIO con capacidad de salida. Es importante tener en cuenta que el software I2C no es tan eficiente en comparación con el hardware I2C. Es posible comunicarse con varios dispositivos I2C en el mismo bus I2C, siempre que cada dispositivo conectado al bus tenga una dirección I2C diferente. Por lo tanto, incluso si solo hay un periférico de hardware I2C disponible en un puerto, se debe utilizar para la comunicación I2C. El software I2C sólo debería ser un último recurso.
I2C en ESP8266
Hay un único controlador I2C en ESP8266. Este controlador está implementado en software y está disponible en todos los GPIO. Dado que la implementación del software de ESP8266 I2C es interna, la clase MicroPython I2C (escrita para hardware I2C) se utiliza para administrar la comunicación I2C en el ESP8266. Los pines I2C estándar en el ESP8266 son GPIO4 (SDA) y GPIO5 (SCL).
A continuación se muestra un ejemplo válido del uso de la clase I2C para la comunicación de datos en ESP8266.
Importación de máquina de pines, I2C
i2c = I2C(scl=Pin(5), sda=Pin(4), frecuencia=100000)
i2c.readfrom(0x3a, 4)
i2c.writeto(0x3a, '0xFF')
Hardware I2C en ESP32
Hay dos periféricos de hardware I2C en el ESP32. Estos periféricos se identifican mediante identificadores: 0 y 1. Los pines SDA y SCL predeterminados para I2C0 son GPIO19 y GPIO18, respectivamente. Los pines SDA y SCL predeterminados para I2C1 son GPIO26 y GPIO25, respectivamente. Sin embargo, cualquier GPIO con capacidad de salida se puede utilizar como líneas SDA y SCL en el ESP32. El siguiente es un ejemplo válido del uso de hardware I2C en ESP32.
Importación de máquina de pines, I2C
i2c = I2C(0)
i2c.readfrom(0x3a, 4)
i2c.writeto(0x3a, '0xFF')
El siguiente ejemplo muestra el uso de pines no estándar para hardware I2C.
Importación de máquina de pines, I2C
i2c = I2C(0, scl=Pin(5), sda=Pin(4), frecuencia=400000)
i2c.readfrom(0x3a, 4)
i2c.writeto(0x3a, '0xFF')
Software I2C en ESP32
El software I2C se puede utilizar en cualquier GPIO del ESP32 con capacidad de salida. Obtenga más información sobre la disponibilidad de GPIO en ESP8266 y ESP32. El siguiente es un ejemplo válido del uso del software I2C en ESP32.
Pin de importación de máquina, SoftI2C
i2c = SoftI2C(scl=Pin(5), sda=Pin(4), frecuencia=100000)
i2c.readfrom(0x3a, 4)
i2c.writeto(0x3a, '12')
buf = matriz de bytes(3)
i2c.writeto(0x3a,buf)
Interfaz ADXL345 con ESP8266 usando bus I2C
Ahora que está familiarizado con la implementación del protocolo I2C en MicroPython, ensuciémonos las manos. Los sensores suelen utilizar protocolos I2C y SPI para interactuar con controladores y microcomputadoras integrados. Conectemos un sensor acelerómetro ADXL345 con ESP8266 utilizando la implementación MicroPython del protocolo I2C.
Componentes necesarios
- ESP8266/ESP32x1
- Sensor acelerómetro ADXL345 x1
- Placa de pruebas x1
- Cables de conexión/cables de puente
- 1 cable micro USB.
Conexiones de circuito
El sensor acelerómetro ADXL345 se puede interconectar con ESP8266 o ESP32 mediante protocolos I2C o SPI. Las placas de conexión típicas del sensor de acelerómetro ADXL345 tienen pines para ambas interfaces o solo para el bus I2C. En la siguiente imagen se muestra una placa de conexión que expone solo el bus I2C para la interfaz con ADXL345.
Conecte los pines VCC y Tierra del sensor ADXL con la salida 3V y GND del ESP8266, respectivamente. Conecte los pines SCL, SDA y CS de la placa de conexión ADXL345 con los pines D0 (GPIO16), D1 (GPIO5) y D2 (GPIO4) del ESP8266. Tenga en cuenta que los pines I2C estándar en la placa ESP8266 son D1 (SCL) y D2 (SDA). Sin embargo, podemos usar cualquier GPIO con capacidad de salida en el ESP8266 para líneas SDA y SCL. Elegimos D0 para SCL y D1 para SDA.
Tenga en cuenta que debe haber cargado el firmware MicroPython para ESP8266 y estar listo para el IDE de uPyCraft antes de continuar con este proyecto.
Secuencia de comandos MicroPython
Acerca del acelerómetro ADXL345
ADXL345 es un sensor acelerómetro MEMS de 3 ejes. Es un sensor inercial digital y utiliza un diseño de acelerómetro capacitivo. Tiene un rango seleccionable por el usuario de hasta +/- 16 g, una resolución de salida máxima de 13 bits, una sensibilidad de 3,9 mg/LSB y una velocidad de datos de salida máxima de 3200 Hz. El sensor tiene interfaces I2C y SPI para comunicarse con los controladores. /ordenadores. ADXL345 mide la aceleración estática debida a la gravedad, así como la aceleración dinámica resultante del movimiento o impacto. Se puede utilizar para detectar aceleración lineal en 3 ejes y detectar la inclinación y caída libre de un objeto. Puede detectar la presencia o falta de movimiento relativo comparando los valores de aceleración con umbrales definidos por el usuario.
El ADXL345 tiene registros integrados que se pueden leer y escribir para configurar los ajustes del sensor y leer los valores de aceleración. ADXL345 ofrece cuatro rangos de medición: +/-2g, +/-4g, +/-8g y +/-16g. El rango de medición predeterminado es +/-2g, que puede detectar una aceleración de hasta 19,6 m/s2 en cualquier dirección a lo largo de cada eje. Las resoluciones máximas son 10 bits para +/-2g, 11 bits para +/-4g, 12 bits para +/-8g y 13 bits para el rango de +/-16g. La resolución predeterminada es de 10 bits, que para el rango +/-2g (predeterminado) permite una sensibilidad de 3,9 mg/LSB. La velocidad de datos predeterminada es 100 Hz. Todas estas configuraciones se pueden cambiar o configurar escribiendo datos en los registros integrados del ADXL345. Un controlador/computadora puede leer la aceleración leyendo valores de los registros 0x32 a 0x37.
Como funciona
ADXL345 se comunica con el microcontrolador a través de I2C o SPI. La placa de conexión ADXL345 expone solo líneas I2C para la comunicación de datos con el sensor. ADXL345 tiene un pin de dirección ALT que se puede conectar para configurar la dirección I2C de este sensor digital. Si el pin ALT ADDRESS está alto en un módulo, la dirección I2C de 7 bits para el dispositivo será 0x1D, seguida del bit R/W. Esto se traduce en 0x3A para escritura y 0x3B para lectura. Si el pin ALT ADDRESS está conectado a tierra, la dirección I2C de 7 bits para el dispositivo es 0x53 (seguida del bit R/W). Esto se traduce en 0xA6 para escritura y 0xA7 para lectura. El pin ALT ADDRESS ya está hacia arriba o hacia abajo en un módulo. La dirección I2C del sensor ADXL345 utilizado en este tutorial es 0x53. Esto se confirma escaneando el bus I2C utilizando el método i2c.scan de la clase MicroPython I2C.
Los registros internos del ADXL345 deben leerse y escribirse para configurar ajustes (como definir el rango de medición, velocidad de transferencia de datos, sensibilidad y resolución) y leer valores de aceleración. A continuación se proporciona una tabla de estos registros.
Para comunicarse con ADXL345, en primer lugar, sus parámetros de configuración se definen escribiendo en registros: DATA_FORMAT (0x31), BW_RATE (0x2C), POWER_CTL (0x2D), INT_ENABLE (0x2E), OFSX (0x1E), OFSY (0x1F) y OSFZ ( 0x20). Después de escribir en los registros de configuración mediante protocolos I2C, la aceleración a lo largo del eje x se obtiene leyendo los registros 0x32 y 0x33 en el bus I2C. La aceleración a lo largo del eje y se obtiene leyendo los registros 0x34 y 0x35 en el bus I2C. La aceleración a lo largo del eje z se obtiene leyendo los registros 0x36 y 0x37 en el bus I2C.
Los valores de aceleración de lectura son de 16 bits. Los valores obtenidos de los pares de registros se convierten en un único valor en complemento a 2 de 16 bits para obtener los valores finales de aceleración sin procesar. Estos valores se multiplican por un factor de 3,9, correspondiente a una resolución de +/-4g, para obtener valores de aceleración en mg.
Código
El código MicroPython comienza importando las clases Pin e I2C desde el módulo de la máquina. El módulo de tiempo se importa para proporcionar retraso y el módulo ustruct se importa para formatear los valores de aceleración a 16 bits.
A continuación se muestra la definición de constantes que representan los registros de configuración del ADXL345. Se definen los pines SCL, SDA y CS para el bus I2C y el pin CS se define como salida digital. Se crea una instancia de un objeto I2C configurando D0 (GPIO16) y D1 (GPIO5) en SCL y SDA, respectivamente, y configurando la frecuencia máxima de I2C en 10,000 Hz. El bus I2C se escanea llamando al método i2c.scan y se devuelve la lista. se almacena en una lista 'slv'. Con la ayuda de un bucle for, los valores de la lista 'slv' se comparan con la dirección I2c conocida del ADXL345. Se imprimirá un mensaje que muestra "Se encontró ADXL345" en la consola si se encuentra la dirección. La dirección correspondiente se almacena en una variable 'slvAddr'.
Se define una función 'writeByte' para escribir bytes en el ADXL345 a través del bus I2C. Se define una función 'readByte' para leer bytes del ADXL345 a través del bus I2C. La función writeByte se utiliza para escribir datos en los registros de configuración: DATA_FORMAT, BW_RATE, INT_ENABLE, OFSX, OFSY, OFSZ y POWER_CTL.
En un bucle while infinito, los valores de los registros 0x32 y 0x33 se leen llamando a la función readByte definida por el usuario. Los valores se formatean en un único número de 16 bits mediante el método ustruct.unpack. Esto da el valor bruto de la aceleración a lo largo del eje x. El valor bruto se multiplica por 3,9 para obtener la aceleración en mg.
De manera similar, se toman los valores de los registros 0x34 y 0x35 para derivar la aceleración a lo largo del eje y. Los valores de los registros 0x36 y 0x37 se toman para derivar la aceleración a lo largo del eje z. El ciclo continúa indefinidamente hasta que el script finaliza en uPyCraft o Thonny IDE.
Resultado