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.