21.3.10

Comunicación RS-485 con la placa TS-ISO485

El estándar utilizado en la UART de la placa TS-ISO485 es el 16C550 y los drivers necesarios para utilizarlo vienen en el SO instalado de fábrica (TS-Linux), con el objetivo de hacer inmediata la integración de los puertos RS-485 la placa madre.
Como se menciona en la descripción del hardware [ref], es necesario configurar los jumpers en la placa TS-ISO485 según el modo de transmisión deseado: full-duplex o half-duplex. Para lograr que la placa tenga la potencialidad de actuar en cualquiera de los modos el diseño de TS incluye dos MAX485 por puerto. Uno de ellos utiliza sólo en modo full-duplex siempre para recepción, mientras que el otro se utiliza siempre para transmisión cuando el modo es full-duplex pero la dirección de los datos depende una señal (TX_ENx en el esquemático de la placa) en modo half-duplex, en alto habilita la transmisión.
Luego de configurar los jumpers para funcionamiento full-duplex y conectar los puertos de la placa entre sí (siguiendo las sugerencias de “Getting started with TS-ISO485”) se ensayan siguientes comandos:

$ setserial -g /dev/cua/2
/dev/cua/2, UART: 16550A, Port: 0x89c003e8, IRQ: 40
$ setserial /dev/cua/2 IRQ 22

$ setserial -g /dev/cua/2 /dev/cua/2, UART: 16550A, Port: 0x89c003e8, IRQ: 22

$ cat < /dev/cua/2 & $ echo "testing serial" > /dev/cua/3
testing serial
$


Obteniendo los resultados esperados (salvo por un pequeño error en la hoja de datos en los jumpers para seleccionar el número de puerto COM).

El éxito, sin embargo, no se repite al efectuar la misma prueba para el modo half-duplex.
Observando las tensiones en la placa a través de un osciloscopio, se puede verificar que las señales TXEN de los MAX que actúan en este modo de funcionamiento se encuentran siempre en alto salvo, paradójicamente, cuando se intenta transmitir.


En la imagen, U12 es el MAX que funciona en el modo half-duplex. En azul se muestra el punto de medición mencionado.

La señal TX_ENB se puede rastrear hasta el CPLD 9536XL, que a es controlado por el integrado TL16C5550 y por el microcontrolador de la placa madre.


El error podría ser causa de una mala programación del CPLD o un deficiente manejo del mismo y/o del TL16C550 por parte del microcontrolador. Sin embargo el CPLD está usado sólo como buffer y como lógica fija, no es programable desde el microcontrolador en tiempo de ejecución. Asimismo, se puede observar desde la consola de Linux que el estado de la señal medida va de la mano del estado de RTS. Luego, el problema está en el software que ejerce el control desde la placa TS-7260 en Linux.
Para subsanar este inconveniente se recurre a el manejo manual de RTS mediante llamadas a ioctl(), función que posibilita la comunicación con el driver del puerto serie desde el espacio de usuario. Este proceder soluciona el problema, pero a un altísimo costo: dado que TS-Linux no es un sistema operativo de tiempo real (la granularidad del reloj del sistema es de 10ms) y por el hecho de que la función se llame desde espacio de usuario (no desde un driver o un módulo del kernel), se producen enormes retardos temporales. En una aplicación que requiera una velocidad de transmisión moderada la modalidad half-duplex debería ser descartada en el presente hardware incluso aunque se manejara desde el espacio de kernel. Como regla general, la señalización de la comunicación debe ser resuelta en hardware para lograr velocidades de transmisión respetables. Podría resolverse utilizando un dispositivo RS-485 que se comunique a la placa mediante USB o Ethernet, pero con esto se resignaría la aislación galvánica provista por los optoacopladores.

La API (Application Programming Interface, interfaz de programación de una aplicación) para terminales de entrada/salida en sistemas Unix se denomina Termios.
El algoritmo básico para utilizar el puerto serie mediante Termios consta en los siguientes pasos:
Abrir el dispositivo serie con la función open().
Configurar las características de la interfaz (por ejemplo: cantidad de bits de datos, bits de parada, bit de paridad, tratamiento de caracteres especiales, velocidad de transmisión, cantidad de caracteres antes de volver de read(), etc.)
Usar read() y write() para recibir y transmitir datos.
Cerrar el dispositivo mediante close().
Para el segundo paso se disponen de una serie de estructuras (definidas en termios.h) y funciones especiales. La estructura más importante es la siguiente:


struct termios {
tcflag_t c_iflag; /* banderas específicas de entrada */
tcflag_t c_oflag; /* banderas específicas de salida */
tcflag_t c_cflag; /* banderas de control */
tcflag_t c_lflag; /* banderas locales */
cc_t c_cc[NCCS]; /* caracteres especiales */
};

A continuación se detallan porciones de código utilizadas en nuestro proyecto, a modo de ejemplos prácticos. Se omiten algunas declaraciones de variables y de funciones por simplicidad.

Inicialización:

system("setserial /dev/cua/4 irq 22"); // verificar que lo haga, A VECES FALLA
stream4=open("/dev/cua/4",O_RDWR | O_NOCTTY | O_NDELAY);
if (!isatty(stream4)){
printf("485: Error abriendo driver RS-485, com 4.\n");
exit(EXIT_FAILURE);
return ;
}
//config es una estructura termios
config.c_iflag &= ~(IGNBRK | BRKINT | ICRNL | INLCR | PARMRK
| INPCK | ISTRIP | IXON | IXOFF | IXANY);
//de esta manera se procede con todos los miembros de la
//estructura
//es importante no olvidar inicializar ningun bit, porque
//los valores por defecto no estan bien documentados...

// Velocidad de comunicacion (version simple)
if(cfsetispeed(&config, B9600) < 0 || cfsetospeed(&config, B9600) < 0) {
printf("485: Error seteando velocidad (9600) del com 4\n");
}

//se manda la configuracion al driver:
tcsetattr(stream4,TCSANOW,&config);


Enviar datos:

mcr = TIOCM_RTS;
err = ioctl(stream,TIOCMBIS,&mcr); // se levanta RTS para escribir)
if(err) {
printf("485: Error en ioctl levantando RTS");
goto no_espera_RTS;
}
mcr=0;
while (!(mcr & TIOCM_RTS)) // se espera que RTS este en 1
ioctl(stream, TIOCMGET, &mcr);

no_espera_RTS:
nanot.tv_nsec=100000;
nanot.tv_sec=0;
nanosleep(&nanot,&nanoaux); // Retardo entre subida de RTS y escritura

write(stream,coms,sizeof(coms)); //coms es un string conteniendo datos para mandar

err = tcdrain(stream); // se espera que se terminen de enviar datos

if (err)
printf("485: Error esperando fin de transmision en tcdrain\n");

tcflush(stream,TCIOFLUSH);

mcr = TIOCM_RTS;
err = ioctl(stream,TIOCMBIC,&mcr); /* se baja RTS */

if(err)
printf("Error en ioctl bajando RTS");



Recibir datos:

while ( (flag_tout&1)==0 ){
got = read(stream, c, MAX_BUF_MIN);
if ( got != -1 ){
if ( (cnt+got) > MAX_CHAR_ENTRADA ){
copy = MAX_CHAR_ENTRADA - cnt;
cnt = MAX_CHAR_ENTRADA;
} else {
copy = got;
cnt += got;
}

fin_aux = 0;
for (j=0;j<MAX_BUF_MIN;j++){ // Busca caracteres de fin de linea o EOF en
if ((c[j]=='\r')||(c[j]==0x0a) || (c[j]==0x00)) // todo el string leido
fin_aux=1;
}

strncpy( pdato, c, copy );
pdato += copy;

if ( (cnt >= MAX_CHAR_ENTRADA) || (*(pdato-1) == 0x0a)
|| (*(pdato-1) == 0x00) || (*(pdato-1) == '\r') || (fin_aux==1) ) {
pthread_cancel(thread_tmr_out_id);
goto bail;
}
}

if ( flag_tout&1 )
goto bail;

usleep(T_READ_DELAY); /* Insertamos retardo entre lecturas (no bloqueantes)
para limitar la carga de procesamiento del uC */
}

bail:
tcflush(stream,TCIOFLUSH);

if (flag_tout&1) /* Si hubo timeout se devuelve 1 para que reintente */
return -1;

return;

// strRecibido[0]='\0'; // Limpiar luego de utilizado para que vuelva a leer

8.8.09

Control de versiones

Después de cierto tiempo trabajando en un proyecto comienza a evidenciarse la necesidad de guardar los cambios cuando se hacen progresos y de documentar todos los cambios relevantes, para que pueda entenderse rápidamente que cambió entre una y otra versión. El concepto que engloba a estas acciones es: control de versiones.
El control de versiones puede hacerse manualmente, por ejemplo copiando la carpeta entera del proyecto a otra cada vez que se hace un cambio y describiendo los cambios en comentarios dentro de cada archivo que sufra modificaciones. ¿Suena tedioso?
Afortunadamente muchos programadores antes que nosotros pensaron lo mismo y es por eso que existe software dedicado a lidiar con estos menesteres.

El que se eligió para este proyecto por su versatilidad y simplicidad es git. Se encuentra disponible para correr sobre Linux o Windows.
Algunas de sus características son:
- almacena cambios incrementalmente, reduciendo el espacio utilizado.
- permite (¡y obliga! a) hacer un comentario cada vez que se almacenan cambios ("se hace un commit").
- permite ver las diferencias entre una versión (commit, un estado salvado) y cualquier otro en forma sencilla.
- permite desarrollo no lineal: es decir que dos (o más) programadores podrían estar trabajando sobre diferentes partes del proyecto (aunque trabajen en archivos en común) y después sumar sus cambios al proyecto en forma automática, siempre que no hayan modificado exactamente las mismas líneas, de ser así el programa le pregunta al usuario para resolver el conflicto.
- es gratuito y de código libre.

Sobre la forma de utilizarlo hay excelentes tutoriales escritos en diversas lenguas como por ejemplo Git Magic, muy bien escrito en inglés, que va de las operaciones básicas cotidianas hasta las maniobras más intrincadas e infrecuentes, mientras que en nuestra lengua tenemos disponible una introducción rápida a git.
Para instalar git en Linux sólo hace falta instalar el paquete git-core (y sus dependencias), es decir, en una distribución de Linux basada en Debian basta con abrir un terminal y poner sudo apt-get install git-core. Sin embargo, git por si mismo es sólo accesible mediante la línea de comandos, para tener una interfaz gráfica hace falta instalar git-gui. Una vez instalado ese paquete, se puede acceder usando el comando git gui. Una interfaz mejorada se obtiene instalando qgit y kompare.


Captura de git gui:


Capturas de qgit:





Captura de kompare:

23.5.09

Makefiles

Para compilar programas desde más de una fuente es útil confeccionar un archivo "Makefile".
Además, cuando tenemos muchos archivos que se compilan en un sólo binario, como sucede frecuentemente en los sistemas embebidos, se vuelve muy útil abreviar la forma de incluirlos en la compilación. Para esto existen macros que evitan la repetición de variables, lo cual no sólo es bueno para agilizar la escritura, sino para el mantenimiento, legibilidad y escalabilidad de los programas.
He aquí un tutorial simple y en español para escribir Makefiles.

23.4.09

LIBUSB

LibUSB es una librería de código abierto que permite desarrollar controladores de dispositivos USB desde espacio de usuario sin necesidad de bajar a nivel de kernel. En nuestro caso, va a ser utilizado para detectar la presencia de dispositivos de almacenamiento USB, que es donde guardaremos los datos adquiridos.
La utilización de esta librería requiere la compilación del código fuente para adaptarlo a nuestra plataforma. De esta manera se genera una librería dinamica (de extensión ".so") que es la cual se cargará en el TS.
A continuación se presentan los pasos a seguir, junto con un programa de ejemplo que busca las Flash USB conectadas y muestra datos de las mismas, incluyendo la cantidad total de dispositivos de almacenamiento detectados.


1- Descomprimimos el codigo fuente de la librería libusb-0.1.12.tar.gz

http://prdownloads.sourceforge.net/libusb/libusb-0.1.12.tar.gz

Otras versiones tal vez no funcionen.


2- Vamos a la carpeta donde descomprimimos el código y tipeamos el siguiente comando:

# CC=arm-linux-gcc CXX=arm-linux-gcc ./configure --prefix=/opt/crosstool/arm-linux/gcc-3.3.4-glibc-2.3.2/arm-linux --program-prefix=arm-linux- --host=arm-pc-linux-gnu

Donde en prefix ponemos la ruta a nuestra toolchain (suponemos que la revisión de la misma es gcc-3.3.4-glibc-2.3.2)

3- Ejecutamos los dos siguientes comandos:

make
make install

Si leemos un poco lo que muestra el ultimo comando, vemos que instala las librerías dinámicas .so en el directorio lib de nuestra toolchain. Los archivos de interés son:

libusb-0.1.so.4.4.4
libusb-0.1.so.4

Donde el primero es la librería propiamente dicha y el segundo es solo un enlace al primero, pero lo debemos tener en cuenta porque el programa que compilemos para el TS-7260 busca un archivo con dicho nombre.


4- Copiamos los dos archivos al del punto anterior a la carpeta "/lib" del TS


5- Hacemos un pequeño programa y lo compilamos con el comando:

arm-linux-gcc -o ejemplo_libusb ejemplo_libusb.c -I/usr/local/include -L. -lnsl -lm -lc -L/usr/local/lib -lusb

Luego lo subimos a la placa.

Pueden bajar el programa de ejemplo "ejemplo_libusb.c" desde el siguiente servidor ftp:

ftp://mandrakenet.com

usr: ts7260
pass: ts7260

6- Una vez subido a la placa el programa, cargamos los modulos USB ejecutando el siguiente Script:

/usr/bin/loadUSBModules.sh

Montamos el sistema de archivos USB con:

mount -t usbdevfs none /proc/bus/usb

7- Por último, podemos ejecutar el programa, se muestra la cantidad de dispositivos flash USB conectados al TS y algunas características de los mismos.

18.4.09

Tutorial

Un excelente tutorial para aprender a hacer scripts en linux.
El nivel es básico y está en inglés.

8.3.09

Del origen de los fondos

La Universidad Nacional de la Patagonia Austral (UNPA) ha abordado un Proyecto Federal de Innovación Productiva, que consiste en la construcción de un sistema de suministro energético con energía eólica y solar para escuela rural Nº 25 Casimiro Biguá (Pasaje Glencross, Santa Cruz).

El equipamiento a instalar en la Escuela Rural Glencross consistirá un subsistema eléctrico-eólico y otro de tipo solar térmico. El primero consta de 2 aerogeneradores de 1kW de potencia nominal eléctrica, dotados de un generador del tipo imán permanente de 48V y origen nacional. Se incluirá además un banco de baterías de 660Ah en 48V y un inversor capaz de entregar 3.6kW de potencia. La presencia de dos grupos diesel en Glencross, hará necesaria la instalación de una llave conmutadora. El segundo subsistema de tipo solar térmico se conectará en serie con el termo-tanque, y se instalará una llave de bypass para que el sistema pueda replicar el funcionamiento actual ante cualquier problema con el panel o subcircuito.

Es dentro de este marco que estamos desarrollando el software de la placa TS-7260 para llevar a cabo funciones de registro de datos y control de algunas variables. Todo el hardware para el proyecto es provisto por dicha universidad.

14.1.09

Creando .csv

Nos informa correctamente wikipedia.org:

Los ficheros CSV (del inglés comma-separated values) son
un tipo de documento sencillo para representar datos en forma de tabla,
en las que las columnas se separan por comas (o punto y coma) y las
filas por saltos de línea.

El siguiente programa en C muestra como crear un archivo .csv.

#include <stdio.h>
int main()
{
    FILE * fp ;
    fp=fopen("miarchivo.csv","a");
    fprintf(fp,"Dato,");
    fclose(fp);
    return 0;
}



Primero se declara fp como puntero a archivo.
En la segunda línea, se utiliza el comando fopen() para abrir un archivo (si no existe
miarchivo.csv, se crea un nuevo archivo con ese nombre). La opción "a", indica que se debe abrir el archivo para añadir contenido al final del mismo, se pueden ver otras opciones en el link proporcionado o bien tipeando en la consola man fopen.
En la tercera línea se ejecuta fprintf(), para escribir donde apunte fp, en este caso al final del archivo miarchivo.csv. Notese que al final del dato debe agregarse la coma.
Si bien en este caso tan simple no se nota la diferencia, es buena práctica liberar el puntero como se hace en la última línea con fclose(). Esta acción libera el archivo para que pueda ser utilizado por otros procesos y libera recursos del sistema operativo.