La mejor forma de depurar su código es configurar otra máquina Linux y conectar los dos ordenadores mediante un cable null-módem.
Use miniterm
, disponible en el LDP Programmers Guide:
(
ftp://sunsite.unc.edu/pub/Linux/docs/LDP/programmers-guide/
en el directorio de ejemplos) para transmitir caracteres a su máquina
Linux. Miniterm se puede compilar con mucha facilidad y transmitirá todas
las entradas en bruto del teclado por el puerto serie.
Sólo las sentencias define (#define MODEMDEVICE "/dev/ttyS0"
)
tienen que ser comprobadas. Ponga ttyS0
para COM1,
ttyS1
para COM2, etc.. Es esencial para comprobar que todos
los caracteres se transmiten en bruto (sin un procesamiento de salida) por
la línea. Para comprobar su conexión, inicie miniterm
en ambos
ordemadores y teclee algo. Los caracteres introducidos en un ordenador
deberían aparecer en el otro y viceversa. La entrada no tendrá eco en la
pantalla del ordenador en el que escribamos.
Para hacer un cable null-modem tiene que cruzar las líneas TxD (transmit) y RxD (receive). Para una descripción del cable vea el Serie-COMO.
También es posible ejecutar estas comprobaciones con un sólo ordenador, si
tiene un puerto serie no utilizado. Puede ejecutar dos miniterm en sendas
consolas virtuales. Si libera un puerto serie desconectando el ratón,
recuerde redirigir /dev/mouse
, si existe. Si usa una tarjeta
multipuerto serie, esté seguro de configurarla correctamente. Yo tenía la
mía mal configurada, y todo funcionaba bien mientras hacía las
comprobaciones en un sólo ordenador. Cuando lo conecté a otro, el puerto
empezó a perder caracteres. La ejecución de dos programas en un ordenador
nunca es completamente asíncrona.
Los dispositivos /dev/ttyS*
tienen como misión conectar
terminales a su linux, y están configurados para este uso al arrancar.
Hay que tener esto presente cuando se programen comunicaciones con un
dispositivo. Por ejemplo, los puertos están configurados para escribir en
pantalla cada carácter enviado desde el dispositivo, que normalmente tiene
que ser cambiado para la transmisión de datos.
Todos los parámetros se pueden configurar fácilmente con un programa. La
configuración se guarda en una estructura struct termios
, que
está definida en <asm/termbits.h>
:
#define NCCS 19
struct termios {
tcflag_t c_iflag; /* parametros de modo entrada */
tcflag_t c_oflag; /* parametros de modo salida */
tcflag_t c_cflag; /* parametros de modo control */
tcflag_t c_lflag; /* parametros de modo local */
cc_t c_line; /* disciplina de la linea */
cc_t c_cc[NCCS]; /* caracteres de control */
};
Este archivo también incluye todas las definiciones de parámetros. Los
parámetros de modo entrada de c_iflag
manejan todos los procesos
de entrada, lo cual significa que los caracteres enviados desde el
dispositivo pueden ser procesados antes de ser leídos con read
.
De forma similar c_oflag
maneja los procesos de salida.
c_cflag
contiene la configuración del puerto, como la velocidad
en baudios, bits por carácter, bits de parada, etc... Los parámetros de
modo local se guardan en c_lflag
. Determinan si el carácter tiene
eco, señales enviadas al programa, etc...
Finalmente la tabla c_cc
define el carácter de control para el
fin de fichero, parada, etc... Los valores por defecto de los caracteres
de control están definidos en <asm/termios.h>
. Los
parámetros están descritos en la página del manual termios(3)
.
La estructura termios
contiene los elementos c_line
.
Estos elementos no se mencionan ni las páginas del manual para termios de
Linux ni en las páginas de manual de Solaris 2.5. ¿Podría alguien arrojar
alguna luz sobre esto? ¿No debería estar incluido en la estructura
termio
?
Hay tres diferentes conceptos de entrada que queremos presentar. El
concepto apropiado se tiene que escoger para la aplicación a la que lo
queremos destinar. Siempre que sea posible no haga un bucle para leer un
sólo carácter a fin de obtener una cadena completa. Cuando he hecho esto,
he perdido caracteres, mientras que un read
para toda la cadena
no mostró errores.
Es el modo de proceso normal para terminales, pero puede ser útil también
para comunicaciones con otros dispositivos. 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.
El proceso de entrada canónica puede, también, manejar los caracteres
borrado, borrado de palabra, reimprimir carácter, traducir CR
a
NL
, etc..
El Proceso de Entrada No Canónico manejará un conjunto fijo de caracteres por lectura, y permite un carácter temporizador. Este modo se debería usar si su aplicación siempre lee un número fijo de caracteres, o si el dispositivo conectado envía ráfagas de caracteres.
Los dos modos descritos anteriomente se pueden usar en modos síncrono y
asíncrono. El modo síncrono viene por defecto, donde la sentencia
read
se bloquará hasta que la lectura esté completa. En modo
asíncrono la sentencia read
devolverá inmediatamente y enviará
una señal al programa llamador cuando esté completa. Esta señal puede ser
recibida por un manejador de señales.
No es un modo diferente de entrada, pero puede ser útil si está manejando dispositivos múltiples. En mi aplicación manejaba entradas sobre un socket TCP/IP y entradas sobre una conexión serie de otro ordenador de forma casi simultánea. El programa ejemplo dado abajo esperará una entrada de dos orígenes distintos. Si la entrada de una fuente está disponible, entonces será procesada, y el programa esperará otra entrada nueva.
La aproximación presentada abajo parece más bien compleja, pero es
importante tener en cuenta que Linux es un sistema operativo multiproceso.
La llamada al sistema select
no carga la CPU mientras espera una
entrada, mientras que un bucle hasta que hay una una entrada disponible
ralentizaría demasiado el resto de procesos que se ejecuten a la misma
vez.