Uso del SPI en full duplex con Raspberry PI

Scooter

Cascarrabias crónico
Pues estoy conectando un conversor AD a la raspi por el puerto SPI y parece ser que no han pensado, no saben o no entienden, que el puerto SPI ES fullduplex.
A lo mejor soy yo que se me escapa algo.
He buscado en las librerías pero todo indica que el funcionamiento es siempre haf duplex, he enchufado el analizador lógico y así es como funciona.
¿Alguien sabe como usarlo o hay que pasar del hardware SPI y hacerlo mediante bitbang. "A mano" sé como hacerlo pero me da cosa. Me parece una cutrería.

El chip el el MCP 3208 de los que tengo varios que me quedaron de un proyecto de hace años.
Tutorial de uso del SPI:
Maunal de la librería de python:

Python:
# AD_spi.py
#
# Programa para la lectura de un conversor AD SPI mcp3208
# Conectado al CE0
#

import time
import spidev

# Solo tenemos disponible el bus0 en la Pi
bus = 0

#Device es el pin chip select pin. CE0 o CE1
device = 0

# Enable SPI
spi = spidev.SpiDev()

# Open a connection to a specific bus and device (chip select pin)
spi.open(bus, device)

# Set SPI speed and mode
spi.max_speed_hz = 10000
spi.mode = 0
#spi.no_cs = True
# Seleccionar el pin
# Los bints van: xxxxx[Start(1)][SGL/Diff][D2] [D1][D0]xxxxxx
# Seleccionamos la entrada 0
msg0 = 0x06
msg1 = 0x00
msg2 = 0x00
msg = [msg0,msg1,msg2]

#spi.xfer2(msg0)
spi.xfer2(msg)


resultado = spi.readbytes(1)
print(resultado)


#Hay que manejar el CS a mano y escribir y leer dos bytes sin parar CS

El problema es que la transferencia se realiza en tres bytes.
Durante el primero y el segundo el master hace la petición de que y como quiere leer en el conversor AD; un bis d start, un bit de si es o no diferencial y tres bits que selecionan la entrada. Solo que los dos últimos bits se envían en el segundo byte.
En el segundo y tercer byte responde el conversor.
Así que en el segundo habla el master y habla el slave.
El código no va de ninguna forma; mirando con un analizador lógico si se envían tres bytes se ve que el conversor responde la lectura. Pero es respuesta se pierde. Tras eso si se solicitan uno o dos bytes con spi.readbytes no contesta ya.

Yo pensaba que spi.readbytes sacaba del buffer lo que se había recibido MIENTRAS se envían bytes, pero no, si pides dos bytes no los saca del buffer, los saca del bus en donde no hay nada porque el conversor ya respondió.
 

Adjuntos

  • MCP3208-c.pdf
    722.3 KB · Visitas: 2
Última edición:
Solucionado por el camino de enmedio.
Seguramente sobren la mitad de los delays pero el datasheet dice que no puede ir mas rápido de 100kHz a 5V y 50kHz a 2,7V y como está funcionando a 3V3... pues mas o menos. Sin delays oscila por encima del MHz el pin así que algo habrá que hacer.
También seguro que queda mas elegante un bucle pero entonces hay que pensar como y cuando hago cada cosa, y pensar es malísimo.
Luego me leo el datasheet que creo que se puede hacer sin mandar tanto bit, con unos cuantos menos funciona porque al hacerlo a mano no hay que usar múltiplos de 8.

Python:
#Prueba de SPI a manubrio

#Zona de Imports
import RPi.GPIO as GPIO
import time

#Defino los pines del SPI
MISO = 9
MOSI = 10
SCLK = 11
CE0 = 8

#Generamos un pulso de reloj
def pulso():
    GPIo_Output(SCLK,0)
    time.sleep(0.000005)
    GPIo_Output(SCLK,1)
    time.sleep(0.00001)
    GPIo_Output(SCLK,0)
    time.sleep(0.000005)

#Configuración de los pines
GPIO.setmode(GPIO.BCM)
GPIO.setup(MISO,GPIO.IN)
GPIO.setup(MOSI,GPIO.OUT)
GPIO.setup(SCLK,GPIO.OUT)
GPIO.setup(CE0,GPIO.OUT)

#Ponemos el valor de lectura a 0
lectura = 0
#Ponemos MOSI a 0
GPIo_Output(MOSI,0)
#Ponemos selección de chip a 0
GPIo_Output(CE0,0)

#Ponemos relok a o
GPIo_Output(SCLK,0)

#Byte de configuración
pulso()
pulso()
pulso()
pulso()
pulso()
GPIo_Output(MOSI,1)    #Bit de start
pulso()
GPIo_Output(MOSI,1)     #1 Modo simple 0 modo diferencial
pulso()
GPIo_Output(MOSI,0)     #Bit 2 de dirección
pulso()
time.sleep(0.0001)
GPIo_Output(MOSI,0)     #Bit 1 de dirección
pulso()
GPIo_Output(MOSI,0)     #Bit 0 de dirección
pulso()
pulso()
pulso()
pulso()
lectura = GPIO.input(MISO) * 2048
pulso()
lectura = lectura + GPIO.input(MISO)*1024
pulso()
lectura = lectura + GPIO.input(MISO)*512
pulso()
lectura = lectura + GPIO.input(MISO)*256
time.sleep(0.0001)
pulso()
lectura = lectura + GPIO.input(MISO)*128
pulso()
lectura = lectura + GPIO.input(MISO)*64
pulso()
lectura = lectura + GPIO.input(MISO)*32
pulso()
lectura = lectura + GPIO.input(MISO)*16
pulso()
lectura = lectura + GPIO.input(MISO)*8
pulso()
lectura = lectura + GPIO.input(MISO)*4
pulso()
lectura = lectura + GPIO.input(MISO)*2
pulso()
lectura = lectura + GPIO.input(MISO)*1
GPIo_Output(CE0,1)

print(lectura)
 
Última edición:
¡Dioggg mio!
Soy mas tonto que una alberca.
La propia FUNCIÓN que manda los bytes RECIBE las respuestas.

Python:
Python:

# AD_spi.py
#
# Programa para la lectura de un conversor AD SPI mcp3208
# Conectado al CE0
#

import time
import spidev

# Solo tenemos disponible el bus0 en la Pi
bus = 0

#Device es el pin chip select pin. CE0 o CE1
device = 0

# Enable SPI
spi = spidev.SpiDev()

# Open a connection to a specific bus and device (chip select pin)
spi.open(bus, device)

# Set SPI speed and mode
spi.max_speed_hz = 10000
spi.mode = 0
#spi.no_cs = True
# Seleccionar el pin
# Los bints van: xxxxx[Start(1)][SGL/Diff][D2] [D1][D0]xxxxxx
# Seleccionamos la entrada 0
msg0 = 0x06
msg1 = 0x00
msg2 = 0x00
msg = [msg0,msg1,msg2]

resultado = spi.xfer2(msg)
print(resultado)

Solo hay que arreglar el resultado para que salga un número algo como
numero = resultado[1]*256+resultado[2] o algo así y ya está.

Por si queréis echar unas risas de mi nivel de inglés os pongo el enlace del foro en el que me sacaron de mi error.
Mensaje automáticamente combinado:

De todos modos por si alguien quiere usar el bit bang, hay que hacer el pulso al revés de como estaba hecho:
Python:
def pulso():
    GPIo_Output(SCLK,1)
    time.sleep(0.000005)
    GPIo_Output(SCLK,0)
    time.sleep(0.00001)
    GPIo_Output(SCLK,1)
    time.sleep(0.000005)
 
Última edición:
El talibán de los delaises indica que si se quitan funciona igual en una raspi4.
Osea que se pueden quitar los time.sleep del generador de pulso.

Y hay dos muy buenos motivos para usar el SPI por software.
  1. Que puedes elegir los pines que quieras y colocar los periféricos que quieras. El hard permite usar solo dos pines como CS.
  2. Y demoledora que si usas los pines el oficial hardware deja de funcionar. Osea, al hacer import RPi.GPIO as GPIO deja de funcionar el SPI hardware y hay que reiniciar. OSea que no se pueden combinar programas que usen el resto de pines y el SPI.
Desconozco si pasará lo mismo con el I²C. Si pasa sería una p****a porque el "potroloco" I²C es mas complejo de implementar "a pelo" que el SPI que es bastante tonto de hacer.
 
Última edición:
Atrás
Arriba