Uso de Java Native Interface para envío y recepción de datos por puerto paralelo

 

Contenido

 

 

 

 

Resumen

 

En este proyecto se muestra el uso del JNI (Java Native Interface) como nexo entre Java y un lenguaje nativo de la maquina en este caso C.

                                                                                                                                     

 Se explica  la implementación  del Java Native Interface para así entender y aprender un poco sobre esta utilidad.

 

 Java es una herramienta muy útil no solo para este proyecto sino también para muchas aplicaciones, ya que tiene la capacidad de crear ambientes gráficos muy amigables y poder ejecutarse en cualquier maquina.

 

 La aplicación que se dio al uso de JNI en este proyecto fue la unión entre Electrónica y Software, para el envío y recepción de datos por el puerto paralelo.

 

Volver al principio

Introducción

 

Aunque la potencia de las API de Java es más que suficiente para casi todo tipo de aplicaciones, algunas de ellas necesitan utilizar la potencia específica de una plataforma en concreto.

 

 Java desde un principio incluyo una forma de hacer llamadas nativas desde la maquina virtual y viceversa, pero tenia 2 problemas:

 

1.      Las llamada a código nativo desde Java accedían a estructuras C, pero no estaba definida la posición exacta que debían ocupar estos campos en memoria con lo que una llamada nativa en una maquina virtual no coincidían con las llamadas en otra maquina virtual.

 

2.      Las llamada nativas en versiones anteriores a JSDK 1.1 se basaban en el uso de un recolector de basura conservativo, este recolector recogía el puntero nativo que apuntaba a un objeto Java, por lo tanto cuando se apuntaba a un objeto Java desde el método nativo éste no se liberaba nunca más.

 

Esta forma de llamadas nativas es la que se usó en las primeras versiones de Java para implementar clases que accedían a la maquina host como java.io*, java.net*.

 

Debido a los problemas que se presentaron al invocar métodos nativos se implemento una nueva forma de acceder a estos métodos nativos a la que se llamo JNI.

 

JNI es un entorno el cual nos permite ejecutar código nativo de la maquina en la cual se esta corriendo desde Java y viceversa.

 

Código Nativo son funciones escritas en algún leguaje de programación como C o C++, donde se ejecuta la maquina virtual.

 

Aquí se muestra un esquema de como un programa escrito en C interactúa con uno escrito en Java.

 

 

 

            El siguiente esquema muestra el caso de un programa Java interactúa con uno escrito en lenguaje nativo en este caso C.

 

            El siguiente esquema muestra el lugar donde actúa JNI.

 

 

 

Volver al principio

Implementación de JNI

 

            Para implementar JNI se debe seguir los siguientes pasos:

 

 

§         Escribir el código en java

§         Compilar el Código Java

§         Crear el fichero .h

§         Escribir la implementación del Método Nativo.

§         Crear una librería Compartida.

§         Ejecutar el programa.

 

 

Volver al principio

 

Escribir el código en java

 

 

            En el código que se generara  se deben definir dentro de una clase Java todos los métodos Java, como los métodos nativos. Para la implementación de un método en un lenguaje de programación distinto de Java, se debe incluir la palabra clave native como parte de la definición del método dentro de la clase java. La clave native indica al compilador Java que la función es una función en lenguaje nativo. En este caso la clase java creada es Parallel.java, donde readOneByte (int address)  y writeOneByte (int address, int oneByte)    son los métodos que están implementados en C.

 


  package parport;

 

  public class ParallelPort {

 

public ParallelPort (int portBase)

                        {

                                    this.portBase = portBase;

                        }

  

public int read ()

                        {

                                    return ParallelPort.readOneByte (this.portBase+1);

                        }

           

public void write (int oneByte)

                        {

                                    ParallelPort.writeOneByte(this.portBase, oneByte);

                        }


  

public static native int readOneByte (int address);

 

                      public static native void writeOneByte(int address, int oneByte);

 

            static

                        {

                                     System.loadLibrary("parport");

                        }

            }

 

            El método System.loadLibrary es invocado para cargar la librería compartida que se creara cuando compile la implementación del código. Este método se coloca con un inicializador static. El argumento de System.loadLibrary es el nombre de la librería. El sistema utiliza un estándard, pero específico de la plataforma, para convertir el nombre de la librería a un nombre de librería nativo.

 

            Luego se debe crear una aplicación Java a parte que ejemplarice la clase que contiene la definición de método nativo. Esta aplicación Java también llamará al método nativo. Como es una aplicación, debe tener un método main. En un fichero fuente separado, que para el proyecto se llama Proyecto.java, se crea una aplicación Java que ejemplariza ParallelPort y llama a los métodos nativos read() y write().

 

Como se puede ver  la llamada a métodos nativos es de la misma forma que la llamada a un método normal. En el programa para el proyecto:

 

            ParallelPort lpt1 = new ParallelPort(0x378);

            lpt1.write(bit);

lpt1.read();

 

            Donde lpt1 es una referencia a la clase ParallelPort.java , 0x378 es la dirección del puerto paralelo y bit es la señal a enviar al puerto paralelo.

 

Volver al principio

 

Compilar el Código Java

 

 

Ahora se procede a compilar la clase Java anterior creada ParallelPort.java de la forma convencional.

 
 javac Proyecto.java  

 

 

Volver al principio

 

Crear el fichero .h

 

La creación del fichero .h es para proporcionar un prototipo de función para la implementación de los métodos nativos write() y read() definidos en la clase ParalellPort().

 

Para crear este fichero se ejecuta el comando:

 

javah ParalellPort

 

El resultado de esta acción crea un archivo parport_ParallelPort.h.

 

Si miramos dentro del fichero creado parport_ParallelPort.h nos encontramos con lo siguiente:

 

     JNIEXPORT jint JNICALL Java_parport_ParallelPort_readOneByte (JNIEnv *, jclass , jint);

 

     JNIEXPORT void JNICALL Java_parport_ParallelPort_writeOneByte (JNIEnv *, jclass , jint, jint);

 

Donde:

 

·        Java_parport_ParallelPort_readOneByte  y Java_parport_ParallelPort_writeOneByte son las funciones que nos ayudaran en la implementación en el método nativo de la clase ParallelPort , esto quiere decir que son los métodos que serán implementadas en C.

·         parport es el nombre del paquete.

·        ParallelPort es el nombre de la clase.

·         readOneByte y writeOneByte son los nombres de los métodos en el lenguaje nativo

·         JNIEnv * y jclass son requeridos para cualquier método nativo. Estos parámetros permiten al método nativo comunicarse con su entorno.

·        Jinit son la dirección del puerto paralelo y bit a mandar en el caso de write.

 

Volver al principio

 

Escribir la implementación del Método Nativo.

 

Ahora se procede a escribir la implementación del método nativo en C.

 

En el proyecto el archivo parport_ParallelPort.c  se incluyen los siguientes ficheros de cabecera:

 

#include <jni.h>

#include <conio.h>

#include "parport_ParalellPort.h"

 

·        jni.h proporciona información que el código nativo necesita para interactuar con el sistema de ejecución Java, este fichero siempre se incluye cuando se escriben métodos nativos.

·        conio.h este fichero es incluido por que ocupa las funciones inp y outp nativas de C.

·        parport_ParalellPort.h necesario para la implementación de los métodos nativos.

 

 

Ahora se procede a escribir las funciones con las mismas firmas de las funciones que se generaron  en parport_ParallelPort.h.

 

Esta función lee en el puerto paralelo y retorna el valor leído (status) a la acción que lo invoco en Java.

 

JNIEXPORT jint JNICALL Java_parport_ParallelPort_readOneByte

             (JNIEnv * algo, jclass otro, jint portStatus)

{

   unsigned short status;

   status = (unsigned short)portStatus;

   return _inp(status);

}

 

Por otro lado esta función envía un Byte al puerto paralelo que fue enviado por una acción en el programa Java que lo invoca.

 

JNIEXPORT void JNICALL Java_parport_ParallelPort_writeOneByte

  (JNIEnv * algo, jclass otro, jint portData, jint oneByte)

{

   unsigned short data;

   int aByte;

   data = (unsigned short)portData;

   aByte = (int)oneByte;

   _outp(data,aByte);

}

}

 

Volver al principio

 

 

Crear una librería Compartida

 

 

Las librerías son ficheros que almacenan las funciones, clases y variables globales y sirve para enlazar los lenguajes. Se pueden crear  librerías de enlace estático o enlace dinámico.

 

Las librerías de enlace estático se incluyen dentro del ejecutable durante el enlace  y una vez generado el ejecutable ya no se necesita de ésta librería. En sistemas UNIX tienen extensión .o y en Windows .lib.

 

Las librerías de enlace dinámico no se añaden al ejecutable sino que al momento de solicitarlas se cargan y enlazan sus contenidos. Su extensión en sistemas UNIX es .so y en Windows .dll.

 

En este caso se utilizara de enlace dinámico ya que la ventaja que tiene sobre la otra es que  varios programas pueden compartir las mismas librerías lo que ahorra uso de CPU.

 

Para el proyecto esta librería será parport.dll donde es ligada en la clase ParallelPort.

 

System.loadLibrary("parport")

 

Hay varias formas de crear una librería, cada compilador y sistema operativo tienen formas distintas de llevar a cabo esta acción.

 

Volver al principio

 

 

Ejecutar el programa

 

 

Ahora queda solo ejecutar el programa Proyecto.java, pero antes hay que guardar en la carpeta classes del compilador java la carpeta con nombre parport con los archivos: parport.dll y la clase ParallelPort .java.

 

 

Volver al principio

 

 

Proyecto.java

 

 

Al ejecutar el programa se desplegara una pantalla tipo Windows donde se encuentran 4 botones los cuales sétean el estado de los leds  conectados a la salida del puerto paralelo.

 

En el programa se ocupo las capacidades graficas del lenguaje Java lo cual hace amigable una interacción de este tipo.

Además se hizo uso de ActionListener para poder transmitir la acción de seleccionar un botón y generar el evento que envía el bit al puerto paralelo.

 

Al momento de ejecutar el programa de envío muestra inmediatamente un panel con cuatro botones los cuales controlan independientemente cada led.

 

 

 

Al ejecutar el programa de recepción de datos se mostrara una ventana que cambiara el fondo de esta al accionar el botón que se encuentra conectado al Puerto Paralelo.

 

El fondo será azul cuando no hay pulsación del botón y rojo cuando se pulsa.

 

 

 

 

 

 

 

 

Volver al principio

 

 
Conclusiones

 

 

El proyecto lleva a la conclusión  de que el uso de java y sus capacidades (en éste caso JNI y capacidades graficas) es una herramienta muy potente para el desarrollo de proyectos y Softwares para resolver problemas o ayudar en algo.

 

            El envío y adquisición de datos con el PC puede tener variados usos como controlar una simple lámpara, un horno, sensores, seguridad, control remoto etc., además con un interfaz amigable que pueda ser entendido y conocido, puede llevar a desarrollar proyectos mas grandes.  

 

 

Volver al principio

 

 

Referencias

 

http://profesores.elo.utfsm.cl/~agv/elo330/2s04/

http://www.programacion.com/java/tutorial/jni/

http://www.itapizaco.edu.mx/paginas/JavaTut/

http://java.sun.com/

http://java.sun.com/j2se/1.4.2/docs/guide/jni/

http://java.sun.com/docs/books/tutorial/native1.1/TOC.html

 

 

Volver al principio