| |
FUNDAMENTOS de la
TRANSMISIÓN SÍNCRONA
|
|
| |
Artículo sobre los
fundamentos de la Transmisión Síncrona, también conocida como Data & Clock
y un ejemplo de su implementación en C (tanto para emitir como para
recibir).
Utilizada desde antaño en protocolos como PS/2 para ratones y teclados o el archiconocido ABA Track II de
las antiguos lectores de Tarjetas de Banda Magnética, pero que aún la
mayoría de lectores dispositivos modernos implementan, al menos como
emulación del mismo. |
|
| Fundamentos de la
Transmisión Síncrona |
|
Una visión general de qué es, para qué sirve y cómo se utiliza. |
| |
|
Para
intentar acercarnos de forma clara a qué es una transmisión Síncrona
voy a utilizar mi habitual forma indirecta de atacar las cosas y vamos a
empezar por su antagonista por naturaleza: la transmisión Asíncrona.
|
|
Primero
démosle un vistazo a su propio nombre y veamos qué significa esa palabreja
de Asíncrona. Etimológicamente significa exactamente "sin reloj" o sea que
no hay ninguna señal que marque los tiempos en que los datos deben leerse
o están disponibles.
|
Esto significa que en una transmisión Asíncrona tanto la información
transmitida como los tiempos en que ésta debe leerse son uno y todo va
junto. El mejor ejemplo de este tipo de transmisión es la transmisión
serie RS232. En esta forma asíncrona de transmitir información
binaria cada bit es representado por un estado Alto o Bajo de la línea de
transmisión durante un tiempo predeterminado. Este tiempo debe ser
siempre el mismo, dentro de los márgenes de tolerancia normales y que son
de aproximadamente de un 2% del valor nominal.
|
Fijaos por
tanto que esto de Asíncrono no significa sin tiempo sino bien al
contrario significa con tiempos perfectamente definidos y acordados de
antemano ya que de otra forma no habría manera de poner de acuerdo al
emisor y al receptor en cuanto a cuando está disponible cada bit para su
lectura.
|
El sistema asíncrono funcionaría entonces así: En cuanto el receptor
detecta el primer cambio de estado, una línea que pasa de Alto a Bajo por
ejemplo en el RS232, sabe con total seguridad que tras cierto número de
microsegundos transcurridos tendrá disponible el primer bit transmitido
por el emisor, y tras otro igual número de microsegundos tendrá el segundo
bit y ... así hasta el último bit que debe recibir.
|
Se detecta el primer flanco de bajada y a partir de ahí solo debe mirar,
cada plazo de tiempo acordado, en qué estado está la línea de transmisión,
si alto o bajo, para asignar ese valor a cada uno de los bits a recibir.
|
De esta forma cuando decimos que una comunicación RS232 es a 8 bits y a
9600 baudios lo que estamos diciendo es que vamos a recibir 8 estados consecutivos
de la línea de transmisión, separados cada uno de ellos 1/9600 segundos, o
sea un estado cada 104 microsegundos, siendo el primero el estado que
tenga tras los primeros 104 microsegundos transcurridos desde el primer
flanco de bajada.
|
A 19.200 baudios en "tiempo" de cada bit será la mitad, 52 microsegundos,
y a 4.800 baudios será el doble o sea 208 microsegundos. A esta unidad de
tiempo la conocemos como el etu de una transmisión, iniciales de
elementary time unit.
Abajo podemos ver una representación gráfica de esto que estamos tratando,
la transmisión Asíncrona de un byte compuesto por 8 bits (un típico 8N1
9.600)
|
|

|
Una conclusión a la que podemos llegar después de expuesto todo esto sobre
la transmisión Asíncrona es que es imprescindible saber a priori
a qué velocidad vamos a recibir los distintos bits para ajustar nuestra
rutina de recepción a dicha velocidad y mirar así la línea de transmisión
en su momento justo, ni antes ni después, para recibir cada uno de los
bits en el momento en que realmente les corresponde. Cualquier error en el
cálculo dichos tiempos puede hacernos leer bits fantasmas debido a que
leemos dos veces un mismo bit o porque nos saltemos alguno de ellos.
|
Y por fin llegamos a nuestra transmisión Síncrona de datos.
|
Síncrono significa "con reloj" y exactamente eso es
lo que necesitamos, un reloj (o dicho en inglés un Clock). La
transmisión síncrona necesita de dos líneas, una de datos sobre la que se
van a representar los distintos estados de los bits a transmitir y una de
reloj donde vamos indicando cuando está disponible cada bit en la línea de
datos. Esta línea de reloj es la de "sincronización" entre ambos
dispositivos, el emisor y el receptor de la transmisión.
|
De esta forma una transmisión síncrona consiste exactamente en
poner el estado de un bit en la línea de datos, generamos un pulso de
subida y bajada en la línea del reloj, ponemos otro estado de bit en los
datos, volvemos a dar un pulso de subida y bajada en la del reloj ... y
así hasta completar el número de bits que deseemos transmitir.
|
Esta forma de transmisión tiene una clara ventaja, y es que no es
necesario poner de acuerdo en velocidad alguna a emisor y receptor de la
transmisión. El emisor coloca su bit y genera el pulso en el reloj, el
receptor detecta el reloj y mira el estado del bit, y así uno tras otro, a
cualquier velocidad, a distinta velocidad cada bit, a toda la velocidad
posible. Hay pulso significa hay dato, leo y a esperar otro pulso, mas
lento o mas rápido es irrelevante solo es importante aquello de pulso-dato
y a empezar de nuevo.
|
La única limitación es que al receptor le debe dar tiempo a leer el
estado de cada bit tras detectar el pulso de reloj antes de que aparezca
un nuevo pulso.
|
|

|
Notad que en estos ejemplo estamos utilizando la "lógica
negativa" es decir que detectamos los pulsos estando la línea en alto
cuando cae a bajo, o sea recibiendo primero un un flanco de bajada y
después uno de subida para conformar un pulso.
|
Todo lo que estamos tratando sería exactamente igual con los pulsos al
revés, en "lógica positiva" con el flanco de subida primero y el de
bajada después. Esta configuración con las líneas en alto y dando pulsos
negativos es la mas utilizada debido a la estabilidad y resistencia al
"ruido" que tienen. Se consigue conectando una resistencia a VCC para que
mantenga la línea a estado alto y nuestro emisor genera los pulsos
poniendo la línea a GND. El receptor está constantemente recibiendo el
estado alto y detecta cada pulso cuando pasa a bajo.
|
|

|
|
Bueno, y ahora vamos a ver cómo podemos implementar una simple
comunicación Síncrona en C. |
| |
|
Las funciones
para Transmitir de forma Síncrona que vamos a implementar
son dos: Transmite_Bit_Clock_and_Data y
Transmite_Byte_Clock_and_Data. La primera de ellas coloca el estado de
un bit en la línea Data y genera un pulso en Clock, la segunda se encarga
de extraer, bit a bit, el contenido de un byte (de 8 bits) y llamar a la
función anterior. |
| |
| |
Funciones para Transmisión Síncrona |
|
| |
#define OUT_CLOCK
PIN_B0
#define OUT_DATA PIN_B1
void Transmite_Bit_Clock_and_Data(int1 bit){
// Coloca Data
if( bit==0){
output_high(OUT_DATA);
}
else{
output_low(OUT_DATA);
}
// Genero pulso en Clock (500 microsegundos ó 2 Khz)
delay_us(250);
output_low(OUT_CLOCK);
delay_us(250);
output_high(OUT_CLOCK);
}
void Transmite_Byte_Clock_and_Data(char c){
int8 i;
int1 b;
for(i=0;i<8;i++){
b = bit_test(c,i);
Transmite_Bit_Clock_and_Data(b);
}
}
|
|
| |
|
|
|
Para las
funciones de recepción Síncrona vamos a usar el recurso de la
Interrupción Externa de los PIC's, eligiendo estratégicamente el PIN
del reloj (CLOCK) de forma que tengamos disponible una de estas
interrupciones.
|
|
La
interrupción externa la configuramos para detectar los flancos de
bajada (ved la nota anterior sobre la "lógica
negativa") De esta forma cada vez que se dispara la interrupción
sabemos que tenemos disponible un bit en la línea de los datos (DATA).
Lo recogemos sobre nuestro recByte y contamos uno más. Cuando
lleguemos a 8 bits recogidos tenemos nuestro Byte completo y
podemos indicarlo convenientemente poniendo a uno el flag reccomplete.
|
|
Cuando en
main detectamos este reccomplete lo monitorizamos por el puerto
serie y reiniciamos todo para recoger el siguiente byte. |
| |
| |
Funciones para Recepción Síncrona |
|
| |
#define IN_CLOCK
PIN_B0
#define IN_DATA PIN_B1
char recByte=0;
int8 nextBit=0;
int1 reccomplete=0;
// INTERRUPCIÓN por EXT0 Clock CK (Data - Clock) --------
#int_ext
ext_isr() {
int1 bit;
bit=!input(IN_DATA);
bit_clear(recByte,nextBit);
if(bit==1) bit_set(recByte,nextBit);
if(++nextBit=8){
nextBit=0;
reccomplete=1;
}
}
// MAIN ------------------------------------------------
void main(void){
ext_int_edge(0,H_TO_L);
enable_interrupts(int_ext);
enable_interrupts(global);
do {
// Lectura Completa
if(reccomplete==1){
readcomplete=0;
putc(recByte);
}
} while (TRUE);
}
|
|
| |
|
|
| Decodificando el
protocolo ABA Track 2 |
|
Un protocolo usado desde que la electrónica era poco mas que magia. |
|
|
|
Como
complemento ideal a nuestro artículo anterior sobre los
Fundamentos de la Transmisión Síncrona qué
mejor que un ejemplo real como la vida misma. Me propongo ahora que veamos
juntos un protocolo, usado por las antiguas lectoras de tarjetas
magnéticas, que lleva decenas de años en uso y aún hoy viene implementado
en la gran mayoría de lectores de tarjetas modernas, Chip, Proximidad,
Mifare ... etc como emulación de aquellas para mantener cierta
compatibilidad entre los distintos dispositivos a los que están
conectados.
|
|
Este
protocolo ABA Track 2 es un invento de la American Banking
Association (de ahí lo de ABA) y todos y cada uno de sus detalles aparecen
absolutamente descritos hasta su último detalle en la norma ISO-7813.
|
|
No es
nuestro propósito describir esta ISO-7813 como si en ello nos fuese
la vida, sino mas bien acercarnos a ella desde un punto de vista mucho mas
práctico y útil para Picmaníacos de pro que lo único que desean es poder
leer e interpretar uno de estos aparatos con protocolo ABA Track 2.
|
|
Empecemos
pues manos a la obra y demos un primer vistazo a lo que se nos viene
encima, y que no es mas que un "cronograma". O sea una
representación gráfica del estado de un par de líneas en función del
tiempo:
|
|

|
|
Supongamos
que tomamos las dos líneas de salida de un dispositivo que transmite en
ABA Track II y somos capaces de monitorizarlas en algún tipo de
visualizador de líneas digitales (en mi caso he usado mi anterior proyecto
RR Logical Analyzer) No es
necesario pero para que veáis qué ocurre nos viene pero que muy bien.
|
Tened en cuenta que lo que
describimos en el anterior artículo es únicamente el cómo se transmite la
información, y no dijimos ni una sola palabra del qué se está
transmitiendo, del significado de lo que se transmite ni de la
interpretación que podíamos hacer de lo que recibimos de esa transmisión.
|
|
Un
protocolo nos obliga a más cosas que al simple método a usar para ser
capaces de recibir o emitir una información. Nos dice también qué se va a
transmitir y qué significado tiene eso que es transmitido y/o recibido. Es
lo que coloquialmente conocemos como "trama". La trama es lo que realmente
aplicamos a lo recibido para obtener una interpretación válida de los
datos. El protocolo pone de acuerdo al emisor y al receptor en todos y
cada uno de los detalles para conseguir una completa, eficaz y coherente
transmisión y recepción de los datos.
|
Con lo aprendido en el artículo
sobre la
Transmisión Síncrona podemos dar un paso más
con solo interpretar nuestro cronograma anterior.
|
En aquél veíamos cómo una línea
de reloj (CLOCK) nos indicaba cuando debíamos mirar, sensar, catar,
leer la otra línea, la de datos (DATA). De manera tal que al
recibir un pulso del CLOCK si la línea DATA estaba en alto,
a nivel de VCC, lo interpretábamos como que habíamos recibido un
bit 0, y si por el contrario la línea DATA estaba en bajo, a
nivel de GND, lo leíamos como un bit 1.
|
Con esta simple norma íbamos
recibiendo toda un ristra de bits, uno a uno sobre la línea de DATA,
y cada uno de ellos al golpe de batuta de nuestra línea de CLOCK.
|
Con lo cuál estamos en
disposición de añadir a nuestro cronograma la interpretación en Bits de lo
que significa:
|
|

|
Quedémonos por ahora con los
quince primeros bits recibidos: 000001101000001.
|
|
¿Significan
algo? Es difícil de decidir con la sola vista de esos bits. Podríamos
intentar decidir si se ajustan a los patrones de la tabla ASCII, pero
parece complicado ya que no son divisibles por 8, que son los bits que
tiene un byte y que la tabla ASCII nos dice qué significan, o si por el
contrario los dividimos en bloques de 4 bits ... o de cinco ... o ... Así
que vamos a concluir que: ¡No! No significan nada salvo que digamos mas
cosas.
|
|
Y entonces
viene en nuestro salvamento el Séptimo de Caballería, que a nuestros
efectos es nuestro viejo amigo el Protocolo ABA Track II, y que
dice en primer lugar, como Dios hablándole a Moisés con voz profunda y
sentenciosa cu Primer Mandamiento: Primero recibirás un montón de ceros,
de cinco a quince, pero no más ... ni menos. Ni cuatro, ni tres, ni dos,
ni uno, sino cinco o mas.
|
¡Hombre! y vemos que en verdad
así ocurre en nuestra ristra de bits, primero nos llegan un montón de
ceros (originalmente venían quince pero los he dejado en cinco, editando
la imagen del cronograma y dejándolos en cinco hasta que no inventen una
pantallas realmente panorámicas, simplemente no caben a lo ancho).
|
|
Y sigue
nuestro ABA Track 2 con su segundo mandamiento: A continuación
cada Byte de información llegará con cinco bits, los cuatro
primeros definen el dato a enviar y el quinto es la paridad. Que
porque me da la gana va a ver Impar. El Primer bit de los cinco
es el Menos Significativo.
|
|
¡Ea! pues ya
tenemos un poco mas de información y podemos empezar a intentar descifrar
la "trama". A partir del primer 1 que encontremos vamos a dividir nuestro
"churrete" de bits en bloques de 5, de los cuales los 4 primeros son "el
dato" y el quinto se calcula en función de los anteriores.
|
|
Troceando,
que es gerundio, tomamos nuestro afilado cuchillo ritual y nos quedan dos
pequeños bloques: 11010 y 00001 .... y ya sabemos que ambos
son "datos+paridad_impar" Luego el primero vamos a escribirlo en la forma
1101-0 y el segundo como 0000-1.
|
|
Como hemos
visto el -0 del primero y el -1 del segundo nos dicen que es
la Paridad Impar. Esto significa que nos sirve para ajustar en cada
bloque de 5 bits el número de unos que aparecen en él, y que por
definición debe ser un número impar. Así 1101 son tres bits a 1, que es
número impar donde los haya, por lo que el bit de paridad impar debe ser 0
para que no se nos convierta en par, por manos del demonio. Y el segundo
dato recibido es 0000 que no tiene ningún 1 y por ello debemos poner el
bit de paridad a 1 para que en ese bloque también haya un número impar de
unos.
|
|
Y ahora otro
palito mas a la burra. San ABA TKII, santo virgen y mártir, nos dijo que
los primeros serán los últimos en el Reyno de los Bytes, así que tenemos
la obligación de darle la vuelta a los Bits recibidos y ponerlos como dios
manda 1101 es en verdad 1011, o escrito en hexadecimal
resulta que es el número 'Bh', y que el 0000 es 0000, o sea el
hexadecimal '0h', como no podía ser de otra forma.
|
|
Resumiendo: Recibimos un montón de ceros, recibimos el dato 'Bh',
recibimos el dato '0h' ...
|
|
Y continúa
esta feria de vanidades. El ABA Track 2 nos dice muchas mas cosas. Sus
mandamientos no acaban en los dos primeros sino que se añaden por lo menos
cuatro mas a la lista.
|
|
Tercer
mandamiento: Tras la recepción del dato 'Bh', recuerda que era
11010 en binario sin darle la vuelta como un calcetín, Recibirás mas
datos en la misma forma que los anteriores, hasta un máximo de 79,
pero que pueden ser 78 ó 77 ó 76 ... o ... ¡Yá!
|
|
Tras el
tercero ha de venir un Cuarto Mandamiento: Los datos se acaban del todo
cuando recibas un dato 'Fh', o sea una tira de bits de la forma 11111
(Nota que son 4 unos, número par de unos, por lo que la paridad debe ser
también un 1 para que sean 5 unos, impar y no violemos la paridad impar)
|
|
Con esto ya
podríamos leer todos los datos completos de una transmisión ABA Track II
pero ... San ABA es persona quisquillosa y metódica que quiere dejar todo
atado y bien atado. Y como no podemos tener un pez sin cola, ni una vaca
sin rabo, ni un hombre sin ... ¡vamos a dejarlo aquí, haya paz hermanos!
|
|
Nuestro
amantísimo protocolo nos dice que tras enviarnos el 'Fh' aún nos ha de
enviar una dato más para que seamos capaces de saber si todo lo anterior
que nos envió es correcto o no. Para ello nos envía un dato especial
(especial pero no tanto, son cuatro bits mas paridad también) que no es
cualquiera sino el resultado de calcular los sucesivos OR EXCLUSIVOS de
todos los anteriores que nos ha enviado, empezando por el 'Bh' y
terminando por el 'Fh'. A esto se les ocurre llamarlo el LRC o sea
el Longitudinal Redundancy Chek o Comprobación Longitudinal Redundante o
Redundancia de la Comprobación Longitudinal o .... ¡Yá!
|
Y como no, por último, como
traca final fin de fiesta, como estertor de una celebración de altura,
otro sano, largo y completo montón de ceros final, diciendo hasta aquí
hemos llegado, amigos.
|
O sea esto:
|
|

|
|
|
|
Aquí tenemos toda una
transmisión (real) de una serie de datos en Protocolo Serie Asíncrono ABA
Track 2 que consiste en: |
- Una serie de 10 ceros.
- Start Character 'Bh' también conocido
como "Sentinel"
- Los datos 0321215679127
- End Character 'Fh'
- LRC o dato calculado para comprobar
transmisión
- Otra serie de 9 ceros.
|
que podemos representar de esta forma:
|
 |
|
|
|
|
| |
Rutinas en C para la transmisión de Data &
Clock en ABA Track 2 |
|
| |
void
Transmite_Bit_Clock_and_Data(char c){
// Data
if((c&0x01)==0){
output_high(OUT_DATA);
}
else{
output_low(OUT_DATA);
}
// Clock
delay_us(250);
output_low(OUT_CLOCK);
delay_us(250);
output_high(OUT_CLOCK);
}
void Transmite_Byte_Clock_and_Data(char c){
int i;
char bt,paridad=0;
for(i=0;i<4;i++){
bt=(c>>i)&0x01;
Transmite_Bit_Clock_and_Data(bt);
paridad+=bt;
}
bt=~(paridad&0x01);
Transmite_Bit_Clock_and_Data(bt);
}
void output_Code_clock_and_data(void){
int i;
char c,LCR=0x0B;
// transmite cabecera de 15 ceros
for(i=0;i<15;i++) Transmite_Bit_Clock_and_Data(0);
// identificador de TX pista2
Transmite_Byte_Clock_and_Data(0x0B);
// bytes de code
for(i=0;i<nextCodeChar;i++){
c=(Code[i]-'0') & 0x0F;
LCR=LCR^c;
Transmite_Byte_Clock_and_Data(c);
}
// identificador fin
Transmite_Byte_Clock_and_Data(0x0F);
LCR=LCR^0x0F;
// transmite LCR
Transmite_Byte_Clock_and_Data(LCR);
// transmite cola de 15 ceros
for(i=0;i<15;i++) Transmite_Bit_Clock_and_Data(0);
}
|
|
| |
|
|
Esta página se modificó el
27/12/2008
|