Programación en Python de SPI

En Python disponemos de varias bibliotecas para acceder a dispositivos SPI:

  • GPIO Zero, que incorpora varios convertidores AD de Microchip.

  • La biblioteca spidev proporciona acceso desde Python a la funcionalidad del driver SPI de Linux.

  • La biblioteca wiringPi2 que no es sino un envoltorio de la biblioteca C.

  • La biblioteca pigpio que también ofrece un envoltorio a la versión remota de la biblioteca C.

La biblioteca más sencilla es nuevamente GPIO Zero. Veamos un ejemplo de la documentación de scaled:

from gpiozero import Motor, MCP3008
from gpiozero.tools import scaled
from signal import pause

motor = Motor(20, 21)
pot = MCP3008(channel=0)
motor.source = scaled(pot.values, -1, 1)
pause()

El potenciómetro está conectado al canal 0 de un conversor AD Microchip MCP3008. Se trata de un dispositivo SPI, igual que nuestro ADS1118. El usuario está por tanto completamente aislado de la interacción SPI. Pero vamos a ver lo que implicaría definir un módulo específico para el ADS1118.

La clase SPIDevice

Todos los dispositivos SPI derivan de la clase SPIDevice. Esta define un atributo privado self._spi que se construye llamando a la función SPI. Esta función devuelve un SPIHardwareInterface o un SPISoftwareInterface dependiendo de los pines utilizados. Si los pines corresponden con un puerto SPI de la Raspberry Pi devolverá un SPIHardwareInterface, que es mucho más eficiente.

Para cambiar la polaridad del reloj y la fase podemos utilizar la propiedad clock_mode del atributo self._spi, que en nuestro caso (ADS1118) debe tener el valor 1.

En lugar de usar SPIDevice como base podemos aprovechar la clase AnalogInputDevice, que añade la lógica necesaria para decodificar el valor escalado al rango [0, 1] dependiendo del número de bits de las lecturas.

Con esto la clase para implementar un dispositivo ADS1118 queda prácticamente igual que los MCP3xxx.

class ADS1118(AnalogInputDevice):

    def __init__(self, channel=0, differential=False, **spi_args):
        self._channel = channel
        self._bits = 16
        self._differential = bool(differential)
        super(ADS1118, self).__init__(16, **spi_args)
        self._spi.clock_mode = 1
        data = self._spi.transfer(2 * self._config_reg())

    @property
    def channel(self):
        return self._channel

    @property
    def differential(self):
        return self._differential

    def _read(self):
        data = self._spi.transfer(2 * self._config_reg())
        result = (data[0] << 8) | data[1]
        if self.differential and result > 32767:
            result = -(65536 - result)
        assert -32768 <= result < 32768
        return result

    def _config_reg(self):
        ''' Configuración en modo continuo a 128 SPS y con rango a
            plena escala de +-2.048V'''
        #     Byte        0        1
        #     ==== ======== ========
        #          sMCCGGGS rrrTP011
        #
        #   s = start single shot conversion
        #   M = differential (1 = single-ended, 0 = differential)
        #   C = channel
        #   G = gain
        #   S = single shot mode (1 = single shot, 0 = continuous)
        #   r = sample rate
        #   T = temperature sensor
        #   P = pull-up in DOUT
        return [0b00000100 + (self.channel << 4) + [0b01000000, 0][self.differential],
                0b10001011]

Ya está, no hay más, con esto ya se puede usar como cualquiera de los módulos AD incluidos en GPIO Zero. Conecta un potenciómetro entre 3.3V y masa y el cursor utilízalo como entrada AN0 del módulo CJMCU-1118. Conecta el módulo a la interfaz SPI, usando como línea de selección CE0.

led = PWMLED(4)
pot = ADS1118(channel=0)
led.source = pot.values

Un LED con intensidad controlada por un potenciómetro.

results matching ""

    No results matching ""