sábado, 27 de junio de 2020

Consola serial en PIC en XC16

Chan ya es famoso por ser el autor de la librería para manejo de archivos FAT32 en memorias SD pero tiene muchas otras creaciones geniales. Aquí describiré un ejemplo para usar la librería xprint que permite dotar de forma sencilla a casi cualquier microcontrolador de una pequeña consola de operación o depuración. Usaré un PIC24FJ32GA002 pero entendiendo la idea general se puede portar facilmente a cualquier otro dispositivo.  Primero, deben descargar los archivos xprinf.h y xprintf.c. Esta librería está escrita en ANSI C por lo que es independiente de la plataforma y el compilador. Las tres funciones más importantes que contiene son:
  • void xputs (const char* str)          : análoga a la función puts()
  • void xprintf (const char* fmt, ...) : análoga a la función printf()
  • int xgets (char* buff, int len)        : similar a la función gets()

 xputs() permite escribir una cadena simple. como un "Bienvenidos al programa". xprintf() nos permite dar formato a la cadena, como ("Temperatura %d",temp), como lo haríamos en un C en una PC. xgets() es muy útil y la que la dará mucha presentación a un programa para microcontrolador. Es en esencia muy distinta a su contraparte gets(). Lo que hace xgets es ir capturando en tiempo real lo que se vaya recibiendo el microcontrolador del puerto serial hasta que se presiona ENTER. La misma función va enviado simultáneamente cada carácter a Tx como espejo (aunque esta opción puede desactivarse en los macros del header). ¡Permite incluso borrar caracteres!    

La librería no puede funcionar por si sola. Es necesario asociarla a funciones de escritura y lectura bajo nivel. Lo que sigue ahora es crear un programa que contenga las rutinas para enviar y recibir un byte por puerto serial con la siguiente forma:

//-- Poner byte en el registro Rx
unsigned char uart_getc(void);

//-- Poner byte en el registro Tx

void uart_putc(unsigned char d);


Esta parte del programa siempre será dependiente del dispositivo y compilador. Estas dos rutinas pueden tener cualquier nombre siempre y cuando se asocien en el código principal de la siguiente manera:

//-- Unir UART con funciones de consola
xdev_in(uart_getc);        
xdev_out(uart_putc);

La implementación de las funciones de comunicación serial también están basadas en la implementación de Chan pero las he reescrito de una forma más clara y comentadas lo mejor que pude. Esta implementación utiliza dos colas (registros FIFO), una de envío y otra de recepción. Esto es muy útil porque permite aprovechar las interrupciones de la UART para no perder bytes. Las referencias a páginas son para la data del GB004 pero es la misma información.   

Aislamiento

Si están utilizando baterías para alimentar al PIC o van a estar realizando depuración es una buena práctica utilizar opto-acopladores. Esto también permite utilizar módulos de conversión USB-Serial que no tengan salida de 3.3v:


 

Programa completo:

viernes, 26 de junio de 2020

Renombrar archivos automaticamente en Linux

El comando rename permite renombrar archivos utilizando la versatilidad de las expresiones regulares de perl. Recomiendo hacer un respaldo de la carpeta con los archivos a renombrar porque los errores de renombramiento pueden ser difíciles de revertir. Pueden utilizar el argumento '-n' o crear una nueva carpeta con las copias de algunos archivos para practicar primero.

Las expresiones de sustitución tiene la forma general:

's/PATRON/REMPLAZO/'modificador

dónde algunos de los modificadores más usados son:
  • i :  Patrón insensible a las mayúsculas.
  • x : Permitir uso de espacios para mayor claridad.
  • g : Remplaza todas las ocurrencias del patrón.

Cuando estaba haciendo mi tesis de licenciatura ocurrió que generé desde Linux cientos de archivos con la hora en el nombre dejando el simbolo ':' como separador. En Linux no es un problema pero en Windows se vuelven inaccesibles. No iba renombrar manualmente esos cientos de archivos, por supuesto. Tomaré este caso como primer ejemplo. Supongamos que tenemos montones de archivos con esta forma:

datos_10:34:01.txt 

Lo que necesitamos hacer es sustituir todas las ocurrencias de ':' por '_'. Por lo tanto el comando para renombrar es:

$ rename -v -- 's/\:/\_/'g *.txt

Notese que si no colocamos el modificador 'g' al final de la expresión entonces solo se sustituirá la primera ocurrencia del patrón e ignorará a las posteriores. Los caracteres ':' y '_' pueden ir sin la barra '\' pero es buena costumbre colocar la barra para distinguir los caracteres del patrón con los operadores de las expresiones regulares.

La expresiones regulares son lo que necesitamos para realizar renombramientos más complejos. Si nunca habían escuchado ese concepto recomiendo ver este video. En pocas palabras es un sub-lenguaje de descripción de patrones de cadenas de símbolos. He aquí una referencia rápida:

.        : carácter único.
\s      : espacio en blanco.
\S     : carácter diferente a espacio en blanco.
\d     : dígito (0-9).
\D     : no-dígito.
\w     : caracter de palabra (a-z, A-Z, 0-9,_)
\W     : no caracter de palabra
[aeiou] : Identifica sólo un carácter del conjunto entre bracetes.
[^aeiou] : Identifica sólo un caracter fuera del conjunto entre bracetes.
(foo|bar|baz) : operador or. Identifica cualquiera de las expresiones dadas.
^   : principio de cadena.
$   : final de cadena.

Cuantificadores
*         : cero o más apariciones de la expresión o símbolo previo.
+        : una o mas apariciones de la expresión o símbolo previo.
{5}    : 5 o más apariciones...
{3,7} : entre 3 y 7 apariciones.
{6,}    : 6 o más apariciones.

 Aprender a escribir expresiones regulares complejas requiere de práctica. Al principio puede ser frustrante pero después de muchos ejercicios se entiende la idea general.

El ejemplo final de esta nota será una demostración del uso de variables. Supongamos que tenemos un directorio con miles de archivos con nombres con la siguiente estructura:

resultados_Rodolfo_Escobar.txt

y se quiere modificar a la forma:

Escobar_Rodolfo_resultados.txt

De nuevo, nadie quiere pasar por la pesadilla de renombrar los archivos a mano. Para crear una expresión de sustitución debemos tener claro el patrón que necesitamos. Tenemos 3 campos separados por _'s. No podemos saber de antemano cuantas letras tendrá cada campo pero si sabemos que contendrán caracteres de palabra, así que debemos describir el patrón como (\w+)_(\w+)_(\w+). Los paréntesis permiten manejar subpatrones de cadenas. Para reutilizar estos supatrones (moverlos de lugar), podemos manejarlos como $n dónde n es el orden en el que aparecen de izquierda a derecha. El comando completo es:

$ rename -v -- 's/(\w+)_(\w+)_(\w+)/$3_$2_$1/' *.txt



miércoles, 10 de junio de 2020

Trazos

Mi persona es como el meme del dibujo del caballo. Algunas partes están muy bien definidas y estilizadas, y quien me conozca sólo desde esos ángulos suele intimidarse o tenerme por una persona con los cabos bien atados. No digo que se equivoquen en todo pero si quiero señalar que pocos saben que el resto de mi son solo bosquejos y garabatos. No digo esto por complejo de inferioridad como podría pensarse, o como alguna vez si lo fue. Es lo que he visto en mis introspecciones y hay, diría, razones históricas. Con la misma evaluación honesta se también que soy perfectamente capaz de tomar un lápiz y terminar de dibujarme. Pero hoy no. Aún no sé que dibujar sobre muchos de los espacios en blanco ni sobre las lineas pobremente trazadas. Sigo inacabado y a veces me lo recuerda una que otra amarga circunstancia.

domingo, 7 de junio de 2020

Optimización en Python con SciPy

Resolver problemas de optimización es una herramienta esencial para la ingeniería y la ciencia y debería ser una prioridad en los programas académicos. La bibliografía para aprender la teoría que podría sugerir es el capítulo 7 del libro gratuito Mathematics for Machine Learning. Por ahora me concentraré a detallar una nota para usar la función minimize() del modulo SciPy. Me estoy basando en gran parte en el ejemplo de APmonitor. No sólo será un traducción sino que añadiré detalles importantes que fueron omitidos en su tutorial.

En la jerga de optimización, se le llama función objetivo a la función que se desea optimizar alguno o algunos de sus argumentos. En este ejemplo la función objetivo a minimizar será:
La solución del problema de minimización consiste en encontrar los valores del vector x para los cuales la salida de la función objetivo sea mínima. En la práctica la función objetivo está sujeta a restricciones por lo que este ejemplo no será la excepción:

Restricción 1. El producto de todas las variables debe ser mayor o igual a 25.
Restricción 2. La suma de los cuadrados de las variables debe ser igual a 40.
Finalmente hay que considerar los intervalos que acotan a cada una de las variables:  
Para escribir el programa de Python usando el submódulo optimize de SciPy necesitamos saber lo siguiente. La función minimize() tomará, para este ejemplo, estos argumentos:

func. Una función que debe definirse con un único argumento vectorial y retornando un único escalar.
method. La lista de métodos de minimización disponibles aparecen en la documentación. Es importante saber que los únicos métodos que permiten optimización con restricciones son ''COBYLA", "SLSQP" y "trust-constr".
bounds. Lista de tuplas con las cotas para cada variable.
constraints. Lista de diccionarios donde cada uno tiene la esctructura:
  • 'type': Tipo de restricción. 'eq' para igualdad o 'ineq' para desigualdad
  • 'func': Nombre de la función que describe la desigualdad (debe crearse) 
Código completo:

jueves, 4 de junio de 2020

El libro del tiempo

En la novela corta "La Historia de tu Vida" del escritor de ciencia ficción Ted Chiang se menciona que Borges tenía un argumento en contra de la pre-definición del futuro que me parece demoledor. Supongamos que el libro de mi vida existe y que en el está cada detalle de mi pasado y futuro. Supongamos ahora que encuentro ese libro. Verifico que es autentico porque en el reconozco mis recuerdos hasta el momento exacto en el que tomo ese mismo libro en mis manos. Lo que ocurre después es extraño. Sé ahora todo lo que "voy" a hacer después de ese momento por el resto de mi vida. ¿Pero de verdad voy a hacer todo lo que dice ahora que tengo esa información? ¿Qué me obliga a seguir la historia de ese libro? Nada. Tengo la opción de elegir. Esto hace que la existencia del libro de mi vida sea lógicamente imposible. Borges hace otra observación. Es posible que ese libro exista si se cumple una condición: la historia del universo debe estar hecha de tal manera que yo nunca tenga acceso a él. Sospecho que este argumento debe estar en los ensayos contenidos en "Otras Inquisiciones" que espero conseguir algún día.

martes, 28 de abril de 2020

Futurismo contemporáneo

Falta poco para el primer cuarto del siglo y seguimos sin vivir en el futuro que imaginaban en los 80's y principios de los 90's. He pensado mucho en esto. De niño me hacia enojar un poco ver que el mundo avanzaba tan rápido que no me daría la oportunidad de participar en algún descubrimiento o avance importante. En ese aspecto, mi preocupación se desvaneció al crecer. Me siento retado por la abundancia de problemas que aún quedan por resolver.  Pero hay otra cosa que quiero hacer notar. Ha habido avances enormes sólo que la mayoría de ellos están ocultos a la vista del publico no especializado. Vemos sólo la punta del iceberg tecnológico. Un ejemplo interesante es la robótica. Los robots que de vez en cuando se ven en las calles o son torpes o tienen que ser controlados por un humano. Esto da la impresión de que estamos lejos de tener un ejercito de sirvientes mecánicos como la ciencia ficción pintaba para esta década. Pero me atrevería a decir que si lo tenemos. Es sólo que estos sirvientes están encerrados en fabricas, almacenes o son invisibles (bots de software que automatizan servicios web y financieros). Quiero mostrarles 3 vídeos cortos y recientes que pienso los pondrán en una perspectiva tecnológica más acertada:




lunes, 20 de abril de 2020

Flor del Edén

Flor del Edén
En teoría de autómatas celulares, un "Jardín del Edén" es una configuración a la que no se puede llegar sin importar la condición inicial ni cuanto evolucione el programa que implementa al autómata. Pero un Jardín del Edén si puede ser una condición inicial del programa, de ahí las dos interpretaciones que le dieron el nombre: la de John Tukey que dice que sólo el creador de la simulación puede construirlo y otra que dice que "una vez perdido, no puede recuperarse". Este concepto ha generado experimentos filosóficos curiosos. En principio, si viviéramos en una simulación, bastaría encontrar un Jardín del Edén como marca de agua de los arquitectos de la Matrix para verificarlo. Esta idea es explorada en la novela de ciencia ficción "Ciudad Permutación" de Greg Egan. La Flor del Edén es un JdE del Juego de la Vida (GoL, The Game of Life ). Sé que John Conway odiaba ser famoso por The Game of Life, pero este post era la mejor referencia que podía hacer en su memoria.

sábado, 11 de abril de 2020

Sobre aprendizaje e internet

Foto: Luis Eusébio
No basta tener acceso a internet o una biblioteca física para ser un autodidacta exitoso. Es necesario pero no suficiente. El problema no es que falte información sino que hemos acumulado una cantidad tan grande que no hemos terminado de organizarla. Nos hacen falta mapas para navegar en ese basto océano y, me parece, tenemos muy pocos. No hace mucho, después de participar en una mesa panel sobre comunidades de ciencia y tecnología en Mérida, se me acerco una persona de unos 50 años. Me pidió consejo sobre como aprender machine learning. Me hizo el comentario que el problema que había encontrado es que la información disponible era muchísima y que no había sabido ni por donde empezar. Creo que esto aplica para muchos otros temas. Ciertamente hay muy buenos tutoriales y cursos introductorios excelentes, pero aún existe una barrera para pasar al dominio intermedio y avanzado. Nos faltan brújulas para la excelencia. Aquí es donde los que nos hemos esforzado para llegar o acercarnos a ese punto debemos tomar la responsabilidad de indicarles el camino más corto (más no siempre fácil) al entendimiento a los demás. Ya he puesto mi granito de arena con la guía para ing. electrónica y estoy haciendo una para redes neuronales. Pero aún hay todo un mundo de temas ahí afuera que requieren de un mapa para quienes quieran o necesiten aprender algo desde cero de la manera menos tortuosa posible. Esto no es por simple comodidad para ellos. Creo que todos ganamos al movernos rápido y poder resolver los problemas difíciles que nos atañen como sociedad. Como ya nos hemos dado cuenta, el mundo puede cambiar muy rápido y necesitamos seguirle el paso.

domingo, 5 de abril de 2020

Comunicación SPI para PIC24FJXXGA004 en XC16

En este ejemplo haré referencia a la hoja de datos para la familia PIC24FJXXGA004, así que deben tenerla a la mano. 'XX' hace referencia al tamaño de la memoria de programa. En este caso particular usaré un PIC24FJ32GA004 (32Kb). Lo primero que hay que saber es que esta familia de PIC's tiene pines remapeables (Peripheral Pin Select). Para usar los pines asociados al SPI, UART, PWM, interrupciones externas, etc, se deben mapear a los pines del puerto virtual RP. Para trabajar con SPI requerimos 3 pines: SCK1,  SDO1 y SDI1. Para poder realizar el mapeo, existen dos grupos de registros que hay que configurar. Uno es el grupo de 24 registros de mapeo entrada: del RPINR0 al RPINR23 (pags. 111-117). El otro es el grupo de 13 registros mapeo de salida: del RPOR0 al RPOR12  (pags 118-124). La forma de mapear entradas es muy distinta a la de mapear salidas. Para mapear un pin de entrada debe elegirse el registro asignado al periférico que nos interesa mapear. Para esto podemos consultar la Tabla 10-2 de la página 108. Ahí podremos ver que la entrada SDI1 se configura con el registro RPINR20. Lo que debemos hacer ahora es escribir en RPINR20.SDI1R el numero de RPx a dónde queremos poner a SDI1. En este ejemplo lo pondremos en RP2 (RB0) de la siguiente manera:

RPINR20bits.SDI1R = 2; // SDI -> RP2/RB2

Para mapear las salidas debe elegirse primero el RPx, RP0 y RP1 para SCK1 y SDO1 respectivamente en este ejemplo. El registro necesario será por lo tanto RPOR0 (pag. 118). Este registro tiene los campos RP0R y RP1R asociados a RP0 y RP1. Para elegir las funciones de estos pines debe consultarse la Tabla 10-3 de la pagina 109. Se podrá ver que el código para SKC1OUT es 8 y para SDO1 es 7.  Configuramos los dos pines de la siguiente manera:

 RPOR0bits.RP0R = 8;  // SCK -> RP0/RB0 
RPOR0bits.RP1R = 7;  // SDO -> RP1/RB1

El circuito simulado en Proteus es el siguiente:
(Click para agrandar)
El código completo: