Kitabı oku: «LabVIEW: Entorno gráfico de programación»
LabVIEW: entorno gráfico de programación
José Rafael Lajara
José Pelegrí
LabVIEW: entorno gráfico de programación

LabVIEW. Entorno gráfico de programación
Segunda edición, 2011
© 2011 José Rafael Lajara Vizcaíno - José Pelegrí Sebastià
© 2011 MARCOMBO, S.A.
Gran Via de les Corts Catalanes, 594
08007 Barcelona
www.marcombo.com
Corrección y maquetación: O3 BCN PACKAGERS
Diseño de la cubierta: NDENU DISSENY
«Cualquier forma de reproducción, distribución, comunicación pública o transformación de esta obra sólo puede ser realizada con la autorización de sus titulares, salvo excepción prevista por la ley. Diríjase a CEDRO (Centro Español de Derechos Reprográficos, www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta obra».
ISBN: 978-84-267-1868-6
D.L.:
Impreso en
Printed in Spain
Prólogo
El alcance del software LabVIEW en la sociedad y cómo contribuye en el mundo científico y tecnológico, para mejorar el mundo en el que vivimos, ha superado nuestras expectativas y esperanzas desde su creación hace 25 años.
Desde los inicios se pensó en crear una herramienta que aportase a los ingenieros y científicos lo que la hoja de datos aportó a los analistas y financieros para trabajar de forma más rápida, eficiente y más fácil. Nuestra visión de que LabVIEW es una herramienta que se use en todos los diferentes sectores para solucionar retos tan complejos a los que se enfrentan los ingenieros de todo el mundo a diario se ha convertido en realidad. LabVIEW es una ayuda para los ingenieros y físicos nucleares del CERN en Suiza que controla la máquina más grande del mundo y también se usa en el diseño de dispositivos médicos innovadores y en los sistemas de test para garantizar nuestra seguridad al viajar en aviones y coches. Dada dicha realidad, LabVIEW ha llegado a ser una herramienta adoptada por universidades, institutos y muchos programas educativos.
Además, LabVIEW juega un papel muy importante en la educación de los ingenieros más pequeños, pues cada año existen nuevas competiciones de robots de niños que pueden diseñar, programar y controlar su creación a través de LabVIEW, lo que hace que los niños se entusiasmen por seguir aprendiendo y vean más atractiva la educación y la ingeniería tan necesaria para nuestro mundo. La colaboración entre National Instruments y LEGO Education ha hecho que haya más de 500.000 niños en distintas partes del mundo aprendiendo conceptos sobre robots y que lleguen a construir el suyo propio a través de la plataforma LEGO Mindstorms.
Hoy en día LabVIEW tiene su propio «ecosistema», que cuenta con más de 100 grupos de usuarios registrados, más de 70.000 usuarios en línea, más de 300 complementos de terceras empresas y más de 400 casos de estudio. Le animamos a aprovechar esta red o comunidad de usuarios de nivel principiante hasta los más expertos en arquitecturas de LabVIEW.
Felicidades por optar por aprender y conocer una nueva forma de enfocar su aplicación o proyecto con el diseño gráfico de sistemas. Esperamos que el material técnico junto con las explicaciones y ejercicios de este libro, le sirvan de gran ayuda para poder avanzar en sus habilidades técnicas.
Por favor, visite www.ni.com/spain e ingrese el código splabviewmarcombo para acceder a los recursos técnicos adicionales tales como la versión de evaluación de LabVIEW, notas técnicas, y foros.
Cristóbal Rus Responsable del Programa Académico de España National Instruments
Carlos Ríos Director General de España National Instruments
Índice
Capítulo 1Introducción a LabVIEW. Entorno
1.1. Entorno
1.2. Menús (paletas)
1.2.1. Menú de herramientas
1.2.2. Controles
1.2.2.1. Personalización de controles
1.2.3. Funciones
1.2.4. Personalización de los menús
1.3. Creación de programas
1.4. Flujo de ejecución
1.5. VI y subVI
1.5.1. Librerías
1.5.2. Polimorfismo
1.6. Proyectos
1.7. Depuración de código
1.8. Otras herramientas
1.8.1. Snippet
1.8.2. Opciones
1.8.3. Jerarquía
1.8.4. Ventana de navegación
1.8.5. Compilación masiva
1.8.6. Búsqueda y comparación
1.8.7. Seguridad
1.8.8. Ejemplos
1.9. Ejercicios
1.10. Bibliografía
Capítulo 2Estructuras
2.1. SEQUENCE
2.2. CASE
2.3. WHILE
2.4. FOR
2.5. EVENT
2.6. TIMED LOOP y TIMED SEQUENCE
2.7. DISABLE STRUCTURE
2.8. FORMULA NODE
2.9. Scripts
2.10. Ejemplos
2.10.1. Ejemplo I: Filtro promediador
2.10.1.1. Explicación teórica
2.10.1.2. Código
2.10.1.3. Resultado
2.10.2. Ejemplo II: Generación de números primos
2.10.2.1. Explicación teórica
2.10.2.2. Código
2.10.2.3. Resultado
2.10.3. Ejemplo III: Bingo
2.10.3.1. Explicación teórica
2.10.3.2. Código
2.10.3.3. Resultado
2.11. Ejercicios
2.12. Bibliografía
Capítulo 3Tipos de datos
3.1. Tipos de terminales
3.2. Tipos de datos
3.2.1. Boolean
3.2.2. Numeric
3.2.3. Strings
3.2.3.1. Transformaciones número-texto
3.2.4. Path
3.2.5. Arrays
3.2.6. Clusters
3.2.7. Waveforms
3.2.8. Referencias
3.2.9. Variant
3.2.10. Dynamic
3.3. Manipulación de datos
3.3.1. Manipulación de bytes y bits
3.3.2. Otras transformaciones
3.3.3. Type Cast
3.4. Variables y propiedades
3.4.1. Variables locales
3.4.2. Variables globales
3.4.3. Variables compartidas
3.4.4. Nodos de propiedades y métodos
3.4.5. Referencias
3.5. Ejemplos
3.5.1. Ejemplo I: varios métodos para convertir la representación ASCII a su valor
3.5.1.1. Explicación teórica
3.5.1.2. Código
3.5.2. Ejemplo II: Carrera de fórmula 1
3.5.2.1. Explicación teórica
3.5.2.2. Código
3.5.2.3. Resultados
3.6. Ejercicios
3.7. Bibliografía
Capítulo 4Ficheros
4.1. Ficheros de texto
4.1.1. Ejemplo I: Ficheros de texto y binarios
4.1.2. Ejemplo II: Lotería
4.2. Ficheros de configuración
4.2.1. Ejemplo
4.3. Ficheros XML
4.3.1. Ejemplo I: Agenda
4.3.2. Ejemplo II: Buscador en la agenda
4.4. Ficheros de imagen
4.4.1. Ejemplo
4.5. Ficheros de sonido
4.5.1. Ejemplo I: Lectura de fichero y procesado
4.5.2. Ejemplo II: Adquisición y VIs Express
4.6. Acceso al registro de Windows
4.6.1. Ejemplo: Registro de LabVIEW
4.7. Reports
4.7.1. Ejemplo: Generación de un fichero PDF
4.8. Storage
4.8.1. Ejemplo
4.9. Datalog
4.9.1. Ejemplo
4.10. Waveform
4.11. Ejercicios
Capítulo 5Comunicación serie
5.1. Introducción
5.2. Historia
5.3. RS-232
5.3.1. Conectores
5.3.2. Puertos
5.3.3. Handshake
5.3.4. Otros estándares
5.4. Comunicaciones serie en LabVIEW
5.4.1. VISA
5.5. Ejemplos
5.5.1. Ejemplo I: Comunicación con un microcontrolador
5.5.1.1. Explicación teórica
5.5.1.2. Código del microcontrolador
5.5.1.3. Código en LabVIEW
5.5.1.4. Resultado
5.5.2. Ejemplo II: Control de un instrumento de laboratorio
5.5.2.1. Explicación teórica
5.5.2.2. Código
5.5.2.3. Resultado
5.5.3. Ejemplo III: Manejo de eventos
5.5.3.1. Explicación teórica
5.5.3.2. Código
5.6. Ejercicios
5.7. Bibliografía
Capítulo 6GPIB
6.1. Introducción
6.2. El estándar IEEE 488.1
6.2.1. Cableado
6.2.2. Handshake
6.2.3. Funcionamiento
6.3. El estándar IEEE 488.2
6.4. SCPI
6.5. GPIB en LabVIEW
6.5.1. VISA
6.5.2. Drivers
6.6. Ejemplos
6.6.1. Ejemplo I: Escáner del bus
6.6.1.1. Explicación teórica
6.6.1.2. Código
6.6.1.3. Resultado
6.6.2. Ejemplo II: Osciloscopio Tektronix TDS210
6.6.2.1. Explicación teórica
6.6.2.2. Código
6.6.2.3. Resultado
6.6.3. Ejemplo III: Uso de VI Express, Instrument I/O Assistant
6.6.3.1. Explicación teórica
6.6.3.2. Procedimiento
6.6.4. Ejemplo IV: Generador HP 33120A usando un driver
6.7. Ejercicios
6.8. Bibliografía
Capítulo 7Adquisición de datos
7.1. Introducción
7.2. Adquisición de señales analógicas
7.2.1. Configuraciones de terminales
7.2.2. Conversión a digital
7.2.3. Procesamiento
7.3. Tarjeta de adquisición de datos NI PCI-6221M
7.4. Measurement & Automation Explorer (MAX)
7.5. DAQ en LabVIEW
7.5.1. DAQ Assistant
7.6. Ejemplos
7.6.1. Ejemplo I: Crear un osciloscopio y un generador de funciones con la tarjeta PCI-6221M
7.6.1.1. Explicación teórica
7.6.1.2. Código
7.6.1.3. Resultado
7.6.2. Ejemplo II: Medida de la temperatura
7.6.2.1. Explicación teórica
7.6.2.2. Código
7.6.3. Ejemplo III: Control de un motor paso a paso
7.6.3.1. Explicación teórica
7.6.3.2. Código
7.6.3.3. Resultados
7.7. Ejercicios
7.8. Bibliografía
Capítulo 8TCP y UDP
8.1. TCP/IP
8.1.1. Nivel de red
8.1.2. Nivel de transporte
8.1.3. Nivel de aplicación
8.2. Arquitecturas habituales
8.3. TCP/IP en LabVIEW
8.4. HTTP en LabVIEW
8.5. Ejemplos
8.5.1. Ejemplo I: Servidor de Telnet
8.5.1.1. Explicación teórica de Telnet
8.5.1.2. Código
8.5.1.3. Resultados
8.5.2. Ejemplo II: Transmisión de voz
8.5.2.1. Explicación teórica
8.5.2.2. Código
8.5.2.3. Resultados
8.5.3. Ejemplo III: Obtener ficheros a través de HTTP
8.5.3.1. Explicación teórica
8.5.3.2. Código
8.5.3.3. Resultados
8.6. Ejercicios
8.7. Bibliografía
Capítulo 9VI Server y comunicaciones avanzadas
9.1. Acceso remoto
9.1.1. Paneles remotos
9.1.2. Publicación en web
9.2. Enlaces de datos
9.2.1. DataSocket
9.2.1.1. API
9.2.1.2. Método directo
9.2.2. Shared variables
9.2.3. Data Binding
9.2.4. Networks Streams
9.3. VI Server
9.4. Servicios web
9.4.1. Servicios web en LabVIEW
9.5. Ejemplos
9.5.1. Ejemplo I: Chat
9.5.1.1. Explicación teórica
9.5.1.2. Código
9.5.1.3. Resultados
9.5.2. Ejemplo II: Controlar Decorations
9.5.2.1. Explicación teórica
9.5.2.2. Código
9.5.3. Ejemplo III: Rendimiento de VI
9.5.3.1. Explicación teórica
9.5.3.2. Código
9.5.3.3. Resultados
9.5.4. Ejemplo IV: Sistema distribuido con variables compartidas
9.5.4.1. Explicación teórica
9.5.4.2. Código
9.6. Ejercicios
9.7. Bibliografía
Capítulo 10Sincronización y multihilo
10.1. Multihilo en LabVIEW
10.1.1. Sistemas de ejecución
10.1.2. Prioridades
10.1.3. VI reentrantes
10.1.4. Pipeline
10.1.5. Paralelismo
10.2. Multinúcleo
10.2.1. FOR
10.2.2. TIMED LOOP
10.3. Sincronización
10.3.1. Occurrences
10.3.2. Semáforos
10.3.3. Notificaciones
10.3.4. Colas
10.3.5. Rendezvous
10.4. Problemas típicos de la programación multihilo
10.4.1. Condición de carrera
10.4.2. Inanición
10.4.3. Inversión de prioridad
10.4.4. Interbloqueo
10.5. Eventos
10.5.1. Ejemplo I: Eventos dinámicos
10.5.2. Ejemplo II: Eventos de usuario
10.6. Ejercicios
10.7. Bibliografía
Capítulo 11Modelos de programación
11.1. Estructuras de interfaz de usuario
11.2. Temporización
11.3. Manejo de errores
11.4. Máquinas de estado
11.4.1. Máquinas de estados en LabVIEW
11.4.2. Ejemplo: Máquina expendedora
11.4.2.1. Explicación teórica
11.4.2.2. Código
11.4.2.3. Resultados
11.5. Comunicaciones
11.5.1. Maestro/esclavo
11.5.2. Productor/consumidor
11.5.3. Ejemplo: keylogger
11.5.3.1. Explicación teórica
11.5.3.2. Código
11.5.3.3. Resultado
11.6. Orientado a objetos
11.6.1. Programación orientada a objetos en LabVIEW
11.6.1.1. Ejemplo: Fichas de empleados
11.7. Scripting
11.7.1. Introducción
11.7.2. XNodes
11.7.3. Ejemplo I
11.7.3.1. Código
11.7.4. Ejemplo II: Metaprograma
11.7.4.1. Código
11.7.4.2. Resultado
11.8. Ejercicios
11.9. Bibliografía
Capítulo 12Código externo
12.1. Librerías externas
12.1.1. CIN
12.1.2. Librerías compartidas en LabVIEW
12.1.3. Ejemplo I: Creación de una DLL y su uso en LabVIEW
12.1.3.1. Explicación teórica
12.1.3.2. Código en C++
12.1.3.3. Código en LabVIEW
12.1.4. Ejemplo II: Wait (μs)
12.1.4.1. Explicación teórica
12.1.4.2. Código
12.1.4.3. Resultados
12.1.5. Ejemplo III: CIN
12.1.5.1. Explicación teórica
12.1.5.2. Código en C++
12.1.5.3. Código en LabVIEW
12.2. Comunicaciones entre aplicaciones
12.2.1. ActiveX
12.2.1.1. ActiveX en LabVIEW
12.2.1.2. Ejemplo: Creación de un control ActiveX y su uso en LabVIEW
12.2.2. .NET
12.2.2.1. .NET en LabVIEW
12.2.2.2. Ejemplo I: Creación de una librería de clases .NET y su uso en LabVIEW
12.2.2.3. Ejemplo II: Navegador Web
12.3. Creación de librerías y ejecutables con LabVIEW
12.4. Ejercicios
12.5. Bibliografía
Capítulo 13Optimización de la interfaz
13.1. Elementos del Panel Frontal
13.1.1. Otros tipos de gráficos
13.1.2. Subpaneles
13.1.3. Splitters
13.2. Localizar la interfaz
13.3. Interactuando con el usuario programadamente
13.3.1. Menús personalizados
13.3.2. Teclado
13.3.3. Drag & Drop
13.4. XControls
13.4.1. Ejemplo: Polímetro virtual
13.4.1.1. Explicación teórica
13.4.1.2. Metodología
13.4.1.3. Resultados
13.5. Consejos para diseñar una interfaz
13.5.1. Layout
13.5.2. Tipografía
13.5.3. Color
13.5.4. Imágenes
13.5.5. Otros
13.6. Ejercicios
13.7. Bibliografía
Capítulo 14Optimización del código
14.1. Diseño de la aplicación
14.2. Estilo del código
14.2.1. Organización
14.2.2. Comentar el código
14.2.3. VI
14.2.4. Cableado
14.3. Control de código
14.3.1. Ejemplo en LabVIEW
14.4. Mejorar el rendimiento
14.4.1. Herramientas
14.4.2. Manejo de memoria
14.4.2.1. Buffers
14.4.2.2. In Place Element Structure
14.4.2.3. Paso de valores por referencia
14.4.3. Técnicas para mejorar el rendimiento
14.4.3.1. Cálculos inútiles
14.4.3.2. No reinventar la rueda
14.4.3.3. Tamaño de los datos
14.4.3.4. Datos simples o complejos
14.4.3.5. Redimensionado de arrays
14.4.3.6. Cuellos de botella
14.4.3.7. Variables y propiedades
14.4.3.8. SubVI
14.5. Ejercicios
14.6. Bibliografía
Capítulo 15Otras plataformas
15.1. PDA
15.1.1. Ejemplo: Escáner de dispositivos Bluetooth
15.1.1.1. Explicación teórica
15.1.1.2. Código
15.1.1.3. Resultados
15.2. FPGA
15.2.1. Ejemplo: Luces del coche fantástico en CompactRIO
15.2.1.1. Explicación teórica
15.2.1.2. Código
15.2.1.3. Resultados
15.3. Bus PXI
15.3.1. PCI
15.3.2. PXI
15.3.3. Ejemplo I: Lectura de los registros de configuración
15.3.3.1. Explicación teórica
15.3.3.2. Código
15.3.3.3. Resultado
15.3.4. Ejemplo II: Generación y adquisición de señales
15.3.4.1. Explicación teórica
15.3.4.2. Código
15.3.4.3. Resultados
15.3.5. Ejemplo III: Medida de capacidad
15.3.5.1. Explicación teórica
15.3.5.2. Código
15.3.5.3. Resultados
15.4. Bibliografía
Capítulo 16LabWindows/CVI
16.1. Introducción
16.2. Librerías
16.3. Generación automática de código
16.4. Creación de GUI
16.5. Manejo de eventos
16.6. Ejemplo
16.6.1. Interfaz
16.6.2. Lógica
16.6.3. Resultados
16.7. Ejercicio
16.8. Bibliografía
Capítulo 17Measurement Studio
17.1. Introducción
17.2. Clases y controles
17.3. Ejemplos
17.3.1. Ejemplo I: Gráficas
17.3.2. Ejemplo II: Filtros digitales y FFT
17.3.3. Ejemplo III: Proyecto web
17.4. Ejercicio
17.5. Bibliografía
Capítulo 18Measurement Studio
18.1. Introducción
18.2. Ejemplos
18.2.1. Ejemplo I: Datos electrocardiográficos en un fichero CSV
18.2.1.1. Navigator
18.2.1.2. View
18.2.1.3. Analysis
18.2.1.4. Report
18.2.1.5. Scripts
18.3. Conectividad con LabVIEW
18.4. Bibliografía
Capítulo 19TestStand
19.1. Introducción
19.2. Ejemplo
19.2.1. Variables
19.2.2. Estructuras
19.3. Conectividad
19.4. Herramientas avanzadas
19.5. Bibliografía
Capítulo 1
Introducción a LabVIEW. Entorno
LabVIEW es el acrónimo de Laboratory Virtual Instrument Engineering Workbech. Es un lenguaje y, a la vez, un entorno de programación gráfica en el que se pueden crear aplicaciones de una forma rápida y sencilla.
National Instruments, la empresa desarrolladora y propietaria de LabVIEW, se fundó en 1976 en Austin, Texas. Sus primeros productos eran dispositivos para el bus de instrumentación GPIB. En abril de 1983 comenzó el desarrollo de lo que sería su producto estrella: LabVIEW, que vería la luz en octubre de 1986 con el lanzamiento de LabVIEW 1.0 para Macintosh (los ordenadores más populares en aquella época y que ya disponían de interfaz gráfica) y en 1990 la versión 2. Para Windows habría que esperar a septiembre de 1992. Los principales hitos de LabVIEW pueden verse en la tabla 1-1.
| Fecha | Hito |
| Abril de 1983 | Comienza el desarrollo de LabVIEW |
| Octubre de 1986 | LabVIEW 1.0 para Macintosh |
| Enero de 1990 | LabVIEW 2.0 |
| Septiembre de 1992 | LabVIEW para Windows |
| Octubre de 1992 | LabVIEW para Sun |
| Octubre de 1993 | LabVIEW 3.0 multiplataforma |
| Abril de 1994 | LabVIEW para Windows NT |
| Octubre de 1994 | LabVIEW para Power Macintosh |
| Octubre de 1995 | LabVIEW para Windows 95 |
| Mayo de 1997 | LabVIEW 4.0 |
| Marzo de 1998 | LabVIEW 5.0 |
| Febrero de 1999 | LabVIEW 5.1, LV para Linux y LV Real-Time |
| Agosto de 2000 | LabVIEW 6i |
| Enero de 2002 | LabVIEW 6.1 |
| Mayo de 2003 | LabVIEW 7 Express, LabVIEW PDA y FPGA |
| Mayo de 2004 | LabVIEW 7.1 |
| Mayo de 2005 | LabVIEW DSP |
| Junio de 2005 | LabVIEW Embedded |
| Octubre de 2005 | LabVIEW 8 |
| Agosto de 2006 | LabVIEW 8.20 (Edición especial por el 20° aniversario) |
| Agosto de 2007 | LabVIEW 8.5 |
| Agosto de 2008 | LabVIEW 8.6 |
| Agosto de 2009 | LabVIEW 2009 |
| Agosto de 2010 | LabVIEW 2010 |
Tabla 1-1. Evolución de las diferentes versiones de LabVIEW.
Como puede verse, en los últimos años se han liberado las nuevas versiones de LabVIEW en agosto, coincidiendo con el evento NI Week. Además, es habitual que aparezca una revisión menor a los 6 meses de la versión principal a modo de Service Pack que corrige bugs.
1.1. Entorno
LabVIEW es una herramienta de programación gráfica. Originalmente este programa estaba orientado para aplicaciones de control de equipos electrónicos usados en el desarrollo de sistemas de instrumentación, lo que se conoce como instrumentación virtual. Por este motivo los programas creados en LabVIEW se guardarán en ficheros llamados VI (Virtual Instrument), y con la misma extensión. De una forma similar, también se da nombre a sus dos ventanas principales: un instrumento real tendrá un Panel Frontal donde estarán sus botones, pantallas, etc. y una circuitería interna. En LabVIEW estas partes reciben el nombre de Panel Frontal y Diagrama de Bloques respectivamente.
Panel Frontal: es la parte que verá el usuario, suele tener fondo gris.
Diagrama de Bloques, es donde se realizará la programación y suele tener fondo blanco.
El Panel Frontal y el Diagrama de Bloques están conectados a través de los terminales (elementos que sirven como entradas o salidas de datos). De la misma forma que un indicador luminoso de la carátula de un instrumento está representado como un diodo en la circuitería interna. En un programa en LabVIEW ese mismo indicador luminoso estará representado como una entrada de tipo booleano en el Diagrama de Bloques, sobre esta entrada el programa o el usuario podrá escribir un valor.
En la figura 1-1 pueden verse las dos ventanas mencionadas antes. En primer término está el Panel Frontal, con fondo gris y dos terminales: uno numérico llamado Meter y otro que simboliza un interruptor, llamado Boolean. En el Diagrama de Bloques también aparecen estos dos elementos bajo los mismos nombres y representados con dos iconos. Una vez que se ejecute este programa, desde el código se escribirá y leerá de estos iconos.

Figura 1-1. Panel Frontal y Diagrama de Bloques
En la parte superior de estas ventanas se sitúa una barra con varias herramientas. En el Diagrama de Bloques esta barra tiene algunas opciones más, como aparece en la figura 1-2.

Figura 1-2. Barra de herramientas
El primer grupo de herramientas sirve para controlar la ejecución de un programa en LabVIEW. El primer botón indica si hay errores en el programa (flecha rota) y, cuando no los hay (flecha completa como en la imagen), ejecuta una vez el programa. El segundo botón ejecuta de forma continua el programa, como regla general este botón no debe usarse, en su lugar se empleará un bucle en el programa. El tercer botón aborta la ejecución y el cuarto permite realizar una pausa o reanudar la ejecución.
El segundo grupo de botones sirve para ayudar a su depuración. El primer botón es Highlight Execution, una de las herramientas más útiles para depurar, ralentiza la ejecución permitiendo ver el camino que siguen los datos en el programa. El siguiente, Retain Wire Values permite que, al colocar un probe, se obtenga el valor anterior. Los tres siguientes se utilizan para ejecutar el programa paso a paso.
El menú desplegable permite formatear textos; es recomendable usar los formatos predefinidos como Application Font o System Font.
En el siguiente grupo se usan los tres primeros para alinear, distribuir, controlar el tamaño, agrupar y ordenar objetos. El último botón se utiliza para realizar una ordenación automática de los elementos en el Diagrama de Bloques; también se pueden ordenar sólo algunos elementos seleccionándolos o excluir una determinada estructura de la ordenación desde su menú contextual.
El primer elemento, una caja de texto, permite hacer una búsqueda, mientras el segundo abre la ayuda contextual.
En el lateral derecho tanto del Panel Frontal como del Diagrama de Bloques aparece el icono que representa al VI,
. Tiene un menú contextual que permite acceder a diversas características que serán tratadas más adelante.
Para colocar funciones en el Diagrama de Bloques y objetos en el Panel Frontal se tienen las paletas o menús flotantes, llamadas paleta de funciones y de controles respectivamente. Además, también se tiene la paleta de herramientas que consta de diferentes utilidades. En la figura 1-3 puede verse un aspecto de estas tres paletas (el aspecto puede modificarse desde Tools > Options > Controls/Functions Palettes).

Figura 1-3. Diferentes paletas de trabajo: funciones, controles y herramientas.
Las paletas de funciones y controles se despliegan haciendo clic con el botón secundario del ratón sobre una zona vacía del Diagrama de Bloques o del Panel Frontal. También pueden dejarse fijas presionando el clip (
) de la esquina superior izquierda. Otra opción muy útil es el botón Search de la esquina superior derecha, que sirve para buscar funciones o controles por su nombre. Estos menús se pueden personalizar desde Tools > Advanced > Edit Palette Set.
Otra ventana muy útil es la de ayuda contextual. Puede abrirse en Help > Show Context Help o desde el atajo CTRL+H. Esta ventana muestra información del objeto que esté situado bajo el cursor, ya sea una función, un VI, un control o un indicador. Además, puede aparecer un enlace a la página de la ayuda relacionada con ese objeto.

Figura 1-4. Ventana de ayuda contextual.
A continuación se muestra la tabla 1-2 con los atajos de teclado más útiles de LabVIEW. Pueden personalizarse en Tools > Options > Menu Shortcuts. Como recomendación personal, podemos decir que tenemos personalizado un atajo de teclado para abrir la paleta de herramientas.
| Combinación de teclas | Función |
| CTRL + R | Ejecuta el programa |
| CTRL + . | Aborta la ejecución |
| CTRL + E | Conmuta entre la dos ventanas principales |
| CTRL + B | Elimina los hilos rotos |
| CTRL + H | Muestra u oculta la ayuda contextual |
| CTRL + ? | Muestra la ayuda |
| CTRL + C | Copia los objetos seleccionados al portapapeles |
| CTRL + X | Corta los objetos seleccionados al portapapeles |
| CTRL + V | Pega los objetos desde el portapapeles |
| CTRL + Z | Deshace la última acción |
| CTRL + SHIFT + Z | Rehacer |
| CTRL + S | Guarda el VI |
| CTRL + U | Ordena de forma automática los objetos del programa |
| TAB | Cambia entre las herramientas de la paleta de herramientas |
| CTRL + arrastrar | Crea una copia de los objetos seleccionados |
| SHIFT + arrastrar | Mueve los objetos solamente en una dirección |
Tabla 1-2. Atajos de teclado más útiles.
1.2. Menús (paletas)
1.2.1. Menú de herramientas
Mediante la paleta de herramientas se puede seleccionar el instrumento que se desea usar; esto hace cambiar el tipo de cursor del ratón. En la figura 1-5 se puede ver la paleta de herramientas con 11 botones.

Figura 1-5. Paleta de herramientas
Automatic Tool Selection
. Si está activado sirve para que se seleccione la herramienta de forma automática dependiendo del elemento sobre el que se sitúe el cursor.
Operate Value
. Sería el cursor disponible cuando el VI está ejecutándose, sirve principalmente para cambiar valores en los controles del Panel Frontal.
Position/Size/Select
. Como su nombre indica, sirve para posicionar objetos, tanto en el Diagrama de Bloques como en el Panel Frontal. También sirve para cambiar el tamaño y seleccionar elementos.
Edit Text
. Cambia el cursor para poder escribir texto. Se puede usar, entre otras cosas, para escribir comentarios en el Diagrama de Bloques o dar nombre a controles e indicadores.
Connect Wire
. Es la herramienta que sirve para cablear. Uniendo unos elementos con otros por cables, los datos fluirán a través de ellos. Si los cables aparecen en color gris y de forma discontinua, significa que el cable está roto (hay un error): el tipo de datos es incompatible o los terminales no son los adecuados. El color y grosor del cable indicará el tipo de datos. Además, desde la versión 2010 se les puede poner etiquetas para indicar su función o poner un comentario.
Object Shortcut Menu
. Despliega el menú contextual asociado a un elemento. Equivale a hacer clic con el botón secundario del ratón.
Scroll Window
. Sirve para mover el contenido de las ventanas del mismo modo que si se usaran las barras de desplazamiento laterales.
Set/Clear Breakpoint
. Crea o borra un punto de ruptura en un determinado elemento (función, VI, estructura o cable). Cuando la ejecución llega a ese punto se detiene; más tarde puede reanudarse mediante el botón de Pause.
Probe Data
. Crea un probe en un cable. Un probe es una ventana flotante que muestra el valor que circula por el cable.
Get Color
. Obtiene el valor del color de un elemento.
Set Color
. Colorea un elemento. Tiene dos posibles colores: el principal y el de fondo; ambos pueden ser asignados de forma independiente. Uno de los colores disponibles está marcado mediante una T en la esquina superior derecha; se trata del color transparente.

Figura 1-6. Colores.
1.2.2. Controles
El menú que aparece en el Panel Frontal es el menú de controles, y en él se pueden seleccionar los terminales que servirán para interactuar con el usuario.
Los terminales se dividen en controles e indicadores, aunque a veces se les suele llamar a todos genéricamente controles. Los controles propiamente dichos son las entradas de datos, y los indicadores las salidas. Están clasificados según su estilo en varios submenús: Modern, System y Classic. Dentro de cada submenú hay otros menús que clasifican los controles por el tipo de datos. Los más usados son los que sirven para dibujar gráficas y los de la primera fila del submenú Modern. Éstos, de izquierda a derecha, corresponden a datos de tipo numérico, booleano, textos y tipos compuestos.
Para usar los controles hay que seleccionar un objeto y llevarlo al lugar deseado del Panel Frontal.

Figura 1-7. Paleta de controles.
Los indicadores para gráficas merecen un trato aparte debido a su importancia. En la paleta de gráficas hay varios tipos, siendo los más importantes Waveform Chart y Waveform Graph.

Figura 1-8. Paleta de gráficos.
Los indicadores Waveform Chart se diferencian de los Waveform Graph en que estos últimos dibujan totalmente la gráfica cuando llegan nuevos datos; sin embargo, los Waveform Chart anexan el nuevo dato a los existentes. El número de datos que se muestran puede ajustarse escribiendo sobre los números que aparecen en los ejes o dejar que LabVIEW los ajuste automáticamente (Auto-Scale). En los Waveform Chart también se puede ajustar el número máximo de puntos que se guardan a través de la opción Chart History Length del menú contextual. Mediante los menús contextuales de los elementos de las gráficas se pueden modificar muchos otros aspectos como el color, interpolación, elementos a mostrar, etc.

Figura 1-9. Comparación entre un Waveform Graph y un Waveform Chart.
En la imagen de la figura 1-9 pueden verse ambos indicadores y sus principales diferencias. El código de la parte izquierda repite un bucle 50 veces. En este bucle se genera un número aleatorio por cada iteración, después se crea un arreglo o array con los 50 números y se muestran en un Waveform Graph y un Waveform Chart. En el primero sólo se dibujan 50 puntos, y éstos se sobrescriben cada vez que se ejecuta el programa. En el segundo, los datos nuevos se van colocando después de los antiguos (véase la numeración del eje X). El resultado se muestra después de dos ejecuciones (100 datos generados). En conclusión: puede decirse que el Waveform Chart tiene memoria y el Waveform Graph no. Tanto este programa como el resto de los que aparecen en este libro pueden consultarse desde el CD adjunto.
Una práctica recomendable en todos los controles e indicadores es añadirles un texto que indique la función que realizan en la ventana que aparece al seleccionar en el menú contextual Description and Tip. Este texto se mostrará en la ventana de ayuda contextual cuando el usuario coloque el ratón sobre el terminal.

Figura 1-10. Ventana que permite describir cada terminal.
Sobre todos los controles e indicadores colocados en el Panel Frontal aparece un texto, por ejemplo Numeric, Boolean, Meter, Waveform Chart, etc. Este texto es la etiqueta o Label que sirve para identificar el elemento tanto en el Panel Frontal como en el Diagrama de Bloques, así como en variables, propiedades, referencias o métodos que dependan de ese control. También existe otro texto llamado Caption que sólo puede ser visible en el Panel Frontal. Aunque no es necesario, es conveniente que cada control tenga un Label diferente que describa su función. Si es necesario, en el Panel Frontal se puede usar Caption y tip para presentar una descripción más extensa. Se puede hacer visible o invisible un Label o Caption presionando con el botón derecho sobre el control en el Panel Frontal para ir al menú contextual y después a Visible Items.
Todos los controles e indicadores tienen elementos comunes en su menú contextual, como hacer que tengan un valor por defecto (Data Operations > Make Current Value Default), hacer que sean visibles o no (desde el Diagrama de Bloques Hide/Show Control), etc. Otros elementos del menú dependen del tipo de control, como en los arrays añadir más dimensiones, en los gráficos modificar colores, fuentes, tipos de interpolación, etc.
1.2.2.1. Personalización de controles
Además de los controles disponibles en este menú, se pueden crear otros nuevos mediante la modificación de uno existente. Para ello, primero hay que colocar en el Panel Frontal un control del tipo de datos deseado y después elegir en el menú Edit > Customize Control.

Figura 1-11. Opción para modificar o crear un control
En la parte superior aparece el botón Edit Mode
y Customize Mode
. El primero de ellos hace que el control se comporte como si estuviera en el Panel Frontal. El segundo separará los distintos elementos que componen el control o indicador. Los controles están compuestos de pequeñas partes, por ejemplo un slide numérico tendría Label, escala, display, unidades, botones de incremento y decremento, partes cosméticas, etc. Estas partes también pueden verse y modificarse en Windows > Show Parts Window. En esta ventana se puede ajustar su tamaño y posición.
Cada parte tiene un menú contextual a través del cual se pueden importar/exportar imágenes. Las partes pueden mostrar diferentes gráficos en diferentes momentos. Por ejemplo, un control booleano tiene cuatro gráficos diferentes: estado FALSE, estado TRUE, estado Switch When Released y estado Latch When Released (mientras está presionado el botón). Cuando existe la opción de tener varios gráficos, el menú contextual tendrá la opción Picture Item para seleccionar uno de ellos.

Figura 1-12. Manejo de los elementos que componen un control.
Cuando se coloca un control personalizado en un Panel Frontal no existe ningún vínculo entre el fichero donde el control está definido (*.ctl) y sus instancias empleadas en cada VI donde es usado, se trata de copias independientes. Si una instancia se modifica no afecta al resto.
Las definiciones de tipos y definiciones de tipos estrictos (Type Def. y Strict Type Def.) se usan para enlazar todas las instancias de un control o indicador a un mismo fichero *.ctl. De esta forma, al modificar el fichero se modifican todas las instancias. Se puede elegir el tipo cuando se crea el control mediante una lista desplegable en la barra de herramientas junto al botón Edit/Customize Mode.
Las Type Definitions definen el tipo de datos de un control. Cuando el tipo de datos cambia, todos los controles asociados a él también cambian. En el menú contextual de una instancia se puede seleccionar Auto-Update from Type Def. para desactivar la actualización automática.
Un Strict Type Definition hace que todas las instancias sean iguales, no sólo el tipo de datos sino también en características como el rango, tamaño, color, etc. De esta forma, si se modifica la definición se actualizarían todas las instancias. Desde el menú contextual se puede eliminar el enlace entre la instancia y la definición.
1.2.3. Funciones
Esta paleta es la que se muestra al trabajar sobre el Diagrama de Bloques; con ella se puede acceder a las diferentes funciones, subVI y estructuras disponibles.

Figura 1-13. Paleta de funciones.
Al igual que con el menú de controles, en éste también hay varios submenús que se dividen dependiendo de la aplicación. Las funciones más usadas son las del submenú Programming.
El primero de los submenús de Programming es Structures. Contiene elementos que son equivalentes a las instrucciones de control de los lenguajes convencionales, es decir, son los bucles como WHILE o FOR y la estructura condicional CASE además de otras. Más adelante se dedica un capítulo completo a las estructuras.

Figura 1-14. Paleta de estructuras.
Los siguientes menús de Programming se dividen atendiendo al tipo de datos. Están los datos simples, como los numéricos, booleanos y strings (texto); además de los compuestos, como los clusters y arrays. Cada uno de estos menús tiene funciones para trabajar con esos datos.

Figura 1-15. Paletas de tipos de datos numéricos, booleanos y texto.
Los datos numéricos se dividen en enteros, de coma flotante y complejos, y dentro de cada uno puede haber distintos tamaños. Se puede cambiar de unos a otros mediante el menú contextual > Representation. Si se aplican dos números, por ejemplo un entero y otro flotante a una función, ésta cambiará automáticamente el tipo de los datos (coercion) para que así se pueda operar con ellos. Los booleanos únicamente pueden tener dos valores: Verdadero (TRUE) o Falso (FALSE), por esto son los apropiados para crear botones.
Los tipos de datos compuestos están, como su nombre indica, formados por otros datos, por lo que no se puede hablar simplemente de arrays sino que se debe decir array de números, array de booleanos, etc. Los arrays, también llamados arreglos, son listas ordenadas de valores, mientras que los cluster son un conjunto desordenado de otros datos y son equivalentes a los STRUCT del lenguaje C. Para indicar de qué están compuestos los datos, basta con arrastrar constantes de otros tipos de datos en el interior de los arrays o clusters.

Figura 1-16. Paletas de datos tipo arrays y clusters.
En la figura 1-17 pueden verse varios datos de cada uno de los distintos tipos vistos hasta ahora. En la parte izquierda se representan como controles, en el centro como constantes y en la derecha como indicadores. Obsérvese el sentido de la flecha blanca en el lateral de los controles e indicadores. Ésta sirve para diferenciarlos: si apunta hacia afuera será un control (lectura de datos), y si apunta hacia dentro será un indicador (escritura de datos). Además, el color del control será indicativo del tipo de datos al que corresponde. En el capítulo 3 se estudiarán los tipos de datos en detalle.
Mediante el menú contextual puede accederse a diferentes opciones. Así para los numéricos se puede elegir su formato de representación; para los strings, la forma en que se visualizarán (normal, contraseña, hexadecimal, etc.). En los arrays se les pueden añadir dimensiones, etc.

Figura 1-17. Diferentes tipos de datos.
Al igual que con los controles, el método para usar las funciones y los VI de la paleta de funciones es ‘arrastrar y colocar’. La diferencia entre una función y un VI es que las funciones son objetos con una funcionalidad fija y sin Panel Frontal, mientras que los VI son programas hechos en LabVIEW por National Instruments u otros programadores que sí tienen Panel Frontal. Se puede acceder a un VI haciendo doble clic sobre él. Los VI disponibles están en librerías dentro del directorio %directorio de instalación de LabVIEW%\vi.lib.
Las funciones y VI generalmente operarán sobre una o varias entradas y proporcionarán una o más salidas. A las entradas se les conectará un dato del tipo requerido que puede provenir de un control, una constante o una salida de otro VI o función y a su salida se podrá conectar un indicador o una entrada de algún VI o función.

Figura 1-18. Función And.
1.2.4. Personalización de los menús
Las paletas de controles y de funciones pueden personalizarse y también se pueden añadir nuevos menús basados en VI creados por uno mismo o por otros programadores.
Cuando se crean menús nuevos, por convención, los VI y controles se guardan en %directorio de instalación de LabVIEW%\user.lib y los nuevos menús deben crearse dentro del submenú User Libraries y User Controls.
Para modificar el aspecto de las paletas hay que ir a Tools > Advanced > Edit Palette Set (Figura 1-19). Al hacerlo se abren las dos paletas principales y se puede navegar por ellas de forma normal, pero no se pueden arrastrar los VI y controles al Diagrama de Bloques y al Panel Frontal, sino que en cada VI y submenú aparece un menú desplegable con una serie de opciones que permiten añadir nuevos menús y VI, modificar iconos, hacer que cuando se usen no se añada el VI como subVI sino su código (Place VI Contents), etc.

Figura 1-19. Modificación del aspecto de paletas.
El esquema de los menús de las paletas se guarda en un fichero con extensión *.MNU. Una vez que se han realizado y guardado los cambios, ya están disponibles los nuevos menús y se podrán utilizar como cualquier menú nativo de LabVIEW.

Figura 1-20. Submenú de la librería de usuario.
También en Tools > Options > Control/Function Palettes hay varios parámetros para cambiar su aspecto. Por último hay que decir que si no se encuentra un VI entre todos esos menús o submenús también se dispone de la opción de buscar un VI o control por su nombre mediante el botón Search de la parte superior de las paletas.
Una opción avanzada y poco conocida es personalizar el menú Tools, para lo cual simplemente hay que guardar los programas que queramos que aparezcan en el directorio Project de la carpeta de instalación de LabVIEW.
1.3. Creación de programas
En LabVIEW la programación se realiza en el Diagrama de Bloques. Un programa habitualmente está formado por:
• Controles: sirven de entrada para los datos.
• Funciones, VI y estructuras: realizan una o varias operaciones con esos datos.
• Indicadores: sirven de salida para los datos.
Los datos ‘circulan’ por el programa mediante cables, que sirven para unir unos elementos con otros. Para realizar la conexión se utiliza la herramienta Connect Wire de la paleta de herramientas. Un cable tendrá una única fuente (control, constante o salida de otro elemento) y uno o varios destinos (indicador o entradas de otros elementos) de tipos compatibles. El cableado, en general, debe ser lo más corto posible manteniendo una claridad suficiente. Una opción útil que aparece en el menú contextual de los cables es Clean Up Wire que realiza un trazado automático de la ruta del cable. También se puede hacer un cableado automático de todo el diagrama en Edit > Clean Up Diagram. Un buen trazado del cableado no sólo es más elegante sino que también puede hacer que el programa tenga unas prestaciones superiores en cuanto a memoria utilizada, pero sobre todo será más fácil de mantener y modificar.
En la figura 1-21 puede verse un programa en LabVIEW. Consta de dos entradas, una de tipo string (String de entrada) y otra numérica (Número de entrada). La función String Length (puede encontrarse en el menú Programming > String de la paleta de funciones) obtiene el número de caracteres de la entrada de texto; esa cantidad es numérica y se suma a la otra entrada mediante la función Add (Programming > Numeric). El programa tiene dos salidas: la primera de ellas es una copia duplicada de la entrada tipo string, y la segunda es la suma de la longitud de la anterior más la entrada numérica. Los Label de ambas funciones se han mostrado mediante su menú contextual > Visible Items.

Figura 1-21. Programa sencillo en LabVIEW.
Una vez creado el programa, se deben introducir los datos iniciales en los controles del Panel Frontal, ejecutarlo presionando el botón Run (CTRL+R) y, cuando acabe, ver los resultados en los indicadores. La figura 1-22 muestra la ejecución del programa anterior.

Figura 1-22. Ejecución del programa anterior.
LabVIEW es un lenguaje compilado para obtener un alto rendimiento. Cuando un programa ha sido modificado y se va a guardar o ejecutar, generalmente se recompila. Al compilar el código del Diagrama de Bloques pasa a código máquina. El código compilado hará llamadas a otras funciones de librerías externas (LabVIEW Run-Time Engine) para tareas como dibujar gráficos o acceso a ficheros.
1.4. Flujo de ejecución
Al lenguaje de programación que usa LabVIEW también se le llama lenguaje G. La mayoría de los lenguajes se basan en una programación imperativa, que es simplemente una sucesión de operaciones. Sin embargo, el lenguaje G no usa una programación imperativa sino una ejecución basada en el flujo de datos (dataflow).
Un programa en LabVIEW consiste básicamente en una serie de funciones unidas mediante cables. Los datos ‘circulan’ o ‘fluyen’ por los cables. Una función sólo podrá ejecutarse cuanto tenga disponibles todos los datos que le sirven como entradas. Esta forma de ejecutar un programa favorece el paralelismo y es más apropiada para sistemas multiprocesador y multihilo, y también es más parecida al funcionamiento de los circuitos hardware e incluso al lenguaje VHDL.
A continuación se mostrarán una serie de imágenes para explicar el flujo de ejecución de LabVIEW mediante un ejemplo.
El programa consiste en dos operaciones matemáticas: la suma de dos números y la multiplicación del resultado de ésta por un tercer número. En la figura 1-23 se puede ver el programa y los datos de entrada en el Panel Frontal.

Figura 1-23. Programa para analizar el flujo de ejecución.
El primer paso de la ejecución llevará los datos de los controles Operando A, Operando B y Operando C a las entradas de las funciones Add y Multiply. En la figura 1-24 puede verse que las entradas disponibles están marcadas con un punto. El flujo de datos se indica con flechas en paralelo a los cables.

Figura 1-24. Valores disponibles en las entradas de la función Add.
La función Add tiene disponibles los valores de sus dos entradas; sin embargo, la función Multiply necesita el valor de otra entrada que no tiene disponible. Por lo tanto, en el siguiente paso se podrá ejecutar la función Add pero no Multiply. Cuando se ejecute el resultado de la suma, su valor pasará al indicador A+B, que lo mostrará en el Panel Frontal, y también circulará hasta la entrada que faltaba de la multiplicación, lo que permite que en el siguiente paso pueda ejecutarse.

Figura 1-25. Siguiente paso de la ejecución.
Finalmente, el resultado de la multiplicación pasa al indicador (A+B)*C y el programa finaliza.
En el ejemplo anterior el orden de ejecución es fijado por la forma de cablear unas funciones con otras. En la figura 1-26 pueden verse dos programas. En el primero ocurre lo mismo; el orden de ejecución lo fija el cableado: primero se ejecutará Increment y después Square Root. En el segundo programa hay dos funciones pero entre ellas no existe ninguna dependencia de datos, por lo que no habrá ningún orden en la ejecución de Decrement y Square; podemos considerar que ambas se ejecutan en paralelo.

Figura 1-26. Flujo de ejecución.
1.5. VI y subVI
Los ficheros con los programas creados en LabVIEW se llaman VI (Virtual Instrument). En muchas ocasiones un programa será de un tamaño tal que habrá que separarlo en varios ficheros o habrá alguna sección de código que convenga reutilizarla varias veces. Un VI puede contener a otro de forma que el segundo sería un subVI del primero; el concepto es equivalente a las funciones o procedimientos de un lenguaje tradicional.

Figura 1-27. Programa que utiliza diferentes funciones.
En el ejemplo de la figura 1-27 puede verse un programa que tiene como entradas Límite superior y Límite inferior. Estas entradas se limitan de forma programada a 360 y 0 respectivamente mediante las funciones de comparación Less?, Greater? y Select. A las salidas de las funciones de comparación se obtendrá un valor TRUE si la comparación es cierta y FALSE en caso contrario. Select funciona como un multiplexor: a su salida estará el valor de la entrada superior (T) si la señal de selección (?) es cierta, y el de la entrada inferior (F) si es falsa. Por otra parte, Random Number (0-1) genera un número aleatorio entre cero y uno.
La parte central del programa resta las dos entradas y el resultado lo multiplica por la suma del límite inferior y el número aleatorio. Con esto se consigue generar un número aleatorio comprendido entre los dos límites indicados, que nunca serán mayores de 360 y menores de 0.
Finalmente, este número generado se empleará como el valor de un ángulo en coordenadas polares, y de él se obtendrá la parte real e imaginaria.
En el ejemplo anterior puede ser deseable hacer una función que se encargue de la generación del número aleatorio entre dos límites, es decir, hacer que esa parte del código sea un VI distinto, de forma que pueda ser usado en otras ocasiones. La forma más sencilla de conseguir esto es seleccionando la parte deseada del Diagrama de Bloques e ir a Edit > Create SubVI. Al hacerlo, el código seleccionado será sustituido por el icono de un VI. Con un doble clic sobre este icono se accederá al código de este subVI.

Figura 1-28. Crear un subVI seleccionando una parte del programa.
Otra forma de crear un VI es definiendo de forma manual su interfaz. La interfaz es la forma en que se realizarán las conexiones cuando se use como subVI dentro de otro VI. El primer paso será guardar el VI, después situarse en su Panel Frontal y hacer clic con el botón secundario del ratón sobre el icono del VI (parte superior derecha) para desplegar su menú contextual, como se puede ver en la figura 1-29. En el menú contextual se mostrará el conector, que es el equivalente a la cabecera de las funciones en otros lenguajes. En el conector se podrán añadir o eliminar terminales, que son los lugares donde se conectarán los cables cuando se use como subVI. Para asignar un control o indicador a un Terminal, se debe seleccionar la herramienta Connect Wire y hacer clic en el terminal y en el control o indicador asociado del Panel Frontal. En ese momento el terminal se coloreará indicando el tipo de datos.
Por claridad se recomienda conectar las entradas a la izquierda y las salidas a la derecha. También se puede indicar si la conexión de un terminal debe ser obligatoria (Required), normal (Recommended) u opcional (Optional). En la ventana de ayuda contextual los terminales se mostrarán en negrita, de forma normal u ocultos respectivamente. Si en el Panel Frontal se había indicado algún valor por defecto en alguna entrada, éste será el valor que tenga el control si no se le conecta un cable cuando se use como subVI. Como regla de estilo podemos recomendar que se indique el valor por defecto en el Label de cada control al final entre paréntesis.
Además del terminal, también se puede crear un icono que represente al VI cuando se coloque en el Diagrama de Bloques de otro VI. En la ventana de la figura 1-30, aparecen en la parte izquierda diferentes pestañas con las diversas opciones para hacer el dibujo, y a la derecha el icono y una barra de herramientas típica de un programa de edición de imágenes.

Figura 1-29. Creación del terminal de un subVI.
Esta herramienta se ha mejorado desde la versión 2009. Entre las nuevas mejoras destaca el empleo de capas (pestaña Layers), de plantillas (Templates) y de imágenes prediseñadas (Glyphs).
Dentro de las plantillas podemos hacer que el icono de un VI sea rectangular en lugar de cuadrado. Esto se suele utilizar cuando únicamente hay una en trada o una salida. Yendo un paso más allá, podemos hacer cualquier forma al icono, como triángulos, borrando todo el contenido en el icono fuera del contorno que queramos darle.
Si se está creando una librería o familia de subVI, es conveniente dar a todos un estilo parecido. Para ello, se pueden usar las plantillas que vienen por defecto o crear y emplear nuestras propias plantillas e imágenes prediseñadas (File > Save as > Template).
Por último, hay que decir que puede accederse a opciones especiales haciendo doble clic sobre algunas herramientas, como seleccionar el tipo de letra con doble clic sobre la herramienta de texto, seleccionar todo con la de select o sobre el dibujo del cuadrado.

Figura 1-30. Editor del icono de un VI.
Desde el menú contextual del icono o desde File > VI Properties se accederá a las propiedades del VI, en las que se podrá ver información y modificar parámetros del VI como:
• General: información sobre la versión, cambios sin salvar, etc.
• Memory Usage: espacio que ocupan las distintas partes que componen el VI.
• Documentation: información sobre el VI. Al usarlo como subVI aparecerá en la ventana de ayuda contextual y un enlace a un fichero de ayuda en el que se podría ampliar la información.
• Revision History: configuración e información sobre el historial del VI.
• Editor Options: algunos parámetros que afectan a la hora de crear el VI.
• Protection: permite bloquear y/o proteger con contraseña el código del fichero.
• Window Appearance: configuración de la ventana que mostrará el Panel Frontal al usuario cuando el VI se ejecute.
• Window Size: tamaño de la ventana del programa cuando se ejecute.
• Window Run-Time Position: posición de la ventana del programa cuando se ejecute.
• Execution: afecta a la forma de ejecución de un VI; para más información consultar el tema acerca de multihilo.
• Print Options: configura la forma en que se imprimirá un VI.
Una vez creado el VI, el siguiente paso será usarlo. Para insertar un VI dentro de otro se puede usar el método de arrastrar y soltar desde el directorio donde esté almacenado hasta el Diagrama de Bloques del VI. También se puede usar Select a VI… desde la paleta de funciones o colocar el VI dentro de un menú de la paleta de funciones.
Como puede verse en las propiedades de un VI (File > VI Properties > Memory Usage), internamente un VI se compone de un espacio de datos, el código compilado, el Panel Frontal y el Diagrama de Bloques. Al cargar un VI se llevará a memoria el espacio de datos y el código compilado; el Panel Frontal y el Diagrama de Bloques se cargarán cuando LabVIEW lo considere necesario.
1.5.1. Librerías
Cada VI se guardará como un fichero. Para agrupar varios VI se puede emplear una librería, la cual es otro fichero con extensión *.LLB. Hay varios métodos para crear una librería:
• En un fichero nuevo, File > Save as > New LLB.
• En Tools > LLB Manager > File > New LLB.
También se puede convertir un directorio que contenga VI en una librería y viceversa desde Tools > LLB Manager (Figura 1-31). Después se debe seleccionar el directorio o librería deseado y se elige Edit > Convert. La ventaja de las librerías es que, a la hora de distribuir una aplicación, todos los ficheros necesarios pueden estar empaquetados en un único archivo.
Desde el LLB Manager se podrá marcar uno o varios VI como Top Level, lo que provoca que, cuando la librería se abra desde la línea de comandos, se abra directamente el VI ‘principal’. Una vez creada la librería, se podrá trabajar con ella desde LabVIEW como si fuera un directorio más, pero para el sistema operativo será sólo un fichero.

Figura 1-31. Explorador de librerías.
Además del uso de los ficheros LLB, también se podrán utilizar librerías de proyecto empaquetadas. Estas nuevas librerías sólo se pueden crear dentro del ámbito de un proyecto, que se estudiará en la siguiente sección.
Otra alternativa a la hora de guardar VI es hacerlo en forma de plantillas (templates). Estos ficheros tienen por extensión *.VIT (o *.CTT para controles). Sirven para guardar en ellos códigos o componentes muy habituales. Son exactamente igual que los VI excepto que en lugar de abrirse el fichero de la plantilla, LabVIEW abrirá una copia como un VI.
Finalmente, resta comentar la posibilidad de crear VI Express mediante Tools > Advanced > Create or Edit Express VI...
1.5.2. Polimorfismo
Al igual que en otros lenguajes, LabVIEW también admite el polimorfismo o sobrecarga de funciones, lo que significa que puede haber funciones distintas bajo el mismo nombre. En principio, todos los VI que componen un VI polimórfico pueden tener distinto tipo de datos en las entradas; la función concreta a usar se puede adaptar a las entradas o seleccionarse de forma manual mediante un pequeño menú bajo el icono del VI. Muchas de las funciones y VI disponibles en la paleta de funciones son polimórficos.
Para crear un VI polimórfico debe partirse de los VI que lo compongan por separado; cada una de las instancias del VI polimórfico debe tener la misma interfaz (la misma estructura en el conector), aunque el tipo de datos obviamente puede ser diferente. En la figura 1-32 se muestra el código fuente de tres VI con los que se creará un VI polimórfico.

Figura 1-32. Código fuente de tres VI con los que crear un VI polimórfico.
Una vez creados los VI con sus respectivos conectores, se debe ir a File > New…> Polymorphic VI. En esta ventana se pueden unir todos los VI en un único fichero como se ve en la figura 1-33.

Figura 1-33. Creación de VI polimórfico.
En la figura 1-34 está el resultado. En la parte izquierda se selecciona automáticamente la instancia concreta del VI a usar de acuerdo al tipo de datos que se conecta, y en la derecha se muestra un VI que en su parte inferior tiene un selector para elegir de forma manual la instancia.

Figura 1-34. Utilización del VI polimórfico.
1.6. Proyectos
Antes de LabVIEW 8.x la organización de proyectos de tamaño medio o grande era complicada, siendo exclusivamente responsabilidad del programador que debía ordenar los VI en directorios en el disco (algo que en la práctica no solía ocurrir) e incluir la documentación y el resto de ficheros necesarios.
La versión 8.0 de LabVIEW introdujo la opción de agrupar todos los ficheros en un proyecto. El proyecto consiste en un fichero en formato XML, con extensión *.LVPROJ que indica qué ficheros componen el proyecto y cómo se ordenan. Los ficheros que componen el proyecto pueden ser VI, controles, ficheros de configuración (por ejemplo para crear un ejecutable), documentación o cualquier otro fichero.
La ventana desde la que se gestiona el proyecto se llama Project Explorer, y muestra el aspecto de la figura 1-35.

Figura 1-35. Explorador del proyecto.
Los ficheros se pueden agrupar en directorios. Para organizar el código también puede ser útil emplear librerías. En el contexto de un proyecto estas librerías no deben confundirse con ficheros LLB, ya que simplemente es otro fichero XML con referencias a los ficheros contenidos y la descripción de sus propiedades. Dentro de las librerías los elementos pueden ser públicos (accesibles desde otras partes del proyecto), privados (accesibles sólo desde otros VI de la misma librería) o community (accesibles desde VI de la misma librería o desde VI de otras librerías marcadas como ‘amigos’ en sus propiedades), lo que es útil para distribuir código entre varios programadores. Para definir los elementos públicos o privados hay que acceder a las propiedades de la librería desplegando su menú contextual > Properties > Item Settings/Friends.
La ventana del proyecto se divide inicialmente en tres ítems:
• My Computer: es el target. En él se muestran los ficheros que componen el proyecto y sus propiedades. De cada target descienden Dependencies y Build Specifications. Al programar sobre otras plataformas también aparecerán otros targets, como una PDA o una FPGA.
• Dependencies: muestra las dependencias del proyecto, por ejemplo librerías compartidas (*.dll).
• Build Specifications: guarda la configuración para las distribuciones del proyecto como archivos comprimidos, ejecutables, librerías, instaladores, librerías empaquetadas, servicios web, etc.
La creación de ejecutables y dll se verá en la sección 12.3, mientras que los servicios web en la sección 9.4. Ahora veremos las librerías empaquetadas. Para crear una debemos acceder al menú contextual de la opción Build Specifications del proyecto. En ese momento aparecerá un asistente en el que, a través del menú de categorías, hay que configurar el nombre de la librería, los ficheros que la componen (los VI visibles se marcarán como top level y deben estar en un lvlib), dónde se guardará, etc. El resultado es un único fichero con extensión *.lvlibp que contiene los VI ya compilados.
1.7. Depuración de código
Cuando se hace un programa es normal cometer errores. Depurar es el proceso de encontrar y corregir los fallos del programa. LabVIEW dispone de varias herramientas para ayudar al programador en esta tarea.
La detección de errores de lo que se podría llamar la sintaxis del lenguaje es automática y simplemente consiste en impedir que se ejecute el VI que contiene un error. Para ello, el botón RUN se sustituye por List Errors y se cambia el icono de la barra de herramientas por uno que simboliza una flecha rota
.
Al presionar este botón aparece una lista de los errores y warnings del programa. Al seleccionar un error aparece una descripción en la parte inferior, y haciendo doble clic o presionando el botón Show Error se traslada el foco del programa a la parte donde se ha producido el fallo.
En la figura 1-36 se indica que hay tres errores en el código:
• Una estructura CASE cuyo selector no tiene nada cableado.
• Una función de suma que presenta errores en sus terminales.
• Un cable ‘roto’, más concretamente que no tiene final.

Figura 1-36. Lista de errores de un VI.
Aparte de los errores sintácticos, puede haber otros debido a innumerables motivos. Una herramienta muy usada en este caso es la Highlight Execution, representada por un icono con forma de bombilla de luz en la barra de herramientas. Cuando esta opción está activada, la ejecución se ralentiza y se puede ver el fluir de los datos por el Diagrama de Bloques. Ésta es una de las herramientas más utilizadas en LabVIEW y muy útil para entender el funcionamiento de otros VI.
A veces, en un programa grande puede ser inviable esperar a que el flujo de ejecución llegue a la parte deseada con la Highlight Execution. Para ello se pueden usar los breakpoints o puntos de ruptura. Éstos se seleccionan en la paleta de herramientas y después se presionan sobre el objeto en el que se desea establecer el punto de ruptura. El objeto puede ser un cable o una estructura o una función (Figura 1-37). Al crear un breakpoint aparece una marca roja indicando su situación. En la siguiente imagen se pueden apreciar tres puntos de ruptura: en el interior de la estructura WHILE, en el cable entre el control de x y la función suma y en la propia función.

Figura 1-37. Utilización de puntos de ruptura para depuración de código.
Una vez seleccionados los puntos de ruptura, se puede ejecutar el programa de forma normal y al llegar la ejecución a uno de ellos, ésta se detendrá automáticamente, igual que si se hubiera pulsado el botón Pause de la barra de herramientas (el cual pasa a tener fondo rojo y se transforma en Continue). Cuando se detiene la ejecución, se puede volver a reanudar con la opción Highlight Execution activada.
Desde la versión 8.6 se dispone de un manejador de breakpoints al que se puede acceder desde View > Breakpoint Manager. En este programa se podrán habilitar, deshabilitar y borrar los breakpoints colocados en el programa.
Si lo que se desea es obtener el valor de un dato cualquiera sin detener ni ralentizar la ejecución, se puede usar la herramienta Probe (Figura 1-38). Se puede activar sobre un control, indicador o cable usando la paleta de herramientas o el menú contextual del cable; cuando se coloque aparecerá una ventana flotante llamada Probe Watch Window en la que puede verse el valor del dato seleccionado en tiempo real de cada uno de los probe del programa. Incluso en determinados tipos de datos se puede disponer de una visualización en gráficos o tablas.
Otra utilidad de los probe es crear un Custom Probe con una condición. En ese caso, cuando se cumpla la condición, el VI se detendrá igual que si hubiera un breakpoint. En este caso aparecerá en Probe Watch Window una nueva pestaña en la que definir las condiciones en las cuales parará la ejecución del programa. Por ejemplo, una condición puede ser que el contenido de un indicador sea un determinado valor. Cuando se coloca un probe sobre un cluster de error, esta opción aparece como una nueva pestaña.
También se puede dotar a un probe de características avanzadas programándolas. A esta opción se puede acceder desde el menú contextual de los cables Custom Probe > New… Al crear un Custom Probe aparece un asistente que guía en su creación. Finalmente, se guarda el VI que implementa el Custom Probe. El Custom Probe permite crear un nuevo programa dentro del propio probe que, por ejemplo, puede guardar los valores en un fichero, parar el programa con una condición que no puede ser especificada mediante el método anterior o enviar una alarma por email. El VI creado como Custom Probe dispone de un indicador con el valor del cable y otro control booleano para indicar si se debe detener la ejecución.
Otro método más para detectar errores es revisar el valor de ciertos datos, como los clusters de error que tienen muchas funciones a la entrada y salida; para esto, hay VI especializados en la paleta Programming > Dialog & User Interface.
Los cluster de error son un dato especial. Se componen de tres parámetros:
• Un booleano que indica si hay error o no.
• Un número identificativo del error.
• Un string que contiene una descripción del error.

Figura 1-38. Creación de tres probes y ventana Watch.
Existe una herramienta que amplía la información sobre el error. Puede accederse directamente a ella desde el indicador del cluster presionando con el botón derecho sobre él y eligiendo Explain Error. También se puede ir a Help > Explain Error. En la sección 11.2 se ampliará la información sobre el control de errores en tiempo de ejecución.

Figura 1-39. Información del error detectado.
1.8. Otras herramientas
1.8.1. Snippet
Desde la versión 2009 se pueden hacer capturas de pantalla de tipo snippet. Para hacer una hay que seleccionar una porción de código e ir a Edit > Create VI Snippet from Selection.
Esta herramienta generará una imagen en formato PNG del código seleccionado, pero además es capaz de embeber el propio código en el fichero de la imagen, de tal forma que, al arrastrarse esa imagen al Diagrama de Bloques de un VI, lo que se copiará no será una imagen, sino el propio código.
Esta herramienta es especialmente útil para intercambiar código en foros a través de internet, pero también puede utilizarse para generar una colección particular de trozos de código que sean muy frecuentes.
1.8.2. Opciones
Se puede acceder a las opciones de configuración de LabVIEW a través de Tools > Options. Esta ventana permite modificar muchos parámetros del programa, como:
• Front Panel: varias opciones relacionadas con el Panel Frontal y los controles.
• Block Diagram: manejo automático de errores y otras ayudas al programador.
• Controls/Functions Palettes: formas de presentar las paletas de controles y funciones.
• Environment: opciones de configuración misceláneas.
• Search: habilitar y priorizar lugares donde realizar búsquedas.
• Paths: las rutas del directorio de instalación de LabVIEW, el directorio temporal, librerías, etc.
• Printing: opciones a la hora de imprimir.
• Source Control: selección del servidor de versiones y configuración.
• Menu Shortcuts: modifica y crea atajos de teclado para las opciones de los menús.
• Revision History: permite guardar información cada vez que se guarda un VI.
• Security: permite restringir el acceso a LabVIEW.
• Shared Variable Engine: ordenadores que ofrecen servicios para manejar variables compartidas.
• VI Server: configuración del acceso a VI Server.
• Web Server: configuración del servidor web que incorpora LabVIEW.

Figura 1-40. Ventana de opciones que presenta LabVIEW 2010.
Además de estas opciones, también pueden realizarse más cambios, algunos de ellos no documentados, en el fichero LabVIEW.ini del directorio de instalación de LabVIEW o en el fichero *.ini asociado a los ejecutables creados con LabVIEW (en Linux es el fichero .labviewrc).
1.8.3. Jerarquía
Cuando se usan unos VI dentro de otros, se va creando una jerarquía de VI. En View > VI Hierarchy se puede abrir una herramienta para ver la estructura jerárquica de un programa (ver el ejemplo de la Figura 1-41).
Además de ver la estructura de un proyecto de forma gráfica, también puede ser de utilidad en grandes proyectos para buscar VI.
Una buena jerarquía es la que está organizada por niveles bien definidos: los VI de un nivel se apoyan en los del nivel inmediatamente inferior y no hay ‘saltos de niveles’.

Figura 1-41. Jerarquía de un VI.
Además de la ventana de jerarquía, también se puede acceder a los VI relacionados (por ejemplo, VI padres o subVI) con el que está activo en View > Browse Relationship.
1.8.4. Ventana de navegación
La ventana de navegación es otra ventana flotante que permite ver toda la ventana activa de LabVIEW mediante un zoom automático. Se puede acceder a ella desde View > Navigation Window y puede ser útil cuando el Diagrama de Bloques tiene un tamaño superior a la pantalla (aunque esto es algo que se debe evitar haciendo el programa más modular mediante el empleo de subVI).
1.8.5. Compilación masiva
La compilación masiva es un método que se usa al actualizar la versión de LabVIEW o para recompilar un proyecto con una nueva versión de LabVIEW.
Su función básicamente es abrir todos los VI de un directorio, relinkar y recompilar todos los VI y subVI que contiene. Se puede acceder a esta herramienta en Tools > Advanced > Mass Compile.
1.8.6. Búsqueda y comparación
En el menú Edit > Find and Replace (CTRL+F) se encuentra una herramienta que permite buscar tanto texto como objetos en los VI cargados en memoria. Los objetos a buscar pueden ser VI, funciones, breakpoints, terminales, etc. En la ventana de resultados se pueden sustituir todas las instancias del objeto buscado por otra función.

Figura 1-42. Utilidad de búsqueda y reemplazo.
LabVIEW también dispone de otra utilidad en el menú Tools > Compare destinada a mostrar las diferencias entre dos VI, tanto en su código, su aspecto, jerarquía u otras propiedades. Con estas herramientas simplemente hay que elegir dos VI y su resultado será una ventana en la que se muestran las diferencias, al hacer doble clic sobre una se mostrarán los VI con una marca en la diferencia.
1.8.7. Seguridad
Las opciones de seguridad en Tools > Security permiten controlar el acceso y permisos a algunos recursos, como a VI Server (Tools > Options > VI Server: Browser Access o Proyecto > My Computer > Properties > VI Server: User Access). El sistema es parecido al de cuentas de usuario, grupos y dominios de los sistemas Windows.
Para que un usuario se registre como tal, debe dirigirse a Tools > Security > Login; además, se puede activar la opción Tools > Options > Security > Show the login prompt at LabVIEW startup time para hacer que se pida un usuario cada vez que se inicie LabVIEW.
Algunos módulos de LabVIEW pueden extender las funciones de seguridad a elementos como variables compartidas o librerías de proyectos.
1.8.8. Ejemplos
Una de las cosas más apreciadas de National Instruments es su soporte, tanto en línea como en sus programas. Dentro de la ayuda de LabVIEW ya se ha visto antes la ventana de ayuda contextual. También dispone de muchos documentos que explican detalladamente cada aspecto del programa. Se puede acceder a la colección de ejemplos desde Help > Find Examples.
Puede modificarse la ventana que muestra los ejemplos en Tools > Prepare Example VI for NI Example Finder.
Finalmente, también hay que comentar que desde la página web de National Instruments se pueden descargar numerosos ejemplos, hacer consultas en los foros o solicitar soporte de los ingenieros de NI.
1.9. Ejercicios
1. Buscar los cinco errores que impiden la ejecución en la siguiente figura:

Figura 1-43. Ejercicio 1.
2. Obtener, mediante las funciones del menú Programming > Numeric, el número áureo y su inverso:
. Ejecutar el programa con Highlight Execution activado.
3. Usar el programa del ejercicio anterior como un subVI de uno nuevo para verificar si
.
4. Depura el siguiente programa para encontrar y solucionar el motivo por el que no funciona correctamente.

Figura 1-44. Ejercicio 4.
1.10. Bibliografía
National Instruments, AN 159: LabVIEW Custom Controls, Indicators, and Type Definitions, 2004.
National Instruments, AN 168: LabVIEW Performance and Memory Management, 2004.
National Instruments, LabVIEW Basics I Course Manual, 2000.
National Instruments, LabVIEW User Manual, 2001.
National Instruments, LabVIEW Upgrade Notes Version 8.0, 2005.
National Instruments, LabVIEW Upgrade Notes Version 8.2, 2006.
National Instruments, LabVIEW Upgrade Notes Version 2009, 2009.
Rick Bitter et alt. LabVIEW Advanced Programming Techniques, CRC Press LLC, 2001.
Capítulo 2
Estructuras
Las instrucciones de control son una de las herramientas básicas de todo lenguaje de programación y permiten a un programa ejecutar un código de forma condicional o repetirlo cierto número de veces.
En LabVIEW estas instrucciones son estructuras que encierran en su interior el código al que afectan. Se encuentran en el menú Programming > Structures.

Figura 2-1. Estructuras.
A continuación se va a tratar cada una de las estructuras que aparecen en la figura 2-1.
2.1. SEQUENCE
En los lenguajes tradicionales basados en texto, el orden de ejecución se corresponde con el orden en que las instrucciones están escritas. Ya se ha visto que el sistema de ejecución de LabVIEW sigue el modelo de flujo de datos (dataflow): un nodo necesita tener disponibles los datos en todas sus entradas para ejecutarse. Pero si hay dos nodos en condición de ejecutarse, no se podrá determinar, en principio, el orden de ejecución; esto en la mayoría de casos no será un problema, es más, será incluso beneficioso. No obstante, puede haber ocasiones en las que haya nodos independientes, ambos en situación de ejecutarse, pero se necesita fijar el orden de ejecución de los mismos.
Las estructuras de tipo SEQUENCE sirven precisamente para esto: establecen el orden de ejecución del código que está en su interior.
Su diseño recuerda a los fotogramas de una película. En una película los fotogramas colocados al principio se visualizarán antes que los siguientes, con un orden secuencial. Las estructuras SEQUENCE también tienen fotogramas o frames ordenados, en el interior de cada frame se situará una sección de código. La ejecución comenzará por el primer frame y, cuando acabe, se pasará a ejecutar el siguiente, y así sucesivamente.
Hay dos tipos de SEQUENCE: STACKED SEQUENCE y FLAT SEQUENCE.
La primera era la única disponible en versiones más antiguas de LabVIEW. Tiene un menú en la parte superior donde se indica la numeración del frame que se muestra, el número total de frames que contiene y además da la opción de situarse en otro. En la figura 2-2 se muestran superpuestos los dos frames de la misma estructura.

Figura 2-2. Frames de una secuencia.
El menú contextual puede desplegarse presionando con el botón secundario del ratón en el borde de la estructura. Este menú permite crear frames antes y después del mostrado, además de otras opciones. La opción Sequence Local crea unos ‘túneles’ entre un frame y los demás para compartir datos; en uno de ellos se escribirá un valor (simbolizado con una flecha hacia el exterior del frame) y en los posteriores podrá leerse (con una flecha hacia el interior); no se podrá leer en frames anteriores al de escritura porque evidentemente el dato en el túnel aún no ha sido escrito.

Figura 2-3. Sequence Local: permite compartir datos entre frames.
También pueden pasarse y recibirse datos desde una estructura SEQUENCE al exterior a través de túneles, representados mediante un pequeño cuadrado en el borde de la estructura. Cuando hay un dato de salida, sólo un frame de la estructura puede escribir valores en él.
FLAT SEQUENCE funciona de igual forma, sólo que es más visual: los frames se ven uno a continuación del siguiente, el orden de ejecución será de izquierda a derecha. En este caso no hay Sequence Local y los datos podrán cablearse directamente desde un frame a otro a través de túneles. El menú contextual también será el que permita añadir y eliminar frames. Se puede cambiar de un tipo de SEQUENCE a otro de forma automática.
En la figura 2-4 puede verse una estructura FLAT SEQUENCE con dos frames: en el primero se genera un número aleatorio y se pasa al segundo frame a través de un túnel.

Figura 2-4. FLAT SEQUENCE con dos frames.
2.2. CASE
La estructura CASE es el equivalente a varias de los lenguajes basados en texto: IF, SWITCH y TRY. Su utilidad es ejecutar un código u otro dependiendo de una condición.
Al igual que una estructura SEQUENCE, en este caso también se tiene un menú en la parte superior donde se puede elegir el subdiagrama que se muestra. En este menú se puede ver la condición para ejecutar el código del subdiagrama correspondiente. En la figura 2-5 se muestra un CASE con dos subdiagramas: uno se ejecutará cuando la condición que se evalúa sea FALSE y otro cuando sea TRUE.

Figura 2-5. Estructura CASE.
El terminal que aparece en el lado izquierdo marcado con el símbolo «?» es llamado selector. El valor que llega a este selector es la condición que se evalúa para seleccionar el subdiagrama a ejecutar. Si el tipo de datos que se conecta al selector del CASE es booleano, éste actuará como una sentencia IF… THEN…ELSE de un lenguaje de texto tradicional. También pueden conectarse otros tipos de datos; en este caso actuará como un SWITCH…CASE. Pueden conectarse al selector datos booleanos, numéricos (incluidos enum y ring), strings y clusters de error. En la figura 2-6 pueden verse todos estos casos.

Figura 2-6. Tipos de datos que son válidos en un CASE.
Para un selector booleano sólo se tendrán dos casos: verdadero o falso. Para numéricos, la condición será que el dato del selector sea igual al mostrado en el menú del CASE, para datos enum o ring se puede escribir el nombre del ítem en lugar del valor numérico. Con los strings sucede lo mismo que con los numéricos enum o ring: el valor mostrado en el menú aparecerá como un texto encerrado en comillas dobles. En caso de conectar al selector un cluster de error, cambiará el color del borde de la estructura, y los subdiagramas se ejecutarán dependiendo de si el selector marca un error o no.
Cuando el selector se conecta a un string o a un dato numérico, es obligatorio tener algún caso que se ejecute por defecto, es decir, debe haber un caso que se ejecute cuando en el selector haya un valor que no esté asignado explícitamente a algún subdiagrama. Para hacer que un subdiagrama sea ejecutado por defecto, debe seleccionarse Make This The Default Case en el menú contextual.
Se pueden agrupar varios casos en los CASE numéricos y strings escribiendo los valores separados por comas. Por ejemplo, en el caso de strings se podría escribir «“texto1”, “texto2”».
Además, en los numéricos se puede asignar un rango de valores; por ejemplo, si se desea ejecutar el mismo código cuando la entrada tiene el valor 4, 5, 6, 7 y 8, en el menú del CASE se escribirá «4..8». También se puede combinar esta técnica con la anterior.
Para pasar datos a los subdiagramas del CASE se utilizarán los túneles. En el caso de datos de salida, todos los subdiagramas deben proporcionar un valor; hasta que esto no ocurra LabVIEW indicará el error missing assignament to tunnel y aparecerá el túnel con el interior vacío, como se muestra en la parte derecha de la figura 2-7. También existe la opción de marcar sobre el túnel Use Default If Unwired, con lo que se consigue que se asigne el valor por defecto para todos aquellos casos en los que no se ha cableado un valor en el túnel de salida. Finalmente, a través del menú contextual del túnel se pueden cablear de forma automática los túneles en todos los subdiagramas con la opción Linked Input Tunnel.

Figura 2-7. Error en un CASE: túnel vacío.
Por último, también hay que decir que se puede cambiar el orden de los subdiagramas desde la opción Rearrange Cases… del menú contextual.
2.3. WHILE
El bucle WHILE repetirá el código de su interior hasta que se cumpla una condición, que es evaluada en cada iteración.
En la figura 2-8 puede verse el aspecto de este bucle, en el que se aprecian dos terminales:
• El terminal de iteración es el cuadrado azul con el símbolo «i». El valor de este terminal es un número entero que irá aumentando en una unidad por cada iteración del bucle, empezando a contar desde cero.
• La condición de stop es el terminal verde de la esquina inferior derecha de la imagen. A este terminal se podrá conectar bien un valor booleano, bien un cluster de error. A través del menú contextual podrá elegirse para los booleanos que el bucle se detenga cuando el valor sea TRUE (Stop if True) o FALSE (Continue if True). En el caso de los cluster de error sucede algo parecido con Stop on Error y Continue while Error.

Figura 2-8. Estructura WHILE.
Otra de las opciones que muestra el menú contextual es Add Shift Register. Esta herramienta añade dos terminales a cada lado de la estructura, terminales que sirven para transferir un valor desde una iteración del bucle a la siguiente. Los valores se pasarán a la siguiente iteración en el terminal de la derecha y se leerán en el de la izquierda. Si se conecta un valor al terminal de la izquierda en el exterior de la estructura, éste será el valor inicial que circulará por ese cable en la primera iteración.
En la figura 2-9 (a) se muestra el uso de un shift register. En la primera iteración se leerá el valor 10 del terminal de la izquierda, se le sumará 1 y se escribirá en el terminal de la derecha el valor 11. En la siguiente iteración este valor será el leído en el terminal de la izquierda, después se le volverá a sumar 1 y así sucesivamente hasta que se cumpla la condición de parada del bucle, en este caso que el número sea mayor o igual a quince. Este proceso puede comprobarse ejecutando el código de la figura con la opción de Highlight Execution activada.

Figura 2-9. (a) Shift register en un bucle WHILE. (b) Varios shift registers.
El shift register de la izquierda puede extenderse para mostrar más terminales. El terminal superior tendrá el valor que se escribió en la derecha en la iteración anterior; el siguiente terminal tendrá el valor que se escribió en la derecha dos iteraciones antes, y así sucesivamente. El concepto es similar al retraso (z-1) en procesado digital de señales.
En el ejemplo de la figura 2-9 (b) se puede ver un ejemplo del uso de varios shift register. La tabla 2-1 muestra los valores que tendrán cada uno de los terminales en cada iteración.

Tabla 2-1. Valores de los shift register en cada iteración.
Hay una utilidad que funciona de igual forma que un shift register: es el Feedback Node. Éste consta de dos terminales:
• El terminal inicializador permite dar un valor inicial al nodo, y equivale a conectar un valor al terminal izquierdo de shift register. Este terminal se coloca siempre en el borde izquierdo de la estructura a la misma altura que el Feedback Node.
• El Feedback Node es el otro terminal y tiene forma de flecha. En el extremo derecho se le conectará la salida, cuyo valor será leído por el extremo izquierdo en la siguiente iteración. En la primera iteración el valor leído por el extremo izquierdo será el conectado al terminal inicializador. Lógicamente, siempre se ejecuta primero la lectura y después la escritura.
El código de la figura 2-10 ilustra el uso del Feedback Node. El programa es igual al de la figura 2-9 (a).

Figura 2-10. Feedback Node.
En el menú Tools > Options > Block Diagram se tiene la opción de insertar automáticamente Feedback Nodes dentro de los bucles cuando sea necesario.
A través del menú contextual de los Feedback Nodes o de su panel de propiedades podremos configurar su aspecto, uno como el mostrado en la figura 2-10, y otro con z-1 (menú contextual > Z-Transform Delay Node), lo cual puede ser más natural en programas de procesado de señal. También se puede configurar cómo se le da el valor inicial (al compilarse el VI, al cargarse en memoria o al comenzar el bucle), el sentido de los datos (derecha a izquierda o al revés), mostrar un terminal de habilitación o el número de iteraciones que hay que esperar antes de que el Feedback Node comience a actuar.
Al igual que en otras estructuras, los datos que entren y salgan de una estructura WHILE lo harán a través de túneles. Una opción muy interesante de los túneles de los bucles (WHILE y FOR) es el Autoindexing, que se puede habilitar a través del menú contextual del túnel cuando se quiera trabajar con arrays. Cuando se cablea un array desde el exterior al interior de un bucle y se habilita el autoindexing, los valores leídos en el interior del bucle de ese terminal serán los elementos que componen el array, uno por cada iteración, en este caso la estructura funciona como un FOREACH. Cuando el autoindexing está en una salida del bucle ocurre lo contrario: se construirá un array cuyos elementos serán los generados en cada iteración.
En la figura 2-11 puede verse un programa parecido al anterior. En el borde derecho pueden verse tres terminales: el superior es un túnel, el siguiente un shift register y el inferior una salida indexada. El valor de los dos primeros será el generado por el programa en su última iteración (15), mientras que el valor en el último terminal será una lista ordenada de todos los números generados en cada iteración (11, 12, 13, 14 y 15).

Figura 2-11. Ejemplo de túnel, shift register y autoindexing.
2.4. FOR
El bucle FOR es muy parecido al WHILE. También repite el código de su interior un número de veces pero, a diferencia del anterior, este número es fijado a priori y no puede cambiarse una vez empiece a ejecutarse.
Consta de dos terminales numéricos:
• El terminal de iteración se sitúa igual que en el bucle WHILE, está en el interior de la estructura y se va incrementando en una unidad por cada iteración empezando desde cero.
• El terminal de cuenta está colocado en la esquina superior izquierda de la estructura simbolizado con una «N». En él se conectará un valor numérico que será el que fije el número de repeticiones del bucle.

Figura 2-12. Estructura FOR.
Además, desde la versión 8.5 de LabVIEW se puede añadir un tercer terminal a través del menú contextual. Este terminal es el de condición que también tiene el WHILE para detener el bucle. Su utilización sería equivalente al empleo de un BREAK en el interior de un FOR. Hay que advertir que el rendimiento del bucle FOR puede disminuir al emplear el terminal de condición.
Todo lo dicho en la explicación del bucle WHILE respecto a los shift register, los Feedback Nodes y la salida indexada también es válido para el FOR. Tanto en el menú contextual del WHILE como del FOR existe la opción de sustituir uno por el otro.
Los túneles indexados son la opción por defecto en los bucles FOR. Cuando se cablea un array de forma indexada como entrada, puede obviarse el terminal de cuenta porque se toma el tamaño del array como el número de veces que se ha de repetir el bucle; es equivalente a un FOREACH.
Si por cualquier motivo, como cablear un cero al terminal de iteraciones o una entrada indexada con un array vacío, provocara que el bucle FOR no tuviera ninguna iteración, a la salida aparecerán los valores por defecto.
2.5. EVENT
La estructura EVENT fue introducida por primera vez en la versión 6.1 de LabVIEW. Es una estructura muy útil en VI con los que interactúa el usuario porque mejora la eficiencia del programa.
Al igual que la estructura CASE, los EVENT tienen varios subdiagramas y un menú en la parte superior para cambiar el que se muestra. En este menú también se indica una condición que hace que el código del subdiagrama correspondiente se ejecute. La diferencia con CASE es que EVENT detiene la ejecución del hilo del programa hasta que se da esa condición, es decir, congela el programa hasta que ocurre un evento.
En la esquina superior izquierda hay un terminal llamado Event Timeout que se usa en el evento por defecto: el Timeout. El código del diagrama para el evento timeout se ejecutará cuando pase el número de milisegundos indicados en el terminal Event Timeout.

Figura 2-13. Estructura EVENT
Para añadir más subdiagramas hay que proceder de igual manera que con CASE, es decir, a través del menú contextual. Cada diagrama debe tener asociados uno o varios eventos, que se configuran desde la ventana Edit Events, como puede verse en la figura 2-14.
En esta ventana primero se muestra el número del diagrama (Events Case) y, a continuación, la lista de los eventos que pueden dar lugar a la ejecución del diagrama (Event Specifiers). Para añadir o eliminar eventos se usan los botones de abajo (Add Event y Remove). Para definir un evento primero hay que especificar su fuente en Event Sources, los eventos de las secciones Application y This VI están predefinidos y son acciones típicas como cerrar la ventana, presionar una tecla, etc. La fuente de eventos Dynamic sólo está disponible cuando se activa Show Dynamic Event Terminals y se cablean convenientemente. Para más información puede consultarse el tema dedicado a programación multihilo en la sección 10.5; Panes se activa cuando el Panel Frontal se divide en varias partes, Splitters sólo se activa cuando hay uno de estos elementos en el Panel Frontal y, finalmente, Controls muestra una lista de todos los controles que hay en el VI. En Events se podrá elegir el evento concreto asociado a la fuente seleccionada.
En este ejemplo se ejecutará el diagrama únicamente cuando el control llamado Boolean cambie de valor.

Figura 2-14. Ventana de configuración de eventos.
En la parte izquierda de cada subdiagrama de la estructura EVENT se sitúa Event Data Node que aporta información sobre la fuente del evento, por ejemplo una referencia al control, su valor actual, el valor anterior al evento, etc.
Si se observa con detalle la figura 2-14 puede verse que hay eventos marcados con una flecha roja que acaban en una interrogación y otros con el mismo nombre pero con una flecha verde y sin interrogación. Los que tienen la flecha roja e interrogación se llaman filtros. Los filtros se ejecutan antes que la acción asociada al evento, pudiendo, entre otras cosas, desactivarlo. Cuando se selecciona un filter event aparecerá automáticamente un nodo en la parte derecha del diagrama llamado Event Filter Node.
El ejemplo de la figura 2-15 muestra el uso de los filter events para descartar un evento; en este caso sirve para impedir que el usuario cierre el Panel Frontal del VI.

Figura 2-15. Ejemplo de uso de filtros en un evento.
Finalmente hay que citar algunas recomendaciones que hace National Instruments sobre el uso de la estructura EVENT, como no usar un EVENT dentro de otro ni dos EVENT dentro del mismo bucle.
Si un EVENT se inserta dentro de un WHILE y éste acaba mediante un botón (un botón de stop del tipo latch when released/pressed) con un evento asociado a dicho botón, se debe insertar el botón dentro del subdiagrama asociado al cambio de valor de dicho botón. La razón es asegurarse que la evaluación de la condición del bucle se realice después de la ejecución del EVENT. En caso de que el botón de stop se conectara directamente al terminal de condición del WHILE, quedaría en paralelo con el EVENT; el problema es que el EVENT congelará su ejecución hasta que se produzca un evento (por ejemplo un cambio de valor en el botón de stop); mientras esa parte del programa está congelada se evaluaría la condición de terminar el código del evento con el valor del botón de stop en reposo, es decir, se evalúa la condición antes de presionar el botón y, por tanto, antes de ejecutar el EVENT; el efecto es que serían necesarias dos pulsaciones del botón para detenerse.
Los cambios de valor en controles de forma programada no generan eventos (en eventos registrados de forma estática), sólo se generan cuando se realiza el cambio de valor mediante la interfaz de usuario. Una excepción es cuando se escribe empleando la propiedad Value (signaling).
En esta sección se han estudiado únicamente los eventos estáticos, pero hay dos tipos más: eventos dinámicos y definidos por el usuario. Éstos se explicarán en el tema sobre programación multihilo en la sección 10.5.
2.6. TIMED LOOP y TIMED SEQUENCE
TIMED LOOP es una estructura que apareció en la versión 7.1. Su funcionamiento consiste en repetir el código de su interior con unas determinadas especificaciones de tiempo o hasta que se cumpla cierta condición, por lo que es muy usado en aplicaciones de tiempo real.
Su dibujo recuerda a un WHILE circundado por un halo azul, aunque a diferencia de éste, no es necesario establecer una condición de parada o continuación. También presenta varios nodos, que son (de izquierda a derecha):
• Input: permite configurar el funcionamiento del bucle por medio de un asistente o cableando los datos. Este nodo puede extenderse para cablear más datos de los mostrados.
• Left Data: proporciona información sobre la iteración anterior, por ejemplo el tiempo que ha tardado (Iteration Duration), si le ha dado tiempo a acabar la tarea antes de que empiece una nueva ejecución (Finished Late? [i-1]) y otras.
• Right Data: se trata de una configuración dinámica que permite modificar los parámetros de configuración de la estructura para la siguiente iteración. Los parámetros son prácticamente los mismos que en el Input Node, excepto el nombre del bucle y el origen del reloj.
• Output: al igual que el Left Data Node, el Output Node proporciona información, pero en este caso la información se genera después de que el bucle se haya detenido.

Figura 2-16. Estructura TIMED LOOP.
La configuración puede realizarse cableando los valores adecuados en el Input Node o mediante los parámetros que aparecen haciendo doble clic sobre el primer y tercer nodo.

Figura 2-17. Ventana de configuración del TIMED LOOP.
A continuación se verán algunas de las principales partes mostradas en la figura 2-17.
Loop Timing Source es la fuente de reloj. Cuando el programa se ejecute sobre un PC, la fuente será un reloj de 1 kHz del sistema operativo; si la ejecución se realiza en otros dispositivos, puede haber otras fuentes disponibles. Para seleccionar la fuente desde el programa puede usarse el VI Create Timing Source, en la paleta Programming > Structures > Timed Structures. Los otros VI de esta paleta permiten abortar programadamente un TIMED LOOP (Stop Timed Structure), sincronizarlos (Syncronize Timed Structure Starts), controlar el comienzo de cada uno (Build Timing Source Hierarchy) y crear, disparar y borrar una origen de reloj.

Figura 2-18. Estructuras temporizadas.
El periodo será el tiempo que pase entre el comienzo de dos iteraciones, y el offset el tiempo que el bucle espera antes de empezar la primera ejecución. Deadline es el tiempo máximo con que cuenta el bucle para ejecutar el código en su interior; en caso de que la ejecución lleve más tiempo se avisará con Finished Late? [i-1]. Los modos de Action on Late Iterations configuran la forma en que el bucle responde cuando la ejecución lleva más tiempo del especificado; las opciones son autoexplicativas, básicamente sirven para ‘alinear’ el comienzo de las ejecuciones y ‘saltar’ iteraciones. En sistemas multinúcleo también se puede elegir con qué procesador se ejecutará el bucle con Processor Assignment.
Para ejecutarse, cada TIMED LOOP crea su propio sistema de ejecución que contiene un único hilo (ver capítulo 10 dedicado a multihilo). La prioridad se refiere a la preferencia de ejecución entre un bucle y los demás. Los valores más altos corresponderán a TIMED LOOP con mayor prioridad. Cuando dos o más bucles vayan a empezar su ejecución en un instante determinado, la prioridad de cada bucle determinará el orden. En caso de que haya varios con el mismo nivel de prioridad, el orden será comenzando por el de menor tiempo invertido en la ejecución.
También se pueden añadir frames a un TIMED LOOP mediante el menú contextual del mismo modo que se hacía con un SEQUENCE, con lo que un TIMED LOOP podrá ejecutar varios subdiagramas secuencialmente, cada uno con sus propias especificaciones temporales.

Figura 2-19. TIMED LOOP con frames.
En este caso el Right Data Node de un frame cambiará los parámetros del siguiente subdiagrama a ejecutarse en lugar de la siguiente iteración y el Left Data Node proporcionará información sobre el subdiagrama anterior.
El Left Node del primer subdiagrama y el Right Node del último proporcionan más parámetros que el resto debido a que pueden afectar al siguiente/anterior subdiagrama o también a toda la estructura.
En el mismo menú, junto a TIMED LOOP se encuentra una estructura parecida llamada TIMED SEQUENCE. La principal diferencia con el TIMED LOOP es que en este caso no se repite la ejecución de los subdiagramas, por lo tanto no se podrá especificar un periodo, pero aun así sí puede tener otras características temporales como offset, deadline, etc.

Figura 2-20. Estructura TIMED SEQUENCE.
2.7. DISABLE STRUCTURE
Estas estructuras también han aparecido recientemente, pues se usaron por primera vez en la versión 8.0. Sirven para comentar el código, por lo que son muy útiles en la depuración de programas. Hay dos tipos de estructuras de deshabilitación: la incondicional y la condicional.
La incondicional es una estructura que, como CASE, STACKED SEQUENCE o EVENT, se compone de varios subdiagramas. Uno de ellos estará habilitado y el resto estarán deshabilitados; lógicamente el habilitado será el único que se ejecute, y el resto no llegarán a compilarse. Para cambiar el subdiagrama habilitado hay que hacer uso del menú contextual de la estructura.

Figura 2-21. Estructura DISABLE.
La condicional funciona de la misma forma que la anterior, excepto que el diagrama habilitado se selecciona de forma automática dependiendo del valor de unos símbolos asociados al proyecto. Algunos símbolos predefinidos son OS y CPU.
También se pueden definir nuevos símbolos en la ventana de propiedades del proyecto correspondiente, como muestra la figura 2-22.

Figura 2-22. Ventana para crear símbolos.
Una vez creados los símbolos pertinentes, hay que editar la condición de la estructura con Edit Condition For This Subdiagram… del menú contextual. Se puede añadir más de una condición con el botón «+».

Figura 2-23. Configuración de la condición DISABLE.
2.8. FORMULA NODE
La estructura FORMULA NODE puede encontrarse tanto en el menú Programming > Structures como en Mathematics > Scripts & Formulas.
A diferencia de las anteriores, FORMULA NODE no controla el flujo de ejecución, sino que evalúa una expresión matemática escrita como texto con una sintaxis parecida al lenguaje C.
El texto consistirá en una serie de sentencias finalizadas por el símbolo «;». Las sentencias normalmente son asignaciones que usan operadores o funciones, aunque también pueden ser declaraciones de variables, bucles o sentencias de condición. También pueden insertarse comentarios de la misma manera que en C.
Casi todas las funciones que se pueden usar dentro de un FORMULA NODE tienen su equivalente como VI. Éstas son: abs(x), acos(x), acosh(x), asin(x), asinh(x), atan(x), atan2(x,y), atanh(x), ceil(x), ci(x), cos(x), cosh(x), cot(x), csc(x), exp(x), expm1(x), floor(x), getexp(x), gamma(x), getman(x), int(x), intrz(x), ln(x), lnp1(x), log(x), log2(x), max(x,y), min(x,y), mod(x,y), pow(x,y), rand( ), rem(x,y), si(x), sec(x), sign(x), sin(x), sinc(x), sinh(x), sizeOfDim(array,dim), spike(x), sqrt(x), step(x), tan(x), tanh(x).
Los operadores son:
• ** exponenciación.
• +, -, ++, - -, !, ~ suma y resta, pre-post incremento-decremento, negación lógica y complemento de bits.
• *, /, %, +, - operaciones básicas.
• ≫, ≪ desplazamientos.
• <, >, >=, <=,!=, = = comparaciones
• &, ^, |, &&, || operaciones lógicas.
• …?...:… evaluación condicional.
• = asignación.
• Las estructuras son:
• If (condición) sentencias1 else sentencias2
• do sentencias while (condición)
• while (condición) sentencias
• for (asignación; condición; sentencia) sentencias
• switch (condición) lista de casos
Otras palabras reservadas son: break, case, continue, default, pi. Para más información puede consultarse la ayuda.
Presionando el botón secundario en los bordes de la estructura pueden crearse variables de entrada o de salida.
En el ejemplo dado en la figura 2-24 se tiene la fórmula de Herón para calcular la superficie de un triángulo a partir de sus lados; en ella hay tres entradas (a, b, c) que corresponden a la longitud de los lados del triángulo, un resultado intermedio que se trata como una salida más (p) y una salida que es la superficie (S).

Figura 2-24. Fórmula de Herón.
2.9. Scripts
Al igual que el nodo FORMULA NODE, hay otras estructuras que también aceptan textos, de forma que se puede combinar la programación propia de LabVIEW con la más tradicional programación textual.
El MathScript Node es otra de las nuevas estructuras de LabVIEW 8.0 y mejorada en 8.20. A diferencia de otras, MathScript Node no llama a otros programas, y su código es compilado junto con el resto del VI.
En Tools > MathScript Window… puede abrirse una ventana (Figura 2-25) para ayudar a depurar el código. En el campo Command Window pueden escribirse los comandos, también puede ejecutarse un script completo desde la pestaña Script y los resultados pueden verse en la pestaña Variables.

Figura 2-25. Depuración de código con MATH SCRIPT.
La sintaxis que se usa en la estructura MathScript Node es en gran medida compatible con la de MATLAB. Dispone de cientos de funciones que pueden consultarse en la ayuda.
Por otra parte, MATLAB Node también se encuentra únicamente en el menú Mathematics > Scripts & Formulas > Script Nodes. Esta estructura llama al servicio «Matlab Server» a través de ActiveX para ejecutar los comandos (sólo en Windows).
En versiones antiguas también existía XMath Script Node, que utilizaba el programa MATRIXx de National Instruments, similar a MATLAB, pero en las versiones actuales ya no se mantiene.
Al igual que con FORMULA NODE, se deben crear variables de entrada y salida, pero en este caso hay que asignar explícitamente el tipo de datos, tal y como puede verse en la figura 2-26.

Figura 2-26. MathScript Node y MATLAB Script Node.
En la figura 2-27 pueden verse estas dos estructuras. El código de ambas es equivalente: se trata del diseño de un filtro y su aplicación a una señal aleatoria. Obsérvese el parecido de los códigos.

Figura 2-27. Implementación de un filtro utilizando las dos estructuras.
2.10. Ejemplos
2.10.1. Ejemplo I: Filtro promediador
2.10.1.1. Explicación teórica
Los filtros integradores o promediadores son un tipo de filtro paso bajo. Se emplean para reducir el ruido de una señal suponiendo que éste es de frecuencia mucho más alta que la propia señal. También se aplica en la edición de imágenes para añadir difuminados.
El funcionamiento es sencillo: calcular el promedio de una señal en un intervalo determinado.
En un promediador móvil existe lo que se llama ‘ventana’, que indica el tamaño del intervalo a promediar. En un sistema discreto este tamaño serán las muestras de la secuencia de entrada que se promediarán. Una ventana móvil avanza en cada paso del algoritmo una posición en la secuencia de entrada para realizar el promediado como se muestra en la figura 2-28.

Figura 2-28. Ventana móvil para realizar el promediado.
Matemáticamente puede expresarse la ecuación en diferencias del sistema como:

Donde:
y(n): valor calculado.
N: tamaño de la ventana.
x: señal de entrada.
k: índice para recorrer los valores a promediar.
Aplicando la propiedad del desplazamiento temporal, se puede hallar la transformada Z del sistema definido por la ecuación anterior y, a su vez, de esta nueva expresión se obtendrá la estructura del filtro.

La estructura de la figura 2-29 se ha particularizado para un orden igual a tres.

Figura 2-29. Filtro de orden tres.
2.10.1.2. Código
Para realizar este ejemplo se implementará un filtro promediador móvil de orden tres que añada un difuminado a una fotografía.
La entrada será una imagen en blanco y negro con formato BMP que consiste en una lista (array) de valores de cada uno de los componentes de la imagen. Estos componentes son RGB (Red, Green, Blue), por lo que para cada punto habrá tres valores. La siguiente lista representa cómo sería este array para una imagen con cuatro puntos:
R1, G1, B1, R2, G2, B2, R3, G3, B3, R4, G4, B4...
Como la imagen sólo contiene grises, los valores de los tres componentes son iguales, por lo tanto bastará con aplicar el filtro a sólo uno de los componentes o lo que es lo mismo, a uno de cada tres valores.
El programa empezará leyendo el fichero y desglosando su información; entre esta información se obtendrá un array RGB como el anterior. Por simplicidad se trabajará en una dimensión; una mejora sería actuar por filas y luego otra vez por columnas.
La función Decimate 1D Array con un tamaño de tres elementos obtendrá en la primera de sus salidas un array cuyos elementos sean los de las posiciones 1, 4, 7, 10, 13… del array de entrada, es decir, obtendrá todos los valores del componente R de cada punto.
Después los valores R de cada punto son promediados: el promediador sumará el último valor leído y los tres anteriores; el resultado se dividirá por cuatro. La ‘ventana’ se implementará mediante shift registers y el resultado se irá indexando en el lateral del FOR.
Para reconstruir la imagen basta con hacer el proceso inverso al de decimar: interpolar. Finalmente, se dibujan las imágenes antes y después de aplicar el filtro.
Mediante la señal selector se podrá elegir el tipo de estructura implementada. Si el selector está activado el filtro será como en la figura 2-29, y si no está activado habrá realimentación de las salidas a la entrada (IIR).

Figura 2-30. VI que implementa un filtro promediador móvil de orden 4.
2.10.1.3. Resultado
Para explicar el efecto del filtro se puede decir que proporciona cierta resistencia o inercia al cambio en la imagen. En la figura 2-31 puede verse el resultado del filtro sobre una fotografía.

Figura 2-31. Resultado del filtro sobre una fotografía.
La figura 3-32 es un detalle del principio de la fotografía después de aplicar el filtro usando realimentación de salidas a la entrada para magnificar este efecto. En ella se ve cómo hay una serie de puntos negros en la esquina que en la original no estaban, esto se debe a la condición inicial del filtro (hay que esperar a que todos los shift registers tengan datos que provengan de la foto).

Figura 2-32. Detalle de los primeros puntos que pasan por el filtro y del borde izquierdo.
Otro efecto que se puede apreciar es que la parte derecha de la imagen parece extenderse en la izquierda; es decir, la línea del horizonte de la parte derecha se puede ver que se extiente por la parte izquierda también. Esto se produce porque se ha considerado la imagen como un único array; para solucionar esto se podría aplicar el filtro a cada una de las filas.
Es aconsejable ejecutar este ejemplo con la opción Highlight Execution activada, pues de esta manera se puede ver un ejemplo práctico del uso de los shift registers y la salida indexada en los bucles.
Como puede intuirse, este método se puede generalizar para otros tipos de filtros FIR e IIR.
2.10.2. Ejemplo II: Generación de números primos
2.10.2.1. Explicación teórica
Como todo el mundo sabe, un número primo es el número natural que sólo es divisible por él mismo y por la unidad.
Desde los tiempos de Euclides (300 a. de C.) se sabe que existen infinitos números primos. Los primeros algoritmos para encontrar números primos también proceden de la época de los antiguos griegos, como la «criba de Eratóstenes». Desde entonces ha pasado mucho tiempo, pero aún se sigue investigando en este campo. Por ejemplo, en el año 2004 se creó otro algoritmo que mejora el anterior llamado «criba de Atkin».
La generación de números primos es una herramienta muy interesante en campos como la criptografía, donde algunos algoritmos como el RSA usan números primos de valores altos como base para realizar el cifrado.
2.10.2.2. Código
En este ejemplo se optará por un algoritmo que sea lo más sencillo posible, consistirá en hacer un barrido de números empezando por el dos hasta el límite indicado por el usuario, es decir, se recorrerán los números 2, 3, 4, 5, 6, 7, 8… Para cada número se volverá a hacer un barrido hasta encontrar un número que sea divisor del primero. Si estos dos números son iguales significa que el único divisor de este número es él mismo, por lo tanto es un número primo. En la figura 2-33 puede verse un diagrama de flujo de este algoritmo. Nótese que en este algoritmo, al igual que en muchos otros, se obvia el número uno.
El código para implementar este algoritmo con el nodo FORMULA es exactamente igual que en lenguaje C. En primer lugar, se declararán las variables y, a continuación, se usan dos bucles FOR, uno para cada barrido.
Para determinar si un número es divisor del otro, se comprobará si el resto de la división entre ambos es igual a cero. Finalmente, los números primos encontrados se almacenan en un array.

Figura 2-33. Algoritmo para la obtención de números primos.

Figura 2-34. Implementación del algoritmo en un VI.
2.10.2.3. Resultado
La figura 2-35 muestra un array con los números primos que hay entre dos y quince como resultado de la ejecución del programa anterior.

Figura 2-35. Números primos obtenidos.
En este ejemplo se han aprendido dos cosas: en primer lugar a usar el FORMULA NODE, y en segundo lugar a darse cuenta de que, a pesar de su nombre, esta estructura no sólo sirve para introducir fórmulas matemáticas sino que también se pueden emplear en ella elementos de un lenguaje de programación como bucles, funciones, etc.
2.10.3. Ejemplo III: Bingo
2.10.3.1. Explicación teórica
En esta ocasión se desea realizar un programa que genere un cartón para jugar al bingo. Para esto se necesita crear una matriz o array de dos dimensiones que contenga valores aleatorios entre 1 y 100. Habrá diez columnas (una para cada decena) y cuatro filas.
2.10.3.2. Código
El programa principal tiene la típica estructura de un WHILE y un EVENT.
El evento que se muestra en la figura 2-36 corresponde al cambio de valor de un botón llamado cartón. Este botón tiene por acción mecánica Latch When
Released (se verá en el próximo capítulo). Cuando se presiona el botón se ejecutará el subdiagrama.
Para generar un cartón se necesitan dos bucles; el primero recorrerá cada una de las decenas. Como la cantidad de ejecuciones es conocida, se usará un FOR. Dentro de este bucle habrá otro; este segundo bucle será un WHILE, y en él se generarán números de forma aleatoria entre dos límites, límites que sirven para acotar los números dentro de la decena correspondiente.
La función Random Number (en Programming > Number) devuelve números entre el 0 y el 1. Para generar números enteros se multiplicará por 10 y se sumará el límite inferior; el valor resultante se aproxima al entero superior. En caso de que el número ya haya sido generado antes se descartará; si no había sido generado, se almacenará en un array. Para detectar si el número ya se había generado, se busca dentro del array si algún valor coincide con el nuevo mediante Search 1D Array (en Programming > Array). Si no había ninguno, la salida de esta función contendrá el valor «-1» y se ejecutará el diagrama mostrado en el CASE. Para cualquier otro número se ejecutará el caso por defecto que simplemente deja pasar el array de la entrada a la salida.
Cuando se han generado cuatro números, se detiene el WHILE, se ordenan los números del array con Sort 1D Array y se pasa a la siguiente decena.
Después de recorrer todas las decenas, en la salida indexada del FOR se habrá generado un array de dos dimensiones con los números de un nuevo cartón.
La estructura EVENT contiene otro subdiagrama para cuando se presione el botón Stop. Este subdiagrama únicamente contiene una constante booleana con el valor TRUE cableada a la condición del WHILE principal. Puede pensarse que sería más sencillo dejar vacío este caso y cablear directamente el botón de Stop a la condición, pero no es recomendable porque, como se ha explicado en la sección 2.5, esto provocaría que la condición del bucle se evaluara antes de presionar el botón, con lo que el valor que tiene en ese instante obligaría a ejecutar otra iteración más.

Figura 2-36. VI para la generación de cartones para jugar al bingo.
2.10.3.3. Resultado
En la figura 2-37 puede verse un cartón de bingo generado con el programa de este ejemplo. Cada vez que se presione el botón Cartón, se generará un nuevo cartón.

Figura 2-37. Cartón obtenido de la ejecución del VI.
Este ejemplo muestra el uso de las estructuras EVENT, WHILE, FOR y CASE. Por una parte, se ha visto cuándo se debe usar FOR (con un número de ejecuciones conocido) y cuándo WHILE (cuando no hay un número determinado de ejecuciones); también se ha visto el uso de la estructura EVENT y el típico problema del botón de Stop y, por último, se ha utilizado un CASE dejando un caso por defecto. En el próximo capítulo se explicarán más detalladamente los arrays.
2.11. Ejercicios
1. ¿A qué es equivalente el siguiente programa?

Figura 2-38. Ejercicio 1.
2. Realizar un programa que calcule el factorial de un número. Hacerlo primero con estructuras de repetición y luego con FORMULA NODE.
3. Realizar un programa que genere un array con el primer millón de números enteros mediante un bucle WHILE y un FOR. ¿Cuál es más eficiente? ¿Por qué?
4. Modificar el programa del ejemplo I para que también acepte imágenes a color.
5. Realizar mediante programación visual el mismo algoritmo que en el ejemplo II. Comparar ambos programas en términos de velocidad y espacio.
6. Añadir al evento del ejemplo III otro caso cuando se presione un botón llamado bola. El subdiagrama debe simular el funcionamiento de un bombo para jugar al bingo, debiéndose tener en cuenta no repetir las bolas que ya habían sido sacadas. Opcionalmente también puede añadirse algún mecanismo para detectar si la bola que ha salido estaba en el cartón. (Pista: ver en el siguiente capítulo las propiedades de los controles.)
2.12. Bibliografía
J. Kodosky, E. Pérez, AN 039: Linear System in LabVIEW, National Instruments, 1993.
National Instruments, AN 200: Using the Timed Loop to Write Multirate Applications in LabVIEW, 2004.
National Instruments, LabVIEW Basics I Course Manual, 2000.
National Instruments, LabVIEW User Manual, 2001.
Rick Bitter et al. LabVIEW Advanced Programming Techniques, CRC Press LLC, 2001.
Capítulo 3
Tipos de datos
3.1. Tipos de terminales
En el Panel Frontal se pueden colocar controles e indicadores. Como se ha dicho en los capítulos anteriores, los controles son el punto de entrada de información al VI y los indicadores son las salidas. Los controles e indicadores tendrán su representación en el Diagrama de Bloques, donde podrán ser cableados para leer o escribir su valor.
En el Diagrama de Bloques, además, se podrán tener tambiéiran constantes. En el menú contextual de estos elementos hay opciones para cambiar de un tipo a otro. En la figura 3-1 se ha marcado la opción para cambiar un control de tipo string a un indicador del mismo tipo.

Figura 3-1. Tipos de terminales básicos y cómo cambiar de un tipo a otro.
Dependiendo del tipo de datos y del tipo de terminal se tendrán unas opciones u otras; las más importantes para cada tipo de datos son:
En los controles de tipo booleano se tiene la opción de Mechanical Action, que les permite actuar como pulsadores (latch) o interruptores (switch) (ver figura 3-2).

Figura 3-2. Interruptores y pulsadores.
Los numéricos pueden acotar el rango de entrada con Data Range y modificar varias opciones de visualización con Format and Precision.
En los controles e indicadores de tipo string se puede ver el contenido de forma normal, representado por unos códigos (ver tabla 3-1), como asteriscos o por su valor hexadecimal.
Junto a un terminal pueden aparecer varios ítems. En la figura 3-3 se muestra un control numérico con los siguientes ítems:
• Label: es un texto que da un nombre al terminal en el Panel Frontal. Esta etiqueta se empleará para identificar al elemento en variables, propiedades, etc.
• Caption: es otro texto asociado al terminal que sólo puede aparecer en el Panel Frontal.
• Incremento/decremento: en los terminales numéricos también se dispone de este elemento para aumentar o disminuir el valor del dato.
• Radix: indica el formato de visualización en los terminales numéricos enteros. Las opciones son: en decimal (d), octal (o), hexadecimal (x), binario (b) y en notación del sistema internacional (p).
• Valor: es el valor que hay en el terminal representado en el formato elegido.
• Unidades: el tipo de datos numérico también puede tener un símbolo que represente sus unidades.

Figura 3-3. Ítems de un Terminal.
Para terminales de otro tipo habrá otras opciones, incluso para terminales con el mismo tipo de datos pero con diferente forma también cambiarán estas opciones. En la figura 3-4 pueden apreciarse tres controles numéricos con formas diferentes.

Figura 3-4. Controles numéricos con diferente formato.
En el Diagrama de Bloques se representan los indicadores con una flecha en el lado izquierdo que apunta hacia el terminal simbolizando que el dato entra en el indicador desde el código, y los controles con una flecha a la derecha apuntando hacia afuera. Estos terminales pueden verse como icono o con su aspecto clásico, como se aprecia en la figura 3-5.

Figura 3-5. Símbolos de los terminales.
La visualización con forma de icono permite ver la forma del terminal, lo que puede ayudar a identificarlo; por otra parte, la vista clásica ocupa menos espacio y será la utilizada en el resto del libro. En Tools > Options > Block Diagram puede cambiarse la forma en que LabVIEW dibuja los terminales por defecto.
3.2. Tipos de datos
Una de las primeras cosas que se aprende en cualquier lenguaje de programación son los tipos de datos disponibles, bucles y estructuras de bifurcación. Las dos últimas ya se vieron en el capítulo anterior; en este se verán los tipos de datos. No debe confundirse tipo de datos con tipo de terminal. Cuando se habla de tipo de datos, se hace referencia a si son numéricos, cadenas de caracteres, booleanos, etc.
El tipo de datos se representa en el Diagrama de Bloques por el color del terminal y del cable; así, un dato booleano tendrá terminales y cables verdes para diferenciarlo de un string, que será rosa.
A continuación, se estudiarán los distintos tipos de datos con sus características más destacables, transformaciones de unos a otros y la forma en que los distintos tipos de datos de LabVIEW se almacenan en memoria. Esto último es algo a tener en cuenta en algunas ocasiones muy concretas, como pueden ser:
• Llamar a código externo.
• Trabajar con ficheros binarios.
• Otras funciones I/O.
3.2.1. Boolean
Los datos de este tipo sólo pueden tener dos posibles valores: verdadero (TRUE) o falso (FALSE), debido a lo cual suelen usarse en controles con forma de botón o pulsador y como resultado al evaluar una expresión.
Cada dato booleano se almacena en memoria en un byte completo. Si este byte tiene todos sus bits a cero, el dato tendrá el valor FALSE y cualquier otro valor del byte hará que el dato pase a TRUE.

Figura 3-6. Dato booleano.
En los controles booleanos se puede seleccionar su Mechanical Action, como mostraba la figura 3-2. Ya se dijo que los switch son equivalentes a interruptores y los latch a pulsadores. En los dibujos de la figura 3-2, que representan cada acción, hay unas letras a la izquierda: «m», «v» y «RD». La primera de ellas («m») simboliza la acción mecánica que hace el usuario sobre el control, es decir, la pulsación. La segunda («v») representa el cambio de estado que sufre el control, se indica el valor del terminal. Finalmente, la tercera («RD») sólo se puede ver en los de tipo latch y alude al momento en que, desde el Diagrama de Bloques, es leído el valor del control. Como puede verse en los iconos tipo latch, el control tiene un ‘estado estable’ y uno ‘aestable’; cuando se activa el control se pasa del estado estable al aestable durante un tiempo, y vuelve al estable inmediatamente después de que el control sea leído.
3.2.2. Numeric
Al trabajar con datos numéricos hay que distinguir entre números enteros, números racionales y complejos.
Los números enteros tienen asociado el color azul y puede elegirse su tamaño (8, 16, 32 o 64 bits), si se emplea un bit de signo (signed o unsigned) y su representación (binario, octal, decimal, hexadecimal).
Los controles de tipo Enum y Ring también son numéricos. Se trata de listas que asocian una cadena de caracteres a un valor numérico. Mediante su menú contextual se puede acceder a Edit Item para definir la lista de valores. La principal diferencia entre estos dos tipos es que en los enum el texto se considera también parte del control, por lo que para conectar un control o constante enum a un indicador enum se necesita que ambos tengan ítems con los mismos nombres, mientras que con los ring no es necesario.
En la figura 3-7 pueden verse dos constantes, el símbolo que aparece a su izquierda (una letra «d» o «x») es su representación, este ítem puede activarse con la opción radix en el menú contextual Visible Items; la primera constante es decimal y la segunda hexadecimal. La primera está conectada a un indicador que visualiza datos con signo de 32 bits (I32), mientras que la segunda lo está a un indicador sin signo de 16 bits (U16).

Figura 3-7. Constantes con diferente representación.
Los números racionales y complejos tienen asociado el color naranja. Siguen el estándar IEEE; el tamaño es de 32 bits para los de precisión simple, 64 bits para los de doble precisión y el tamaño de los extendidos depende del procesador: puede ser 128, 96, 80 o 64 bits (en procesadores más antiguos sobre Windows el más habitual solía ser 80, en procesadores actuales es 128). El primer bit siempre es el signo, los siguientes el exponente, que es de tamaño 7, 10 y 14 bits respectivamente, y finalmente la mantisa. Los complejos simplemente son dos números racionales de uno de los tamaños anteriores.

Figura 3-8. Representación en memoria de números en coma flotante.
En la figura 3-9 puede verse un número complejo, dos constantes con los valores y e y tres constantes que se conectan a tres indicadores, cada uno de la precisión indicada.

Figura 3-9. Diferentes tipos de valores numéricos.
En la versión 8.5 de LabVIEW también se introdujeron los números en coma fija, que se representan con color gris. El espacio de memoria para almacenarlos es de 64 bits, y debe configurarse para indicar cómo se deben interpretar los valores que contengan.
Para configurar un dato en coma fija hay que dirigirse a la pestaña Properties > Data Type del menú contextual como aparece en figura 3-10. La configuración consiste en indicar cuántos bits se dedican en memoria a almacenar el número (Word length) y cuántos se deben usar para interpretar la parte entera (Integer word length). Por ejemplo, si ambas cantidades son iguales y de valor 8, sería equivalente a un entero de tamaño 8 bits y el valor máximo que se puede representar sin signo es 255 (FFh en memoria). Si se cambia Integer word length a 7 y el valor y Word length se mantienen en 8, el valor entero máximo será 127 y el bit que queda se utilizará para representar valores decimales; al tener sólo un bit para esto sólo se puede representar el 0,0 y 0,5. Si, por el contrario, Integer word length es 9 el valor más alto representado será el 510 (FFh en memoria), pero el salto de uno a otro será de dos unidades, es decir, el valor en memoria 00h será el valor 0 y el 01h será el valor 2. Los valores máximo y mínimo que pueden ser representados y el incremento de uno al siguiente (delta) se pueden ver en Range.
En las funciones numéricas, como Add o Multiply, se puede acceder a Properties > Output Configuration donde se obtiene un menú similar al de la figura 3-10 en el que, además, se indica el tipo de redondeo.
La principal utilidad de los números en coma fija se da en aplicaciones para FPGA.

Figura 3-10. Configuración de un control de coma fija.
Las conversiones entre tipos de datos numéricos suelen ser automáticas. Cuando se hace esto, se representa mediante un pequeño punto de color rojo (coercion dot) justo donde el cable se conecta al indicador, como se puede ver en el indicador de precisión simple de la figura 3-9 (en versiones anteriores a la 8.20 el color por defecto es gris, pero puede cambiarse en Tools > Environment > Colors). También existen funciones para forzar el tipo de conversión de un dato numérico a otro.
Otra característica interesante es la opción de usar unidades junto con los números. Así en la figura 3-11 se puede ver la resta de 1 V y 1 mV; el resultado mostrado en el indicador será 0,999 V.

Figura 3-11. Resta de dos números utilizando unidades.
Otro tipo de datos es el de tiempo (Time Stamp). Éste es un conjunto de cuatro enteros de 16 bits, de los que los dos primeros indican el número de segundos transcurridos desde el 1 de enero de 1904 a la 1:00:00; los otros dos enteros son las fracciones de segundo.
3.2.3. Strings
Los strings son cadenas de caracteres. LabVIEW asocia el color rosa a este tipo de datos. En memoria se almacenan como un puntero de cuatro bytes que apunta a una estructura compuesta por el tamaño y a continuación los valores de los caracteres. Así, el texto «hola» se almacenará como «0000 0004 686F 6C61», siendo 4 el tamaño y 68h, 6Fh, 6Ch y 61h los códigos ASCII de cada una de las letras del texto anterior.

Figura 3-12. Representación en memoria de strings.
En la figura 3-13 puede verse una constante con el contenido «texto» y otras dos constantes que representan un string vacío y un retorno de carro.

Figura 3-13. Strings.
Aunque los strings están destinados a texto, pueden contener valores que no tienen un carácter de representación. Esto puede comprobarse cambiando la forma de representación a hexadecimal a través del menú contextual o con la opción visible Items > Display Style. Al visualizar los códigos se verán los caracteres precedidos de una barra inversa, desde /00 a /FF; algunos códigos representan caracteres especiales; éstos se escriben siempre en minúsculas, y son:
| Código | Descripción |
| \b | Retroceso (BS) |
| \f | Fin de documento (FF) |
| \n | Fin de línea (LF) |
| \r | Retorno de carro (CR) |
| \t | Tabulador |
| \s | Espacio |
| \\ | Barra invertida |
| %% | Porcentaje |
Tabla 3-1. Códigos especiales.
3.2.3.1. Transformaciones número-texto
Las transformaciones de números a texto y viceversa son las más usadas en LabVIEW. Muchos VI trabajan con datos de tipo texto (strings) como, por ejemplo, los de GPIB, sin embargo puede ser que los datos que se envían o reciben sean números y necesiten convertirse para realizar algún cálculo con ellos.
En la paleta Programming > String > String/Number Conversion se encuentran varios VI para realizar estas transformaciones.

Figura 3-14. Paleta de conversión número/texto.
En la primera fila se encuentran las funciones para pasar de número a texto, la segunda fila es para pasar de texto a número. El formato de los números puede ser entero o de coma flotante, decimal, hexadecimal u octal. Los dos VI de la derecha son Format Value y Scan Value. Estos VI tienen un terminal llamado format string en el que se puede especificar el formato de la conversión; para más información sobre la sintaxis de este string, consultar la ayuda de LabVIEW.
En el menú string también aparecen dos funciones llamadas Scan From String y Format Into String. Estos VI realizan funciones de escaneo, transformaciones y concatenación. Básicamente son una herramienta configurable con la que transformar números y textos a otro formato. Todos los VI de esta ‘familia’ trabajan de forma parecida a la función sprintf del lenguaje C. Tienen un terminal común, el format string que sirve para configurar la transformación mediante unos códigos precedidos del símbolo «%». Los códigos más habituales son: «%s» para strings, «%f» para números en coma flotante, «%.xf» para indicar el número de decimales de los números flotantes, «%d» para enteros decimales, etc.
Por ejemplo, si se desea construir un texto que diga: «He medido 3,64 voltios en la entrada 2.», siendo 3,64 y 2 entradas numéricas, se podría usar el código de la figura 3-15.

Figura 3-15. Ejemplo VI para el manejo de cadenas y texto.
En el ejemplo anterior se usa un string inicial que contiene «He medido», también hay tres entradas más: un número en coma flotante, un string y un número entero. El format string se muestra en la parte superior e indica el formato de cada entrada mediante %XY, donde X es la longitud e Y el tipo de datos; además, en este string también se puede incluir texto.
Para configurar este tipo de VI se puede recurrir a la documentación para conocer la sintaxis del terminal format string. También se puede usar un asistente haciendo doble clic sobre el icono y rellenando los campos de forma correcta (Figura 3-16).
La mayoría de estos VI trabajan con expresiones regulares. Las expresiones regulares son un método para representar patrones. Se suele aplicar a textos para encontrar y manipular cadenas de texto que cumplan una determinada condición. Utilizan un complejo sistema para codificar estos patrones que puede consultarse en la ayuda de LabVIEW.
Otros VI para transformar texto en números y viceversa son los que se encuentran en Programming > Numeric > Conversion, concretamente String To Byte Array y Byte Array To String. Estos dos VI no sólo convierten un carácter de texto a un número o viceversa, sino que lo hacen con cadenas de caracteres; por lo tanto, son idóneos para transmitir y recibir señales a través de interfaces como TCP o el puerto serie.

Figura 3-16. Asistente del Format string.
3.2.4. Path
Los path sirven para indicar rutas relativas o absolutas a directorios o ficheros, tanto de la máquina local como de otra en red.
Al guardarse en memoria, se almacena un puntero a una estructura que contiene las partes que forman la ruta (subdirectorios); cada parte de la ruta se guarda como un string. De esta forma se consigue que la ruta sea independiente de la plataforma, es decir, la misma ruta podría ser entendida por LabVIEW funcionando sobre Windows, Unix o MacOS.
En la figura 3-17 se puede ver una constante que se conecta a un indicador y abajo dos nodos que representan a la ruta del VI que lo contiene y la ruta del directorio de datos de LabVIEW.

Figura 3-17. Utilización de un path.
3.2.5. Arrays
Los arrays o arreglos son un conjunto de datos ordenados y de un tipo determinado; de esta forma no puede hablarse simplemente de array, sino de array de booleanos, array de strings, etc.
El cableado de un array es del mismo color que el tipo de datos que contiene, pero más grueso y, en el caso de los numéricos, el cableado es de doble línea para dos o más dimensiones. En el terminal del Diagrama de Bloques aparecerá el tipo de datos entre corchetes.
Junto a los terminales (control, indicador o constante) de cualquier tipo de array aparecen uno o varios índices numéricos que indican el primer elemento que se muestra (nótese que no afecta al contenido, sólo a la visualización, es como un scrollbar). Estos índices también indican la dimensión del array, de manera que si sólo hay uno será de una dimensión, si hay dos índices será de dos dimensiones y así sucesivamente.
Al guardarse en memoria se guarda un puntero a una estructura que consta, en primer lugar, del tamaño de cada dimensión en cuatro bytes por cada una, y a continuación van los datos guardados en el formato que se corresponda al tipo de datos.

Figura 3-18. Representación en memoria de un array de dos dimensiones.
En la figura 3-19 hay cuatro arrays: el primero es uno vacío, al cual para asignarle un tipo, hay que arrastrar una constante a su interior; debajo de éste hay un array numérico de una dimensión; el de la parte superior a la derecha es otro array numérico, pero éste de dos dimensiones (tiene dos índices); el último es un array de strings que muestra los elementos a partir del segundo (el índice tiene un valor de 1).
Al usar un array no hay que declarar el tamaño de cada dimensión, como en otros lenguajes, como C; el array puede re-dimensionarse cuando sea necesario.

Figura 3-19. Diferentes tipos de arrays.
Los arrays, gráficas y tablas pueden exportarse desde el menú contextual al portapapeles, DIAdem o a Excel (si están instalados).
3.2.6. Clusters
Al igual que los arrays, los clusters son un tipo de datos estructurado. También son un conjunto de datos, pero a diferencia de los anteriores, los cluster pueden contener datos de varios tipos en su interior, incluidos otros clusters. El concepto es equivalente a los STRUCT en C o a los RECORD en Pascal.
La figura 3-20 muestra un cluster vacío, otro compuesto de un entero y un array de strings, y otro con un booleano, un entero y un string. Este último cluster es un tipo especial porque es el usado para describir errores (desde la versión 8.20 se muestra por defecto con un color marrón).

Figura 3-20. Clusters.
Para crear un cluster se coloca uno vacío y se arrastran a su interior otros tipos de datos. El orden de los datos en su interior es el mismo en el que se añaden, aunque puede variarse yendo a la opción Reorder Controls In Cluster del menú contextual. En el capítulo sobre la creación de la interfaz se explicará el funcionamiento de esta utilidad cuando se hable sobre el orden de tabulación, sección 13.3.2.
Si se crea un cluster cuyos datos son todos del mismo tipo, podrán aplicarse sobre el cluster las mismas operaciones que podrían aplicarse sobre los elementos de forma individual, como muestra la figura 3-21 (nótese el coertion dot). Por ejemplo, si se crea un cluster cuyos datos son numéricos se podrá sumar a otro número.

Figura 3-21. Suma entre un entero y cluster con los datos numéricos.
Los clusters son recomendables cuando en un subVI hay una gran cantidad de conexiones, pues al emplearlos se pueden agrupar y hacer la conexión más sencilla.
Para facilitar la visualización de constantes de tipo cluster en el Diagrama de Bloques, se pueden visualizar como iconos desde la versión 2010 a través de su correspondiente menú contextual.
3.2.7. Waveforms
Los waveforms son parecidos a los clusters. Se componen de otros datos, que son:
• t0: es un time stamp que indica el momento correspondiente al primer valor.
• dt: es la diferencia de tiempo entre dos valores consecutivos; se supone que ésta no varía.
• Y: un array que contiene los valores del eje vertical.
• attributes: otra información que puede añadirse.

Figura 3-22. Waveforms.
3.2.8. Referencias
Las referencias (refnum) o manejadores se usan para simbolizar un objeto externo al programa; éste puede ser el contenido de un fichero (no confundir con path), una conexión TCP, otro VI, etc.
Normalmente lo primero que se hace con las referencias es abrir una conexión y luego se irá pasando secuencialmente la referencia entre los VI, nodos de propiedades o métodos que trabajan con ese objeto.
En memoria se almacenan como números de 32 bits.

Figura 3-23. Algunos tipos de referencias.
3.2.9. Variant
Los variant son un tanto especiales, pues no se trata de un tipo de datos en concreto y, además, pueden incluir propiedades. Se usan cuando los datos pueden cambiar de tipo; por ejemplo, un dato que unas veces pueda ser numeric y otras string. También se usan al trabajar con objetos ActiveX.
Además del dato, contienen el tipo, como puede verse en la figura. Para esto, hay que elegir la opción Show Type en el menú contextual del indicador.

Figura 3-24. Variant.
En memoria se almacena como un puntero a una estructura. La estructura reserva cuatro bytes para codificar el tipo de dato.
3.2.10. Dynamic
Este tipo de datos se utilizan con los VI Express. Incluye datos y atributos asociados con una señal, como el nombre, fecha, etc. Los tipos de datos dynamic, al igual que los waveform, enfatizan el hecho de que los datos son medidas y no sólo números en una columna.
3.3. Manipulación de datos
3.3.1. Manipulación de bytes y bits
Las funciones más sencillas para manipular datos son las que trabajan con bytes y bits. Éstas se encuentran en la paleta Programming > Numeric > Data Manipulation.

Figura 3-25. Paleta para manipulación de datos.
En la segunda fila de este menú están los VI para realizar rotaciones y desplazamientos. La tercera fila tiene VI para dividir y unir bytes en words y viceversa, además de intercambiar la parte alta y baja de un entero, mientas que los de la primera fila son más avanzados y se verán a continuación.
3.3.2. Otras transformaciones
En la figura 3-26 pueden verse las funciones Flatten To String y Unflatten From String.
La primera, Flatten To String (traducido como aplanar), es una función que convierte todo el bloque de memoria que ocupa el dato en un string. Se puede elegir que la información estructural también se tenga en cuenta con el terminal prepend array or string size? y el formato de los datos con byte order (bigendian, little-endian y el nativo de la plataforma). La entrada anything puede ser cualquier tipo de datos.
Unflatten From String realiza el proceso inverso. En el menú contextual se puede indicar que manejen los datos como lo hacía LabVIEW en otras versiones.

Figura 3-26. Distintas funciones para manipulación de datos.
A diferencia de los variant, cuando se aplana un dato se pierde información sobre el tipo de datos origen, no tienen formato, por lo que al ‘des-aplanar’ es necesario indicar el tipo de datos destino.
Por ejemplo un array de números de 8 bits se almacena como una ‘cabecera’ consistente en un número de 32 bits que indica su longitud y tantos bytes como elementos tenga el array. Al aplanar este array, si no se indica nada en el terminal prepend array or string size?, se considera todo como parte de los datos resultando un string cuyo valor es el resultado de anexar la ‘cabecera’ y los datos e interpretarlo todo como un texto. Si este array fuese de dos elementos con los valores 1 y 2, el resultado es un string cuyo valor hexadecimal es «0000 0002 0102».
Algunas funciones realizan el aplanado de datos automáticamente. También puede ser necesario al trabajar con librerías externas.
3.3.3. Type Cast
Hay dos tipos de conversión: la coercion (implícita) y la cast (explícita). La primera la hace automáticamente el compilador. Para realizar la segunda hay que utilizar alguna instrucción. Antes se han visto varias para transformar de un tipo concreto a otro tipo también concreto. Type Cast es la función más genérica para transformar datos de un tipo a otro, y puede verse en la figura 3-27.
El terminal x es el dato a convertir y type el tipo al que será convertido. Por defecto, si no se conecta nada, lo considera string. Esta función no realiza ningún tipo de comprobación, así que al usarla hay que tener en cuenta que las entradas type y x sean compatibles, si no el resultado puede no ser el esperado.

Figura 3-27. Función type.
Por ejemplo, este VI podría sustituir a String To Byte Array si en el terminal type se conecta un array de enteros de ocho bits.
En el menú contextual también existe la opción de manejar los datos como se hacía en la versión 4 de LabVIEW, lo que afecta únicamente a los datos booleanos porque en esta versión los guardaba ocupando únicamente un bit y desde la versión 5 ocupan un byte completo.
3.4. Variables y propiedades
3.4.1. Variables locales
Para llevar un dato desde/hacia un control/indicador se han usado cables. A veces no se puede realizar el cableado, como en bucles que funcionan en paralelo, y otras veces sencillamente se quiere escribir en un control o leer de un indicador. Para todas estas situaciones sirven las variables locales.
Las variables locales están asociadas a los controles e indicadores presentes en el Panel Frontal de un VI, mediante ellas se puede leer y escribir el valor de ese control o indicador.
Para crear una variable local se puede ir a la paleta Programming > Structures > Local Variable y después asociar esa variable a uno de los controles o indicadores que existen mediante el menú contextual > Select Item (Figura 3-28).

Figura 3-28. Variables locales.
También pueden crearse directamente a partir del Terminal. Para ello, en el Diagrama de Bloques se despliega el menú contextual y se selecciona Create > Local Variable, de la forma que indica la figura 3-29.

Figura 3-29. Creación de variables locales desde el propio Terminal.
Una vez creada la variable, se puede indicar si se usará como lectura o escritura (por defecto es escritura) con Change To Read o Change To Write. Que la variable local sea de lectura o escritura es independiente de si el terminal es un control o un indicador, que es lo que permite escribir sobre un control y leer un indicador.
Las variables locales tienen un alcance que comprende al VI en el que está colocado el control o indicador al que hacen referencia. No pueden usarse fuera de ese VI.
Como conclusión, hay que decir que en general es preferible cablear las señales porque sigue el modelo de Dataflow. Las variables locales sólo deben usarse cuando sea necesario, porque pueden aparecer problemas de no inicialización de datos, condiciones de carrera, etc.
3.4.2. Variables globales
Las variables globales se usan para compartir información entre distintos VI que se ejecutan en la misma máquina. Lo podemos considerar como un espacio de memoria compartido por varios VI.
Pueden crearse también desde la paleta de estructuras, arrastrándolo al Diagrama de Bloques y haciendo doble clic, o también desde File > New… > Global Variable.

Figura 3-30. Creación de variables globales.
Conceptualmente son como un VI normal pero sin código. Una vez abierta la variable global, hay que colocar en su Panel Frontal un control o indicador del tipo que se desee. La información se almacenaría en este nuevo ‘VI sin Diagrama de Bloques’ y podría ser usada en los VI que hagan referencia a la variable. Para usar la variable global se aplica el mismo método que para cualquier subVI.
Si se añaden varios controles o indicadores a la variable global, podrá elegirse en concreto cuál quiere usar mediante el menú contextual eligiendo la opción Select Item. Al igual que con las variables locales, también debe indicarse si se va a leer o escribir.
Como se ha dicho antes, el alcance de una variable global llega a cualquier VI. Cuando se use en varios VI a la vez, hay que prestar atención porque pueden aparecerse problemas de condición de carrera. Para más información puede consultar el capítulo 10. Una forma de evitar estas situaciones es lo que se denomina ‘variable global funcional’, que consiste en ‘embeber’ la variable global en un subVI que, además del valor, también tiene una entrada para indicar la operación: lectura o escritura.
3.4.3. Variables compartidas
Las variables compartidas (Shared Variable) introducidas en la versión 8 de LabVIEW son una forma más genérica aún de compartir información. En este caso la comunicación puede ser entre programas que pueden estar en ordenadores distintos. También se suelen utilizar para comunicar dispositivos como un compactRIO o un FieldPoint con un PC.
Con las variables compartidas el traspaso de información entre los distintos programas es transparente para el programador, que no tiene que preocuparse por el protocolo de transmisión y otros detalles.
Para utilizar variables compartidas hay que crearlas dentro de una librería de un proyecto y, a través de su menú contextual, configurar sus propiedades.
Se estudiarán en la sección 9.2.2.
3.4.4. Nodos de propiedades y métodos
Los nodos de propiedades sirven principalmente para controlar el aspecto y el estado de un terminal del Panel Frontal de forma programada, es decir, desde el Diagrama de Bloques.
Todos los terminales llevan asociadas una serie de propiedades. Hay algunas comunes como visible o Position, otras varían dependiendo del tipo de terminal.
Para crear un nodo de propiedad puede hacerse de la misma forma que para una variable local: en la opción Create del menú contextual, como indica la figura 3-31. De esta forma, se crea un nodo de propiedad para ese terminal; para seleccionar la propiedad en concreto, se hará desde el menú contextual del nodo. Al igual que con las variables, se deberá elegir si son de lectura o escritura. El nodo puede expandirse y albergar otras propiedades. En este caso el orden de ejecución es de arriba hacia abajo.

Figura 3-31. Creación de un nodo de propiedad.
Hay cientos de propiedades, así que no se pueden explicar todas en este libro, pero en la ayuda contextual se ofrece una breve descripción de cada propiedad. Algunas de las más usadas son: Visible, Position, Disabled y Bounds. La propiedad Value sirve para leer y escribir el valor de un control, pero debe evitarse y sustituirse por una variable local (o mejor aún, un cable) cuando sea posible, pues los nodos de propiedad no son tan eficientes como una simple variable. Se verán las razones en la sección 14.4.3.7.
En el menú de la figura anterior también puede verse Invoke Node, que sirve para crear un nodo muy parecido al de propiedades, en este caso de métodos. Estos nodos no están relacionados con el aspecto, sino que efectúan alguna acción, como por ejemplo reiniciar a los valores por defecto.
Estos nodos también se pueden usar de forma genérica, es decir, que a priori no esté asociado a ningún Terminal. Para ello, debe usarse Programming > Application Control > Property Node. Después hay que cablear una referencia al terminal sobre el que actuará. Las referencias se crean igual que los nodos de propiedad o las variables locales.
En la figura 3-32 pueden verse dos controles booleanos a la izquierda: se trata de dos botones. En la esquina superior derecha se usa un nodo de propiedad para detener el parpadeo (Blinking) del primero de ellos. En el centro de la imagen se hace visible o se oculta uno de los dos botones, dependiendo de cuál se elija; este nodo es genérico, por lo tanto puede actuar sobre cualquiera de los dos botones a través de sus referencias. Finalmente, en la parte inferior se muestra un nodo de métodos, en este caso Get Image, que obtiene una imagen del botón; el VI al que está conectado es Draw Flattened Pixmap y simplemente lo dibuja.

Figura 3-32. Ejemplo del uso de nodos de propiedades y métodos.
3.4.5. Referencias
En la sección 3.2.8 se ha hablado de referencias junto con el tipo de datos refnum, que no debe confundirse con las referencias a terminales del Panel Frontal, que será lo que se estudie a continuación.
Se crean, igual que las variables locales y las propiedades, en el menú Create del menú contextual. Una vez creada, puede cambiarse el terminal al que está asociada desde el menú Link to. En la figura 3-31 puede verse en el menú cómo crear referencias, y en la figura 3-32 dos nodos de referencia de los controles Boolean y Boolean 2.
Como su nombre indica, sirven para hacer una referencia al terminal al que están asociados. Estas referencias se aplican principalmente a nodos de métodos o propiedades genéricos. Pueden servir para hacer que un nodo actúe sobre uno u otro Terminal, como se muestra en la figura 3-32, pero su mayor utilidad consiste en poder usar los nodos de propiedades y métodos asociados a un terminal desde un VI distinto al que están colocados. Las referencias no tienen alcance como las variables locales, pueden pasarse a otro VI como un parámetro más. El concepto sería similar al de pasar un parámetro como un puntero en lugar de por valor.
3.5. Ejemplos
3.5.1. Ejemplo I: varios métodos para convertir la representación ASCII a su valor
3.5.1.1. Explicación teórica
Este ejemplo se explica perfectamente con el título, no necesita más descripción, aquí simplemente se justifica el motivo de incluirlo: a muchos usuarios noveles que trabajan con interfaces como el bus serie RS-232 se les presenta el problema de que lo enviado y transmitido es un string; sin embargo, deben interpretarlo como números.
En principio se puede pensar en usar las funciones de Programming > String > String/Number Conversion, pero éstas no son válidas porque lo que convierten es un carácter numérico al número que representa o viceversa. No pueden convertir un carácter a su valor ASCII; por ejemplo, en un string el carácter «1» (cuyo valor ASCII en hexadecimal es el 31h) no se convertiría a su valor ASCII (31h = 49), sino en el 1. Para realizar la conversión que queremos (carácter «1» a número 49), hay que emplear otros métodos.
3.5.1.2. Código
Para realizar este programa ya se han explicado algunos métodos. Aquí se recopilan tres de ellos.
El primero es Type Cast. En el terminal type se debe conectar una señal del mismo tipo de la que se desea obtener a la salida, en este caso un array de enteros de ocho bits.
El segundo método es usar String To Byte Array. Ésta es una función diseñada expresamente para este problema.
El último método recopilado es usar Unflatten From String. Este método aprovecha que el dato de entrada es un string; sin embargo, para transformarse al array de enteros de salida necesitaría los 32 bits iniciales que indican la dimensión de esa dimensión del array. Como el string no lo tiene, se marcará en la entrada data includes array or string size? como FALSE.
El string mostrado en la figura 3-33 es «abcdef», cuyos valores van del 97 para la «a» hasta el 102 para la «f». Los resultados se muestran a la derecha de la imagen.

Figura 3-33. VI que pasa un ASCII a su valor.
3.5.2. Ejemplo II: Carrera de fórmula 1
3.5.2.1. Explicación teórica
El objetivo de este ejemplo será crear un sencillo simulador de carreras con tres coches. Los coches avanzarán en línea recta desde la salida hasta la meta. Cuando lleguen, el primero obtendrá una medalla de oro, el segundo de plata y el tercero de bronce.
Los coches están representados por unos indicadores booleanos numerados como «1», «2» y «3». De estos botones se crearán unos nodos de propiedad en los que se seleccionará Position > Left. Al escribir en este nodo un número, el control se desplazará hasta esa coordenada, así se simulará el avance.
El núcleo del programa consistirá en tres bucles en paralelo, uno por cada coche, que se estarán ejecutando hasta que el coche correspondiente llegue a meta (coordenada 600). En cada iteración del bucle aumentará el valor de la posición un valor aleatorio entre el 1 y el 4.
Cuando todos los coches lleguen a la meta se construirá un array con el tiempo que han tardado; este tiempo servirá para que el color del fondo de la etiqueta cambie a uno que represente una de las medallas. En esta ocasión se usará un nodo de propiedad genérico y se elegirá el control a colorear seleccionando su referencia.
3.5.2.2. Código
El código para este programa se puede ver en la figura 3-34.

Figura 3-34. Código del simulador de carreras.
El programa consiste en un SEQUENCE. En el primer frame se resetean los colores de las etiquetas y en el segundo se obtiene el tiempo en la salida; el tercer frame contiene tres bucles WHILE que hacen avanzar los controles. Cuando estos bucles acaban, también se obtiene el tiempo, que se restará del anterior para calcular el tiempo invertido en el recorrido. Con los tiempos de los tres controles se crea un array en el que se busca el menor valor que corresponderá con el primer control en acabar el recorrido; la referencia de ese control se pasará al nodo que cambia el color de la etiqueta a dorado. El valor en el array se sustituirá por infinito y se procederá de igual forma para obtener el segundo y el tercer clasificado.
3.5.2.3. Resultados
En la figura 3-35 se muestra una captura de una ejecución del programa. Los controles booleanos han sido personalizados como se vio en la sección 1.2.2 para que tengan el aspecto de un coche. El número que aparece sobre el capó es su etiqueta.

Figura 3-35. El coche n° 2 va en cabeza.
Los coches tienen una longitud de 117 píxeles y la pista de carreras 717 píxeles. Los coches recorrerán 600 píxeles desde la salida a la meta.
Este ejemplo muestra el uso que se puede hacer de las propiedades de los controles para cambiar el aspecto de los mismos de forma programada. Ésta es una herramienta muy útil para mejorar la interfaz con el usuario y darle cierto grado de interactividad.
3.6. Ejercicios
1. Realizar un programa que testee el tiempo necesario para leer y escribir un dato. Comprobar si es más eficiente una variable local, una global o la propiedad value.
2. ¿Qué ocurre si en el terminal type del Type Cast del ejemplo I se usa un array de enteros de 16 bits en lugar de enteros de 8? ¿Por qué?
3. Añadir al programa del ejemplo II una opción de foto-finish (pista: ver los métodos asociados al VI).
4. Construye un VI que acepte como parámetro de entrada un dato que pueda ser de tipo numérico, booleano, array o cluster y cuya salida sea un array de strings en el que cada elemento tenga el valor de cada componente del dato de entrada. Por ejemplo, si la entrada es un array de booleanos, la salida será un array de strings con los valores 0 o 1; si la entrada es un cluster de un dato numérico y un string, la salida será un array en el que el primer elemento sea el valor del dato numérico y el segundo el del string; si la entrada es un booleano o un numérico, el array de salida lógicamente sólo tendrá un elemento del mismo valor que la entrada (Ayuda: ver en el tema 4 las funciones sobre XML).
3.7. Bibliografía
Archana Shrotriya, AN 087: Writing Win32 Dynamic Link Libraries (DLL) and Calling Them from LabVIEW, National Instruments, 1996.
Jean Pierre Drolet y Jim Kring, LabVIEW Data, Data Types and Storage Formats, 2002.
National Instruments, AN 154: LabVIEW Data Storage.
National Instruments, AN 168: LabVIEW Performance and Memory Management, 2004.
Ücretsiz ön izlemeyi tamamladınız.