lunes, 3 de abril de 2017

Controlar un display de un dígito y siete segmentos con un chip 74HC595

En el anterior post vimos como controlar un display de forma directa con la Raspberry Pi. Pero aveces es necesario ahorrar en salidas GPIO, para usarlos con otros fines o bien porque nuestro dispositivo tiene un número de salidas limitado. En este post os voy a mostrar como usar un chip 74HC595 para controlar el display con solo 5 puertos GPIO y unos ejemplos en Python para poder practicar. El display es el modelo SMA42056



Como podéis ver el dispositivo dispone de 7 segmentos, más un punto en la parte inferior derecha, iluminados por led. Dispone de un total de 10 conexiones (5 en la parte superior y cinco en la inferior para hacerlo funcionar). 

El chip 74HC595 es lo que en inglés se llama un Shift Register, este modelo es del tipo 8 bits serial-in, parallel-out, o sea, que acepta la entrada de de 8 bits en serie y su salida es en 8 pines paralelos (no nos pongamos nerviosos... que ya lo iremos viendo). y la función básica es la de poder "multiplicar" los pines. Aquí tenéis una imagen

Para este ejercicio vamos a necesitar, cables de conexión, 1 display de un dígito de 7 segmentos, el chip 74HC595 y la RaspberryPi. Si sois nuevos en esto os aconsejo leer los anteriores posts (del uso de los GPIO en el apartado Programación/GPIO, y el anterior post para controlar el display de forma directa).


1. Distribución de las conexiones y los segmentos del display


Vamos a ver como se distribuyen las conexiones del display en relación con los segmentos del mismo, lo mejor es que veáis la siguiente imagen

como podéis ver, el display tiene un total de 10 pines, el pin 3 y 8 son los pines del cátodo o ánodo común (según el tipo de display que tengamos que se identifican con la letra K), sería el pin de tierra. Los pines 1, 2, 4, 5 en la parte inferior y los pines 6, 7, 9 y 10 en la superior son los polos positivos de cada uno de los segmentos del display, aunque se llame display de 7 segmentos, en total hay 8, ya que hay que tener en cuenta el punto decimal (DP) en la parte inferior derecha. En la imagen podéis ver la correspondencia entre los pines y los segmentos.


2.Distribución de las conexiones del chip 74HC595


Ahora vamos a ver como se distribuyen las conexiones de nuestro chip (a esto se le llama el pinning), aquí tenéis una imagen



para no equivocaros con la orientación del chip fijaos en la muesca con forma semicircular en un extremo del chip. Vamos a ver los pines que vamos a necesitar para los ejemplos que os muestro luego. En primer lugar las entradas (todas en la parte derecha de la imagen): MR, SHCP, STCP, OE, DS, VCC; y ahora las 8 salidas, Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, todas ellas se encuentran en el lado izquierdo del chip excepto el Q0; y finalmente el pin GND también en el lado izquierdo.
Las entradas sirven para enviar la información al chip y que se pueda almacenar, y las salidas permiten enviar a otro dispositivo (en este caso un display) la información necesaria para que este se ilumine.
Para saber más sobre el funcionamiento de este chip os dejo el siguiente link

https://drive.google.com/file/d/0BzaKjvCRihgbNFVEOUY5NWRTSmc/view

3. Organización del circuito


En primer lugar vamos a ver como nos debe quedar nuestro circuito para poder controlar un display de un dígito. Aquí os dejo una imagen para que veáis la distribución que he usado, tened cuidado que en este ejercicio hay mucho cable.



si no habéis leído los anteriores post os recomiendo hacerlo.
Bien, ahora podría hacer una larga descripción de como he realizado el cableado (lo que sería bastante aburrido de leer) creo que es mejor usar una tabla para hacerlo más sencillo

Raspberry Pi Color cable Chip 74HC595 Color cable Display
GND negro GND (pin8) negro pin3 (K), pin 8 (K)
3V3 rojo Vcc (pin16)
GPIO17 verde DS (pin14)
GPIO27 gris claro STCP (pin12)
GPIO22 naranja SHCP (pin11)
GPIO25 amarillo MR (pin10)
SPISCLK (GPIO11) azul OE (pin13)
Q0 (pin15) lila pin1 (e)
Q1 (pin1) fucsia pin2 (d)
Q2 (pin2) azul oscuro pin4 (c)
Q3 (pin3) verde claro pin6 (b)
Q4 (pin4) azul claro pin7 (a)
Q5 (pin5) gris oscuro pin9 (f)
Q6 (pin6) naranja oscuro pin10 (g)
Q7 (pin7) amarillo oscuro pin5 (DP)

fijaos que el cable negro (GND) va conectado a un pin del Chip 74HC595, y a dos pines del display. El resto de conexiones creo que son bastante sencillas las que van de la Raspberry Pi al Cip y las que van del Chip al Display.
Tened en cuenta que si usáis otro modelo de chip o display las conexiones pueden ser diferentes, consultad la documentación técnica para no equivocaros. Por otro lado es mejor que os aseguréis que en la pestaña Interfaces de la pantalla de Configuración de Raspberry Pi, no tengáis activado el SPI, el I2C, etc. ya que vamos a usar pines GPIO que pueden tener varias funciones al activarlos.


3. Código Python

3.1 Encender los diferentes segmentos del display

Vamos a crear un pequeño programa en python que al ejecutarlo nos permita controlar los diferentes segmentos del display. Si no sabéis como crear un archivo con un programa en python os recomiendo leer los post sobre programación python del apartado Programación/GPIO del blog.

En primer lugar abrimos el IDLE de Python en nuestra RaspberryPi (yo uso Python3), una vez abierto creamos un nuevo fichero (en el menú File-New File) y introducimos el siguiente código (es largo pero no os preocupéis que es muy sencillo)

# importamos libreria GPIO y time
import RPi.GPIO as GPIO
import time
# guardamos en variables los puertos GPIO que usaremos con el Chip
DATASET=17 # Pin para entrada de datos DS
CLOCK=22 # Pin control de lectura de datos SHCP
LATCH=27 # Pin para mostrar datos almacenados STCP
CLEAR=25 # Pin que controla el borrado datos MR
OE=11 # Pin Output enable Pin controla la salida de informacion OE
# guardamos en demora el tiempo de espera
demora=1
# definimos variables que guardan el valor hexadecimal para
# iluminar cada uno de los segmentos, al lado os pongo el
# equivalente en binario para ver la equivalencia, que
# luego guardaremos en cada pin de salida del chip 
# para activar los diferentes segmentos (los 8 valores
# son los que se guardan y activan-desactivan los segmentos)
segmentoA=0x08  #00001000
segmentoB=0x10  #00011000
segmentoC=0x20  #00111000
segmentoD=0x40  #01111000
segmentoE=0x80  #11111000
segmentoF=0x04  #11111100
segmentoG=0x02  #11111110
segmentoDP=0x01 #11111111
# creamos una lista con las variables
ledrange=[segmentoA, segmentoB, segmentoC, segmentoD, segmentoE, segmentoF, segmentoG, segmentoDP]
# funcion para inicializar los GPIO
def setup():
    # desactivamos mensajes error GPIO
    GPIO.setwarnings(False)
    # indicamos el uso de  la identificacion BCM para los GPIO
    GPIO.setmode(GPIO.BCM)
    # inicializamos GPIO
    GPIO.cleanup()
    # indicamos los GPIO que usaremos como salida
    GPIO.setup(DATASET,GPIO.OUT)
    GPIO.setup(CLOCK,GPIO.OUT)
    GPIO.setup(LATCH,GPIO.OUT)
    GPIO.setup(CLEAR,GPIO.OUT)
    GPIO.setup(OE,GPIO.OUT)
    # indicamos a cada GPIO el estado inicial
    GPIO.output(DATASET,False) # Se usa para introducir los datos
    GPIO.output(CLOCK,False)  # controla la entrada de datos
    GPIO.output(LATCH,False)  # Muestra los datos almacenados
    GPIO.output(CLEAR,True)   # El valor False borra todos los datos
    GPIO.output(OE,False)     # Output Enable, para salida de datos
    
# definimos una funcion que apaga el display
def cleanup():
    #
    writenumber(0)
    #
    writeout()
    #
    writeout()
    time.sleep(0.7)
    GPIO.cleanup()

# Funciones para activar segmentos del display

# Envia por el GPIO si el valor input es = 1 o <> 1
# al chip para guardarlo en memoria del chip
def shift(input):
    if input == 1:
        input=True
    else:
        input=False
    # enviamos un valor al pin de datos DATASET
    GPIO.output(DATASET,input)
    # activo el pin de lectura para leer el valor CLOCK
    GPIO.output(CLOCK,GPIO.HIGH)
    # desactivo el pin e lectura
    GPIO.output(CLOCK,GPIO.LOW)
    # finalizo en envio del dato al pin DATASET
    GPIO.output(DATASET,GPIO.LOW)

# writenumber recoge el valor del segmento que
# queremos activar o desactivar, y ejecuta 8
# veces (para cada valor de almacenamiento del chip)
# la orden shift para enviar el valores a guardar
def writenumber(number):
    for x in range(0,8):
        # ejecuto la orden shift pasando a binario
        # el valor del segmento
        shift((number>>x)%2)

# Envia a los GPIO la orden para mostrar los datos
# guardados en el chip
# y que se refleje en el display
def writeout():
   # Activamos salida de datos guardados con LATCH
   GPIO.output(LATCH,GPIO.HIGH)
   #
   time.sleep(demora)
   # Finalizo la salida de datos guardados de LATCH
   GPIO.output(LATCH,GPIO.LOW)

# funcion que recibe la lista de segmentos para recorrerlos
def writexorrange(range):
    # con esta orden evitamos que 'character' provoque un error
    character=range[0]&range[-1]
    # recorremos las 8 variables para activar los segmentos
    for x in range:
        character^=x
        writenumber(character)
        writeout()
    # recorremos las 8 variables para apagar los segmentos
    for x in range:
        character^=x
        writenumber(character)
        writeout()
    # recorremos las 8 variables a la inversa para activar los segmentos
    for x in reversed(range):
        character^=x
        writenumber(character)
        writeout()
    # recorremos las 8 variables a la inversa para apagar los segmentos
    for x in reversed(range):
        character^=x
        writenumber(character)
        writeout()

# llamamos a la funcion que inicializa los GPIO
print("-->Inicializamos GPIO")
setup()

# usamos estructura de control de errores si presionamos CTRL+C
# se interrumpira la ejecución
try:
    print("-->Encendemos el display")
    # bucle infinito que llama a la funcion que enciende los segmentos
    while True:
        # llamamos a la función que activa el display
        writexorrange(ledrange)

# esto se ejecuta al presionar CTRL+C
except (KeyboardInterrupt, SystemExit):
    print("Exit...")
# si interrumpimos el codigo ejecutamos cleanup()
finally:
    # apagamos el display
    cleanup()
   

guardamos el archivo en una ubicación conocida (en el menú File-Save As), si es necesario apuntad la ruta por si la necesitáis usar más tarde. 

Podemos ejecutar directamente este pequeño programa presionando la tecla F5 (también se puede ejecutar des de el menú Run-Run Module)

La primera parte del código importamos las librerías, preparamos los GPIO, y creamos una lista (ledrange) que contiene el identificador hexadecimal para activar cada unos de los segmentos del display. 
Seguidamente he definido la función setup() que inicializa los GPIO para poder enviar la información al chip 74HC595. 
Después he definido la función cleanup() al utilizarlo apagamos todos  los segmentos del display (la orden writeout() se debe ejecutar 2 veces).
Ahora empezamos con las funciones para activar el display. La primera función que he definido es shift() que recibe la variable input, esta función envia al chip el valor 0 o 1 (valor binario) para que sea  guardado en el chip.
La siguiente función es writenumber() que recibe el parámetro number, que es el valor hexadecimal que hemos guardado en la lista (0x08, 0x10,...) lo transforma en binario (en un valor de 8 dígitos, 00001000, 00011000,...) y usa la función shift() para guardarlos en el chip.
La función writeout() envía la orden al chip para que muestre en el display la información guardada.
Ahora vamos a por las última función. La función writexorrange() recibe como variable range, que contiene la lista de segmentos con los valores hexadecimales. Esta función recorre 4 veces (con 4 bucles), los valores de la lista para activar y desactivar los segmentos.
El resto del código es un bucle infinito que podemos interrumpir con el teclado  presionando CTRL+C, y que ejecuta de forma indefinida la función writexorrange()

Si todo es correcto se abre una nueva ventana de IDLE de python y empieza la ejecución, el display encenderá uno a uno los segmentos, luego se apagarán, y empezarán a iluminarse de forma inversa, y así se repetirá hasta que interrumpáis el bucle



3.2 Visualizar números en el display


Ahora vamos a ver un segundo ejemplo, vamos a crear un pequeño programa en python que al ejecutarlo nos permita ver los números del 0 al 9 en el display. Si no sabéis como crear un archivo con un programa en python os recomiendo leer los post sobre programación python del apartado Programación/GPIO del blog.

En primer lugar abrimos el IDLE de Python en nuestra RaspberryPi (yo uso Python3), una vez abierto creamos un nuevo fichero (en el menú File-New File) y introducimos el siguiente código (el ejemplo es muy parecido al anterior, también es largo pero no os preocupéis que es muy sencillo)


# importamos libreria GPIO y time
import RPi.GPIO as GPIO
import time
# guardamos en variables los puertos GPIO que usaremos
DATASET=17 # Pin para entrada de datos
CLOCK=22 # Pin control de lectura de datos
LATCH=27 # Pin para mostrar datos almacenados
CLEAR=25 # Pin que controla el borrado datos
OE=11 # Pin Output enable Pin controla la salida de informacion
# guardamos en demora el tiempo de espera
demora=1
# definimos variables que guardan el valor hexadecimal para
# mostrar los números en el display.
cero=0xFC
uno=0x30
dos=0xDA
tres=0x7A
cuatro=0x36
cinco=0x6E
seis=0xEE
siete=0x38
ocho=0xFE
nueve=0x3E
#creamos una lista con las variables
letterrange=[cero,uno,dos,tres,cuatro,cinco,seis,siete,ocho,nueve]
# funcion para inicializar los GPIO
def setup():
    # desactivamos mensajes error GPIO
    GPIO.setwarnings(False)
    # indicamos el uso de  la identificacion BCM para los GPIO
    GPIO.setmode(GPIO.BCM)
    # inicializamos GPIO
    GPIO.cleanup()
    # indicamos los GPIO que usaremos como salida
    GPIO.setup(DATASET,GPIO.OUT)
    GPIO.setup(CLOCK,GPIO.OUT)
    GPIO.setup(LATCH,GPIO.OUT)
    GPIO.setup(CLEAR,GPIO.OUT)
    GPIO.setup(OE,GPIO.OUT)
    # indicamos a cada GPIO el estado inicial
    GPIO.output(DATASET,False) # Se usa para introducir los datos
    GPIO.output(CLOCK,False)  # controla la entrada de datos
    GPIO.output(LATCH,False)  # Muestra los datos almacenados
    GPIO.output(CLEAR,True)   # El valor False borra todos los datos
    GPIO.output(OE,False)     # Output Enable para salida de datos

# definimos una funcion que apaga todos los segmentos
def cleanup():
    #
    writenumber(0)
    #
    writeout()
    #
    writeout()
    time.sleep(0.7)
    GPIO.cleanup()

# Funciones para activar segmentos del display

# Envia por el GPIO si el valor input es = 1 o <> 1
# encendido al chip para guardarlo en memoria
def shift(input):
    if input == 1:
        input=True
    else:
        input=False
    # enviamos un valor al pin de datos DATASET
    GPIO.output(DATASET,input)
    # activo el pin de lectura para leer el valor CLOCK
    GPIO.output(CLOCK,GPIO.HIGH)
    # desactivo el pin e lectura
    GPIO.output(CLOCK,GPIO.LOW)
    # finalizo en envio del dato al pin DATASET
    GPIO.output(DATASET,GPIO.LOW)

# writenumber recoge el valor del segmento que
# queremos activar o desactivar, y ejecuta 8
# veces (para cada pin de almacenamiento del chip)
# la orden shift para enviar el valor a
# guardar em cada pin
def writenumber(number):
    for x in range(0,8):
        # ejecuto la orden shift pasando a binario
        # el valor del segmento
        shift((number>>x)%2)

# Envia a los GPIO la orden para leer los datos
# guardados en el chip
# y que se refleje en el display
def writeout():
   # Activamos salida de datos guardados con LATCH
   GPIO.output(LATCH,GPIO.HIGH)
   #
   time.sleep(demora)
   # Finalizo la salida de datos guardados de LATCH
   GPIO.output(LATCH,GPIO.LOW)

# funcion que recibe la lista de numeros para recorrerlos       
def writerange(range):
    for x in range:
        writenumber(x)
        writeout()
    
# llamamos a la funcion que inicializa los GPIO
print("-->Inicializamos GPIO")
setup()

# usamos estructura de control de errores si presionamos CTRL+C
# se interrumpira la ejecución
try:
    print("-->Encendemos el display")
    # bucle infinito que llama a la funcion que enciende los segmentos
    while True:
        # llamamos a la funcion que activa el display
        writerange(letterrange)
        
# esto se ejecuta al presionar CTRL+C
except (KeyboardInterrupt, SystemExit):
    print("Exit...")
# una vez interumpido o finalizado el codigo ejecutamos cleanup()
finally:
    cleanup()

guardamos el archivo en una ubicación conocida (en el menú File-Save As), si es necesario apuntad la ruta por si la necesitáis usar más tarde. 


Podemos ejecutar directamente este pequeño programa presionando la tecla F5 (también se puede ejecutar des de el menú Run-Run Module)

La primera parte del código importamos las librerías, preparamos los GPIO, y creamos una lista (letterrange) que contiene el identificador hexadecimal para mostrar cada uno de los snúmeros en el display. 
Seguidamente he definido la función setup() que inicializa los GPIO para poder enviar la información al chip 74HC595. 
Después he definido la función cleanup() al utilizarlo apagamos todos  los segmentos del display (la orden writeout() se debe ejecutar 2 veces).
Ahora empezamos con las funciones para activar el display. La primera función que he definido es shift() que recibe la variable input, esta función envia al chip el valor 0 o 1 (valor binario) para que sea  guardado en el chip.
La siguiente función es writenumber() que recibe el parámetro number, que es el valor hexadecimal que hemos guardado en la lista (0x08, 0x10,...) lo transforma en binario (en un valor de 8 dígitos, 00001000, 00011000,...) y usa la función shift() para guardarlos en el chip.
La función writeout() envía la orden al chip para que muestre en el display la información guardada.
Ahora vamos a por las última función. La función writerange() recibe como variable range, que contiene la lista de núemros con los valores hexadecimales. Esta función recorre la lista para mostrar los números en el display.
El resto del código es un bucle infinito que podemos interrumpir con el teclado  presionando CTRL+C, y que ejecuta de forma indefinida la función writerange()

Si todo es correcto se abre una nueva ventana de IDLE de python y empieza la ejecución, el display mostrará uno a uno los números del 0 al 9, y así se repetirá hasta que interrumpáis el bucle