miércoles, 21 de octubre de 2020

 Simulador de un farol con llama.

 
Hace un tiempo, Big Clive, publico un farol que usaba LEDs para simular la llama. El link a ese video es este: https://www.youtube.com/watch?v=tWUz7Yg0Z_g
 
 La verdad es que la simulación es verdaderamente espectacular. Por eso decidí crear mi versión.

Primeramente copie la secuencia original pero, como bien explica Clive, esta es muy corta y se repite muy seguido. Aun así cree la versión 1 que seria exactamente la misma que la del producto.

Para intentar alargar un poco mas y que la secuencia no se repita tanto añadí un generador de números aleatorios con lo que la secuencia se repite cada 2 minutos aproximadamente en vez de cada unos pocos segundos. Esta seria la versión 2.

Para intentar darle mas diversidad cree una tercera versión en la que el retardo entre secuencias es aleatorio por lo que la llama crecerá mas o menos lento dependiendo de ese valor. En este caso es la versión 3. Pero la verdad que no me convence mucho el resultado.

El esquema de conexión es este:

Como quedo armado dentro de un tarro de plástico: 

 No es de lo mas lindo pero funciona. Para la alimentación use un cable USB con un diodo en serie para evitar que se alimente con la polaridad invertida.

Videos:


Como puede verse, en el primer video no se aprecia mucho, en el segundo se ve un poco mejor como es en realidad. Es difícil de filmar, la cámara siempre sobre expone y queda sin detalle.

El firmware de las 3 versiones para grabar en el micro se encuentra en: https://github.com/Switchxxi/CandleLEDs

martes, 2 de julio de 2013

Tarjetas telefonicas.

Hubo un tiempo en que el celular no era el rey en la calle y cuando alguien debía hacer una llamada recurría a un misterioso cubículo llamado: "Cabina telefónica". En un comienzo se usaban monedas especiales como método de cobro, las cuales eran vendidas en los quioscos.
 
Luego de que mucha gente utilizara los mas diversos métodos para que la moneda ingrese para luego poder recuperarla o bien trabar el sistema para realizar llamadas infinitas se paso a una tarjeta con banda magnética.
 
La tarjeta magnética era frágil y también vulnerable. El siguiente paso fueron las tarjetas con chip de memoria integrada a la misma, mas resistentes que las anteriores pero llego la masificación del celular y las cabinas telefónicas publicas terminaron casi por extinguirse. Aunque aun hoy, en algún lugar se las puede ver.
 
De este ultimo método mencionado es del que mostrare hoy algunas cualidades y funcionamiento de las mismas.
 

Tarjetas CHIP.


Con el tamaño de una tarjeta de crédito, las tarjetas chip se diferencian de estas ultimas por carecer de toda banda magnética recurriendo a un chip inserto en la misma para almacenar la información (si bien hay algunas tarjetas de crédito que, ademas de la banda magnética, poseen chip integrado).

De las tarjetas chip hay dos grupos bien diferenciados:

- Memorias EEPROM Microprocesadas.
- Simples memorias PROM.

El primer grupo son los chip que ademas de poseer memoria para almacenar la información poseen un sistema que coordina tanto la escritura como el borrado de la memoria, incluyendo un sistema de clave para evitar la modificación por personas no autorizadas.

El segundo grupo es el que nos interesa y del que explicare como funciona.

 
Funcionamiento.

Para comenzar hay que explicar un poco lo de memoria PROM ya que parece confuso.
Una memoria PROM, como su nombre indica, es una memoria que solo permite ser grabada una vez y sin limite en la lectura.
El sistema es sencillo: Al ser fabricadas toda la tarjeta esta con todo su contenido en cero. Aplicando alta tensión se puede programar cada posición pero con una particularidad: Un bit solo puede pasar de cero a uno y no viceversa. Y es así como, al ir grabando bit tras bit, la cabina telefónica va descontando el crédito que la misma posee.

Ademas de almacenar el crédito que posee la tarjeta, estas vienen grabadas con ciertos datos que ayuda a la cabina a identificar a que empresa corresponde (ya que una tarjeta de una compania podia funcionar en una cabina de otra empresa), de que valor es la tarjeta (ya que había distintos precios), identificadores varios, numero de serie de la tarjeta, etc.

Las tarjetas usadas en telefonía eran de 256 bits o 32 bytes, donde generalmente los primeros 12 bytes poseían la información sobre la tarjeta y los 20 bytes restantes guardaban cuanto llevaba gastado el usuario con dicha tarjeta.


Conector.


Se observa en la imagen que el conector que comunica el chip con el mundo exterior posee 8 pines. La función de cada uno se detalla a continuación:

Vcc:   Voltaje de alimentación de la tarjeta. (+5v)
GND: Tierra.
VPP:  Tensión de programación. Al estar este pin a 21v permite grabar el contenido de la tarjeta.
           En funcionamiento normal debe estar conectado a +5v.
OUT:  Salida serial. Mediante este pin se va leyendo uno a uno los 256 bits que la tarjeta posee.
NC :   Pin no usado (No Conectado).
RST:   Activo en bajo, este pin permite resetear el puntero de la memoria que pasara a apuntar al primer bit.
CLK:  Señal de reloj para sincronizar la comunicación.
IN:      Entrada de datos. Por medio de este pin se graba el contenido de la memoria.


Diagrama de tiempos.


En la imagen anterior se observa el protocolo usado por la tarjeta tanto para su lectura como para su reset (no se muestra el protocolo de grabación de la misma)

Como se dijo anteriormente, el pin VPP debe permanecer conectado a +5v cuando la tarjeta es leída. Para producir un reset de la misma primero se coloca el pin RST a tierra, luego se aplica un pulso de clock y en el pin de salida (OUT) aparecerá el valor del primer bit de la memoria (bit 0). Luego en cada pulso de reloj se podrá leer los siguientes bit.

Al leer el ultimo bit (bit 256) y aplicar un nuevo pulso de reloj la tarjeta volverá al inicio (bit 0). Como se puede ver no hay método para ingresar la dirección de un bit en particular teniendo que leer todos los bits hasta llegar al deseado.
Tampoco se puede retroceder. Si el puntero esta en el bit 75 y se desea leer el bit 2 habrá o bien dar pulsos hasta que el puntero señale la dirección del bit 2 o bien resetear la tarjeta y dar un pulso de reloj.


Usos.
 

Ya sabiendo como funcionan las tarjetas telefónicas nuestro propósito sera crear una cerradura que utilice dichas tarjetas.
Se utilizaran los 3 bytes que componen el numero de serie ya que no hay dos tarjetas telefónicas con un mismo identificador dando mas seguridad al sistema.

Se podrán almacenar varias tarjetas para tener de esta forma varias llaves que abrirán la cerradura. Un indicador sonoro nos servirá para saber si una tarjeta es invalida así como también para indicar que la programación de una tarjeta en particular fue correctamente guardada en el sistema.

martes, 21 de mayo de 2013

Generación de números aleatorios con PIC en ASM.

- Introducción -

La generación de numero aleatorios suele ser complicado mas cuando se programa en ensamblador ya que al hacerlo en un lenguaje de medio o alto nivel los mismos ya poseen funciones especificas para la tarea haciendo la programación mas fácil.

Ahora bien, ¿ que pasa cuando programamos en ansamblador ? Pues tendremos que crear nuestras propias funciones y aquí veremos como crear una de ellas.

  
- Aproximación -

Para generar números aleatorios se puede recurrir a numerosos procedimientos. Por ejemplo, Supongamos un dado electrónico en el que el usuario presiona un pulsador y, luego de la animación, aparece un valor aleatorio en el mismo. Una aproximación, sin cuidar demasiado la probabilidad de cada numero, es hacer correr el TMR0 (Temporizador), cuando el usuario presiona la tecla se lee el mismo y de ahí se calcula para terminar con un numero aleatorio entre 1 y 6 que sera luego lo que el dado mostrara.
En ese caso se cuenta con que el usuario no sabe en que numero esta el TMR0 y que presionara el pulsador de una manera aleatoria y no cada un tiempo especifico y preciso.

La aproximación funciona y haciendo un poco mas de trabajo podemos extraer un numero aleatorio. Ahora supongamos que el sistema tiene que mostrar cada 10 segundos un numero aleatorio. ¿ Serviría el método anterior ?
Veamos: digamos que son 10 números (1 al 10), el temporizador se incrementa cada 1 segundo por lo que tardara 10 segundos en rebalsar. Encendemos el sistema y el mismo, cuando lee el TMR0 este posee el numero 3.
Ahora el sistema espera 10 segundos y lee nuevamente el TMR0. Después de 10 segundos obviamente el TMR0 estará en 3. No muy aleatorio ¿ verdad ?  :lol:

Una solución para el problema anterior seria crear una tabla de números aleatorios. El problema es que si necesitamos 255 numero ocuparía muchísima memoria en el programa.

Para solucionar eso creamos nuestra función que nos devolverá un numero aleatorio. Para este caso también hay muchas maneras, la que usaremos ahora esta basada en el desplazamiento y en compuertas XOR. Este método, si se aplica correctamente, nos devolverá un numero aleatorio de longitud máxima. ¿Que significa de longitud máxima ? que no repetirá ningún numero y al final obtendremos 255 números aleatorios hasta que la secuencia vuelva a comenzar. Hay que aclarar que debido a lo ultimo este método genera números Pseudos-aleatorios.



- Código -

El método se basa en el siguiente diagrama:



Se aprecia que el sistema toma 2 grupos de 2 bits cada uno, realiza una operación XOR con cada grupo y el resultado de cada uno alimenta otra compuerta XOR. Por ultimo el resultado de esta ultima compuerta alimenta el bit de mayor peso luego de que toda la cadena haya sido desplazada a la derecha.
La condición esencial del circuito es que la cadena no puede comenzar con el numero 0. De suceder esto el sistema quedara en 0 indefinidamente. Por lo que, antes de empezar habrá que cargar la cadena con un valor cualquiera distinto de cero -muchas veces llamado este valor "semilla" ya que es el que alimenta/genera el resto de los números-

Como código termine con el siguiente -debería recordar karnaugh y ver si se puede reducir a algo mas fácil pero seguí con las compuertas XOR-

Random
              btfsc   Dato, 4
              incf     Conta, f
              btfsc   Dato, 3
              incf     Conta, f
              btfsc   Dato, 2
              incf     Conta, f
              btfsc   Dato, 0
              incf     Conta, f

              rrf       Conta, f
              rrf       Dato, f

              clrf     Conta
              return

Se preguntaran donde esta las operaciones XOR en el código. Es ahí donde radica un poco lo lindo del código:

Veamos como es la tabla de verdad de la compuerta XOR:

AB ----- Out
00 ------- 0
01 ------- 1
10 ------- 1
11 ------- 0

Ahora si observamos cual seria la salida de la compuerta que alimenta el registro de desplazamiento veremos algo curioso:

ABCD ---- Out ---- Cantidad de unos.
0000 ------ 0 ------------ 0
0001 ------ 1 ------------ 1
0010 ------ 1 ------------ 1
0011 ------ 0 ------------ 2
0100 ------ 1 ------------ 1
0101 ------ 0 ------------ 2
0110 ------ 0 ------------ 2
0111 ------ 1 ------------ 3
1000 ------ 1 ------------ 1
1001 ------ 0 ------------ 2
1010 ------ 0 ------------ 2
1011 ------ 1 ------------ 3
1100 ------ 0 ------------ 2
1101 ------ 1 ------------ 3
1110 ------ 1 ------------ 3
1111 ------ 0 ------------ 4

(Donde AB seria los bits 0 y 2 que alimentan una de las XOR, CD los bit 3 y 4 que alimentan otra de las compuertas XOR y por ultimo la salida es la que presenta la compuerta XOR que opera sobre los dos grupos mencionados anteriormente AB y CD)

Se ve que la salida de la tercera compuerta es alta solo cuando la cantidad de unos que alimentan las dos primeras compuertas es un numero impar. Por lo que solamente habrá que contar la cantidad de números 1 que aparecen en los 4 bit (bit 0, bit 2, bit 3 y bit 4), de ser impar alimentar la cadena con un 1 y en caso contrario con un 0. Todo esto obviamente luego de desplazar la cadena a la derecha.

- Conclusiones -

Cuando se inicializa el PIC habrá que cargar el registro "Dato" con algún numero distinto de 0 como explique anteriormente y luego de llamar la función esta cambiara el registro Dato con el nuevo valor aleatorio.

La función solo tarda 11 pulsos de reloj (si no contamos los 2 para llamar la función y los 2 para volver de la misma) con lo que es muy rápida.

jueves, 16 de mayo de 2013

74HC595. y un poco de codigo para un PIC.



--- 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 ---

Como se sospecha usare el registro de desplazamiento 74HC595 el cual posee entrada serial y salida paralela.


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.