Se explican aquí algunos
conceptos y detalles acerca de las herramientas que fueron utilizadas
para hacer los programas.
Puerto USB:
Como se menciona en la
sección de hardware, se utilizó un XBee Explorer USB, que cuenta con un
chip FTDI FT2232 encargado de convertir la comunicación UART a
USB.
En Linux a partir
del kernel 2.6.9 trae soporte para este chip y no es
necesario descaragar ningún driver.
Configuración de puerto
USB:
Debido a que ya se
encuentra soportado el chip, para trabajar con el puerto USB se
configura como si fuera un puerto serial.
Pasos básicos:
Es necesario declarar
la librería termios.h que controla el
comportamiento del puerto:
#include
<termios.h>
Se definen variables
estáticas, una para la tasa a utilizar y otra abrir el puerto
correcto.
#define BAUDRATE B9600
#define MODEMDEVICE "/dev/ttyUSB0"
Dentro del main se hace lo siguiente:
Se crean dos estructuras una para
almacenar la antigua configuración del puerto y otra para la nueva
configuración que crearemos, esto es necesario para que al finalizar
el programa el puerto se restaure a la antigua configuración.
struct
termios oldtio,newtio;
Posteriormente se
configura el puerto de la forma que mejor nos convenga, el siguiente
material fue extraído de http://www.wikilearning.com/tutorial/como_programar_el_puerto_serie_en_linux-programas_ejemplo/20528-3.
Esta configuración es
para un proceso de entrada Canónico, esto significa que toda la entrada es
procesada en unidades de líneas, lo que significa que un read sólo devolverá una
línea completa de entrada. Una línea está, por defecto, finalizada
con un NL (ASCII LF ), y fin de
fichero, o un carácter fin de línea. Un CR (el fin de línea
por defecto de DOS/Windows) no terminará una línea con la
configuración por defecto.
/* Abre el dispositivo modem para lectura y escritura y no como controlador tty porque no queremos que nos mate si el ruido de la linea envia un CTRL-C. */ fd = open(MODEMDEVICE, O_RDWR | O_NOCTTY ); if (fd <0) { perror(MODEMDEVICE); exit(-1); } tcgetattr(fd,&oldtio); /* almacenamos la configuracion actual del puerto */ bzero(newtio, sizeof(newtio)); /* limpiamos struct para recibir los nuevos parametros del puerto */ /* BAUDRATE: Fija la tasa bps. Podria tambien usar cfsetispeed y cfsetospeed. CRTSCTS : control de flujo de salida por hardware (usado solo si el cable tiene todas las lineas necesarias Vea sect. 7 de Serial-HOWTO) CS8 : 8n1 (8bit,no paridad,1 bit de parada) CLOCAL : conexion local, sin control de modem CREAD : activa recepcion de caracteres */ newtio.c_cflag = BAUDRATE | CRTSCTS | CS8 | CLOCAL | CREAD; /* IGNPAR : ignora los bytes con error de paridad ICRNL : mapea CR a NL (en otro caso una entrada CR del otro ordenador no terminaria la entrada) en otro caso hace un dispositivo en bruto (sin otro proceso de entrada) */ newtio.c_iflag = IGNPAR | ICRNL; /* Salida en bruto. */ newtio.c_oflag = 0; /* ICANON : activa entrada canonica desactiva todas las funcionalidades del eco, y no envia segnales al programa llamador */ newtio.c_lflag = ICANON; /* inicializa todos los caracteres de control los valores por defecto se pueden encontrar en /usr/include/termios.h, y vienen dadas en los comentarios, pero no los necesitamos aqui */ newtio.c_cc[VINTR] = 0; /* Ctrl-c */ newtio.c_cc[VQUIT] = 0; /* Ctrl-\ */ newtio.c_cc[VERASE] = 0; /* del */ newtio.c_cc[VKILL] = 0; /* @ */ newtio.c_cc[VEOF] = 4; /* Ctrl-d */ newtio.c_cc[VTIME] = 0; /* temporizador entre caracter, no usado */ newtio.c_cc[VMIN] = 1; /* bloqu.lectura hasta llegada de caracter. 1 */ newtio.c_cc[VSWTC] = 0; /* '\0' */ newtio.c_cc[VSTART] = 0; /* Ctrl-q */ newtio.c_cc[VSTOP] = 0; /* Ctrl-s */ newtio.c_cc[VSUSP] = 0; /* Ctrl-z */ newtio.c_cc[VEOL] = 0; /* '\0' */ newtio.c_cc[VREPRINT] = 0; /* Ctrl-r */ newtio.c_cc[VDISCARD] = 0; /* Ctrl-u */ newtio.c_cc[VWERASE] = 0; /* Ctrl-w */ newtio.c_cc[VLNEXT] = 0; /* Ctrl-v */ newtio.c_cc[VEOL2] = 0; /* '\0' */ /* ahora limpiamos la linea del modem y activamos la configuracion del puerto */ tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&newtio);
Para
la comunicación se utilizan las funciones read
y write, aquí un ejemplo:
read(fd,buf,255)
Hebras:
En
la sección de software aparece un diagrama de alto de nivel de
funcionamiento de todo el conjunto, y como se observa, dentro del
programa principal, éste despliega un menú interactivo usando una
hebra.
Las
hebras, hilos o threads son necesarias para
realizar múltiples tareas dentro de un mismo proceso. Es necesario
además proveer mecanismos de sincronización como mutexs.
Estos elemento son declarados de la
siguiente manera.
pthread_t ntid;//id de la thread que contendra el menú
pthread_mutex_t mutexRxTx
= PTHREAD_MUTEX_INITIALIZER;//ejemplo de un mutex.
Para
crear la hebra:
pthread_create(&ntid,
NULL, thr_fn, NULL);
La
tarea que contendrá el menú estará dentro de “thr_fn”, que es declarada de la siguiente
manera.
void * thr_fn(void *arg);
Para
mayor información consultar el manpage de
las threads de linux.
Procesos
hijos:
Para
desplegar los gráficos utilizando Octave se utilizan procesos hijos,
estos procesos son creados con la función fork().
Un
ejemplo sencillo de uso de la función fork():
pid_t pid;//
identificador del proceso
if ((pid = fork()) < 0)
{
perror("fork");
exit(1);
}
if (pid == 0) {// En el
hijo la función fork() retorna un 0
//aquí el hijo
}
//Aquí el padre
|