--- Introducción ---
Muchas
veces tenemos un microcontrolador que anda perfecto para una tarea pero
lamentablemente no posee la cantidad de pines que uno necesita.
Una
solución es comprar un microcontrolador con mas pines. Esta solución muchas veces presenta el problema de que la
diferencia en precio es grande. Otras veces uno puede estar experimentando en
fase de aprender y por eso, como yo, se decanta por otro método.
En
mi caso estoy trabajando con un PIC12f675 el cual posee solo 5 pines
I/O y 1 que solo es entrada. Como se ve la cantidad de pines disponible
no es mucha.
En el circuito que uso necesito disponer de 8 salidas
para comandar una red DAC R-2R. Acá es donde generalmente uno de afuera
diría: usa un PIC16f628 o, si necesitas un ADC, un PIC16F88.
Supongamos
ahora, para el que aconseja eso, que mi circuito es un POV LED que
tiene que comandar 36 LEDS. ¿ Irse a un PIC32 ? ¿ un FPGA ?.
Es acá donde un poco de conocimiento sobre los expansores de puertos puede ser útil y es a lo que apunta esta guía.
---- El registro de desplazamiento 74HC595 ---
El diagrama parece complicado pero no es tan así, veremos que es mas fácil de manejar de lo que parece.
Tenemos:
1 Linea de entrada de datos serial.
4 Lineas de control.
1 Linea para hacer cascadas (se pueden conectar "infinitos" circuitos).
8 Salidas de datos.
Pin a pin que hace cada cosa:
PIN desde el 1 al 7 y el 15: Empezando por lo mas fácil estos ocho pines serán por donde uno obtendrá la salida paralela. Acá se conectaran los LEDs, motores, circuitos, etc, que uno quiere comandar.
El pin 15 sera el de menos peso -LSB- y el 7 el de mayor peso o -MSB- (esto para tener en cuanta cuando se envíe los datos serialmente, luego debido a que es uno el que controla que dato aparece en cada salida el dato de menor peso o mayor peso podrá ser cualquiera).
PIN 10: Siguiendo por lo fácil, el pin 10 es el de reset. Este pin resetea la cuenta que lleva el dispositivo en el desplazamiento como así también limpia las salidas que pasaran a 0.
En el caso de no disponer de los suficientes pines en el micro habrá que poner este pin (pin 10) a Vcc para dejar al registro sin resetear.
PIN 13: Habilitador de salidas. Este pin "conecta" las salidas. En caso de que este a Vcc -salidas desconectadas- las mismas estarán flotantes o también llamado en HI-Z alta impedancia.
En caso de conectarlo a GND las salidas estarán "conectadas" con lo que ahora los pines están a 0 o 1 dependiendo de lo que se le haya enviado al registro a través del micro.
Como en el caso anterior si no se dispone de los suficientes pines en el micro este pin puede conectarse permanentemente a Vcc y así dejar las salidas permanentemente conectadas.
PIN14: Entrada serial de datos. Por este pin ingresan, uno a uno, los bits que compondrán la salida. Para validar y guardar cada bit habrá que hacer uso del siguiente pin:
PIN11: Pulsos de reloj para validar los bits de entrada. Al recibir este pin un flanco de subida desplaza el contenido del registro hacia la derecha almacenando el valor en el bit 0 del registro de desplazamiento.
PIN12: Una vez se haya enviado los 8 bit que compondrán la salida, se envía un pulso a este pin para que en el flanco de subida pase todo lo que este guardado en el registro de desplazamiento hacia las salidas (Registro de almacenamiento). La salida permanecerá inalterable aun cuando se vuelva a ingresar datos y solo cambiara cuando, mediante este pin (pin 12) se vuelva a dar un pulso para actualizar las salidas con el segundo valor que se cargo.
PIN9: Por ultimo, como dijimos que se pueden colocar mas registros en cascada el micro deberá enviar no ya 8 bits sino 16 bits (por ejemplo si tenemos solo 2 registros en cascada).
Al ingresar el 9no bit, el primero que se ingreso saldrá por este pin (pin 9) y entrara al pin 14 del siguiente registro para acumularse ahí.
Si es complicado de entender supongase lo siguiente: hay una mesa en donde entran 4 cajas; coloco una caja, luego y empujando a esta (desplazándola) coloco otra y así hasta que en la mesa haya 4 cajas. Ahora si coloco otra caja, al no entrar mas, la de la punta caerá al suelo perdiéndose.
En una cascada el pin 9 vendría a ser un puente por donde se deslizaría esta caja que en el otro caso se perdería e iría a parar a una segunda mesa. Obviamente cuando coloque 8 cajas terminare con las dos mesas llenas con 4 cajas cada una (osea dos registros de desplazamiento de 4 bits llenos y listo para transferir ese contenido a las salidas).
En el caso de una cascada todos los pines 10, 11, 12 y 13 estarían conectados a los respectivos pines 10, 11, 12 y 13 de los otros registros mientras que el pin 9 del primer registro estaría conectado al pin 14 del segundo, a su vez el pin 9 del segundo estaría conectado al pin 14 del tercero y así sucesivamente.
--- Código ASM para PIC y algo de optimización ---
El código para manejar un 74HC595 no es tan difícil:
Envia595
movlw 0x08
movwf Conta ; Cargamos un contador para contar los 8 bits.
A1
bcf GPIO, DS
btfsc Dato, 7
bsf GPIO, DS ; Ponemos en la salida el bit correspondiente MSB primero.
bsf GPIO, SHCP
bcf GPIO, SHCP ; Damos un pulso para validar el bit que acabamos de colocar.
rlf Dato, F ; Desplazo para colocar el siguiente bit a sacar.
decfsz Conta, f ; Compruebo si ya saque los 8 bits.
goto A1
bsf GPIO, STCP
bcf GPIO, STCP ; Doy un pulso para pasar los 8 bit almacenados a la salida.
return
Ahora bien, hagamos la cuenta de cuanto tardara en enviar los 8 bits suponiendo un tiempo de ejecución de cada instrucción de 1us (para hacer cálculos fáciles). Dado que es una función tengo:
1. 2us para ir, 2us para volver = 4us.
2. Cargar "Conta" con 8 = 2us.
3. Leer que valor es el bit y sacarlo = 3us.
4. Darle el pulso al registro para almacenar el bit = 2us.
5. Rotar el dato = 1us
6. Comprobar si ya se enviaron los 8 bits = 3us.
7. Dado que del paso 3 al 6 lo tengo que hacer 8 veces seria (3+2+1+3) * 8 = 72us
8. Por ultimo darle el pulso para pasar los datos guardados en el registro a las salidas = 2us.
Ahora si hacemos cálculos nos da que todo eso tarda: 4 + 2 + 72 + 2 = 80us para enviar un byte. O, lo que es lo mismo, actualizar la salida unas 12500 veces por segundo... No me gusta... MMM... me pregunto si se podrá hacer de otra forma... Veamos:
Envia595
bcf GPIO, DS
btfsc Dato, 7
bsf GPIO, DS
bsf GPIO, SHCP
bcf GPIO, SHCP
bcf GPIO, DS
btfsc Dato, 6
bsf GPIO, DS
bsf GPIO, SHCP
bcf GPIO, SHCP
bcf GPIO, DS
btfsc Dato, 5
bsf GPIO, DS
bsf GPIO, SHCP
bcf GPIO, SHCP
bcf GPIO, DS
btfsc Dato, 4
bsf GPIO, DS
bsf GPIO, SHCP
bcf GPIO, SHCP
bcf GPIO, DS
btfsc Dato, 3
bsf GPIO, DS
bsf GPIO, SHCP
bcf GPIO, SHCP
bcf GPIO, DS
btfsc Dato, 2
bsf GPIO, DS
bsf GPIO, SHCP
bcf GPIO, SHCP
bcf GPIO, DS
btfsc Dato, 1
bsf GPIO, DS
bsf GPIO, SHCP
bcf GPIO, SHCP
bcf GPIO, DS
btfsc Dato, 0
bsf GPIO, DS
bsf GPIO, SHCP
bcf GPIO, SHCP
bsf GPIO, STCP ; Pasa el valor alamacenado a las salidas.
bcf GPIO, STCP
return
Ahora bien, alguien que vea el código podrá pensar que tarda mas pero es lo mismo que preguntar: ¿ que vale mas un billete de 10 pesos o 10 de 1 peso ?. Por un lado tiene el problema de que ocupa mas código y cuando estamos apretados ya con el resto del código quizás no sea viable usarlo, pero en mi caso prefiero optimizar velocidad a código, así que hagamos los cálculos a ver cuanto tarda en enviar los 8 bits:
1. Como también es una función tendré 2us en llamarla y 2us en volver de ella = 4us.
2. Enviar un bit al pin del micro 3us.
3. Enviar el pulso al registro para validarlo = 2us.
4. Como se ve ese pedazo de código (paso 2 y 3) se repite 8 veces para enviar los 8 bits por lo que tardara (3 + 2) * 8 = 40us.
5. Enviar el pulso para pasar los datos guardados en el registro a las salidas = 2us.
Si hacemos las cuentas, en este caso, para enviar los 8 bits, tardara 4 + 40 + 2 = 46us o lo que es lo mismo, actualizara la salida cada 21739 veces... Pequeña diferencia ¿ No ?
--- Conclusión ---
Como se ve no es difícil usar el registro de desplazamiento y se obtienen varias ventajas.
Por el lado del software vimos que en cierto punto uno debe pararse y evaluar que es lo que necesita: ¿ Optimizar código ? u ¿ Optimizar velocidad ? es acá cuando saber un poco de ensamblador puede ser de una gran ventaja ya que un compilador no importa si fuese C, basic, pascal, habrá terminado con la primera aproximación que como vimos es la mas lenta. Desde luego los compiladores poseen opciones para incrustar pedazos de código en ensamblador y es ahí donde sobresaldremos si tenemos los conocimientos necesarios al usar las herramientas en tareas finas.
También hay que tener ne cuenta que si bien se pueden colocar "infinitos" registros en cascada para "llenarlos" se tardara "infinito" tiempo xD. La cuenta es fácil, en una cascada de X registros se tardara X veces lo que se tarda en cargar un solo registro.
Se puede pensar que con cargar mas rápido los registros se puede obtener mejores tiempo y eso es cierto hasta un determinado punto ya que el integrado 74HC595 tiene un limite de funcionamiento de 100Mhz (de todas formas, como se ve, es bastante rápido).
Saludos.
Podrías subir un programa .asm completo.
ResponderEliminarHola.
ResponderEliminarNo es tan dificil usar el pedazo de codigo ya que es una funcion.
1. Configuro el micro. (fuses, pines, reloj, etc).
2. Limpio los pines de salida.
bcf GPIO, DS
bcf GPIO, SHCP
bcf GPIO, STCP
3. Cargo el registro "Dato" con el valor que quiero sacar .
movlw 0x3F ; O el valor que se quiera sacar.
movwf Dato
3. Llamo a la funcion.
call Envia595
4. Sigue el resto del programa.
Solo hay que pegar uno de los dos trozo de codigo arriba (el que se ajuste a lo que necesitas -poco codigo pero lento- o -mucho codigo pero rapido-) en algun lado del programa.