Introducción
Interfaz.
Conexión
e interacción entre hardware, software y el usuario. El diseño y construcción
de interfaces constituye una parte principal del trabajo de los ingenieros,
programadores y consultores. Los usuarios “conversan” con el software. El
software “conversa” con el hardware y otro software. El hardware “conversa” con
otro hardware. Todo este “diálogo” no es más que el uso de interfaces. Las interfaces
deben diseñarse, desarrollarse, probarse y rediseñarse; y con cada encarnación
nace una nueva especificación que puede convertirse en un estándar más, de
hecho o regulado.
1
5.1
Conceptos básicos
•
Interfaz:
dispositivo electrónico que se conecta entre el PC y los elementos a
ser controlados (actuadores, interruptores, pulsadores, relés, circuitos,
motores, etc.).Su misión es garantizar el correcto aislamiento eléctrico entre
los puertos del PC y los dispositivos externos.
•
Bus:
Normalmente se refiere al conjunto de señales con las que se comunica el microprocesador
con el entorno: memoria o periféricos (a través de las interfaces).:
Figura 1.
Representación lógica de una interfaz
Las
funciones más importantes de un interfaz son:
Interpretar
las órdenes que recibe de la CPU y transmitirlas al periférico
Controlar
las transferencias de datos entre la CPU y el periférico (convertir formatos, adaptar
velocidades,..).
Informar
a la CPU del estado del periférico.
Detección
de errores (defectos mecánicos o eléctricos en el funcionamiento del dispositivo.
Ejemplos: atasco de papel, cambio de un bit, etc.)Los interfaces también se
denominan controladores, interfaces o tarjetas de E/S.
2
5.2
Clasificación
En una
primera aproximación, los puertos y los interfaces del computador se pueden
clasificar en función del tipo de transmisión de información que permitan:
Serie
Puerto
serie: Interfaz RS-232.
Puerto/interfaz
USB.
Puerto
Fire Wire: Interfaz IEEE1394.
Paralela
Puerto
paralelo: Interfaz Centronics ó IEEE 1284.
De igual
forma, las interfaces que interrelacionan en el diálogo entre usuario-máquina
son periféricos que se pueden clasificar según el sentido de la comunicación:
Dispositivos
de entrada: usuario-Máquina.
Los dispositivos de entrada son los que se
utilizan para introducir información en el ordenador. Los sensores del
dispositivo transforman la información en señales físicas que envían a un
controlador o programa del sistema operativo para su interpretación.
Los
dispositivos de entrada más populares son: teclados, ratones, escáneres,
micrófonos, cámaras, monitores táctiles, joysticks, lectores de tarjetas,
códigos de barras, etc.
Dispositivos
de salida: máquina-usuario
Los
dispositivos de salida son los que generan, a partir de una orden de un
usuario, unidades de significado mediante formas y símbolos empleados en la
representación gráfica de la información, o el sonido en el caso de la
representación auditiva. Los dispositivos de salida más populares son: monitores,
impresoras, altavoces, cascos, etc.
5.3
Programación de bajo nivel
Lenguaje
de bajo nivel Un lenguaje de programación de bajo nivel es el que proporciona poca
o ninguna abstracción del microprocesador de un ordenador. Consecuentemente es
fácilmente trasladado a lenguaje de máquina. La palabra "bajo" no
implica que el lenguaje sea inferior a un lenguaje de alto nivel; se refiere a
la reducida abstracción entre el lenguaje y el hardware.
Perifericos
El modelo de Entrada/Salida de UNIX/Linux
UNIX supuso una
revolucion por muchas razones. Sin embargo, una de las mas importantes fue, sin
duda, su modelo de Entrada/Salida. En UNIX, los perifericos se encuentran
representados como simples ficheros del sistema de archivos. De esta forma, si
se quiere enviar datos a, por ejemplo, la pantalla, basta con abrir el fichero
asociado a ella y escribir con las funciones de siempre. El resultado aparecera
en el monitor. Lo mismo ocurre con los puertos serie, paralelo, etc.
El hecho de que se
acceda a todos los perifericos de igual forma simplifica notablemente la
programacion en un sentido, pues el programador no necesita aprender cientos de
funciones especificas para cada dispositivo, y a fin de cuentas, en el 99% de
los casos no se pretende hacer filigranas con los perifericos, sino simplemente
enviar algo y nada mas. Sin embargo ¿que ocurre cuando sí hace falta enviar
opciones especificas a un dispositivo? ¿Que hago para indicar, por ejemplo, la
velocidad de transmisión de un puerto serie, o las coordenadas de impresion si
se trata de un adaptador de video?
La funcion IOCTL
Todo driver soporta
una serie de funciones basicas. En concreto ha de soportar: apertura, lectura,
escritura y cierre. Estas funciones (junto a alguna que otra mas) son las que
permiten acceder a los dispositivos como si de ficheros se tratara. Sin embargo,
existe una quinta funcion que nos permite enviar comandos especificos para cada
periferico. Es la funcion IOCTL (Input/Output ConTroL).
Esta funcion recibe
tres parametros y devuelve uno:
#include <sys/ioctl.h>
int ioctl(int d, int request, void *arg);
El primer parametro
(d) es un identificador de dispositivo. Puesto que se trata de una variable de
tipo int y no de tipo FILE, el fichero del dispositivo ha de abrirse usando la
funcion open() en lugar de fopen(). Por la misma razon, para leer o escribir en
el ha de usarse read() y write() en vez de fread() o fwrite(), y para cerrarlo
se usara close() en vez de fclose(). Como siempre, una mirada a las paginas del
manual resolveran cualquier duda sobre estas cuatro funciones.
El segundo parametro
(request) especifica el comando que se ha de enviar al dispositivo. Estos
comandos dependen del periferico con el que se este tratando. Asi pues, el
comando 1 hara cosas distintas si lo enviamos a un puerto serie o a uno
paralelo.
El tercer parametro
(arg) es siempre un puntero al/los parametros que se le han de pasar al driver,
o bien en donde ha de retornar este los resultados. Como tipo de puntero he
puesto void simplemente porque dependiendo del comando que se envie habra que
poner un puntero a int, a char, o incluso a una estructura.
Por ultimo, ioctl()
nos devuelve un valor que sera cero si la funcion se ha ejecutado con exito, o
-1 si ha habido algun problema. En este caso, el tipo de error lo podremos ver
en la variable global errno (para ello no hay que olvidarse de hacer un
#include <errno.h> al principio del codigo).
El puerto serie
Los puertos serie
tienen asociados una serie de ficheros en el directorio /dev. En versiones
antiguas de Linux estos eran los ficheros
/dev/cua
seguidos de un numero
que especificaba que puerto era cada uno. Asi pues, /dev/cua0 seria el COM1 de
MS-DOS.
En las versiones mas
recientes del nucleo (versiones 2.x) los puertos serie tienen asociados los
ficheros
/dev/ttyS
seguidos tambien de
un numero. Asi pues, /dev/ttyS0 es el COM1 de MS-DOS. Por razones de
compatibilidad con viejos programas, se siguen soportando los viejos ficheros
cua, pero no se recomienda su uso en programas nuevos.
Cambiando los permisos de acceso
Para poder acceder a
un periferico es preciso que su fichero asociado tenga permisos de acceso
adecuados, igual que ocurre con el resto de los ficheros normales del disco
duro. Puesto que el dueño de los ficheros de dispositivo es el usuario root, es
necesario hacer el cambio entrando como el. Asi, para permitir que cualquiera
pueda leer y escribir en cualquier puerto serie, se debe hacer (siempre como
root):
chmod 666 /dev/ttyS*
Si se quiere permitir
que cualquiera acceda al segundo puerto serie, se hara:
chmod 666 /dev/ttyS1
¿De que entradas y salidas dispongo?
Un puerto serie
dispone de los siguientes pines, cuyo numero en el conector es el siguiente:
Nombre Sentido Conector 25 pines Conector 9 pines
TXD salida 2 3
RXD entrada 3 2
RTS salida 4 7
CTS entrada 5 8
DTR salida 20 4
DSR entrada 6 6
DCD entrada 8 1
RI entrada 22 9
GND masa 7 5
TXD y RXD son
respectivamente la salida y la entrada de datos serie. Estos datos se envian en
el siguiente formato:
Por defecto, TXD se
encuentra a -12 voltios (nivel alto). Cuando se quiere enviar un byte, la linea
se pone a 12 voltios (nivel bajo) durante el tiempo de un bit. Es el bit de
inicio, que marca el comienzo de una transmision. A continuacion se envian los
bits del dato, empezando por el menos significativo. El cero (nivel bajo) se
representa con 12 voltios, y el uno (nivel alto) se representa con -12 voltios.
Por ultimo se envian uno o dos bits de parada a -12 voltios (nivel alto) antes
de iniciar la transmision del siguiente.
Vemos que los niveles
de tension se encuentran invertidos con respecto a lo que cabria esperar (el 0
son 12 voltios y el 1 son -12 voltios). Esto solo ocurre en estas dos lineas.
En el resto, el cero son -12 voltios y el uno son 12.
Todas las lineas
estan limitadas en corriente a unos 200mA. Esto protege al puerto en caso de
que ocurra cualquier cortocircuito. Esta caracteristica tambien es aprovechada
por muchos circuitos simples que se conectan al puerto serie, de modo que se
consigue una notable economia de componentes.
Debido a que las
lineas TXD y RXD son controladas por el propio puerto serie y no por el
procesador, en principio no resulta posible indicar directamente un valor de
tension en TXD o leer el que se encuentre en RXD. Todo lo que podemos hacer es
enviar un byte al puerto para que este lo envie por si mismo en el formato
indicado. Sin embargo, en la practica, las UARTs disponen de un bit que, al
activarlo, fuerzan un cero (+12 voltios) en dicho pin.
Funciones IOCTL disponibles para el puerto serie
Seis son las
funciones ioctl que nos interesan para el control de las lineas del puerto
serie: TIOCMGET, TIOCMBIS, TIOCMBIC, TIOCMSET, TIOCSBRK y TIOCCBRK. Para
disponer de estas definiciones es necesario hacer:
#include <asm/ioctls.h>
La funcion TIOCMGET
precisa que se le pase un puntero a int como argumento de la funcion ioctl().
Asi pues, hariamos:
retorno=ioctl(fichero,TIOCMGET,);
En 'argumento' nos
almacenara un patron de bits indicando el estado de las distintas entradas y
salidas del puerto serie.
La funcion TICMBIS
tambien precisa de un puntero a int como argumento de la funcion ioctl(). Asi
pues:
retorno=ioctlfichero,TIOCMBIS,);
activara aquellas
salidas que le indiquemos, dejando inalteradas el resto. Por su parte, la
funcion TICMBIC hace lo contrario: desactiva (pone a cero) las salidas que le
indiquemos, sin modificar el resto. La usariamos asi:
retorno=ioctlfichero,TIOCMBIC,);
La funcion TIOCMSET
activa las salidas que le indiquemos y desactiva el resto. Precisa tambien un
puntero a int como argumento de la funcion ioctl(). Asi pues:
retorno=ioctl(fichero,TIOCMSET,);
activara las salidas
que le indiquemos en funcion del patron de bits que contenga 'argumento'.
El bit que
corresponde a cada entrada y salida viene definido tambien en el fichero
ioctl-types.h. Este fichero se incluye automaticamente con ioctl.h.
En concreto, estan
las definiciones:
TIOCM_DTR DTR 0x002
TIOCM_RTS RTS 0x004
TIOCM_CTS CTS 0x020
TIOCM_CAR DCD 0x040
TIOCM_CD DCD 0x040
TIOCM_RNG RI 0x080
TIOCM_RI RI 0x080
TIOCM_DSR DSR 0x100
Por ultimo, las
funciones TIOCSBRK y TIOCCBRK permiten poner a cero (+12 volts) o devolver al
estado normal, respectivamente, el pin TXD. Como dicho estado de reposo es un
uno, estas funciones nos permiten controlar a voluntad dicha salida.
retorno=ioctl(fichero,TIOCSBRK,);
retorno=ioctl(fichero,TIOCCBRK,);
El puntero no es
modificado, por lo que se puede dar cualquiera.
Por supuesto, existen
otras muchas funciones IOCTL para el puerto serie (man ioctl-list), pero todo lo
que se puede hacer con ellas tambien resulta factible hacerlo con la libreria
TERMIOS.
No hay comentarios:
Publicar un comentario