Conjunción en Luna llena.

20 agosto, 2016 Deja un comentario

Conjunción Marte-Saturno en Escorpión

Esta noche fría, Saturno y Marte se balancean sobre los brazos del escorpión.
con su ojo de Antares el picudo animal les observa parpadeante,
incapaz en su distancia inconmensurable de poder hacerles ningún mal.
La Luna asciende desde el horizonte envuelta en fina neblina,
ilumina con su vieja cara adolescente a La Tierra, que reposa en la penumbra,
cansada ya de los caprichos que trae la luz solar.

Sonríe Selene menguando ligeramente su blanca y redonda tez
mientras contempla la veleta brillante que forman los astros ecplíticos.
En vano trata de encontrar el destino que marcan en la negrura del infinito,
intuye que existe en aquella geometría particular y vistosa un indicio, un significado.
Apela en su esperanza a los augurios de la antigua astrología,
los conoce de memoria luego de escuchar por eones las predicciones de los brujos y adivinos:

Marte, el ungido en sangre guerrera, atrapado en la tenaza del escorpión;
oscuro oráculo de muerte, de derrota en batalla.
Saturno escapando ágil por el costado mientras el aguijón penetra la armadura,
señor de la ciega y la cosecha, indicando en su huida abundante vendimia,
buscando ayuda en el sagaz centauro, guardián del corazón galáctico.
La misma Luna derramándose en la noche desde el ánfora del aguador,
anunciando que su altruismo de fémina se esparcirá en la humanidad,
mitigando el dolor de la pérdida acaecida en el desigual conflicto.

Luego de construidas sus cuartetas pinta en la bruma fría un halo donde clavar esas saetas.
Recuerda que luego de tantos desaciertos de charlatanes y falsos adivinos,
esa es la única manera de acertar en plena forma en el augurio,
de develar sin titubeos el significado de los versos ambiguos,
de sentir que no ha perdido un ciclo de su tiempo en un baladí.

Evoca entonces a los nuevos adivinos, maestros de las cifras,
esos que cambiaron los signos de los dioses por ecuaciones algebraicas.
Magia poderosa capaz de predecir en dónde y cuándo se hallarán en conjunción,
el señor de las mil batallas y el decorado portador de la hoz,
frente a las pinzas poderosas del mortal escorpión.

Nada dicen los cálculos sobre el significado de aquel encuentro,
de lo que implica aquel triángulo brillante de astros que raya el cielo,
únicamente indican con la más detallada de las precisiones,
cuántos giros ha de dar a la mancillada Tierra para volverse a encontrar
con su blanca tez derramando pálida luz blanca desde el ánfora del aguador,
mientras mira celosa aquel triángulo brillante
que le roba el protagonismo en la noche de luna llena.

Desencantada encuentra como conclusión,
que no le ayudan los viejos y confusos augurios
ni las perfectas posiciones que dan los números
para hallar el sentido de aquella formación singular.
No le queda más remedio que esconderse tímida sobre un nimbo perdido,
y disfrutar contemplando la belleza de aquel curioso momento orbital,
dejando por una noche su protagonismo de luna llena
a una estrella gigante roja, y a un par de planetas
que pintan una hermosa flecha hacia lo infinito
apuntando a ninguna parte en esta noche glacial.

Agosto 19 de 2016.

Categorías:Cuento Etiquetas: , , ,

Macro en Word para ajustar las líneas de un texto copiado desde un documento PDF

30 octubre, 2013 2 comentarios

El propósito del siguiente post es mostrar el uso de una macro en word, en la misma se implementó el uso de un formulario (UserForm) sobre el cual se elaboró una barra de progreso utilizando etiquetas de texto (Label).  El resultado se puede ver en el siguiente video:

Habitualmente las macros, que son pequeños segmentos de código en Visual Basic para aplicaciones (VBA), son muy utilizadas en Excel dada la potencia y versatilidad que ofrecen para el manejo de las hojas de cálculo y la automatización de procesos dentro de las mismas. Microsoft Oficce Permite el uso de macros en cualquiera de los programas del paquete, sin embargo en Word no es muy común utilizarlas y se limitan casi siempre al ajuste de los textos a plantillas predeterminadas, tarea que se puede hacer fácilmente a través de los menús del programa.

Como una muestra de lo que es posible hacer a través de macros en Word se ha escrito un pequeño programa el cuál es capaz de ajustar las líneas de un texto que hemos copiado desde un documento PDF o algún otro tipo de documento que tenga un formato propio para las líneas de texto y que al momento de copiar en Word mantienen los retornos de línea del documento original y no se ajustan a los de la plantilla actual de Word.

La Macro “Ajustar_Lineas” llama al formulario de usuario UserForm3 que al iniciar (evento UserForm_Activate) modifica algunos parámetros de los objetos del form y llama seguidamente a la función “ajuste_lineas” la cual es como sigue:

'** ===================================================================
'**     Función     : ajuste_lineas
'**     Descripción : Ajusta las líneas del texto del dodumento.
'**     Parámetros  : Ninguno
'**     Retorna     : Nada
'** ===================================================================
Function ajuste_lineas()
    Dim myRange As Range
    Dim r As Range
    Dim nc As String
    UserForm3.Caption = "Ajuste de líneas de texto"
    UserForm3.Labelsubproceso.Caption = "Identificando espacio de fin de línea"
    Selection.WholeStory
    Set r = ActiveDocument.Range(Start:=0, End:=Selection.End)
    palabras = Split(r.Text, Chr(13))
    Application.ScreenUpdating = False   ' Desactivar actualización de pantalla (acelera el proceso)
    j = UBound(palabras)
    x = 0
    Do While x < j
        If palabras(x) <> "" Then
            ultch = Mid(palabras(x), Len(palabras(x)), 1)
            prich = Mid(palabras(x + 1), 1, 1)
            If ultch = "." Then
                 palabras(x) = palabras(x) & Chr(13) & Chr(13)
            ElseIf prich <> " " Then
                palabras(x) = palabras(x) & " "
            End If
        End If
        Actualizar_Barra_Progeso x / (j - 1)
        x = x + 1
    Loop
    Selection.WholeStory
    Selection.Delete Unit:=wdCharacter, Count:=1
    UserForm3.Labelsubproceso.Caption = "Reescribiendo el Texto"
    x = 0
    Do While x < j
        Selection.InsertAfter (palabras(x))
        Actualizar_Barra_Progeso x / (j - 1)
        x = x + 1
    Loop
    Application.ScreenUpdating = True   ' Activar actualización de pantalla
End Function

Esta función selecciona todo el texto del documento, captura el texto a través de un objeto Range y lo pasa a un array de cadenas de caracteres correspondientes a cada línea dentro del texto mediante la función Split. lo siguiente es comparar cada uno de los finales de las líneas de texto y determinar si finalizan con un punto, si es así se interpreta que es el final de un párrafo y se coloca dentro del texto la separación correspondiente. En caso de tener un fin de línea sin punto se interpreta que el texto debe continuar en la siguiente línea y de este modo el salto de línea se reemplaza por un espacio. Terminada esta comparación se selecciona todo el texto y se elimina, seguidamente se escribe un nuevo texto utilizando la secuencia del array “Palabras” Mientras se suceden los dos bucles que contiene esta función se va actualizando el valor de entrada de la función Actualizar_Barra_Progeso la cual modifica a su vez la propiedad Width del objeto LabelProgress que es el que nos simula una barra de progreso.

'** ===================================================================
'**     Función     : Actualizar_Barra_Progeso
'**     Descripción : Actualiza el indicador en la barra de progreso
'**     Parámetros  : valor de progreso (de 0 a 1)
'**     Retorna     : Nada
'** ===================================================================
Function Actualizar_Barra_Progeso(Porcentaje As Single)
    With UserForm3
        kk = Format(Porcentaje, "0 %")                         ' Ajustar número de porcentaje sin decimales
        If kk <> .AcFrameProgress.Caption Then                 ' Si el número es diferente del anterior modifique la barra de progreso
            .Label_porcentaje.Caption = kk                     ' Modifica el Label de porcentaje a su nuevo valor
            .LabelProgress.Width = Porcentaje * (.AcFrameProgress.Width - 3) ' modifica el ancho del control Label (barra).
            DoEvents                                           ' DoEvents permite que el formulario se actualice.
        End If
    End With
End Function

Finalmente, el documento Word con la macro ejemplo que se ha desarrollado se encuentra disponible pulsando aquí.

Categorías:Informática Etiquetas: , ,

Puerto Serie En Processing

5 abril, 2013 5 comentarios

Visualización de entrada de datos por puerto RS232 mediante Processing

Visualización de entrada de datos por puerto RS232 mediante Processing.

 El propósito de este post es mostrar un sencillo, pero funcional ejemplo de manejo de puerto serie para capturar datos incluyendo una visualización básica y almacenamiento de los mismos mediante una aplicación desarrollada en Processing. Inicialmente es necesario introducir brevemente al manejo de este lenguaje, si ya lo conocen entonces pueden saltar hasta la explicación del código desarrollado.

¿Qué es Processing?

Processing es un lenguaje de programación de código abierto basado en Java cuyo entorno está pensado para personas que desean elaborar imágenes, animaciones e interacciones. Es utilizado por estudiantes, artistas, diseñadores, investigadores y aficionados para el aprendizaje, creación de prototipos y producción. Está enfocado a enseñar fundamentos de programación en un contexto visual y para servir como una libreta de apuntes de software a la vez que una herramienta de producción profesional, siendo sus mayores virtudes una rápida curva de aprendizaje, tamaño ligero y gran flexibilidad. Processing es un software de descarga gratuita y se encuentra disponible para GNU/Linux, Mac OS X y Windows.

Existe gran cantidad de material introductorio disponible en la web. De estas opciones una guía básica, pero bastante completa y en español, la he encontrado aquí. Si por lo pronto sólo se desea una breve introducción esta presentación resulta conveniente.

Utilidad en electrónica y microcontroladores.

Dada la sencillez del lenguaje y el enorme potencial que supone tener a disposición las bibliotecas de clases de Java, este entorno se ha vuelto atractivo para usarlo en áreas diferentes al diseño gráfico (para el que se concibió originalmente), incluyendo la electrónica. La plataforma de hardware de open source Arduino, que permite de manera sencilla crear prototipos de proyectos electrónicos, utiliza un lenguaje de programación que posee un entorno de desarrollo muy semejante al de Processing, por lo cual quienes han programado en el Wiring de Arduino  se acoplan facilmente al IDE de Processing.

Normalmente los proyectos académicos y los desarrollos de sistemas embebidos de pequeño alcance se centran principalmente en el hardware y la programación de bajo nivel propia del mismo a fin de garantizar robustez en el funcionamiento del instrumento, dando menor importancia o delegando a programadores de sistemas las utilidades de presentación a nivel de usuario (en la U casi siempre por afán😉 ) que hoy por hoy revisten una enorme importancia dada la gran cantidad de dispositivos electrónicos de uso cotidiano y su alta capacidad de interacción (PC, Portátiles, Smartphones, etc). De hecho existe una versión de Processing para elaborar aplicaciones para Android, con lo cual con unas pocas funciones es posible lograr una aplicación, quizá no demasiado compleja, pero funcional, que interactúe con nuestro hardware desarrollado.

Manejo del puerto serial.

La mayor parte de micontroladores poseen un puerto serial asincrónico, por lo que es el elemento de comunicación de uso más común en estos dispositivos, consecuentemente, elaborar programa de manejo de puerto serie en processing no es un tema nuevo. De hecho, me he basado en gran parte en un post del Club de Informática, robótica y Electrónica a partir del cual he escrito un programa orientado a mostrar gráficamente los datos recibidos y efectuar el almacenamiento de los mismos. No me detendré en la explicación de las funciones de manejo de la comunicación RS232 (que se encuentra en el link), en cambio he descrito en detalle la estructura de datos de la comunicación que incluye la posibilidad de configurar la tasa de muestreo del microcontrolador desde la aplicación en el PC, esto mediante sencillas tramas de datos para la interacción entre el microcontrolador y el programa.  La idea es poder usar cualquier microcontrolador o dispositivo de comunicación por puerto serie que capture una secuencia de datos (mediciones) y las visualice y almacene ya que la mayor parte de la literatura y los códigos disponibles están pensados para trabajar con Arduino.

 Ejemplo Desarrollado.

Visor de captura

Visor de captura

El puerto RS232 es comparativamente lento con respecto a otras interfaces de comunicación, por lo que se pretende capturar datos con periodos de tiempo mayores a 1 ms. Se quiere igualmente hacer una visualización básica de los datos de entrada con el draw de processing al tiempo que se almacenan los datos obtenidos en un archivo plano separados por punto y coma (CSV), para ser llevados posteriormente a MATLAB, Excel o algún otro programa que nos permita hacer gráficas y análisis de los datos de forma más elaborada.

                         Programa del microcontrolador.

Se ha trabajado con un microcontrolador Freescale QE128 configurando el puerto serie a usar en 19200 bits/s entregando una trama de datos con dos variables de medida provenientes de dos ADC con resolución de 12 bits. Habitualmente cuando se envían datos por un puerto COM de un micro a un PC se visualizan en programas como Hyper Terminal, los cuales interpretan los bytes como caracteres ASCII por lo que para una correcta visualización de los datos numéricos se requiere convertir los datos binarios que entregan los ADC en cadenas de caracteres que puedan interpretarse fácilmente. Esto tiene el inconveniente de aumentar las operaciones del microcontrolador así como alargar las tramas de datos y con ello el tiempo que el dispositivo tarda en el procesamiento y envío de datos, lo que se refleja en un aumento del periodo mínimo en el cual es capaz de efectuar el muestreo. Por este motivo, el envío de datos del ejemplo se efectúa en formato binario haciendo más corto el tiempo de envío de tramas para tratar de evitar la saturación del buffer de entrada del PC y mejorar el periodo de muestreo. Para obtener los datos de entrada para la prueba se aprovechará el acelerómetro de tres ejes MMA7260Q que trae incluida la primera versión de la tarjeta DEMOQE (que es la tengo a la mano) tomando mediciones de los ejes Y y Z.

void EVALUACION(void){
   int ktem;
   byte bkl,index,k;
   byte envio=0;
   byte tempomuestra=0;
   ktr_ciclo=0,ktr_pantalla=0,ktr_dataok=0,ktr_convert==0; //Variables de control a cero
   traza[numtrama][0]=0;
   //configuración inicial
   while(ktr_pantalla==0){
      if(ktr_dataok==1){
        input_ClrVal();
        COM_RecvChar(&amp;captura);                             // Capturo caracter proveniente de la planta
        ktr_dataok=0;
        if((captura=='-')) {
          index=0;
          while (captura!='+'){
            while(ktr_dataok!=1){}
            ktr_dataok=0;
            COM_RecvChar(&amp;captura);
            comando[index]= captura;
            index++;
          }
          comando[index-1]= '\0';
          ktem=atol(comando);
          (void)FMUESTREO_SetPeriodMS((word)ktem);
          ktr_pantalla=1;
        }
        input_SetVal();
      }
   }
   // Bucle principal
Bucle while de inicio del microcontrolador

Luego de las configuraciones de inicio, la función EVALUACION permanece en un bucle while hasta que se recibe una trama de datos de configuración desde el PC, la estructura de la trama que debe entrar es: “-1000+” donde – y + representan inicio y fin de trama y el número es el periodo de muestreo en milisegundos, para el ejemplo se configuraría el timer que controla la lectura de datos de los ADC para que genere un evento cada segundo. Posterior a esto la función ingresa en un bucle for en el cual espera a que se cumpla el evento de medición, efectúa la muestra y la envía por el puerto serie. Para este microcontrolador el ADC soporta resoluciones de hasta 12 bits (pudiendo configurarse también en 8 o 10 bits) caso en el cual almacena el resultado en 2 bytes colocando la medida en los bits más significativos (de izquierda a derecha) de modo que se aprovecha la estructura TWREG para efectuar un corrimiento de 4 bits a la derecha dejando el nibble de ceros a la izquierda y separar el dato en cada byte: el menos significativo se coloca en la trama a enviar tal cual mientras del más significativo sólo se toman los 4 bits con información y se juntan con los 4 bits más significativos del segundo ADC para formar un único byte que se agrega en la trama, de modo que la trama de envío queda de la siguiente manera:

(Número de secuencia)(byte LSB ADC1)(byte LSB ADC2)(byte nibble LS ADC1 + nibble LS ADC2)

   // Bucle principal
   for(;;){
      // recepción de datos de PC
      if(ktr_dataok==1){
        ktr_dataok=0;
        COM_RecvChar(&amp;captura);                      // Capturo caracter proveniente de la planta
        input_ClrVal();                              // inidico el inicio de proceso de conversión
        if(captura=='*'){                            // Bucle de medición (según la petición del PC)
          PWR_ClrVal();
        }
        if(captura=='P'){                            // Bucle de medición (según la petición del PC)
          PWR_SetVal();
          ktr_pantalla=0;
        }
      //lectura ADC,armando y envío de trama
      if((ktr_ciclo==1)&amp;&amp;(ktr_pantalla==1)){
         ktr_ciclo=0;                             // Si llego al tope de mediciones disparo variable de control del bucle
         PWR_ClrVal();
         TEST_ClrVal();
         (void)ENTRADA_Start();                   // Inicia la conversión ADC
         while(ktr_convert==0){}                  // Bucle de espera de fin de conversión
         ktr_convert=0;                           // Seteo bit de control de fin de conversión
         TEST_ClrVal();
         index=1;
         for(bkl=0;bkl&lt;NUMADC;bkl++){
            ADC_result[bkl].w=(unsigned short)((int)ADC_result[bkl].w&gt;&gt;4);
            traza[numtrama][index]=ADC_result[bkl].b.low;
            index++;
         }
         traza[numtrama][3]=(char)((int)(ADC_result[0].b.high&lt;&lt;4) | (int)ADC_result[1].b.high);
         traza[numtrama][0]=(byte)controlenv;
         tempomuestra++;
         numtrama++;
         if(numtrama&gt;=TESTERMAX){
            numtrama=0;
         }
Captura y ajuste de datos de ADC

Número de secuencia es un valor de 0 a 255 que se incrementa con cada trama y puede servir para sincronizar la entrada de datos así como para detectar eventuales pérididas de tramas. Como ejemplo tomemos la entrada hexadecimal: F1EE5A05, F1 correspondería al número de secuencia 241, EE indica que el byte menos significativo del ADC1 es 238, 5A indica que el byte menos significativo del ADC2 es 90 y 05 indica que el nibble más significativo del ADC1 es 0 y el del ADC2 es 5. Con esta información se pueden recuperar los valores originales de los dos ADC siendo estos para el ADC1=256*0+238=238 y para el ADC2=256*5+90=1370.

Durante el bucle for, el microcontrolador aun mantiene la lectura de entrada del puerto serie, de modo que si recibe una nueva trama de configuración de periodo de muestreo, este cambiará efectivamente al nuevo periodo. Sin embargo, es conveniente detener primeramente el envío de datos, por lo que al recibir el comando “P” el micro dejará de enviar los datos de las lecturas del ADC por el puerto serie.

// envío de trama
TEST_SetVal();
PWR_ClrVal();
index=0;
while(index&lt;4){
   while(k==0){
      bkl=COM_SendChar(traza[envio][index]);
      if(bkl!=ERR_TXFULL)k=1;
   }
   k=0;
   index++;
}
PWR_SetVal();
controlenv++;
envio++;
if(envio&gt;=TESTERMAX){
   envio=0;
}
Envío de Trama de datos

                        

  -Programa del PC en Processing.

Inicialmente se importa la librería de manejo de puerto serie y se definen las variables globales del programa.

import processing.serial.*;          // Carga la librería para el manejo de puerto serial
String portname = &quot;COM1&quot;;            // Puerto al que está conectado el microcontrolador
Serial puerto;                       // Objeto de puerto
PFont font;                          // Objeto de tipo de fuente
PrintWriter fichero;                 // Objeto de Archivo (Fichero)
char[] datosc=new char[13];          // Array de almacen de trama recibida en el puerto serie
int[] datos2=new int[17];            // Array de datos ajustados para guardar en archivo
int[][] pantalla=new int[700][2];    // Array para la visualización de datos en pantalla (ORIGINALES)
int[][] pantalla1=new int[700][2];   // Array para la visualización de datos en pantalla (MODIFICADOS A LA ESCALA)
int[] rgbbutton1={155, 245, 170};
// variables globales
int muestras;                        // Indica la ubicación donde se almacenará la muestra actual en el array pantalla1
int velmuestras= 5;                  // Indica el periodo de muestreo en ms
char inByte;                         // Almacena el byte recibido del puerto serie
int m;                               // Almacena el tiempo actual del programa
int index=0;                         // Index del array de datos de entrada del puerto
String tipocaptura=&quot;Iniciar Captura&quot;;// Mensaje de button1
boolean ktr_traza=false;             // Control de Inicio-fin de trama del puerto
boolean ktr_captura=false;           // Control de captura de datos del puerto
boolean ktr_incio=false;             // Control del inicio de captura de datos

// Constantes globales
int largew=660;                      // Largo de la ventana de la gráfica
int button1x=275;                    // Posición x del botón de inicio-fin
int button1y=309;                    // Posición y del botón de inicio-fin
int button1width=150;                 // Ancho del botón de inicio-fin
int button1high=27;                  // Alto del botón de inicio-fin

En la función setup() que se ejecuta una única vez al inicio del programa se especifican las dimensiones del diálogo de la aplicación, se carga el tipo de fuente para el texto, se crea el archivo con nombre “Datos_año_mes_día_hora_minuto_segundo.txt” donde se almacenarán los datos (quedará ubicado en la misma carpeta de la aplicación o el stech), se configura e inicia el puerto serie y se ordena el envío de datos al microcontrolador.

void setup() {
  println(Serial.list());
  println(&quot;limpiando matriz&quot;);
  for (int i = 0; i &lt;largew ; i = i+1) {          // inicializa las matrices de visualización de datos
    for (int j = 0; j &lt;2 ; j = j+1) {
      pantalla[i][j]=280;
      pantalla1[i][j]=280;
    }
  }
  println(&quot;OK&quot;);
  println(&quot;Configurando diálogo&quot;);
  size(700, 350);                                // Especifica las dimensiones del diálogo de la aplicación (define with y heigh)
  smooth();                                      // Activa el suavizado de imagen (activado por defecto)
  font = loadFont(&quot;Verdana-16.vlw&quot;);             // Carga el tipo de Fuente
  textFont(font);                                // Establece el tipo de fuente
  frameRate (60);                                // Establece el número de ejecuciones de draw() por segundo

  println(rgbbutton1);
  println(&quot;OK&quot;);
  println(&quot;Inicializando Variables&quot;);
  muestras=0;
  index=0;                                       // Crea fichero donde se almacenarán los datos
  fichero = createWriter(&quot;Datos_&quot; + str(year())+ &quot;_&quot; + str(month())+ &quot;_&quot; + str(day()) + &quot;_&quot; + str(hour()) + str(minute()) + str(second()) + &quot;.txt&quot;);
  println(&quot;Abriendo Puerto Serie...&quot;);
  puerto = new Serial(this, portname, 19200);    // El último valor es el BaudRate
  puerto.write(&quot;P&quot;);                             // Ordena parada de envío de datos al micro
  puerto.clear();                                // Limpia buffer del puerto
  println(puerto);
  println(&quot;OK&quot;);
  puerto.write(&quot;-&quot; + str(velmuestras) + &quot;+&quot;);    // Ajusto periodo de muestreo del microcontrolador
  puerto.write('*');                             // Ordeno reinicio de toma de muestras al microcontrolador
  puerto.buffer(1);                              // El buffer generará el evento SerialEvent al recibir un caracter
  println(&quot;OK&quot;);
  println(&quot;Ordenando datos al microcontrolador&quot;);
  ktr_incio=true;
}

La función draw(), que se ejecuta automáticamente si está establecida la función frameRate(), refresca los datos del diálogo redibujando el cuadro donde se visualizan los datos y llamando a la función grafiko() que se encarga de colocar los datos del array pantalla1 dentro de dicho cuadro.

void draw() {
  int w;
  background(204);                                                   // Redibuja el diálogo
  stroke(64, 64, 64);                                                // Establece el color utilizado para dibujar líneas y bordes alrededor de las formas
  fill(63, 102, 150);                                                // Establece el color de relleno del comando siguiente
  rect(15, 20, 670, 280);                                            // Ventana de visualización de la gráfica
  stroke(49, 65, 111);
  for (int k = 0; k &lt;=4 ; k++) {
    w=24+64*k;
    line(20, w, 680, w);                                             // Líneas de división horizontales de la gráfica
  }
  line(19, 24, 19, 280);
  line(680, 24, 680, 280);
  fill(rgbbutton1[0],rgbbutton1[1],rgbbutton1[2]);                   // Establece el color de relleno del comando siguiente
  rect(button1x, button1y, button1width, button1high);               // Dibuja un rectángulo de button1
  fill(0);                                                           // Establece el color de relleno del comando siguiente
  text(&quot;Datos Actuales: &quot; + datos2[1] + &quot;,&quot; + datos2[2], 22, 330);   // Escribe los datos obtenidos en el diálogo
  text(tipocaptura,button1x+10,button1y+20);                         // Escribe mensaje de Button1
  grafiko();                                                         // Actualiza la gráfica
  //puerto.write('*');
}

La función serialEvent() se ejecuta cada vez que se recibe en el buffer de entrada la cantidad de caracteres establecida en puerto.buffer(); en ella se efectúa el armado de la trama. Al completarse efectúa dos cálculos, uno tendiente a recuperar las mediciones del ADC1 y ADC2 que se almacenan en el archivo “Datos_…” y otro que modifica los datos del array pantalla1 para efectuar la visualización.  Al final envía por el puerto serie un carácter asterisco (“*”) para indicar el fin del proceso, esto para poder verificar externamente que se está completando el proceso de recepción.

void serialEvent(Serial puerto_serie) {
  if ((puerto_serie == puerto)&amp;&amp;(ktr_incio)) {
    inByte=puerto.readChar();
    if ((ktr_traza==false)) {
      index=0;
      ktr_traza=true;
    }
    if (ktr_traza==true) {
      datosc[index]=inByte;
      index++;
    }
    if (index&gt;=4) {
      ktr_traza=false;
      if (ktr_captura) {
        datos2[0]=datosc[0];// número de muestra.
        datos2[1]=datosc[1]+(datosc[3]&gt;&gt;4)*256;
        datos2[2]=datosc[2]+(datosc[3]&amp;15)*256;
        // Datos originales
        pantalla[muestras][0]=datos2[1];
        pantalla[muestras][1]=datos2[2];
        // Datos modificados para la visualización
        pantalla1[muestras][0]=280-int(datos2[1]&gt;&gt;4);
        pantalla1[muestras][1]=280-int(datos2[2]&gt;&gt;4);
        muestras=muestras+1;
        if (muestras&gt;=largew)muestras=0;
      }
      puerto.clear();
      fichero.println(datos2[0] + &quot;;&quot; + datos2[1] + &quot;;&quot; + datos2[2]);
      fichero.flush();
      //println(&quot;Trama Actual: &quot; +int(datosc[0]) + &quot;,&quot; + int(datosc[1]) + &quot;,&quot; +  int(datosc[2]) + &quot;,&quot; + int(datosc[3]) + &quot;.&quot; + &quot;   datos: &quot; +  int(datos2[1]) + &quot;,&quot; + int(datos2[2])+ &quot;.&quot;+ &quot;   tiempo: &quot; +  int(millis()-m)+ &quot;.&quot;);
      puerto.write('*');  // * indica que se recibió una trama correctamente y solicita la siguiente
      m = millis();
    }
 }
}

La función mousePressed() se ejecuta cada vez que se pulsa alguno de  los botones del ratón, si se orpime el botón derecho en el intervalo del botón de parada-inicio del diálogo, processing modificará el valor de la variable de control ktr_captura, de modo que si está en TRUE la aplicación captura y grafica los datos y si está en FALSE, los ignora y detiene dibujo y el almacenamiento.

void mousePressed() {
  println(&quot;mouseX: &quot;+mouseX +&quot; mouseY: &quot;+mouseY);
  if (mouseButton == LEFT) {                                                                                  // Eventos de click derecho
    if (mouseX&gt;=button1x&amp;&amp;mouseX&lt;=(button1x+button1width)&amp;&amp;mouseY&gt;=button1y&amp;&amp;mouseY&lt;=(button1y+button1high)) { // Evento del botón1 &quot;Conectar-Desconectar&quot;
      if (ktr_captura){
        ktr_captura=false;
        tipocaptura=&quot;Iniciar Captura&quot;;
        rgbbutton1[0]=155;rgbbutton1[1]=245;rgbbutton1[2]=170;
      }
      else {
        ktr_captura=true;
        tipocaptura=&quot;Detener Captura&quot;;
        rgbbutton1[0]=250;rgbbutton1[1]=255;rgbbutton1[2]=125;
      }
    }
  }
}

Finalmente dejo un sencillo video de la aplicación en operación para observar el resultado así como los códigos escritos.

Descargar programa en CodeWarrior para el microcontrolador.

Descargar programa en Processing para la captura de datos.

A partir de estas líneas de código es posible realizar aplicaciones más sofisticadas, como poder seleccionar el puerto de entrada, la velocidad de trasmisión de datos del microcontrolador o modificar el gráfico de visualización alterando a nuestro gusto las escalas de amplitud y periodo y colocando dichas escalas en la ventana. Espero que este post y este material sean de utilidad para proyectos de mayor alcance. Saludos y no olviden referenciarme.

Vúmetro estéreo con Microcontrolador Freescale.

18 marzo, 2013 2 comentarios

Basado en el interesante diseño de un vúmetro sobre un ATMEGA8 se ha elaborado un circuito un poco más complejo, esta vez con un microcontrolador Freescale, para mostrar, por un lado, la programación en lenguaje C en CodeWarrior de dicho micro y por el otro, aumentar la funcionalidad del vúmetro colocando 8 LEDs para cada canal de audio. El montaje resultante se muestra en la imagen siguiente.

Vúmetro Freescale

Componentes.

Aparte del protoborad y los demás elementos de conexión, se requieren los siguientes componentes:

6 Condensadores de 10uF a 16V
2 Condensadores de 22pF
1 Condensador de 0.1uF
2 Diodos rápidos 1N4148
1 IC TL072 Amplificador Operacional dual
16 LED de  3 mm
1 Cristal de   4.9152 MHz
16 Resistencias de 330 Ohm 1/4 W
2 Resistencias de 47 K 1/4 W
5 Resistencias de 10 K 1/4 W
1 Resistencia de 10M 1/4 W
4 Resistencias de 1 K 1/4 W
2 Resistencias de 51 K 1/4 W
2 Resistencias de 150 Ohm 1/4 W
1 potenciómetro dual de 10 K
2 transistores CP945 (NPN)
1 microcontrolador MC68HC908JK3CP

El JK3 es un microcontrolador de 8 bits de la familia HC08 (de los viejitos) de 20 pines, dada su reducida memoria (apenas 128 bytes de RAM) es habitual que la programación se efectúe en Assembler, sin embargo, para aplicaciones sencillas que no requieran memoria de forma importante es posible ayudarse en Processor Expert para elaborar código en lenguaje C de forma rápida y efectiva.

La etapa de audio ha sido elaborada usando el Amplificador Operacional dual TL072, el cual garantiza ganancia adecuada para señales pequeñas para de este modo introducirlas en los ADC del Microcontrolador. Previamente estas señales son fijadas mediante un potenciómetro dual (en el montaje se usaron 2 reóstatos, uno para cada canal) y amplificadas (ganancia 47) para ser rectificadas seguidamente con la ayuda del diodo rápido, de esta forma se garantiza que los voltajes que mide el ADC siempre están por encima de un valor umbral, que para el estado de reposo equivale al voltaje obtenido por la polarización de los dos resistores de 10 K en serie (5/2=2.5 V) menos el voltaje de polarización del diodo, siendo cercano a 2 voltios. El conocimiento preciso de este valor es importante porque de él depende el valor inicial de los niveles de voltaje en el ADC. En paralelo a la salida rectificada (fijada por un resistor de 51 K) se colocó un capacitor en serie con una resistencia cumpliendo la función de filtro pasabajas.

Diagrama de conexión (picar para agrandar).

vumetro Microcontrolador

Los diodos LED están conectados a 8 pies de salida del Microcontrolador, una pareja por pin. La activación de los mismos se efectúa de forma alternada mediante un temporizador con periodo de 5 ms para cada canal, la cual genera dos señales de control que mueven de corte a saturación el punto de operación de los transistores C945 conectados a cada hilera de diodos.

Programa.

Como se mencionó antes, el programa se desarrolló en Lenguaje C mediante la herramienta Processor Expert del CodeWarrior (v 6.3) No es intención de este post el mostrar el manejo de dicho programa, sino explicar las funciones elaboradas en él. Para información completa es posible remitirse a notas de aplicación y otras ayudas disponibles.

Posterior a las asignaciones de pines de entrada y salida y a la configuración de los Timer se elaboraron dos funciones que corresponden a la rutina del micro. La función principal, llamada vumetro inicializa los bits de control y luego permanece en un bucle infinito en el cual espera a que el bit de control de la visualización (ktr_vista) se ponga a 1, cuando esto ocurre, setea dicho bit y luego comprueba cuál de los dos canales se encuentra activo (ktr_izq es 0 o 1), inicializa la lectura del ADC y terminada esta ejecuta la función llamada nivel.

void vumetro(){
  ADER_SetVal();
  AIZQ_SetVal();
  ktr_ciclo=0;
  ktr_convert=0;
  ktr_vista=0;
  ktr_izq=0;
  for(;;){
    if(ktr_vista==1){               // Bucle de medición (cada 5 ms)
       ktr_vista=0;                 // Si llego al tope de mediciones disparo variable de control del bucle
       if (ktr_izq==0){
         ktr_izq=1;
         (void)AD1_Start();         // Inicia la conversión ADC
         while(ktr_convert==0){}    // Bucle de espera de fin de conversión
         ktr_convert=0;             // Seteo bit de control de fin de conversión
         ADER_PutVal(0);
         nivel(ADC_result[CANALL]);
         AIZQ_PutVal(1);
       }
       else{
         ktr_izq=0;
         (void)AD1_Start();         // Inicia la conversión ADC
         while(ktr_convert==0){}    // Bucle de espera de fin de conversión
         ktr_convert=0;             // Seteo bit de control de fin de conversión
         AIZQ_PutVal(0);
         nivel(ADC_result[CANALR]);
         ADER_PutVal(1);
      }
    }
  }
}

La función nivel simplemente toma los valores obtenidos en el ADC, según el canal escogido, y los compara con una tabla llamada limites, dicha tabla es un static byte en el cual se han introducido los siguientes números, correspondientes a los 8 posibles niveles del vúmetro

static byte limites[]  ={ 105, 110, 115, 120, 130, 135, 140, 145 };

Recordemos que el ADC del JK3 es de 8 bits tenido 255 niveles disponibles de los cuales se pierde un parte por efecto de la polarización del diodo. La función pone a CERO (Encendido) cada salida si el valor del ADC es mayor que cada nivel, en caso contrario la establece en UNO (Apagado)  Para ajustar los niveles de la función al circuito que obtengamos, basta modificar los valores del static byte limites.

void nivel(byte valor){
  if (valor>limites[0])L1_PutVal(ON);
  else  L1_PutVal(OFF);
  if (valor>limites[1])L2_PutVal(ON);
  else  L2_PutVal(OFF);
  if (valor>limites[2])L3_PutVal(ON);
  else  L3_PutVal(OFF);
  if (valor>limites[3])L4_PutVal(ON);
  else  L4_PutVal(OFF);
  if (valor>limites[4])L5_PutVal(ON);
  else  L5_PutVal(OFF);
  if (valor>limites[5])L6_PutVal(ON);
  else  L6_PutVal(OFF);
  if (valor>limites[6])L7_PutVal(ON);
  else  L7_PutVal(OFF);
  if (valor>limites[7])L8_PutVal(ON);
  else  L8_PutVal(OFF);
}

El desarrollo completo del programa se puede descargar AQUI

Por último se muestra el circuito en operación demostrando el funcionamiento de la visualización dinámica y la separación de los dos canales de audio.

Eso es todo, espero que esta actividad sea de utilidad en sus proyectos.

Control difuso de un motor DC implementado en un microcontrolador.

29 octubre, 2012 8 comentarios

Control Difuso
Montaje de un controlador difuso sobre microcontrolador.

Una de las preocupaciones que se tiene al usar como estrategia de control el control difuso es la capacidad de cómputo del controlador. Un gran número de reglas así como el tipo de implicaciones y la desfusificación elegida afectan fuertemente la cantidad de operaciones requeridas para lograr el adecuado funcionamiento del controlador. Según los requerimientos o el coste de la planta a controlar, es justificable el uso de microprocesadores o FPGA’s que por sus características efectúan operaciones matemáticas eficientemente y a muy alta velocidad. Sin embargo, muchos sistemas de bajo costo o de bajas prestaciones no justifican controladores con hardware de alto precio, limitando de este modo las posibilidades de aplicación de un controlador difuso eficiente. Afortunadamente, en los últimos años los microcontroladores han mejorado muchísimo en cuanto a su velocidad de procesamiento, de modo que a un bajo costo se puede pensar en la aplicación de controladores digitales (usen o no lógica difusa) con adecuados tiempos de respuesta agregando además los módulos embebidos propios de los microcontroladores (conversores ADC, DAC, PWM, etc) de suerte que es posible experimentar con el diseño y la aplicación de controladores difusos para plantas de bajo coste, o simplemente, con fines académicos y educativos, como es el caso.

A continuación se presenta el diseño e implementación de un controlador difuso de velocidad, equivalente a un controlador PI, para un sistema motor-generador de corriente directa. La planta consta de dos motores iguales de 12 voltios acoplados mecánicamente. El controlador se ha implementado sobre un microcontrolador MCF51QE128 de Freescale, haciendo uso de la tarjeta de desarrollo DEMOQE. Se ha buscado hacer lo más simple posible la estructura del controlador difuso con un propósito: demostrar la capacidad de un conjunto de reglas basadas en el lenguaje para obtener resultados aceptables con pocos elementos. El diagrama de bloques se muestra en la figura 1.

Control Difuso PI

Figura 1. Esquema de Bloques del sistema completo.

El actuador del motor está formado por un circuito de potencia regulado mediante una señal PWM generada desde el microcontrolador. Para la etapa de potencia se implementó el circuito mostrado en la figura 2. El control de potencia se realiza conmutando un MOSFET IRF840, disparándolo con un circuito conformado por dos transistores bipolares complementarios NPN y PNP (C945 y A733) en configuración Totem-Pole y un tercer transistor para efectuar el corte-saturación de dicha red.

En el extremo del generador se implementó un circuito con dos fines, acondicionar el voltaje de salida del generador para la medición del ADC del microcontrolador, cuyo rango de voltaje va de 0 V a 3.1 V mediante un divisor resistivo, y atenuar el ruido producido por la vibración mecánica usando un filtro pasabajos. La inclusión del actuador del motor y el acondicionador del generador modifican la función de transferencia de la planta ampliada. Una descripción matemática de la misma nos sirve para efectuar simulación. Para el diseño del controlador nos basaremos en reglas lingüísticas.

Control Difuso Motor

Figura 2. Circuito del sistema completo.

El proceso de desarrollo del controlador difuso contiene los siguientes pasos

1. Selección de las variables y del Universo de Discurso.

Como se deduce del diseño del actuador y el acondicionador de la planta ampliada, la señal de entrada al microcontrolador consiste en un voltaje analógico que llega a un ADC de 12 bits, el rango de entrada queda entonces con valores entre 0 y 4095 equivalentes a voltajes entre 0 V y 3.1 V. El error, que corresponde a la entrada al controlador difuso, es la diferencia entre el valor del ADC y la referencia, que para el ejercicio se logra con otra entrada ADC que se conecta con el potenciómetro que trae integrado la DEMOQE, de modo que se pueda medir voltaje de referencia contra voltaje de salida del generador para comprobar la regulación.  La otra entrada del controlador es el integrador, que simplemente es la suma acumulativa de los valores de error, el rango se ha establecido entre -24576 a 24576 equivalentes a 6 muestras consecutivas con la entrada en el rango mínimo-máximo de error (por ejemplo referencia en 0 y medición en 4095). Para la salida se hará uso de una señal PWM cuyo rango equivale al porcentaje de ciclo útil (0% a 100%) establecida por una función de 16 bits (rango 0 a 65535)

2. Fusificador.

Se hizo uso de un fusificador tipo Singleton dado que en términos de operaciones matemáticas es el más sencillo de todos.

Funciones de pertenencia

Figura 3. Funciones de pertenencia.

3. Funciones de pertenencia.

Las variables difusas se definen asignando valores de un grado de pertenencia a los conjuntos difusos para cada elemento del universo de discurso. Existen varios tipos de función pertenencia, para este ejercicio se han usado las dos más sencillas y comunes, las funciones triangular y trapezoidal. en la figura se muestra la forma de las funciones de pertenencia. Se han definido 3 conjuntos como error positivo (EP), error negativo (EN) y error cercano a cero (C).

4. Construcción de la base de reglas.

La base de reglas se puede diseñar basada en el plano de fase cuando se dispone de poca experiencia previa con el proceso a controlar, esta metodología suele ser de gran utilidad porque permite, por una parte, conocer el proceso en cuestión y por otra, obtener una ley de control a partir de ideas intuitivas. Para el caso que nos ocupa sabemos que el comportamiento del motor-generador es lineal y simplificable a uno de primer orden, por lo tanto se pueden inferir las siguientes reglas:

-Si el error es negativo el ciclo útil del PWM debe ser pequeño.

-Si el error es positivo el ciclo útil del PWM debe ser grande.

-Si el error es cero el ciclo útil del PWM debe ser central.

para el integrador aplica un lógica similar:

-Si la integral del error es negativo el ciclo útil del PWM debe ser pequeño.

-Si la integral del error es positivo el ciclo útil del PWM debe ser grande.

-Si la integral del error es cero el ciclo útil del PWM debe ser central.

5. Mecanismo de inferencia.

Para la base de reglas establecida, se definió el conjunto de salida como la unión (combinación Mamdani) de los conjuntos difusos resultantes de las reglas. Como en el caso de las funciones de pertenencia, esta es la técnica de inferencia con menor coste computacional.

6. Selección de la estrategia de defusificación.

Se escogió el cálculo del promedio centro ponderado. Como antes, es el que implica un menor número de operaciones en el microcontrolador.

Con todos estos datos establecidos se procedió a elaborar la simulación en MATLAB (Figura 4). Posteriormente se escribió el programa para el microcontrolador en lenguaje C con la ayuda de la herramienta Processor Expert del CodeWarrior para microcontroladores Freescale.

Se efectuaron algunas pruebas de laboratorio (aunque infortunadamente no tengo imágenes de los resultados) y se comprobó que el controlador regula adecuadamente la velocidad del motor. Ante una entrada paso en la referencia, el sistema completo exhibe una oscilación muy semejante a la de un sistema de segundo orden con un sobrepico cuyo valor máximo depende del punto de operación que se establezca y un tiempo de establecimiento compara.

Respuesta del controlador

Figura 4. Respuesta del sistema completo ante entrada paso de 1.5 V

A continuación dejo a su disposición:

Archivos de la simulación. (Simulink)

Archivos del programa (Code Warrior).

 la simulación muestra una frecuencia de 1KHz de PWM porque ajustando la simulación a 20KHz (frecuencia en el montaje) tardaba considerablemente más tiempo en ejecutarse, en la practica se puede ajustar una frecuencia que sea por lo menos cinco veces mayor a la frecuencia de muestreo de los ADC obteniéndose resultados semejantes. En teoría este controlador puede usarse para cualquier sistema motor-generador siempre y cuando se haga la correcta adecuación del actuador y el acondicionador del sensor de entrada. Espero que este sencillo artículo sea de utilidad en sus propios diseños.y si efectúan mejoras no duden compartirlas por aquí mismo. Saludos.

También le puede interesar: Tareas del curso de Control III (Sistemas no lineales y Control óptimo)

El Guerrero antes de la batalla.

24 octubre, 2012 Deja un comentario

En otras épocas sentía el temor, me estremecían los prolegómenos de mis batallas, pero ahora después de un sinfín de derrotas ya no siento tales escalofríos, no percibo el aire magro de saberse prontamente enfrentado con la muerte, con la destrucción.

Es consecuencia por supuesto del cúmulo de fracasos que pesan en mi ser, que mantienen oculto mi horizonte y encapotan mi cielo con nimbos lóbregos, presagios de terribles tempestades, de lágrimas que caerán al suelo con furia, que retumbarán por doquier como celebrando pomposamente el triunfo de la inutilidad, de la ineptitud, de la impotencia.

Oscuridad en la que pulula la desidia, pequeño orbe que se deshace en silencio, incapaz de luchar ante lo adverso, ante su destino. Ahora pongo a punto mis armas, mañana, cuando sobre mí caiga el fragor de la guerra, la sed de sangre, las blandiré una vez más; las ungiré con la savia de mi enemigo así como aquél hará después festín de mis despojos, dejándolos a la vista del orbe, para que viendo lo que fue de mi las gentes llenen sus pupilas de escarnio y terror.

Disfrutó de lo que con toda seguridad será mi último día, lo amo con toda intensidad. Me caliento bajo un sol mancebo pero indestructible, señor dador de vida, padre de cada árbol, hierba, insecto y animal de la tierra. Todos aquellos regalos que se me antoja en una fantasía real y una realidad fantástica. Mis manos se untan con las salsas de mil manjares, mi boca se llena de sabores, mis dientes desgarran aquella fibra animal exótica y exquisita asada al fuego de las brasas. Como, veo y me embriago hasta la saciedad, mi mente se aleja de mí mismo pero no se aleja de mis negros designios, de mi destino.

Mañana entraré al tártaro, a los campos Elíseos de los mártires, de los paladines incansables inmolaron sus cuerpos. Allá me reuniré con mis camaradas al igual que con mis rivales, para seguir en la eterna competencia por la superioridad, sin morir nunca, sin cansancio alguno detenga el ataque feroz. Allí mis miedos se disiparán, mis fracasos y mis tropiezos habrán terminado y serán sólo una anécdota magra en una eternidad sin pasado y sin porvenir.

Categorías:Cuento Etiquetas:

Sistema (Planta) de tercer orden mediante un circuito electrónico.

22 octubre, 2012 4 comentarios

A continuación se describe el diseño e implementación de un circuito electrónico que tiene un comportamiento lineal de tercer orden con un par de polos complejos conjugados dominantes y un polo real ubicado a una distancia de por lo menos 5 veces la parte real de los complejos conjugados. Muchos sistemas lineales (y no lineales también) poseen comportamientos de orden superior que se pueden simplificar como un sistema de segundo orden lineal, el cual ha sido estudiado en profundidad, y que permite la elaboración rápida y con pocos elementos de controladores PI, PD, PID entre otros. Tener un planta de orden superior electrónica a disposición permite probar rápidamente el diseño de un controlador electrónico sin necesidad de tener dicho controlador conectado a través de transductores y actuadores al tipo de planta que vaya a ser controlada finalmente, para de este modo tener una idea del funcionamiento del controlador y las diferencias que se puedan presentar al considerar un sistema de orden superior como uno de segundo orden.

Como ejercicio teórico-práctico se obtendrá una aproximación del sistema a un modelo de segundo orden a partir de la respuesta a un escalón para comparar las diferencias que se presentan. Igualmente se trazarán experimentalmente el lugar geométrico de las raíces (LGR) y el diagrama de Bode (respuesta en Frecuencia).

El desarrollo teórico completo se encuentra en el documento PDF disponible aquí.

El plano completo del circuito implementado es el siguiente.

Plano Planta de Tercer Orden

Como se puede constatar en el documento PDF el sistema completo consta de un circuito de orden 2 que corresponde a un filtro con amplificador operacional tipo Sallen Key, un circuito de orden 1 que es un filtro pasabajos convencional con AO y un sumador con ganancia que hace las veces de control proporcional. Dicho control proporcional (en lazo cerrado) permite que podamos alterar la ubicación de los polos de la planta a fin de obtener respuestas amortiguadas, subamortiguadas o sobreamortiguadas. El circuito se ha montado en una tarjeta PCB diseñada para tal fin, se dejaron una serie de pines para poder efectuar rápidamente mediciones con el osciloscopio, igualmente se colocó un interruptor que hace las veces de selector, de modo que la planta se puede usar como un sistema de segundo orden o uno de tercer orden con la ganancia proporcional. La ganancia K se puede modificar mediante un potenciómetro. Todo fue puesto en una pequeña caja para mayor comodidad.

El diagrama de conexiones de la tarjeta PBC, el diseño se elaboró en Eagle y se puede descargar aquí.

PCB planta tercer ordenplanta-plano_componentes

  Muestra de como quedó ensamblado.

La planta terminada y conectada.

planta orden 3 terminada

Finalmente una muestra de distintos comportamientos ante una estrada escalón variando el valor de la ganancia K. (picar sobre las imágenes para agrandar)

->Segundo orden ante una entrada paso

  ->Tercer orden con ganancia de lazo cerrado (K=2)

  ->Tercer orden con K=3

->K=4

Categorías:Electrónica Etiquetas: , ,
Seguir

Recibe cada nueva publicación en tu buzón de correo electrónico.