comunicacion por infrarrojos

70
Ingeniería en Informática Eliezer Ramírez Cabrera Rodrigo Heredero Robayna

Upload: independent

Post on 29-Jan-2023

0 views

Category:

Documents


0 download

TRANSCRIPT

Ingeniería en Informática

Eliezer Ramírez Cabrera Rodrigo Heredero Robayna

1 Introducción .................................................................................................................. 3 2 Objetivos de la práctica ................................................................................... 4 3 Hardware............................................................................................................................. 5 3.1 Sensor Infrarrojos TFMS 5360 ............................................................... 5 3.1.1 Conexionado y acondicionamiento del sensor al microcontrolador................................................................................................... 9

3.2 Conexionado y acondicionamiento del diodo emisor. ...... 10 4 Desarrollo de la práctica............................................................................. 11 4.1 Ejemplo de implementación de programa de prueba para el diodo emisor de infrarrojos. ............................................................... 13

5 Protocolo de comunicación .............................................................................. 16 5.1 Nivel Bajo ............................................................................................................ 18 5.2 Nivel Medio ......................................................................................................... 29 5.3 Nivel Alto ............................................................................................................ 31

6 Implementación ........................................................................................................... 32 6.1 Creación de Librerías............................................................................. 33 6.2.1 Emisor_Nivel_Bajo.asm...................................................................... 34 6.2.2 Receptor_Nivel_Bajo.asm ................................................................ 39

6.3 nivel_paquetes.lib ....................................................................................... 45 6.3.1 Emisor_Paquete.asm........................................................................... 46 6.3.2 Receptor_Paquete.asm ........................................................................ 49

6.4 nivel_paquetes_variables.lib ............................................................. 51 6.5 Divide_Paquetes_Emisor.asm .................................................................. 51 6.6 Divide_Paquetes_Receptor.asm ............................................................. 54 6.7 Divide_Paquetes_Emisor (2).asm........................................................ 56 6.8 Implementación del nivel bajo en modo rápido.................. 59 6.9 Implementación del nivel bajo con paridad. ........................ 61 6.9.1 Emisor............................................................................................................. 62 6.9.2 Receptor: ..................................................................................................... 65

7 Manual de instrucciones ................................................................................... 65 7.1 implementación física:............................................................................. 65 7.2 implementación software:..................................................................... 65

8 Restricciones del sistema de comunicación por infrarrojos. ..................................................................................................................... 68 9 Ejercicios Propuestos......................................................................................... 69 10 Referencias bibliográficas ......................................................................... 70

1 Introducción

En la presente práctica vamos a hacer uso del microcontrolador PIC16f84 para el control de un sistema de comunicación basada en Infrarrojos. El microcontrolador se hace idóneo para este tipo de comunicaciones, ya que:

Como en todo sistema de comunicación, se dan un emisor del

mensaje y un receptor del mismo. Es un caso básico de comunicación, donde el mensaje a transmitir entre un dispositivo y otro es necesario encapsularlo en un protocolo, o lo que es lo mismo codificarlo. El PiC, como microcontrolador, posee capacidad de procesamiento de datos, lo que permite codificar y decodificar un mensaje.

En un sistema de comunicación es fundamental la flexibilidad, que nos

permita explotar los recursos al máximo y estudiar distintas estrategias de comunicación. El PIC nos permite esa ductilidad, ya que nos permite configurar cualquier sistema de comunicación sin hacer modificaciones en el Hardware, simplemente cambiando el programa del microcontrolador.

A parte, estamos ante un caso típico de instrumentación basada en sensores y actuadores de infrarrojos. Debido a la naturaleza del infrarrojo, estos dispositivos trabajan en un rango de frecuencias bastante altos ( por ejemplo, como se verá, el sensor de infrarrojos trabaja a 36khz). Esto requiere el uso de señales ( tanto en el emisor como en el receptor) acondicionadas con una frecuencia elevada, que permita el muestreo correcto de la señal sin pérdida de información.

.El ciclo de instrucción del PIC consta de 4 ciclos del reloj principal, y por tanto el tiempo de ciclo de instrucción es 1 µs (ya que la frecuencia del reloj es de 4Mhz ). Esto lo hace lo suficientemente rápido para poder hacer el muestreo de la señal en el receptor o la emisión de la señal en el correspondiente emisor.

Por estas razones, por ejemplo, un módulo de adquisición como LabJack con muy baja frecuencia de muestreo y actuación no sería suficiente para este sistema.

Finalmente, la lógica discreta con la que trabaja el microcontrolador

permite el manejo de señales de una manera más robusta y ajena de fallos, rasgo fundamental en un sistema de comunicación donde un solo bit pude modificar completamente el mensaje que se quiso enviar originalmente.

Para el diseño y estudio de este sistemas, vamos a pasar por distintas etapas, que

consistirán en: Estudio de los componentes básicos a utilizar, principalmente el

Sensor de Infrarrojo TFMS5360 y el emisor de Infrarrojo LED. Estudio del Acondicionamiento necesario para acoplamiento de los

dispositivos Hardware a la placa en la que se encuentra el PIC16f84. Estudio del Protocolo de comunicación para envío de mensajes entre

emisor y receptor. Aplicación a programas del sistema de comunicación implementado.

2 Objetivos de la práctica

En esta segunda práctica de “Diseño de sistemas basados en microprocesadores” hemos decidido realizar una práctica basada en la comunicación por infrarrojos. La idea de hacer una práctica de este tipo no fue repentina. Antes de realizar esta práctica barajemos otras posibles prácticas de libre elección para esta asignatura. Algunos ejemplos de prácticas candidatas fueron: control de un lector de códigos de barras, traducción de código Morse a palabras en lenguaje natural y viceversa o las mismas prácticas propuestas por el profesor. La primera opción, la de un control de un lector de código de barras, fue descartada porque a simple vista parecía bastante complicada. Empecemos a pensar en cómo realizarla y comencemos a atormentarnos antes de tiempo, aunque quizás sin razón. Pensando en la manera de realizarla nos dimos cuenta de que quizás tendríamos que implementar una librería de operaciones matemáticas tales como la división o la multiplicación, ya que la idea para identificar cada una de las barras del código de barras, suponiendo que no siempre el sensor de luz se va a mover a la misma velocidad ya que estaría manejada por una mano humana pero suponiendo también que cuando empieza a leer el código de barras lo leerá todo a la misma velocidad, era tener una primera barra de referencia a partir de la cual midiéramos el ancho de cada una de las barras según el tiempo que estuviera sobre ellas el sensor de luz. Esta forma de medir el ancho de las barras conllevaría tener que realizar alguna operación matemática debido a las reglas de tres que tendríamos que haber usado para identificar el ancho de las barras usando como medida el tiempo. Es por ello por lo que era probable que tuviéramos que realizar la librería matemática. La verdad es que esta idea nos daba un poco de respeto, ya que tendríamos que realizar una librería matemática, comprobar que funcionara y tendríamos que suponer que la velocidad con la que se recorre el código de barras es constante (algo poco probable si es una mano humana la que maneja el sensor de luz). En principio teníamos intención de realizar esta práctica, hasta que ser nos ocurrió que una práctica interesante también de realizar sería la segunda opción comentada, la del código Morse. Esta práctica, aunque interesante, sería también bastante sencilla. Si utilizáramos el teclado para introducir las señales necesarias al PIC, solo tendríamos que comprobar el tiempo que permanece pulsada una tecla del teclado y compararlo con una tabla de rangos de tiempos (tendrían que ser rangos de tiempos debido a que quien lo manejaría sería una persona, con lo que no podría medir con una precisión de microsegundos el tiempo que tiene que mantener pulsada la tecla) para determinar el símbolo al que representa el código Morse identificado. Aunque esta práctica parecía sencilla, se podría complicar si pensáramos en realizar el proceso contrario, es decir, traducir una ristra de letras a una salida en código Morse. Además se podría añadir que esa salida en código Morse se pudiera transmitir a otro PIC que leyera el código Morse, lo tradujera, y mostrara en la LCD los símbolos correspondientes a la transmisión en Morse traducidos.

En principio, esta transmisión se realizaría por contacto físico, es decir, mediante un cable conectando los dos PIC. Fue entonces, pensando en la manera de realizar esta práctica, cuando se nos ocurrió que la transmisión podríamos realizarla sin necesidad de un contacto físico debido a que podríamos utilizar medios de transmisión como el de un diodo emisor de infrarrojos, como el mando de la televisión, ya que éste acepta un rango bastante amplio en la dirección del emisor y es fácil de conseguir tanto el emisor como el receptor de infrarrojos. Finalmente, de aquí surgió la idea de realizar un PIC emisor de señales en infrarrojo y un PIC receptor de señales infrarrojo. Pero lo que decidimos finalmente no fue enviar código Morse, puesto que si ya teníamos los instrumentos necesarios para realizar la comunicación mediante infrarrojos, podríamos enviar bytes en lugar de código Morse, y hacer que dos PICs se comunicaran mediante este método. Esta práctica es interesante de realizar y se le puede añadir la complejidad que se desee. Además, como ya hemos dicho antes, los instrumentos necesarios para realizar esta práctica eran fáciles de conseguir, ya que todos los televisores tienen un receptor de infrarrojos y un mando con un diodo emisor. En el caso de que no pudiéramos conseguir estos dispositivos (el diodo emisor y el receptor de infrarrojos), podríamos comprarlo en una tienda especializada en electrónica, ya que estos dispositivos se pueden conseguir a bajo precio. Es por esto por lo que definitivamente decidimos hacer esta práctica. En principio haríamos que dos microcontroladores PICs se comunicaran entre ellos y después le iríamos añadiendo robustez y especificidad a esta emisión-recepción, ampliando la práctica mientras tuviéramos tiempo. 3 Hardware

3.1 Sensor Infrarrojos TFMS 5360 Este sensor de infrarrojos es un sensor que se suele utilizar en los televisores y en vídeos para recibir las señales que tenían los mandos a distancia a éstos. Son bastante comunes y fáciles de conseguir. Además en Internet se pueden conseguir hojas de características de este tipo de sensor. Este sensor tiene tres patillas, una para el voltaje que se le suministre desde fuera, otra para la tierra y la tercera para la salida de datos. Estas tres patillas están conformadas como se muestran en la siguiente figura:

Las patilla GND es la patilla que deberá de estar conectada a tierra. La patilla Vs será la patilla que deberá estar conectada a la fuente (que deberá de ser de 5 voltios). La patilla OUT es la que nos proporcionará la salida del sensor, según la información que consiga captar de las ondas de luz infrarroja que reciba. Este sensor es de la familia de los sensores TFMS 5..0. Las características a destacar de esta familia de sensores son:

Poseen un detector de luz y un preamplificador en un solo encapsulado.

Una salida activa a nivel bajo. Esto quiere decir que la salida del sensor estará proporcionando 5 voltios mientras el sensor no reciba nada. Cuando reciba un tren de pulsos a una velocidad adecuada, el sensor de infrarrojos tendrá en su salida de datos 0 voltios. Este tipo de lógica nos permite conocer cuando se ha estropeado el sensor, ya que, mientras no reciba el tren de pulsos necesario, deberá contener 5 voltios en su línea de salida. Si hubiera un cero podríamos deducir que no está funcionando correctamente.

Filtro interno para frecuencias PCM.

Alta inmunidad contra ambientes luminosos.

Consumo de 5 voltios, baja consumo de potencia

Compatibilidad con TTL y CMOS

Transmisión continua es posible. Más adelante se comentará esta característica más detenidamente.

Todos los sensores de infrarrojos de la familia TFMS 5..0 tendrán la misma forma de la figura y la característica más relevante que los diferenciarán será la frecuencia en la que trabajarán estos. En las características de estos sensores se ha hablado de alta inmunidad contra ambientes luminosos. La alta inmunidad es conseguida debido a que para que el sensor de una respuesta a una onda de luz infrarroja deba recibir esa onda con unas características determinadas. Esto es así porque si diera una salida a bajo nivel para cualquier onda de luz infrarroja, se produciría una lectura incontrolada del sensor, estaría constantemente leyendo siempre y cuando hubiera una fuente de luz que pudiera proporcionar ondas de luz infrarroja al sensor, como puede ser la luz solar. El sensor debe poder distinguir entre cuando recibe la luz infrarroja de forma controlada y cuando recibe ruido. Es por ello por lo que para que el sensor dé una respuesta acertada tengamos que enviar un tren de pulsos bien definidos de ondas infrarrojas. Este tren de pulsos tiene que tener una determinada frecuencia asociada que coincida con la frecuencia reconocida por el sensor. Más adelante se explicará como ha de enviarse un tren de pulsos y con qué retardos para poder recibir con el sensor de infrarrojos una respuesta continua.

En concreto el sensor de infrarrojos utilizado por nosotros es el TFMS 5380. Este sensor trabaja a una frecuencia de 38 KHz. Eso significa que el período de cada pulso de un tren de

pulsos de infrarrojos deberá durar 1 0,0000263138000

= segundos, es decir, 26,31

microsegundos. Otro punto de las características de las que se ha hablado es el de la transmisión continua porque debido a las especificaciones del sensor, éste debe recibir un tren de pulsos, como se acaba de comentar, para que responda a unas ondas de luz infrarroja. Este tren de pulsos deberá mantenerse activo durante un tiempo menor o igual al 40 % del tiempo total que dura el pulso que se obtendrá del sensor, es decir, que el período en el que está recibiendo el sensor el tren de pulsos tiene que ser igual a 0,4 veces el tiempo que tarda el sensor en recibir el tren de pulsos mas estar un período inactivo antes de recibir el siguiente tren de pulsos. Evidentemente, para lograr obtener estas características tiene que haber dentro del encapsulado mucho más que un diodo receptor de luz infrarroja. En la siguiente figura se puede ver como está estructurado el sensor de infrarrojos interiormente:

Se puede observar que hay un circuito de control, un demodulador, un transistor para la salida,.... El circuito de control probablemente sea el que controla que los tiempos de envío de las ondas de infrarrojos (trenes de pulsos, silencios, ...) sean los correctos, mientras que el circuito modulador sea el que permite que la salida del sensor sea una salida continua en respuesta al tren de pulsos de entrada. En cambio, el circuito del transistor lo que permite es mantener una salida a 5 voltios mientras el transistor, algo que ocurrirá cuando reciba una señal correcta el sensor de infrarrojos. Esto es así puesto que el transistor actúa como una especie de conmutador, que cerrará el circuito cuando reciba una señal correcta y lo abrirá mientras esto no ocurra, manteniendo la salida a 5 voltios mientras esté abierto y a 0 voltios (puesto que está a tierra) cuanto el transistor actúe cerrando el circuito. En la siguiente figura se muestra como debe de ser una transmisión para que el sensor la detecte adecuadamente:

En la figura las dos barras pequeñas sobre el eje x indican una especie de abreviatura. Por ejemplo, las dos barras pequeñas sobre el eje x que se encuentran en el periodo inactivo de la señal indican que la señal permanecerá inactiva durante un tiempo mayor al que se ha podido representar en el gráfico. Lo mismo ocurre con las dos barras en el eje x durante el tren de pulsos. Indicarán que el tren de pulsos se mantendrá durante un tiempo que era tedioso de representar. En esta figura se puede ver que se ha llamado tpi al tiempo que permanece activo el tren de pulsos y T al tiempo que transcurre desde que se activa el tren de pulsos de la primera señal hasta que se recibe el siguiente tren de pulsos. Ahora, una vez puesto nombres a los tiempos, podemos repetir la característica que expresábamos antes de una forma menos tediosa de entender. Para que el sensor de infrarrojos reciba de una forma correcta los datos, el tiempo T debería estar compuesto por un 40% (como máximo) de su periodo de un tren de pulsos y un 60% (como mínimo) de una señal inactiva. Además, en la hoja de características se especifica que para un funcionamiento óptimo del sensor, el tpi debería de durar como mínimo 400 µs. Esto implica que para que funcionara de forma óptima el sensor TFMD 5380 debería recibir como mínimo 400/26 pulsos (un pulso dura 26 µs aproximadamente a 38 kHz de frecuencia) que son unos 15 pulsos. En la hoja de características viene especificada la respuesta que daría el sensor a una señal recibida como la de la anterior figura. Según la hoja de características la señal que proporcionaría el sensor a una entrada como esa es parecida a la siguiente:

Según esta figura que nos proporciona la hoja de características, el tiempo en le que el sensor estará dando una señal a nivel bajo es igual al tpi, aunque con un error de ±160 microsegundos. En verdad, aunque la hoja de características de este sensor insiste en que debe de haber un 60% de tiempo inactivo y un 40% de tiempo a tren de pulsos para un correcto funcionamiento hemos comprobado empíricamente que en realidad con que el periodo inactivo y el periodo a tren de pulsos sea un 50% del periodo total basta para que el funcionamiento sea correcto.

Es por ello que el tiempo que hemos utilizado en nuestra práctica para el tren de pulsos es la mitad del tiempo total, ya que es más fácil de manejar dividiendo por dos y multiplicando por dos los periodos, que en adelante trataremos como marcos de bits para la transmisión. Debido a estas características para la transmisión, si quisiéramos enviar la información como bits, en donde un nivel bajo en la salida del sensor indicaría la recepción de un 1 y un nivel alto la inexistencia de la recepción o la recepción de un cero, para enviar un byte de información la forma de enviar los datos por parte del emisor sería la siguiente: Enviando el byte 10101010

A una transmisión como esta el sensor de infrarrojos respondería así: Es decir, que en el control del receptor tendríamos que definir un marco de bit de duración suficiente para que le de tiempo a leer al sensor el tren de pulsos y el periodo inactivo. Además, tendríamos que definir un programa controlador que trabajara con lógica inversa. La salida del sensor es continua, como se ha dicho antes. Esto facilita las cosas de cara a implementar un programa para el controlador del sensor (en este caso un PIC), ya que solo habrá que comprobar el nivel en el que está la señal, y no si se produce un tren de salida del sensor.

3.1.1 Conexionado y acondicionamiento del sensor al microcontrolador.

En el siguiente circuito se puede ver un esquema general de conexionado de un sensor infrarrojos de la familia TFMS 5..0 con un microcontrolador.

Marco de bit

1 1 1 1 0 0 0 0

Marco de bit

1 1 1 1 0 0 0 0

Tren de pulsos a 38 KHz

En la figura se puede ver el diodo emisor (el diodo situado a la izquierda) y el receptor (el situado a la derecha). El diodo receptor está dentro del encapsulado del sensor de infrarrojos. En nuestro caso el diodo emisor tiene que estar apuntando directamente al sensor de infrarrojos, ya que debido a la baja intensidad que proporciona el PIC al diodo emisor, éste no puede emitir la luz infrarroja en un amplio rango de actuación. Aún así, el diodo emisor puede estar situado a unos metros del sensor, aunque siempre apuntando al sensor. La salida del sensor irá directamente conectada al microcontrolador. En el circuito implementado por nosotros iría directamente al bit 4 del puerto A. Se puede ver en la figura anterior como se pueden añadir una resistencia de 330 Ω en el cable de alimentación del sensor y un condensador de 4.7 µF conectando la entrada de suministro de voltaje del sensor a la salida a tierra del mismo. Esto nos serviría si quisiéramos o necesitáramos evitar perturbaciones en el suministro de energía. Otro aspecto importante para el acondicionamiento del sensor es el añadido opcional de una resistencia entre la entrada de suministro de energía del sensor y la salida de datos. Ésta resistencia deberá de tener un valor resistivo de más de 10 kΩ para su correcto funcionamiento. Éste último aspecto fue determinante en nuestra práctica, ya que uno de los problemas que nos surgió fue el que no conseguíamos que funcionara correctamente la conexión del PIC con el sensor. La razón era que la intensidad, sin esta resistencia añadida, circulaba hacia la tierra y no llegaba hasta el sensor de infrarrojos. Una vez colocada esta resistencia logremos que el sensor funcionara correctamente. Ahora habíamos conseguido que la intensidad no fuera toda a tierra y que el sensor pudiera devolver intensidad al PIC en su respuesta. 3.2 Conexionado y acondicionamiento del diodo emisor. El emisor es un simple diodo emisor de infrarrojos conectado a un microcontrolador que le proporcionará voltaje o no según este microcontrolador quiera enviar o no ondas de luz infrarrojas. El circuito implementado es como el que sigue:

Para que el diodo emisor funcione con la intensidad que proporciona el microcontrolador debe de tener menos de 250 Ω de resistencia. En nuestro caso hemos implementado algunos añadidos adicionales a la práctica. Es por esto por lo que necesitamos el puerto B del microcontrolador PIC tanto para el teclado como para establecer una comunicación con el diodo emisor. Esto trae ciertos problemas consigo, como podría ser que no llegan los datos necesarios a través del puerto B y desde el teclado hasta el microcontrolador. Es por ello por lo que, como acondicionamiento, hemos añadido un potenciómetro entre la salida del puerto B y la entrada al diodo emisor para poder controlar la intensidad que llega al diodo. En nuestro caso, cuando queramos manejar el teclado, tendremos que ampliar el valor de la resistencia del diodo. En cambio, si queremos manejar el diodo emisor tendremos que disminuir bastante el valor la resistencia para que llegue hasta el emisor la intensidad adecuada para que pueda emitir infrarrojos. La intensidad que proporciona el PIC es de aproximadamente unos 0.65 Miliamperios. Esto puede ser un problema. En el caso del diodo emisor, esta intensidad es suficiente como para que emita los infrarrojos, aunque lo hace en un rango muy restringido. Habrá que situar el diodo emisor en la dirección que apunta al sensor de infrarrojos para que éste pueda recibir la luz infrarroja que éste emite. Creemos que este rango de emisión tan restringido es debido a la baja intensidad proporcionada por el microcontrolador PIC, ya que este emisor lo hemos obtenido de un mando de una televisión. En el mando de la televisión este emisor lograba alcanzar un rango de emisión bastante amplio. Es por ello por lo que creemos que si lográramos amplificar la intensidad (mediante un transistor) que le llega al diodo conseguiríamos un mayor rango de actuación del emisor. 4 Desarrollo de la práctica.

Antes de empezar con esta práctica había que conseguir el sensor de infrarrojos que queríamos utilizar. La forma de conseguirlo podría ser obteniéndolo de un vídeo o televisor casero, tanto el emisor como el receptor, o comprándolo en un tienda de electrónica. Inicialmente logremos conseguir tanto un sensor como un emisor de un televisor y un vídeo. Al principio de esta práctica utilizábamos un sensor TFMS 5360 a 38 khz. posteriormente, cuando ya teníamos bastante inicializada la práctica, este sensor pasó misteriosamente a mejor vida y nos vimos obligados a comprar uno similar.

µC

5V

GROUND

< 250 Ω

El sensor que logremos comprar en una tienda de electrónica fue un sensor TFMS 5360, que va a 36 kHz, por lo que no nos hizo falta modificar demasiado el programa para que este sensor aceptase el tren de pulso que era enviado por el emisor, ya que como se puede ver en la siguiente fórmula, tenemos que el número de instrucciones que tenemos que ejecutar para conseguir el tren de pulso a 38 kHz son dos menos que para conseguir que el tren de pulsos vaya a 36 kHz, por lo que solo habrán de añadirse dos instrucciones nops, una en cada semiperiodo de un pulso del tren de pulsos.

1 1000000º 26,315738 38000

MHzN deinstruccionesKHz

= = =

1 1000000º 27,777836 36000

MHzN deinstruccionesKHz

= = =

Antes de comenzar a realizar el programa de control del emisor queríamos comprobar que el sensor que teníamos funcionaba, y como funcionaba. Esto lo hicimos con la ayuda de un osciloscopio y con una fuente de corriente alterna, aunque con la fuente alterna no conseguimos una señal por parte del sensor con el suficiente sentido como para poder sacar conclusiones. El sensor lo conectemos a una fuente de corriente continua que nos proporcionaba 5 voltios. El osciloscopio lo conectemos a la patilla de salida del sensor, situada a la derecha de éste desde un punto de vista frontal, para poder conocer la respuesta del sensor a los diferentes trenes de pulsos.

El osciloscopio tenía que estar configurado para que pudiera mostrar una salida de 5 voltios. De la forma en la que nosotros lo teníamos configurado era para que mostrara una escala de cinco voltios por cuadrado de la pantalla del osciloscopio. Con esto sabíamos cuando estaba a 5 voltios la salida del sensor de una forma muy simple, ya que cada cuadro equivalía a 5 voltios. Para configurar el tiempo en el osciloscopio se puede ir modificando la escala de los cuadros para ver cual es la escala preferida, según la frecuencia a la que esté recibiendo el sensor. Lo que interesa es poder ver el ancho del pulso para conocer el tamaño del marco de bit que nos está proporcionando el sensor de infrarrojos.

Osciloscopio 5 Voltios

Con la ayuda del osciloscopio vimos que la fuente de corriente alterna no conseguía hacer funcionar al sensor de una forma controlada. Esto se puede explicar debido a las características del sensor. El sensor, para funcionar correctamente, necesita periodos de inactividad tan amplios o más amplios aún que el periodo de tiempo en el que está recibiendo el tren de pulsos. Las otras alternativas al generador de corriente alterna eran implementar un programa de prueba para probar el sensor o utilizar un mando a distancia para intentar ver lo que recibe el sensor cuando se le apunta con la mano y se intenta establecer una comunicación con el sensor. La primera alternativa era la más sencilla de realizar, ya que teníamos el mando y habíamos conectado el sensor al osciloscopio, por lo que solo teníamos que apuntar y apretar un botón. Con el mando de la televisión vimos que el sensor funcionaba puesto que daba una respuesta que parecía bastante más controlada y regular que la respuesta que nos proporcionaba el generador de corriente alterna. Una vez probado que el sensor de infrarrojos funcionaba, teníamos que ver como funcionaba el emisor de infrarrojos. Esto era un poco más complicado, ya que para ello había que conseguir que el diodo emitiera una serie de pulsos que fueran acordes con las características del sensor. Con el fin de emitir el tren de pulsos implementemos un programa sencillo en ensamblador. Conectemos el diodo emisor al PIC y comencemos la transmisión. Estuvimos probando y no obteníamos en el sensor de infrarrojos lo que queríamos, hasta que nos dimos cuenta que era por el tiempo de inactividad que había que dejar después de cada tren de pulsos en el emisor. Estuvimos probando entonces con variaciones en le tiempo del tren de pulsos, poniéndolo en el 60% del periodo total, el 40%, .... Hasta que finalmente dimos con que a pesar de que la hoja de características del sensor nos decía que tenía que ser el 40% del periodo o menos, utilizando el 50% también obteníamos unos resultados aceptables. Con trenes de pulsos de mayor duración con respecto al período inactivo la respuesta no estaba controlada. No obteníamos una respuesta regular, (al igual que lo que obteníamos con el generador de corriente alterna), mientras que con trenes de pulsos con una menor duración con respecto al período inactivo obteníamos una respuesta mucho más regular, y que concordaba con lo que queríamos transmitir. A continuación veremos un programa ejemplo que se puede utilizar para obtener una respuesta conocida del sensor de infrarrojos.

4.1 Ejemplo de implementación de programa de prueba para el diodo emisor de infrarrojos.

Como ya se ha dicho, el sensor de infrarrojos TFMS 5360 no recibe cualquier tipo de señal basada en infrarrojos, debido a que si fuera de esta forma, estaría constantemente recibiendo señales debido a la luz solar.

Debido a esto, y para probar el sensor infrarrojos, debemos implementar un programa para el PIC 16F84 en el que reproduzca un tren de pulsos para el emisor de infrarrojos con el fin de poder analizar la respuesta del sensor de infrarrojos ante una señal de estas características. El PIC 16F84 va a una frecuencia de 1 MHz. Necesitamos una frecuencia para el tren de pulsos de 36 KHz, por lo tanto, con el microcontrolador PIC 16f84 tendremos una velocidad suficiente para poder reproducir el tren de pulsos necesario. Lo único que se habrá de tener en cuenta para poder implementar un programa que defina un tren de pulsos adecuado al sensor de infrarrojos TFMS 5360 es el tiempo que tardan las instrucciones en ejecutarse, ya que no todas las instrucciones se ejecutarán en el mismo período de tiempo, para que el sensor de infrarrojos logre distinguir el tren de pulsos de señales ruidosas externas. Es por ello que antes de empezar tendremos que hacer una valoración del número de instrucciones que debemos introducir entre el inicio y el fin de cada semiperíodo del tren de pulsos para poder definir éste con una precisión aceptable para el sensor. Una instrucción en el PIC 16F84 se ejecutará en 1 microsegundo, mientras que una instrucción de salto, o una instrucción condicional en la que se produzca el salto debido al valor de la condición, tardará en ejecutarse 2 microsegundos, es decir, dos ciclos de reloj del microcontrolador. Si tenemos en cuenta estos tiempos de ejecución para las instrucciones, podría implementar una macro fácilmente que realizara un semiperíodo para un tren de pulsos. Para crear esta macro debemos tener en cuenta la siguiente fórmula en la que determinamos el número de instrucciones que necesitamos para simular el tren de pulsos:

1 1000000º 27,777836 38000

MHzN deinstruccionesKHz

= = =

Esta fórmula se puede determinar mediante una simple regla de tres, diciendo algo parecido a que “si el número de instrucciones que se deberían ejecutar si el microcontrolador fuera de 36 KHz es de una instrucción para completar un período, ¿Cuántas necesitaríamos si tuviéramos un microcontrolador a una frecuencia de 1000 KHz?”. Con lo que el número de instrucciones que necesitaríamos se calcularía con la siguiente regla de tres: El resultado de esta fórmula ha dado un número decimal de instrucciones, pero no afectará demasiado a la forma en la que lee el sensor la señal si redondeamos hacia abajo. Es decir, que para realizar un período completo necesitaremos las instrucciones necesarias para completar los 27 microsegundos. Para implementar los trenes de pulsos podríamos utilizar macros, o llamadas a funciones que realicen el semiperíodo positivo y el semiperíodo negativo. En este caso utilizaremos macros, ya que si utilizáramos funciones tendríamos que descontar las instrucciones de salto que se producen tanto en la llamada como en el retorno de la función. De todas formas, habrá que descontar las instrucciones que se produzcan para realizar los saltos el bucle, en el que implementaremos el número de pulsos que queremos que contenga el tren de pulsos.

36 KHz 1 instrucción

1000 KHz x instrucciones

El procedimiento podría ser de la siguiente forma: La macro semiperiodo estaría diseñada como sigue: MACRO semiperiodo fill (nop),12 ENDM Como se puede ver, tenemos solo 12 instrucciones, en lugar de 13, esto es debido a que además del semiperíodo, necesitamos ejecutar las instrucciones bsf y bcf que activan o desactivan el bit cero del puerto B. ; 1º Ponemos el puerto B como salida, vamos a enviar la señal al diodo emisor desde el bit cero del puerto B. bsf STATUS,RP0 movlw 00h movwf TRISA bcf STATUS,RP0 ; 2º Creamos el bucle llamando a la macro que realiza el retardo correspondiente a un semiperíodo, para ello cargamos el número de veces que queremos que se ejecute el bucle. movlw Npulsos mowf Contador Bucle bsf PORTB,0 semiperiodo bcf PORTB,0 semiperiodo decfsz Contador,1 goto Bucle end El tren de pulsos que conseguiríamos por la salida del bit cero del puerto B con este programa sería parecido al siguiente:

Cada vez que se ejecute el bucle se ejecutará un periodo del tren de pulsos. Se puede ver como entre pulso y pulso además se ejecutan tres instrucciones. Esto se podría arreglas utilizando el retardo del semiperiodo a cero, pero no es necesario, ya que la velocidad a la que

se ejecutan las instrucciones nos permiten cierto error. Además veremos que el sensor de infrarrojos nos permite cierto error también. No es demasiado rígido con lo que nos especifica la hoja de características. Con este programa ya tendríamos implementado un tren de pulsos. Ahora solo queda implementar el período de tiempo que necesitamos que se encuentre inactivo el diodo emisor para que hay una respuesta controlada por parte del sensor de infrarrojos. Para implementar este periodo de inactividad simplemente habrá que hacer el mismo bucle anterior pero las dos instrucciones que activan y desactivan el puerto, ahora tienen que ser las dos de desactivado del puerto, es decir, bcf, o también ponerlo a cero una vez y realizar el bucle anterior sin activar ni desactivar el puerto de salida B, puesto que ya estaría desactivado. Podría ser algo como sigue: movlw Npulsos mowf Contador Bucle1 bcf PORTB,0 semiperiodo bcf PORTB,0 semiperiodo decfsz Contador,1 goto Bucle1 end Se han puesto las dos instrucciones bcf puesto que aunque no son necesarias, si queremos contabilizar al 50% exacto del periodo total sin mucho error deberíamos poner el mismo número de instrucciones. Con este programa ya tendríamos implementado un programa de prueba para el sensor de infrarrojos. 5 Protocolo de comunicación

Primeramente, decir que la unidad básica de transmisión es el Byte. Por tanto, una de nuestras tareas de síntesis del mensaje será precisamente descomponerlo hasta llegar a la unidad básica transmisible en este sistema de comunicación. Este tamaño es suficiente para lo que es el propósito de esta práctica, que no es más que el de la transmisión de mensajes en forma de Caracteres ASCII.

Llegados a esta unidad básica de transmisión, dadas las restricciones Hardware que presenta el proyecto ( tenemos un único emisor de infrarrojo y un único receptor de infrarrojo), vamos a desarrollar una comunicación unidireccional (1 pic controlará al emisor y el otro al receptor).

Por este, el sistema de control de errores se verá afectado por esta “unidireccionalidad”, y, como veremos más adelante, será imposible que el receptor pueda comunicar al emisor que

debe de mandar de nuevo el último envío ya que llegó con error, por tanto, nuestro sistema de control de errores se basará en la redundancia ( ya que no puede el receptor avisar al emisor que le llegó mal el envío, que el emisor mande varias veces cada envío por si hiciera falta repetir el mensaje).

Por otra parte, la transmisión va a ser de tipo serie, ya que es imposible formar varios canales de comunicación simultáneos con un único emisor y receptor. Por ello, vamos a usar una solución al problema de la transmisión de datos basada en el protocolo de comunicación estándar del RS-232, y trabajaremos sobre este protocolo de comunicación base para desarrollar un propio protocolo que será descrito a continuación.

Finalmente, para fijar todos los objetivos a conseguir con la implementación posterior, tenemos que decir que no va a ser objetivo la rapidez de transmisión, aunque una vez conseguidos los objetivos básicos intentaremos hacer una breve introducción a este tópico. En cambio, si primará como objetivo la robustez del protocolo de comunicación, y capacidad de detectar errores y, en consecuencia, hacer uso de la redundancia que veremos que poseen los mensajes para poder enmendar estos errores que pudieran producirse.

La filosofía básica a la hora de la implementación será la modulación: dividir el protocolo en diversas capas independientes de manera que las capas superiores usen las funciones que les proporcionan las inferiores, sin necesidad del conocimiento interno de las capas inferiores, simplemente usando una interfaz que el proporciona esa capa inferior. Es decir, las capas superiores usan las llamadas a funciones inferiores, pero no pueden ver internamente como están implementadas, por lo que no pueden acceder a cualquier parte del código de la capa inferior, solo a las que eta capa inferior exporta como externa y proporciona como servicios. La modulación nos aporta facilidades a la hora de la síntesis del Sistema, por tanto solo son beneficios para el diseñador del sistema de comunicación, no para el usuario. Si el sistema de comunicación funciona, le es lo mismo al usuario que haya sido creado modularmente o no. Esta ventajas que proporciona al diseñador del sistema de comunicación son:

Fácil Depuración y localización de errores: las capas son independientes, por lo que se pueden analizar cada una individualmente.

Fácil solución de Errores, sin necesidad de modificar todo el sistema de comunicación, solo modificando aquellos módulos defectuosos.

Fácil escalabilidad: si se desea modificar la filosofía de alguno de los niveles se puede hacer sencillamente manteniendo las interfaces ( para que los niveles superiores puedan seguir funcionando), e igualmente se pueden añadir capas de abstracción a partir de las básicas que vamos a desarrollar.

De hecho, para el estudio de este protocolo de comunicación también va a ser más sencillo el explicarlo capa a capa de forma inductiva: partiendo de la base vamos abstrayendo niveles superiores del protocolo, hasta llegar el nivel más alto.

Emisor Receptor

10010110

0

Emisión: Serie Unidireccional

Las capas que en la presente práctica se han desarrollado para sintetizar el protocolo han sido:

Nivel Bajo: es el nivel más básico, en el que, a partir de los 8 bits del byte (unidad básica en este nivel) a mandar, se confeccionará la codificación de estos bits (startbit, stopbit, etc) para ser mandados y se enviarán en modo serie. Por parte del receptor, a partir de bits individiales se recogerán y decodificarán para obtener el mensaje de 8 bits originales.

También en este nivel se desarrollan funciones con el fin de sincronizar los envíos, bien sea a nivel de bytes, o a nivel de paquetes.

Nivel Medio: en este nivel se usan las funciones de envío y recepción

de bytes, envío y recepción de sincronismos para los bytes y los paquetes, para sintetizar paquetes (unidad básica en este nivel) como una reunión de conjuntos de bytes. Por tanto los bytes se organizarán en paquetes o grupos.

Nivel Alto: es el máximo nivel de abstracción, se usan las funciones

de envío y recepción de paquetes del nivel anterior (además de los de sincronización de paquetes del nivel más bajo). Estos paquetes se envían redundantemente ( varias veces cada paquete, no cada byte, sino cada grupo de paquetes), y se desarrollará una lógica para, en función de los errores que se produzcan, volver a tomar el mismo paquete o esperar al siguiente diferente. La unidad básica son los conjuntos redundantes, y en última instancia es el programa de usuario que usa las herramientas para un fin determinado.

Nótese que, a la hora de abstraer en estos niveles, no se trata de que un elemento de un nivel superior pueda usar solo funciones de su capa inmediatamente inferior. Puede usar funciones de cualquier nivel inferior a él, sea o no inmediata. Lo que no podrá nunca es usar funciones de un nivel superior a la capa en la que se encuentra este elemento. Un posible diagrama que exprese lo aquí explicado sería:

5.1 Nivel Bajo Comencemos a explicar este protocolo de comunicación desde el sustrato más básico y elemental, que servirá como base sobre la que se “ edificará” todo el sistema de comunicación.

Nivel Alto(programa de protocolo)

Nivel Medio(paquetes)

Nivel Bajo (bytes)

En este nivel, la unidad básica es el Byte, de manera que los algoritmos desarrollados irán destinados al envío de un byte. Más adelante veremos como conformar un mensaje mediante Bytes. Un Byte se conforma de 8 bits, de manera que para ser enviados necesitarán de una fase previa de codificación en el emisor, y de decodificación en el receptor. Este Byte Codificado necesita ser mandado en serie (bit a bit) a través del Led de infrarrojos, y recibido por el receptor de infrarrojos, por tanto definamos previamente qué es un bit para este protocolo de comunicación.

Marco de Bit El marco de bit, que es el periodo que dura la transmisión de un bit, va a influir notablemente en la velocidad de la transmisión ( a menor marco de bit menor es el tiempo que tarda la transmisión) y en la robustez del mismo (es más fácil que se produzcan errores de sincronismo entre el emisor y el receptor cuanto menor sea el Marco de Bit) El hardware del que disponemos nos dará la guía definitiva sobre que marco de Bit usar. Como hemos visto, el sensor de infrarrojos TFMS 5360 trabaja a una frecuencia de 36kHz, que supone un periodo de 27.78 µs. Como sabemos, este sistema de infrarrojos trabaja por ráfagas: en función de la relación entre el tiempo en que se emite un pulso periódico a una determinada frecuencia, y el tiempo en el que no se emite ningún pulso, se genera una determinada señal de salida en el sensor TFMS 5360. La combinación de un pulso y un “ silencio” o ausencia de pulso conformarán 1 marco de bit.

Por ello, a la hora de elegir el pulso periódico que forme parte de la zona de la ráfaga en la que se emite un pulso, parece razonable tomar una pulso con un periodo de señal similar a la que el sensor de infrarrojos puede tomar, esto es, unos 27.78 µs.. Cuando pasemos a programar una simulación de este pulso en el Pic ( en el emisor) veremos que no vamos a tomar exactamente este valor, sino que tomaremos unos 26 µs. Esto se explica debido a que en el Pic no existe ninguna instrucción que tenga una duración de menos de 1 µs, lo cual implica que no podemos tomar un semipulso de 27.78/2 = 13.89 µs. En este caso vamos a tomar una redondeo inferior, tomando un semipulso de 13 µs, por tanto un periodo del pulso de la ráfaga de 26 µs. En cuanto a la duración del marco de bit, nuevamente recurrimos a las especificaciones del sensor de infrarrojos TFMS 5360, analizando que:

El tiempo de duración de la parte de ráfaga con pulso activo (con emisión de pulso periódico) debe ser superior o igual a 400 µs para obtener una transmisión óptima (Página 4 del fichero de especificaciones tfms.pdf). Esto supone un número de pulsos superior a 400/ 26 ≈ 15 pulsos completos.

Nos conviene que el sensor sea capaz de mantener una señal de

salida continua, esto significa, que la señal de entrada en modo de ráfagas (periodo con pulso activo y periodo con pulso inactivo) sea copiada por el sensor de infrarrojos a una salida con un periodo activo igual al periodo del pulso inactivo de la ráfaga de entrada, y un periodo inactivo igual al periodo del pulso periódico de la ráfaga de entrada.

Lo Expresado en el diagrama es lo deseado: poder controlar desde el emisor cual va a ser los periodos activos o inactivos del marco de bit del receptor, ya que éste último no sea más que una “ traducción” de la ráfaga de entrada en una Salida digital ( y por tanto, que el marco de bit tenga la misma duración en ambos dispositivos, emisor y receptor). Para ello, la señal devuelta por el sensor debe ser continua. Para que sea continua, es fundamental que se cumpla la interrelación [1] PeriodoActivoRafagaEntrada / MarcoDeBit ≤ 0.4 (Página 1 del fichero de especificaciones tfms.pfd), por lo que el periodo de pulso periódico de la ráfaga de entrada debe suponer menos de un 40% del Marco de Bit total. En la experiencia [2], nos damos cuenta de que esta cifra no es más que orientativa, ya que con que no suponga más de un 50% aproximado del marco de bit es suficiente para que pueda mantener en sensor una salida continua.

Barajando estas dos restricciones que nos impone el sensor TFMS5360, vemos que el marco de bit teórico debe de ser con un periodo activo de la ráfaga de entrada de 400 µs, y, suponiendo que esto supone un 40% del marco de bit total, el marco de bit total sería de 1000 µs. Más aún, el número de pulsos del periodo activo de la ráfaga de entrada serían 400/26 ≈ 15 pulsos, y , puesto que en 1000 µs hay cabida para 1000/26 ≈ 38 pulsos, 28 periodos de pulsos como periodo inactivo de la ráfaga de entrada. En la realidad, esto es arriesgar demasiado, y , puesto que tenemos como objetivo una señal lo más robusta posible, a menor marco de bit nos obligaría a tener medidas con mucha más precisión todas las instrucciones que formen parte de las funciones del Pic para emitir y recibir bytes, para evitar la desincronización entre emisor y receptor. Esto dificulta notablemente la tarea de programación del microcontrolador. Un marco de bit más amplio nos da mucha más flexibilidad y holgura sin que se desincronicen ambos dispositivos. En vistas de lo dicho, y de lo aclarado en el punto [2] , vamos a tomar nuestro primer consenso, y configurar nuestro marco de bit de la siguiente manera:

Marco de Bit Marco de Bit

Ráfaga de entrada

Señal de Salida del Sensor “continua”.

Periodo “Silencio” o inactivo.

Periodo Nivel Bajo Salida

Periodo Nivel Alto Salida

Periodo Pulso Periódico o activo

El periodo con el que se transmite el pulso del periodo activo de la ráfaga de entrada es de 26 µs.

El Marco de Bit, tanto en la señal del Emisor como en la del Receptor, será de 200 pulsos, esto es 200 * 26 = 5200 µs. Es un marco de bit lo suficientemente holgado para permitirnos una programación más sencilla.

Nótese que, debido a la restricción [1] no es posible que el periodo activo de la ráfaga ocupe todo el marco de bit, pues la señal de salida no se mantendría continua. Por ello, transitivamente, es imposible que podamos obtener una salida completamente a nivel bajo durante todo el marco de bit, ya que las salidas a cero tienen el mismo periodo que el periodo activo de la ráfaga. Sin embargo, si podemos poseer un periodo inactivo de la ráfaga de entrada que ocupe todo el marco de bit, y por tanto, si puedo obtener una salida del sensor que sea todo el marco de bit a nivel alto.

Esto hace que Emitir un 1 el emisor no sea tan sencillo como mandar durante todo el marco de bit el pulso, ya que la salida no se mantendría, pero si que se puede emitir un cero como todo el marco de bit sin pulso, ya que el receptor si generaría una salida con todo el marco de bit a nivel alto (si mantendría la señal continua). Por tanto, en vez de enviar Bits como presencia de pulso en todo el marco de bit, o ausencia el cero en su defecto, vamos a tomar este convenio:

♦ Si queremos mandar un cero ( señal a nivel bajo), mandamos todo el marco de bit sin pulso, por lo que la salida del sensor será continua a nivel alto:

♦ Si, por el contrario, queremos enviar un uno ( señal a nivel

alto) mandaremos un escalón, con el primer semiperiodo activo (mandar pulso con periodo 26 µs) y el segundo inactivo ( sin pulso). El marco de bit se divide por tanto en un 50% activo (100 pulsos) y 50% inactivo ( suficiente para mantener una señal de salida continua según lo explicado en [2]). De esta manera, el envío de un bit 1 se traduce en:

Véase que en el receptor, el escalón tiene el primer semiperiodo a nivel bajo y el segundo a nivel alto. Esto indicará que lo que se está emitiendo es un 1.

Ejemplo de bit a 1 Enviado por el Emisor, con todos los parámetros señalados:

Marco de Bit

....0..

Emisor

Marco de Bit

Receptor

Marco de Bit

....1..

Emisor

Marco de Bit

Receptor

Codificación / Decodificación del Byte a transmitir En primer lugar, vamos a situarnos desde el punto de vista del emisor, el cual, para poder mandar el Byte necesitará codificarlo antes convenientemente. [3] Hay que tener en cuenta que a partir de ahora, cada vez que hagamos referencia al envío de un bit a 1 será, en última instancia, según el formato que explicamos anteriormente ( una ráfaga con un semiperiodo con pulso periodico activo y con un semiperiodo en silencio), de igual manera que un bit a 0 será según el formato explicado (periodo en silencio o inactivo de la ráfaga ocupa todo el marco de bit). Esta codificación va ser muy similar a la que se hacía en el protocolo de comunicación RS-232. Por lo que dispondremos de:

♦ Un StartBit Inicial: el Start Bit es un bit 1 emitido por el emisor (tal como vimos, se efectuará una ráfaga con semiperiodo con pulso activo y semiperiodo sin pulso). Indica al receptor que el emisor tiene intención de emitir un byte, por lo que permite al receptor preparase para poder capturar ese byte. Previo al StartBit la señal tenia que haber estado a nivel bajo.

♦ Envío en serie de los bits: En un determinado registro de declaje tendremos los 8 bits correspondientes al Byte que se quiere enviar. Pues bien, en esta fase el emisor debe de ir desplazando este registro, y capturando el carry o bit que, por el overflow, abandona cada vez al registro, para enviarlo en serie por el canal de comunicación ( 1 marco de bit para cada bit, en total 8 marcos de bits son consumidos en esta tarea).

Por tanto, el método de emisión será comenzar a enviar el Byte por su bit más significativo (MSB, ya que el registro de declaje se desplaza hacia la izquierda registro hacia la izquierda), uno a uno, desplazando en el registro de declaje y enviando por el canal el bit overfloweado, hasta llegar al bit menos significativo (LSB)

♦ Control de Errores se puede situar tras el envío del Byte algún marco de bit referente a control de errores como la Paridad. Pero en principio, en el desarrollo de esta práctica vamos a intentar tratar y detectar errores de otra manera distinta a la Paridad como veremos más adelante, por lo que, en el programa básico de este dispositivo de comunicación de datos no vamos a añadir este bit en la codificación.

♦ StopBit: en principio, tiene como funcionalidad dar tiempo al receptor para que pueda re sincronizarse con el emisor (por si éste desea enviar más

Marco de bit

Periodo Activo Periodo InActivo (equivalente a 100 pulsos. Periodo=100*26=2 600µs)

26µ

13µs 13µs

bytes) tras el tratamiento de la información recibida y también le vamos a dar un papel fundamental en la detección de fallos en el mensaje recibido a causa de desincronizarse. A través del Stobit vamos a montar un sistema de detección de desincronizaciones, para que el receptor pueda actuar en consecuencia y re sincronizarse con el emisor previo a cualquier otro envío de datos.

Ya tenemos los elementos básicos para codificar el byte a transmitir. Antes de

ver como organizar estos elementos en un “mensaje” concreto, vamos a ver que significado tendrán estos elementos básicos en el receptor:

♦ El StartBit inicial: previo a este, el receptor se encontrará rastreando la

señal del sensor, que deberá estar a nivel alto mientras no se reciba nada (si no hay rafaga de entrada, el sensor mantiene una salida constante a nivel alto) hasta que el sensor devuelva una señal a nivel bajo, que será el StartBit, lo que significará que el siguiente envío será ya el bit más significativo del dato a enviar. El receptor preparará un registro acumulador, en el que irá guardando todos los bits que le lleguen.

♦ Acumulación de Bits: los siguiente 8 envíos de bits se correponden con el de la información a transmitir, por lo tanto se irán traduciendo a bits ( si es un escalón la salida del sensor es un bit a 1, y si está totalmente a nivel alto es un bit a cero) y acumulando en un registro de desplazamiento (desplazando hacia la izquierda el mismo e introduciendo por la parte menos significativa del registro, ya que el primero es el bit más significativo).

♦ Control de Errores: En caso de que se envíe una paridad ( que en el caso base que vamos a estudiar no se va a dar), la acumulaciónd e bits también incluiría un conteo de paridad, para finalmente comparar con este último bit de paridad enviado y detectar si hay errores o no. En caso de Errores, simplemente se guardará en un registro para que los niveles superiores puedan darse cuenta de que hubo un error.

♦ StopBit: primeramente, el receptor deberá garantizar que se envía un determinado número de Stopbits, y en caso de que ese número no se cumpla, significará que hay un fallo de sincronización entre emisor y receptor, y por tanto se indicará en un registro que podrá ser observado en niveles superiores de abstracción. Solo cuando se garantiza un número mínimo de StopBits se procede con la resincronización, en la que el receptor volverá a preparar todos los registros y se pondrá a la “escucha” de nuevo por si el emisor le envía otro dato seguidamente.

A grandes rasgos, este es el protocolo a seguir. Más adelante será analizado con más

detalle y diagramas de flujo pertinentes, cuando ya estemos a la altura de la Implementación. Pongamos por ejemplo que se desea enviar el Byte = 10011000. La codificación del

mismo en el Emisor sería la siguiente ( sin considerar el bit de paridad, ya que no es el caso base que vamos a tratar):

[Emisor] Codificación Lógica (unidad básica es el bit) Véase que todavía no hemos definido que tipo de StopBit vamos a usar, de manera que, hasta que lo decidamos, lo mantendremos como indeterminado. Véase que además, sea como sea el Stop bit, tiene que terminar en una etapa de silencio previo a cualquier transmisión posterior.

[Emisor] Codificación Física (Unidad básica es la ráfaga)

En este nivel vamos a traducir los bits a ráfagas, tal y como especificamos en el apartado de la definición del marco de bit, y en el punto [3], que será la señal que realmente reciba el receptor.

Nuevamente no conocemos el StopBit, pero sí sabemos que tiene que acabar en alguna ráfaga con “semiperiodo” inactivo o en silencio que ocupe todo el marco de bit.

[Receptor] Decodificación Física El sensor receptor recibe la ráfaga de entrada, en función de ella, produce salidas “continuas” (pues para eso se hizo el análisis pertinente en la sección del Marco de Bit), bien un escalón, o bien una señal a nivel alto. Así, la señal devuelta por el sensor de infrarrojos ante la ráfaga anterior sería: Vemos, no solo la señal devuelta por el sensor de infrarrojos, sino además, el muestreo que va a hacer el Pic receptor, para poder tomar la información necesaria del mensaje. Cada flecha es una muestra. Así, comienza con una alta frecuencia de muestreo, mientras la línea esté a nivel alto, pues querrá decir que el emisor no desea enviar nada. En el momento que detecte que la línea está a nivel bajo, significa que el emisor ha mandado un StartBit, por lo que va a comenzar una emisión. Ahora, el Pic tiene que calcular el

Start Bit

1 (msb) 0 0 0 0 0 (LSB)

StopBit

1 1

Start Bit

1 (msb) 0 0 0 0 0 (LSB)

StopBit

1 1

tiempo que debe de esperar hasta volver a tomar una muestra. Como vemos, debido a que la señal o es un escalón o un nivel alto, siempre el segundo semiperiodo del marco de bit está a nivel alto. No es más que redundancia que no nos aporta ninguna información. Solo el primer semiperiodo del marco de bit nos proporciona información util. Por ello, el tiempo que deberá esperar hasta volver a tomar una muestra deberá de ser El tiempo del marco de bit ( situarse al comienzo del siguiente marco) más la mitad del primer semiperiodo del segundo marco de bit ( unos 100/2= 50 pulsos, el tiempo de 50 pulsos de 26 µs). El situarnos a la mitad del primer semiperiodo nos permite mayor holgura y flexibilidad, de manera que robustece un poco más el sistema y no permite que el emisor y receptor se desincronicen fácilmente). A partir de aquí, el periodo de muestreo será el mismo que la duración de 1 marco de bit, para situarse siempre a la mitad del primer semiperiodo. Véase que en el periodo de StopBit no se pone a la escucha directamente, sino que un cierto número de marcos de bits va a seguir muestreando para comprobar que el StopBit se cumple completamente, y así detectar si hay error de sincronización o no. Siempre terminará poniéndose a la escucha para el siguiente envío.

[Receptor] Decodificación Lógica

Simplemente será detectar cual es el StartBit, y a partir de el, tomar las siguientes 8 muestras, tomando su complemento a 2 (si esta nivel alto es un cero y si esta a nivel bajo es un 1) y almacenándolas en un registro de desplazamiento, que terminará conteniendo los 8 bits enviados.

El StopBit

La tarea de elegir un StopBit no es una tarea trivial. Tenemos que elegir un StopBit que permita resincronizarse al receptor (el emisor tiene que dar tiempo suficiente al receptor para poder ponerse a la escucha y atenderle) y además detectar si el dato recibido por el receptor es válido o fruto de una desincronización ( detectar desincronizaciones). El StopBit a utilizar será:

Un primer marco de Bit a 1 en el emisor. Los once marcos de bits siguientes a 0 en el emisor.

Por su parte, el receptor deberá de comprobar que recibe primero n escalón, y por lo

menos 9 marcos de bits siguientes deben de ser a nivel alto. A partir de ese momento, puede ya ponerse a la escucha, pues el emisor y el receptor siguen sincronizados, y el byte que se acaba de recibir por tanto es válido.

El motivo de elegir este StopBits se puede ver con varios ejemplos de sincronización y detección de los mismos:

CASO A) Imaginemos que deseamos mandar el byte 10011001. Imaginemos que el

receptor detecta el start bit, el bit más significativo, el siguiente, y antes de terminar, por ejemplo, cuando se recibió el bit representado en magenta, imaginemos que alguien se mete en medio de la vía de comunicación durante un largo tiempo, de manera que es como si el emisor siguiera enviando todo ceros. El receptor por tanto, interpretaría que el byte enviado fue

10011000, que evidentemente es erróneo. Si el StopBit fuera simplemente mantener la línea a nivel alto durante un número de marcos de bits ( como si el emisor no mandara más que ceros) sería imposible detectar que ese dato recibido es fruto de un fallo de sincronización.

Pues bien, que el primer stopbit sea un 1 ( escalón en el receptor) solventa este problema. Al comprobar el receptor que todos los StopBits son correctos, detectará que , como el primero no es un 1 ( pues al meterse alguien en medio, es como si el emisor no mandara nada, o sea, el sensor devuelve la línea a nivel alto) hay un fallo de sincronización, y por tanto desechará este byte como válido y hará uso de alguno de los sistemas de corrección de errores que describiremos más tarde.

CASO B) Supongamos que se esta enviando un Byte, y , por una desincronización, no lo tomas

desde el comienzo sino desde la mitad. Por ejemplo:

Imaginemos que, por error de desfase, el receptor toma como StartBit el primer bit del Stop bit a 1 ( en un caso extremo). Si el número de StopBits a nivel bajo es pequeño, puede pasar que, cuando el receptor vaya a comprobar el primer StopBit esta a nivel bajo coincida con el Start bit o un bit posterior a este a nivel alto, y por tanto, no pueda detectar jamás que el StartBit tomado no era el verdadero. Por ejemplo si el número de StopBits a cero después del primero a 1 son 8 (número de bits que forman el mensaje que se desea transmitir) pasaría esto: Por tanto, si el siguiente envío coincide que son todo ceros, al receptor los comprueba como si fueran los 8 StopBits a nivel bajo que hemos puesto, y es incapaz de ver que en realidad se está desfasando, y que no tomó el StartBit correcto. La solución para esto es tomar un número de StopBits a nivel bajo superior al número de bits que se transmiten (8bits, 1 byte). Es por ello, que hemos usado 11 StopBits a cero tras el primero como un bit a 1 ( las referencias a bit 1 y bit cero hacen referencia al emisor).

Start Bit Primer Stop Bit a 1 Resto

StopBits a cero

Start Bit Falso!! En realidad primer bit a 1 del conjunto de StopBits.

Primer StopBit falso!! En realidad StartBit del primer Envio.

Stopbits

En cambio, el receptor solo tiene que comprobar que hay 9 StopBits correspondientes a un cero emitido ( 9 sigue siendo mayor que 8 bits), tras lo cual se pone a la escucha. Véase que el emisor manda 11 y el receptor solo comprueba 9. Por tanto, tiene 2 StopBits para resincronizarse con el emisor.

Protocolos de sincronización

Ya tenemos conceptos suficientes para poder enviar y recibir bytes, la unidad básica en el nivel bajo que estamos describiendo. Veremos más adelante diversos protocolos para el manejo de estos Bytes codificados. En estos protocolos de niveles superiores hacen falta herramientas de sincronización: son en realidad separadores que permiten distinguir o separar dos entes de la misma naturaleza ( como Bytes codificados de la manera en la que los hemos codificado). Evidentemente, estas herramientas deben de ser diferentes que los entes que se pretenden separar. Como veremos, estos entes que queremos separar entre sí no van a ser más que bytes codificados de la manera que se ha explicado. Por ello, la manera de codificar estas herramientas de sincronización entre entes debe ser diferente que la de codificar bytes, pues si no fuese así sería imposible distinguir al separador ( si el separador es lo mismo que se pretende separar, no sirve como separador). Debido a lo explicado, aunque estas herramientas de sincronización se van a usar en niveles superiores, conviene implementarlas en el Nivel Bajo, ya que, si no fuese así:

O se utiliza para crear estas herramientas elementos del nivel bajo. Pero en este caso, las herramientras del nivel bajo no es mas que emisión de bytes, luego el separador es lo mismo que lo que se pretende separar. No nos sirve.

O implementarlas en niveles superiores, pero como el nivel bajo solo exportaría herramientas para transmitir bytes, no podrían usar ninguno de los recursos usados en el nivel bajo, y tendrían que hacer la implementación completa, incluyendo el sistema de ráfagas en el emisor y el de muestreo en el receptor. Esto evidentemente es desperdicio de código.

Por estas razones, es en el nivel bajo donde vamos a encasillar estas herramientas de

sincronización, para aprovechar los recursos de este nivel en la codificación. Principalmente vamos a necesitar dos herramientas de sincronización, cuya utilidad será explicada más adelante. Aquí solo vamos a ver como se implementan y por tanto como lograr que “ el separador sea distinto de lo separado”.

Sincronismo

Es usado en el nivel medio, para la formación de paquetes. Así, básicamente separa paquetes entre sí. 1 paquete se forma por tanto de bytes codificados y de 1 sincronismo que lo encabeza. Evidentemente, la codificación de este sincronismo debe de ser diferente a la codificación del Byte, pues si no podría servir para separar grupos de bytes ( paquetes) entre sí. Para ello, simplemente se codificará como un StartBit Inicial, 9 bits de datos a 1 desde el punto de vista del emisor (esta es la diferencia que los distinguirá de los bytes) y 1 primer StopBit como bit a 1 seguido de 11 StopBits como bits a cero. El que tenga 1 bit más de datos, hace que se pueda distinguir perfectamente de los Bytes, de manera que si en el lugar que hace falta 1 Sincronismo el receptor se encuentra con 1 byte, le va a faltar un bit a 1 antes de los Stopbits ( se detectará el fallo de sincronización), e igualmente, si cuando toca un Byte se recibe un sincronismo, también se detectará (sobrará un bit a 1, por tanto, en la revisión de StopBits a cero, la presencia de un 1 hará que se detecte el error.

Nótese que, a pesar del aumento de bits de datos, el número de StopBits a cero sigue siendo superior ( 11 ) por tanto sigue sirviendo para detectar errores de desfases entre Emisor y Receptor. Trama En el nivel más alto, hará falta distinguir entre conjuntos de paquetes. Como un paquete se forma de bytes y 1 Sincronismo, la herramienta para separar conjuntos de paquetes debe de ser diferente a la codificación del Byte y del Sincronismo. Simétricamente a la solución anterior, solucionaremos la codificación de la Trama añadiendo un bit de datos, y por tanto codificando 1 StartBit, 10 bits de datos a 1 ( bits a uno desde el emisor), 1 primer Stopbit con valor de bit a 1, y 11 Stopbits a bit a cero. Véase que el número de Stopbits a cero sigue siendo superior que al número de bits de datos, por lo que siguen pudiéndose detectar desfases entre emisor y receptor. -------------------------------------------------------------------------------------------------------------------------------- Una importante anotación hay que hacer respecto al Sincronismo y la Trama: a diferencia de la decodificación en el receptor, si se detecta algún fallo en la emisión, no se refleja ese fallo en un registro que pueda verse desde niveles superiores. Dado que se trata de herramientas de sincronización, si se detecta algún fallo en la decodificación del Sincronismo y la Trama simplemente se vuelve a la espera del StartBit, y solo se continua y se sale de este bucle cuando emisor y receptor estén sincronizados, es decir, se detecte un Sincronismo o una Trama ( según el caso) de forma completa y sin errores. Con esto se asegura que cada cierto tiempo (el que tarda el receptor en pedir en el protocolo un Sincronismo o Trama) se resincronicen emisor y receptor. Codificación de un Byte (10011000) [Emisor] Codificación de un Sincronismo [Emisor] Codificación de una Trama [Emisor]

StartBit 8 Bits Datos 11 StopBits a cero StopBit a 1

StartBit 9 Bits Datos (Bits a 1)

11 StopBits a cero StopBit a 1

StartBit 9 Bits Datos (Bits a 1)

11 StopBits a cero StopBit a 1

* En la recepción, la única diferencia entre Sincronización y Trama, y la codificación de los Bytes, consiste en que, mientras que en los Bytes , los datos se almacenan en un registro de declaje, en la Sincronización y Trama se comprueba que cada bit de dato es un bit 1 enviado por el emisor, sin necesidad de almacenarlo.

5.2 Nivel Medio Este nivel hará uso de las herramientas de protocolo desarrolladas en el Nivel Bajo para el envío y recepción de Bytes, se extraerá un nuevo nivel de abstracción como unidad básica: El Paquete. Un paquete consistirá en un grupo de bytes codificados con información. De esta manera, en el envío de un paquete, ¿cómo diferenciar un paquete de otro, si todo lo que se envían son Bytes codificados? Hace falta de un Ente diferente a la codificación de los Bytes, que sirva como marcador o separador entre paquetes distintos ( grupos de Bytes distintos). Esta herramienta será el Sincronismo ( ya implementado y descrito en el Nivel Bajo). Para encabezar un paquete usaremos un Sincronismo que básicamente separará la codificación del último byte del paquete anterior de la codificación del primer Byte del paquete actual que queremos enviar. El receptor, al leer un Sincronismo, detectará la intención del emisor de mandar un paquete y preparará todos sus registros para tal causa. En definitiva, tras descomponer un mensaje en bytes, agrupamos los bytes en grupos o paquetes, que no son más que “slices” del mensaje original.

Mensaje “hola_mundo”

Este Sincronismo carece de cualquier información sobre el paquete, solo sirve como separador e indicador de comienzo de nuevo paquete. La función que desempeñaba el StartBit en la codificación a nivel bajo de un Byte, es la misma que la que desempeña Sincronismo en este nivel de paquetes. El número de bytes que forma cada paquete vamos a ver en la implementación que puede ser prefijada por el programador del sistema, o se puede elegir por el usuario. Lo que si que no puede pasar es que un mismo mensaje se divida en paquetes de tamaño variable: cada mensaje se organiza en paquetes de envío del mismo tamaño entre sí. El tamaño de cada paquete va a influir principalmente en el tiempo de envío del mensaje: si el paquete tiene por ejemplo 1 único Byte, y el mensaje tiene 10 Bytes, ¡entonces se envían 10 Bytes y 10 Sincronismos !. Si, por el contrario tomamos un paquete de mayor tamaño, por ejemplo 5, entonces se harían 2 envíos de 5 bytes cada uno ( con 1 Sincronismo cada uno). Es decir ¡10 Bytes, y solo 2 Sincronismos !. Nos hemos ahorrado el tiempo que tardan en emitirse 8 Sincronismos. Pero esta solución se contrapone al principio de hacer el mensaje más turgente: hemos dicho que si se produce un error en el envío de Bytes ( si se da en sincronismo simplemente

o l a _ m u n d o h

o l a _ m u n d o h * *

Sincronismo h Byte Codificado * Byte Codificado. 8 Bits a cero

Paquete

Nivel Bajo

Nivel Medio

hasta que no se sincronice no avanza el receptor) éste se muestra en un registro que puede ser leído desde niveles superiores. Cuanto menos bytes tenga el paquete hay menos posibilidades de error, y más posibilidades de detectar donde se produjo el fallo y corregirlo ( por redundancia como veremos). Cuanto más bytes tenga un paquete es más propenso a error, y más propenso a que después de toda la redundancia con la que enviemos los paquetes ( en el nivel Alto) siga existiendo error. Por tanto, hay que llegar a una solución de compromiso. En el caso más básico hemos considerado un paquete de 3 bytes, pero no va a ser el único caso que implementemos. Otro requisito básico es que el receptor tiene que conocer de antemano el tamaño del paquete, ya que en caso contrario, ¿Cuándo llama a la función de recibir Sincronismo? Si no “espera un sincronismo” , cuando el emisor le mande un sincronismo no va a poder tomarlo, y lo considerará como un error en la emisión, por lo que volverá a sincronizarse el receptor (pero una vez que ya pasó el sincronismo) y para ello perderá ya algún byte (justo el que seguía al sincronismo). Esto provocaría por ejemplo que el sistema de comunicación siempre perdiera el primer Byte que se emite. Finalmente, si se observa el diagrama atentamente, se observará que se puede dar el caso de que no hayan suficientes Bytes para formar un último paquete completo. ¿Qué hacer en tal caso? Además, el receptor no sabe de antemano el tamaño del mensaje. ¿Cómo se entera de que el mensaje ha terminado?. Es importante saber que no deseamos un receptor con espera activa, es decir, que se esté infinitamente comprobando si le llega algún otro mensaje. Vamos a diseñar un receptor capaz de detectar el final de un mensaje, y en tal caso, poder pasar el sistema a un modo de bajo consumo (SLEEP del microcontrolador PIC16f84). Por todo esto, vamos a imponer una restricción a este protocolo:

al final del mensaje guardado en memoria debe de almacenarse 1 byte a cero completo, como si también se quisiera transmitir.

Lo que hará el emisor será leer de memoria y organizar los bytes codificados en paquetes, hasta que llegue a codificar un Byte que sea 0h. En este caso, si el paquete del que forma parte este Byte a 0h no esta completo, completar con ceros.

Lo que hará el receptor será ir recibiendo paquetes, hasta que en un paquete se de un 0h (al menos uno). En este caso no esperar por la recepción de más paquetes y dormirse el receptor tras tratar el último paquete.

Por tanto, es fundamental que, en el mensaje que queramos transmitir, no hayan bytes totalmente a 0h, pues todo byte que venga después no será tratado por el receptor y en ese punto se cortará el mensaje de cara al receptor aunque el emisor siga mandando datos.

Por tanto, el diagrama antes hecho, no esta completo, y debería de ser: Nivel Medio

Nivel Bajo

o l a -- m u n d h

o l a _ m u n d o h * *

p *

Un cero al final del mensaje en memoria como si fuera parte del mensaje

Rellenar con ceros

5.3 Nivel Alto En este nivel, el protocolo se basará en usar los paquetes del Nivel Medio para implementar el método de transmisión final. Así, vamos a organizar los paquetes en conjuntos Redundantes: cada paquete se va a enviar un determinado número de veces, que debe de ser prefijado y el mismo para receptor y emisor. Cada conjunto redundante no es más que el mismo paquete repetido varias veces. Esto va a conformar el sistema de corrección de errores ( el de detección ya lo hemos visto, que son los StopBits), de manera que si el receptor, al recibir un paquete, detecta que en alguno ( 1 cualquiera) de sus Bytes hubo algún error de sincronización (leyendo el registro en el que se guardaba si había habido un error o no, visible desde el nivel más alto), deshecha lo obtenido, y se pone a la escucha del siguiente envío, que será el mismo paquete ( otra oportunidad). El número de oportunidades es el número de veces que se envía un paquete. Si , por el contrario, se recibe un paquete perfectamente, ya no hará falta volver a recibirlo, por lo que el receptor ya podrá ponerse a la escucha de otro paquete diferente. El problema reside en ¿Cómo detectar si el paquete siguiente es diferente o es el mismo de antes?. Los Sincronismos no poseen información acerca de ello, por tanto no puede saberse. La única manera es la utilización las Tramas ( implementadas en el nivel bajo) para separar “conjuntos de paquetes “. Si se encabeza cada conjunto redundante con una Trama, se puede distinguir cuando se está enviando el mismo paquete o es otro diferente. Así, si el receptor recibe bien el paquete, se pone a la escucha no del siguiente paquete, sino de la siguiente Trama ( o sea del siguiente conjunto redundante o paquete diferente). El número de Tramas es el mismo que el número de paquetes distintos a emitir. Nuevamente, la Trama carece de cualquier información útil para el sistema de comunicación, por tanto, cuanto menos sean, más rápido será el sistema ( menos redundante). Por ello conviene tener el menor número posible de Paquetes, y por ello, ante un mismo mensaje, que el tamaño del paquete sea grande para que se divida menos el mensaje.

Sin embargo, con paquetes demasiado grandes se corre el riesgo de mayor número de errores. Al tener más bits, más posibilidades de errores, y esto hace que quizás el número de oportunidades ( o redundancia) que brinda el sistema no sea suficiente para completar un envío de un paquete “limpio” y carente de errores. Como nuestro principal objetivo es la robustez, llegaremos a una solución de compromiso pero tendiendo a paquetes no muy grandes para que sean más robustos. La elección del número de veces que se envía un paquete ( la redundancia) implica que, si se elige un número bajo, las “ oportunidades” de corregir un error serán menores por lo que será menos robusto, sin embargo será mas rápido. En cambio, si se selecciona un algo número de veces que se envía un paquete, el sistema será más robusto, pero más lento. El consenso al que vamos a llegar es que la redundancia o el número de veces que se repite el envío de un paquete sea 3 veces. Por tanto, cada Conjunto Redundante posee 3 veces el mismo paquete, tal como muestra la figura que representa la emisión del emisor.

hol hol hol a m a m a m

und und und o** o** o**

Trama

6 Implementación

En este apartado vamos a proceder con la programación de los PICs implicados (1 PIC que controle al emisor y otro al receptor) en el sistema de comunicación, para que se respeten todas las premisas descritas en el apartado de protocolo. Primeramente, y para organizar esta tarea de implementación del control del emisor de infrarrojos y el receptor, o la parte “Software”, vamos a adoptar una filosofía de trabajo. Ya que el protocolo ha sido descrito de un modo modular, parece evidente que la implementación de ambos sistemas de control ( para emisor y receptor) vaya a ser modular, a través de Librerías. Es decir, vamos a dividir el sistema de control de cada parte de la comunicación en 3 niveles, que se corresponderán con los 3 niveles de protocolo ya descritos.

El nivel más inferior será una librería que exportará funciones relativas a la emisión y recepción de datos, tramas y sincronismos. Existirá otra librería que represente al nivel medio, y que, usando la librería básica antes descrita, se encargue de organizar la información en paquetes.

Finalmente, habrá un programa principal que, usando ambas librerías, organice la información en Conjuntos redundantes, y en el receptor, lleven a cabo la detección y corrección de errores ( por redundancia), además de mostrar los datos al usuario para que pueda observar la bondad de los resultados obtenidos. Este programa principal representa al protocolo de nivel más alto.

Éste último nivel no se ha implementado como una librería ( aunque también se pudiera) ya que, en tal caso, lo único que tendría que hacer el programa principal sería tratamiento de ristras: llamar a una función de esta supuesta librería, y previamente, el emisor tendría que cargar o tomar la frase para cargarla en memoria y pasársela a esta librería de nivel Alto, y el receptor tratar la ristra que el mismo almacena en memoria durante la emisión. Hemos preferido interpretar este nivel Alto directamente como el programa principal, e incluir en este el tratamiento de la ristra que conforma el mensaje.

Por tanto, este programa tiene tres estratos: los dos más bajos en forma de librerías y el más alto en forma de programa principal. Además, véase que cada estrato esta bifurcado en dos partes simétricas entre sí: el control del led emisor y el del sensor de infrarrojos receptor ante lo mandado por el led emisor. Por tanto, a la hora de explicar la implementación hecha, seguiremos una inducción, comenzando por el nivel más bajo, y siempre interrelacionando en cada módulo las acciones del emisor frente a las que desempeña el receptor para recibir el mensaje.

Librería nivel_bajo.lib

Emisor Receptor

Librería nivel_paquetes.lib

Emisor Receptor

Main • Emisor • (Ristra + NivelAlto)

Main • Receptor • (Ristra + NivelAlto)

6.1 Creación de Librerías

Antes de comenzar a describir la implementación, hay que hacer hincapié en la técnica usada para implementar las librerías. Podríamos tomar un único fichero ASM que tuviera todas las funciones suministradas al emisor y las suministradas al receptor. Así, al compilar crearíamos un único fichero de tipo objeto, y una librería basada en ese fichero de tipo Objeto. Sin embargo, esta implementación tan trivial de las librerías tiene un problema básico: si el emisor hace un acceso de alguna de las funciones de la librería, se carga en memoria, no solo las funciones pertinentes a la emisión, si no también las referentes a la recepción. Por ejemplo, se podría estar reservando espacio en la memoria de datos para registros que en realidad nunca se van a usar por que solo son usados por el receptor. Esto nos sugiere otro modo de implementar cada librería: las funciones de emisor y receptor separadas en dos ficheros ASM separados, de manera, que al compilarlos cada uno individualmente, se cree un fichero objeto para cara uno, y con dos ficheros objeto, crear la librería. La diferencia ahora es que, cuando el emisor acceda a alguna función propia suya, accederá solo a un fichero objeto dentro de la librería, por lo que no se cargará en memoria todos los registros y funciones del otro objeto. Se que, con una misma librería, mantengamos independientes las funciones y registros del emisor y receptor. En el diseño de los programas se ha tenido en cuenta que el emisor no haga referencia a ninguna de las funciones o registros exportados por el receptor, y viceversa en el caso del receptor. 6.2 nivel_bajo.lib En esta librería se van a implementar, tanto para el emisor, como para el receptor, herramientas para codificar y decodificar bytes, tramas y sincronismos. Esta librería poseerá dos objetos provenientes de dos ficheros *.asm diferentes:

Emisor_nivel_bajo.asm: posee todas las funciones de nivel bajo necesarias controlar la emisión del Led de infrarrojos, y en última instancia, estas funciones serán las que tengan un contacto directo con el control de este actuador led a través de los pines del puerto B del PIC. Básicamente va a exportar 3 funciones:

EnviarByte: codifica el byte a transmitir, y lo traduce de un lenguaje lógico (bits) a un lenguaje físico( traducción en ráfagas). Esta traducción a lenguaje físico será la que se usará como salida y actuará directamente sobre el Led.

EnviarSincronismo: codifica el sincronismo descrito en el protocolo, lo traduce a lenguaje físico (ráfagas) y lo transmite al Led.

Emitir_Trama: idénticamente, codifica la trama, la traduce a ráfagas y la transmite al Led.

Receptor_nivel_bajo.asm: en este fichero se encuentran todas las

funciones referentes al control y muestreo del sensor de infrarrojos, funciones que tiene por tanto un contacto no transitivo y directo con la salida del sensor a través del puerto A del PIC. Básicamente exportará 3 funciones y un registro:

RecibirByte: Se encarga de recibir el byte codificado,

decodificarlo y guardarlo en un registro de declaje. Si se produce algún error, lo indicará en el registro Fallo.

RecibirSincronismo: Se encarga de comprobar que la señal de sincronismo llega correcta. Mientras no llegue completa y sin errores el receptor no saldrá de esta función.

Recibir_Trama: idénticamente, no deja avanzar al receptor hasta que se reciba la trama de forma completa y sin errores.

Fallo: es un registro que exporta, y donde se refleja si ha habido algún error en el último byte recibido o no. Sirve para que niveles superiores, en función de este registro, acepten el byte recibido o lo rechacen.

Pasemos pues a una descripción exhaustiva de cada función exportada en cada

submódulo de la librería.

6.2.1 Emisor_Nivel_Bajo.asm En primer lugar, vamos a ver una serie de funciones internas ( no exportadas y por tanto no visibles exteriormente) destinadas a la traducción de los bits en nivel lógico a ráfagas. Estas funciones son Pulso1 ( para traducir un bit a 1 a ráfagas, según protocolo visto) y Pulso0. Se dan dos registros claves en ambas funciones:

CONTAct: guarda el número de pulsos activos ( pulsos periódicos) que debe de tener el marco de bit correspondiente a la ráfaga a enviar. De acuerdo con esto, si se desea mandar un cero, no se debe mandar ningún pulso ( registro a cero), y si se desea mandar un uno, entonces hay que mandar 50% pulso activo y 50% silencio en la ráfaga del marco de bit. Por tanto cargaremos un 100 ( en decimal) en el registro para que justo la mitad del marco de bit (duración equivalente a 200 pulsos activos) sea de ráfaga activa.

CONTInact: similar al anterior, guarda el número de pulsos equivalentes al tiempo dentro del marco de bit que debe estar en “ silencio” o no mandar anda el emisor. (sabiendo que cada pulso son 26µs) . Si se desea mandar un cero, todo el marco de bit debe de estar en silencio el emisor ( por tanto se inicializa el registro a 200, que son pulsos totales que pueden haber en un marco de bit), y si se desea enviar un 1, cargaremos un 100 correspondiente al 50% del marco de bit, para formar un 50% de pulso periódico y 50% de silencio dentro del marco de bit.

Una vez que cada función inicializa ambos registros convenientemente según lo

explicado, invocan a una función que traduce el contenido de ambos registros a pulsos emitidos a través del bit 0 del puerto B hacia el emisor. Esta es la función Pulso.

La función Pulso se desdobla en dos partes o secciones:

Per_a_tren: emite tantos pulsos periódicos de periodo 26 µs como indica el registro CONTAct. Por tanto, si la ráfaga a emitir posee un tren de pulsos periódico ( o periodo activo) éste se emitirá antes que el inactivo, debido a la ordenación secuencial de la función Pulso. Para crear ese pulso, pone el bit 0 del puerto B a 1 y llama a la función NOPS, que lo único que hace es un retardo de 13 µs en total, contando el número de nops y ciclos de instrucciones de call y return. Acto seguido, pone el bit 0 del puerto B a 0 y llama de nuevo a la función NOPS, que vuelve a hacer un retardo de 13 µs. En definitiva, se desarrolla un pulso de 26 µs, con mitad a nivel alto y mitad a nivel bajo. Pues bien, esta operación se repite tantas veces como indica el registro antes mencionado.

Per_inact: similar al anterior, ahora hace la traducción del registro CONTInact, con la salvedad de que no genera un pulso, sino que solo genera el espaciamiento temporal equivalente a tal pulso. Se consigue poniendo el bit 0 del puerto B a cero y llamar a NOPS dos veces, con lo que se completa el retardo de 26 µs en simple “ silencio” ( ya que las dos veces el bit 0 del puerto B se mantiene a cero).

1 Nótese que, en el caso de que el Pulso1 ea invocado, como interesa hacer un semiperiodo con tren de pulsos y otro sin tren de pulsos, se llama a la función Pulso tal cual, que hará secuencialmente el tren de pulsos y después el silencio. Sin embargo, en el caso de Pulso0, no interesa comenzar por Pulso, ya que pasaría primero por Per_a_tren, que sirve para enviar tren de pulsos que en realidad no necesita. Por tanto, emplea un atajo, y en vez de llamar a Pulso, llama a Per_Inac directamente, como si fuera otra función, que solo se centrará en un marco de bit completo de “silencio” y por tanto sin emisión de ningún tren de pulsos. Pulso1

Pulso0 Por tanto, las funciones internas Pulso1 y Pulso0 permiten que, en las funciones exportadas, se pueda trabajar a nivel lógico ( con bits a 1 y a cero, tal como se definieron en los protocolos) y con simplemente llamar a estas dos funciones descritas, se traducen automáticamente a lenguaje físico o de ráfagas.

Per_a_tren bsf PORTB,0 CALL NOPS x CONTAct veces. BCF PORTB,0 CALL NOPS

Per_a_tren bcf PORTB,0 CALL NOPS x CONTIact veces. BCF PORTB,0 CALL NOPS

Pulso Per_a_tren

Per_inact

Directamente Silencio

Tanto Tren de Pulsos como Silencio

>>EnviarByte Esta función se encarga de enviar un Byte, que deberemos de pasarle en el acumulador w del PIC. Para enviarlo, lo codifica en función del protocolo a nivel bajo descrito. Ya que es un protocolo de emisión serie de datos, esta función presenta un aspecto secuencial. Además trabaja a nivel lógico ( enviando bits a 1 o a cero), de manera que al invocar a Pulso1 o Pulso0 según el caso se hace la conversión automática de estos bits a 1 o cero a ráfagas o señales que controlan al Led emisor de infrarrojos. Para describir la implementación, haremos una traza por sus distintos hitos de ejecución:

1) Etapa de configuración: guarda el dato a enviar en el registro Dato, configura el bit 0 del puerto B como salida, ), pues será la línea a través de la cual mandará la señal al led infrarrojo, (véase que al invocar a Mod_TrisB, usa la constante SAL que sirve para configurar el bit 0 del puerto B como salida. Esta constante está definida en el fichero de Constantes.inc, y finalmente desactiva el “Pull Up”. Esto es necesario, por que, como veremos en niveles superiores, se usan las librerías del teclado y del LCD, que activan el “Pull Up” y no lo desactivan. Al activarse el “Pull Up“ del puerto B puede afectar a la correcta transmisión de la señal desde el Pic hasta el led infrarrojo.

2) StartBit: envía el Startbit ( un bit a 1, invocando a Pulso1 para

traducir el bit a ráfagas), almacena en INC el número de bits de datos que se van a enviar, que serán siempre 8 ( en caso de ser 9 o 10 como en Sincronismo o Trama, ya veremos que solución se adopta para enviar el bit de más).

3) LeerByte: Se trata de un bucle, de manera que en cada iteración (en

total 8 iteraciones, 1 por cada bit de datos, ya que el bucle se basa en el decremento de INC) desplaza el registro que contiene el dato (Dato) a la izquierda, de manera que el acarreo ( que será el bit más significativo) nos indica, si es un 1 que se debe mandar un Pulso1 pues ese bit más significativo era un 1, o Pulso0 en caso contrario. Véase que el resultado del desplazamiento se guarda en el propio registro Dato. Por tanto en la siguiente iteración, el bit más significativo será el que antes era anterior al más significativo. Con esto se consigue, tras 8 iteraciones, leer los 8 bits y traducirlos a ráfagas con Pulso1 y Pulso0.

4) Ampliación de Datos: esta sección no tiene etiquete en sí, pero se

sitúa justo a la salida del mismo bucle, y, en función del registro Banderilla, permite ampliar el número de bits de datos a 9 o 10. El modo de funcionamiento es el siguietne:

BTFSc Banderilla,0 call Pulso1 btfsc Banderilla,1 call Pulso1

Si se invoca a EnviarByte, Banderilla se encuentra a cero en

su bit menos significativo (LSB) y el contiguo a el, por tanto no se emite ningún Pulso1 de más. Esto quiere decir que el número de bits de datos no se amplía, y se queda en 8.

Si se invoca a EnviarSincronismo, se llama transitivamente a EnviarByte pero con el LSB de Banderilla a 1, por lo que emitirá el primer Pulso1, no emitiendo el segundo (ya que solo el bit menos significativo se encuentra a 1, no el siguiente menos significativo, según la lógica implementada). Esto quiere decir que se ha ampliado el número de bits de Datos a 9. La razón de enviar un 1 y no un 0, es que, como vimos en el protocolo de nivel bajo, el Sincronismo consisten en 9 bytes de Datos, todos siento bits a 1. Se supone que los 8 primero ya se han enviado (y estaban a 1, se conseguirá como veremos más adelante), y ahora simplemente envía el noveno.

Si se invoca a EnviarTrama, de idéntica manera , se llama transitivamente a EnviarByte pero con Banderilla con sus dos bits menos significativos a 1, por tanto, emitirá los dos Pulso1s según la lógica implementada (al testear que el bit menos significativo esta a 1, emite un Pulso1, y al testear que el siguiente bit menos significativo también esta a 1, emite otro Pulso1). El número de bits de datos serán entonces 10: 8 primeros en LeeByte, y los dos últimos en esta lógica. Véase que la Trama necesita 10 bits de datos todos a 1, por esto es por lo que se añaden dos Pulso1 a los 8 bits ya empleados, y no Pulso0.

Esta estructura optimiza el código, ya que tanto para enviar un byte, como una trama y un sincronismo se emplea exactamente el mismo código, pero con el registro Banderilla con valor diferente para añadir o decrementar el número de bits de datos.

5) StopBit: simplemente se lanza el Stopbit ya descrito en el protocolo:

un primer bit a 1 ( llamada a Pulso1) y, tras ello, se incrementa el registro INC a 11, para entrar en el bucle Etiqueta, que se encargará de enviar Pulso0 tantas veces como decrementos de INC hasta llegar a cero (es decir, 11 bits a 0).

Podría hacerse ya un breve resumen de la utilidad de los registro declarados en este nivel bajo:

Registro Descripción CONTAct Indica el número de pulsos que deben de generarse en el Marco de bit a convertir

a ráfaga. CONTInac Indica el número de pulsos que es equivalente al tiempo que tiene que

permanecer el emisor recibiendo una señal a nivel bajo del puerto B del PIC. INC Su valor indica el número de iteraciones totales de los distintos bucles usados en

la función EnviaByte. Dato Almacena el Dato a enviar. De su declaje irán extrayéndose los bits a enviar por

el puerto serie. Banderilla Si esta a cero, el número de bits de datos a enviar es 8 (envío de un Byte), si es

1, el número de bits de datos es 9 (envío de Sincronismo) y si es 3, el número de bits de datos es 10 (envío de Trama).

>>EnviarSincronismo Pone a 1 el valor menos significativo de Banderilla, carga en w el valor ffh, y llama a EnviarByte. Con esto consigue enviar un Sincronismo, con 9 bits de datos a 1 ( 8 de los 8 bits a 1 del registro w, más el que se añadirá por el valor del bit menos significativo de Banderilla). Esta manera de implementarlo disminuye el número de líneas de código considerablemente. Es importante hacer notar que, tras el envío del sincronismo, se devuelve Banderilla a su estado original, antes de modificar el bit menos significativo a 1. Esto permite que si, por ejemplo, tras un sincronismo se llame a un EnviarByte, la Banderilla no continúe a 1, y por tanto no se vuelva a mandar un bit de datos de más. >>EnviarTrama Similar al anterior, pero en este caso, no solo se inicializa w a ffh y se inicializa el bit menos significativo de Banderilla a 1, sino que también se inicializa el bit contiguo al menos significativo a 1 también, por lo que no solo se enviarán los 8 bits a 1 correspondientes a w, sino también 1 pulsos a 1 adicionales, siendo en total 10 pulsos a 1, como se describió en el protocolo. De la misma manera, tras el envío, se deja lso dos bits de Banderilla a cero, para evitar que al enviar un Byte contiguo al envío de una Trama se le asignen 10 bits de datos debido al valor de Banderilla. Los siguientes diagramas de flujo se corresponden con el algoritmo visto:

* W Dato * Configuración de bit0 del PuertoB como Salida (Mod_TrisB SAL) * Desactivación del Pull Up

StartBit: Pulso1 8 INC

INC = 0 ¿?

No <<Dato

Pulso1 Pulso0

Si No

INC – 1 INC Banderilla(LSB) = 1¿?

Si

Pulso1

Banderilla(LSB+1) = 1¿?

Si No

Pulso1 No Si

LEYENDA: Bucle Recorrido secuencial. << Desplazamiento a la Izquierda = comparación de igualdad.

asignación. Reg(LSB) bit menos significativo Reg(LSB+1) bit contiguo al LSB.

6.2.2 Receptor_Nivel_Bajo.asm En este fichero, que contiene las funciones que se complementan con las ya explicadas (de cara a la recepción de las mismas) posee una función interna no visible desde el exterior, que sirve para el muestreo. Como se explicó en el protocolo, el muestreo no es continuo: una vez que recibe el primer StartBit, deja de muestrear continuamente, para muestrear:

Justo tras el StartBit, deja de muestrear el tiempo suficiente para saltarse el marco de bit correspondiente al StartBit (como al detectar el Startbit se le detecta casi desde su comienzo, este salto es de un marco de bit completo, o sea, el tiempo equivalente a 200 pulsos) y situarse a la mitad del primer semiperiodo del siguiente, ya que es el primer semiperiodo el único que le permite distinguir entre si lo que se manda es un cero o un 1 (la mitad de un semiperiodo es el tiempo equivalente a 50 pulsos). Por tanto, tras recibir el primer StartBit, el Pic debe de esperar el tiempo equivalente a 250 pulsos para volver a hacer la siguiente muestra.

A partir de aquí, para muestrear justo en la mitad del siguiente semiperiodo, debe esperar para muestrear un tiempo equivalente a 200 pulsos ( 1 marco de bit completo).

1 vez acabado el StopBit, debe volver a muestrear sin tiempo de espera, continuamente.

StopBit * Pulso1 * 11 INC

INC = 0 ¿?

No Pulso0 INC-1 INC

Si

RETURN

250 pulsos 200 pulsos

200 pulsos 200 pulsos

200 pulsos ................

Muestreo Continuo

Pues bien, esa función interna a la que hacíamos referencia era ESPERA, que se

encarga de, dado un número de pulsos en el registro NPulsos, detener al receptor durante un tiempo equivalente al número de pulsos que posee el registro NPulsos.

La manera de calcular el tiempo que equivale a ese número de pulsos es analizar el algoritmo que genera pulsos en el Emisor.

En primer lugar, observamos que del emisor solo hemos tomado Per_a_tren. En

realidad tanto Per_a_tren como Per_inac son similares y secuenciales, por tanto podemos coger para el análisis solo 1 de los dos ( a lo sumo, serán mitad y mitad del marco de bit, si suponemos que NPulsos es 100, y CONTAct esta a 100 también, ambos deberían de tardar el mismo tiempo en ejecutarse.

La función NOPS es la misma en los dos, por tanto dura lo mismo. Sin embargo, si contamos el tiempo que tarda 1 y otro, vemos que, en el caso del Emisor se dan siempre dos instrucciones más que en el Receptor, debido a la puesta del puerto B a 1 o 0 marcados en [*] . Este error de dos ciclos, se multiplica por el número de bucles, que es 100. Por tanto, se daría un desfase de 200 µs, entre el emisor y el receptor. Por este motivo hay que añadir dos NOP en cada iteración de ESPERA, para evitar este desfase entre el emisor y el receptor, que se iría retrasándo cada vez más hasta salirse del marco de bit y desincronizarse con respecto al Emisor.

De hecho, en la práctica no nos dimos de añadir estos dos NOPS, y comprobamos empíricamente que se desfasaban ambos dispositivos. El Emisor y Receptor.

Ahora sí, la función ESPERA nos permitirá retrasar el muestreo el tiempo equivalente del número de pulsos que esté almacenado en NPulsos. A continuación vamos a analizar las funciones que exporta este fichero. >>RecibirByte Se encarga de muestrear el sensor de infrarrojos, y analizar la información que este le devuelve, para decodificar todo byte enviado ( de forma codificada) por el emisor) y guardar este dato en el registro acumulador del PIC, el registro w. Por tanto, al recibir un Byte, y decodificarlo, el byte sin codificar lo guarda en el registro w. Véase que al ser un registro de propósito específico del microcontrolador, no hace falta declararlo como externo o global para que pueda ser visto desde niveles superiores su valor.

ESPERA CALL NOPS CALL NOPS Nop

Nop

DECFSZ NPulsos GOTO ESPERA return

Receptor Per_a_tren bsf PORTB,0 [*] CALL NOPS BCF PORTB,0 [*] CALL NOPS DECFSZ CONTAct,1 GOTO Per_a_tren return

Emisor

La implementación puede explicarse describiendo secuencialmente (al ser una recepción

de tipo secuencial de la información) cada hito que presenta el algoritmo, tal y como se hizo en EnviarByte, de la manera:

1) Configuración: en esta fase se invoca a Mod_TrisA ENT,

para configurar el bit 4 del puerto A como “entrada” que es a través de la cual el PIC recibirá la señal del circuido del sensor de infrarrojos. También se desactiva el “Pull UP”, ya que en niveles superiores puede ser activado, sobre todo al invocar a la librería del teclado y la LCD. El que esté activado podría afectar la entrada a través del bit 4 del puerto A.

2) HearFast: es el modo de muestreo rápido. En este caso se

encuentra constantemente testeando la salida del sensor, hasta que esté a nivel bajo (sea un cero lo que devuelve en el bit 4 del puerto A). En tal caso se ha recibido un StartBit, es decir, el emisor tiene intenciones de enviar un bit. Sale de este bucle de muestreo rápido y va a StartBit, donde invocará a ESPERA para situarsea la mitad del primer semiperiodo del marco de bit siguiente. Véase, que se carga en NPulsos el valor “SalgoMayor”, que si analizamos en el fichero cabecera Constantes.inc, veremos que es un valor de 250 pulsos, tal como antes indicamos.

3) Bits: Ya situados en la mitad del primer semiperiodo del siguiente

marco de bit, se inicializa el registro INC a 8 ( pues 8 serán los bits que se envíen por parte del emisor), y borra el registro de Declaje, que será usado para ir guardando y desplaznado los bits que se van recibiendo.

De esta manera, comienza un bucle (LeeBit), donde se lee la señal que hay en la línea, y si es un cero el bit 4 del puerto A es que se ha enviado un 1 en el emisor, por lo que se acarrea un 1 en el Registro Declaje y se desplaza a la izquierda), en cambio si es un 1 el bit 4 del puerto A es que se ha enviado un 0 el emisor, por lo que se acarrea un 0 en Declaje. Tras esto, se llama a ESPERA con un número de pulsos igual a “SaltoNormal” ( que se corresponderán con 200 pulsos para situarse en la mitad del primer semiperiodo del siguiente marco de bit). Con esto, queda listo para muestrear el siguiente bit. Este proceso se repetirá 8 veces en función de INC.

4) Stopbittb: una vez ya completados los 8 bits, se comprueba que se

recibe el Stopbit completo y sin errores. Primeramente se comprueba que el primer StopBit es un 1 lógico enviado por el emisor ( en el receptor, que el bit 4 del puerto A esté a nivel bajo, es decir a cero). Si así es, se hace un retardo de “SaltoNormal” pulsos para situarse en la mitad del primer semiperiodo del siguiente marco de bit ( para leer el siguiente). Pero si no es así, significa que hay un error de desincronización de errores. Al detectarse el error, se va a CONTROL_ERROR, que simplemente pone el primer bit (menos significativo) del registro Fallo a 1, y va a GuardarMostrar. Que guarda el contenido de Declaje en w ( el dato recibido lo guarda en W, que será rechazado en niveles superiores debido a que hay un fallo) y retornará.

A partir de este punto, se inicializa INC a valor 9, y comienza un bucle, donde se comprueba si el bit 4 del puerto A esta a 1

(emisor ha mandado un cero). Si es así, espera los 200 microsegundos , decrementa INC y vuelve a buclear. Si todos los marcos de bit, los 9, son bit ceros mandados por el emisor ( es decir, siempre que se muestrea cada 200 microsegundos la línea del bit 4 del puesto A esta a 1) terminará en GuardarfMostrar (lo que significará que el byte recibido es bueno) para guardar ese byte recibido en W y poder devolverlo al proceso que invocó esta función y retornar.

Pero si en alguna de las comprobaciones el bit 4 el puerto A esta a cero ( el emisor envió un 1) hay uin fallo de sincronización, por lo que se va a CONTROL_ERROR tal como antes.

Es importante ver que, en caso de Error, simplemente se avisa en el registro Fallo, en su

bit menos significativo. Como este registro es global, va a poder ser revisado por un nivel superior y comprobar, antes de tratar el Byte devuelto en w, si es correcto o no. El siguiente diagrama de flujo explica este algoritmo:

* Configuración de bit4 del PuertoA como Entrada (Mod_TrisA ENT) * Desactivación del Pull Up

StartBit

PortA (4) = 0 ¿? No << Declaje

PortA (4) =0¿?

1 Declaje (0)

Si No

INC – 1 INC

Si

Si

No

ESPERA(200) 9 INC

INC = 0 ¿?

ESPERA(200)

PORTA (4) = 0 ¿?

Si

No CONTROL ERRORES 1 Fallo (0 )

GuardarMostrar Declaje W

RETURN

StopBits

INC = 0 ¿? No

Si

PORTA (4) = 1 ¿?

No INC – 1 INC

ESPERA(200)

Si

>> RecibirSincronismo

Si bien, podríamos haber optado, como en el emisor, por usar el mismo código, no lo hemos hecho, ya que, ahora el objetivo no es almacenar en un registro un dato, sino más bien comprobar que el dato que se manda son bits a 1 enviados por el emisor.

Ya que son dos filosofías completamente diferentes hemos optado por no emborronar el código original del RecibirByte con subterfugios para amalgamarlo con el RecibirSincronismo, sino considerarlos dos códigos deparados.

Aún así, ambos códigos se parecen mucho, por lo que no vamos a volver a explicarlo. Solo vamos a explicar los puntos en los que difiere el RecibirSincronismo con respecto al algoritmo del RecibirByte.

Ahora el registro Declaje no tiene sentido usarlo como declaje propiamente dicho, pues no necesitamos almacenar los bits que llegan, solo comprobarlos. Por tanto lo vamos a usar tal y como usábamos Banderilla en el Emisor, de manera que si el registro Declaje tiene el bit menos significativo a 0, significa que nos encontramos ante un Sincronismo¸ y por tanto el número de bits de datos tras el StartBit que se espera recibir son 9.

Si por el contrario, este bit menos significativo de Declaje está a 1, significa que un EnviarTrama fue el que invocó al EnviarSincronismo, por lo tanto, no se esperan recibir 9 bits a 1 (desde el emisor) de datos como en el caso anterior, sino 10 bits.

Decidido ya si se esperan 9 bis a 1 desde el emisor (LSB de Declaje a 0) o 10 ( lo contrario), se entra en un bucle donde, a diferencia del EnviarByte, no se guardan, solo se comprueban que son bits a 1 desde el emisor (bit 4 de PORTA a cero en receptor). Si así es, se hace un ESPERA y se vuelve a comprobar el siguiente. Si no se cumple hay un Error.

Pasado esto se comprueban los StopBits. Pero tanto en la comprobación de los StopBits como en la de que los bis de datos, todos se correspondan con bits a 1 emitidos por el emisor, si hay algún error no se indica en ningún registro, sino que se vuelve a la etiqueta HearFast2, es decir, el receptor se vuelve a poner a la escucha. De esta manera, hasta que no se sincronice ( respecto a Sincronización o trama) con el emisor de forma completa, no saldrá de esta función el receptor.

>> RecibirTrama Similar al EmitirTrama, carga en el bit menos significativo del registro Declaje un 1 para indicar que se espera una trama, y acto seguido invoca a RecibirSincronismo. Cuando se vuelva desde la función RecibirSincronismo querrá decir que emisor y receptor estarán sincronizados. Se pone el registro de Declaje de nuevo su bit menos significativo a cero, para evitar problemas en llamadas futuras a funciones que usen el Declaje.

A continuación se muestra un diagrama de flujo correspondiente al

RecibirSincronismo:

* Configuración de bit4 del PuertoA como Entrada (Mod_TrisA ENT) * Desactivación del Pull Up

StartBit

PortA (4) = 0 ¿? No

PortA (4) =0¿?

Si

INC – 1 INC

Si

Si

No

ESPERA(200) 9 INC

INC = 0 ¿?

ESPERA(200)

PORTA (4) = 0 ¿?

Si

No RETURN

StopBits

INC = 0 ¿? No

Si

PORTA (4) = 1 ¿?

No INC – 1 INC

ESPERA(200)

Si

Declaje (0) = 0 ¿?

9 INC 10 INC

No Si

ESPERA(250)

No

Finalmente, vamos a hacer una descripción de los registro usados y su funcionalidad en

el receptor a nivel bajo:

Registro Descripción

Fallo Registro global, que ( en este nivel) si posee el bit menos significativo indica que en la última recepción hubo un error de sincronización, y si esta a cero que fue una recepción sin incidentes. El bit contiguo al menos significativo será usado en niveles superiores para detectar si el byte recibido fue un 00h ( si esta activado) o no, por tanto, si se ha llegado al fin del mensaje o no.

NPulsos Indica el número de pulsos, cuyo tiempo equivalente ( aproximadamente ) es Npulsos * 26µs, que será el tiempo que la función ESPERA permanecerá inactiva “ haciendo tiempo” hasta que llegue el momento del siguiente muestreo. Es una espera activa.

INC Su valor indica el número de iteraciones totales de los distintos bucles usados en la función RecibirByte y RecibirSincronismo

Declaje En RecibirByte almacena los bits recibidos por el sensor de infrarrojos y traducidos a nivel lógico. Su contenido final es el dato enviado por el emisor, pero decodificado. En RecibirSincronismo, simplemente indica con su bit menos significativo si se tien que recibir un Sincronismo ( bit a 0) o una Trama (bit a 1).

6.3 nivel_paquetes.lib En esta librería se dan todas las funciones propias del Nivel Medio descrito en la sección de protocolo, para el envío y recepción de “ Paquetes”. El rasgo fundamental que lo diferenciará del resto de librerías de paquetes desarrolladas, es que el tamaño de los paquetes es fijo, y viene definido por NBytes_Paquete, que se encuentra en el fichero cabecera Constantes.inc. En principio su valor es 3, por tanto, con esta librería vamos a establecer un protocolo de paquetes donde el tamaño de los mismos sea de 3 bytes. Nuevamente, nos encontramos con que esta librería de forma de los objetos compilados y construidos de dos objetos provinientes de dos ficheros *.asm diferentes: uno para el Emisor y otro para el Receptor:

Emisor_Paquete.asm: Posee funciones para ensamblar los paquetes a partir de las funciones de nivel bajo de emisión ya descritas, de manera que permite que los niveles superiores puedan usar como unidad básica de división del mensaje el paquete ( codificar el mensaje a través de paquetes). Las funciones y registros que exporta son:

Emitir_Paquete: Dado un puntero a una posición de memoria donde se encuentra el mensaje, toma los 3 bytes que existan en memoria a partir de esa posición y los envía anteponiéndole un envío de sincronización, por tanto, esta función toma 3 bytes y hace y envía con ellos un paquete.

Retarda_Paqjuete: simple función que simplemente hace un bucle para provocar una espera activa en el programa que lo invoca. Servirá para espaciar la emisión de los paquetes en el nivel Alto que usa como unidad de transmisión el paquete y así evitar desfases con el receptor.

FalloE: Registro que se exporta para indicar al programa que invoco un EmitePaquete si el paquete que se envió era el último del mensaje ( pues contiene un cero) o no.

Receptor_Paquete.asm: Contiene todas aquellas funciones

referentes a la recepción de paquetes, de manera que organiza las señales que le llegan del sensor en Paquetes, reconstruyendo pues los paquetes que le son enviados. Por tanto, dota al nivel alto de una herramienta para recibir unidades básicas no tan “ diminutas” como Bytes, sino unidades básicas mayores ( más información). Este fichero exporta:

RecibirPaquete: permite a los niveles que la invocan esperar no bytes sino conjuntos de bytes, ya que construye paquetes a través de funciones y unidades básicas proporcionadas en el nivel bajo. Así, los bytes que le llegan los almacena en la posición de memoria a la que señala el registro FSR. Por tanto, el que invoca esta función debe de pasar en FSR la dirección de memoria donde se desea que se almacenen los 3 bytes a recibir.

6.3.1 Emisor_Paquete.asm En sí, este fichero no posee más que las funciones que exporta:

>> retarda_paquete Al hacer un recuento de las instrucciones que se dan ya en el programa principal de nivel alto, y compararlas con las del receptor a nivel alto, encontramos un cierto “ desfase”, ya que el receptor poseía más instrucciones que el emisor entre el envío de cada paquete, por tanto cada vez se iba retrasando más y más hasta que terminaba por desincronizarse. Pero el problema del conteo de instrucciones no es fácil cuando nos encontramos en un nivel alto, ya que hay muchas funciones que forman parte de otras librerías. Por ello, ante la imposibilidad de realizar una cuenta totalmente exacta, si optamos por una solución lógica: retardar en el emisor el envío de cada paquete o conjunto de paquetes.

Así, siempre que el emisor tarde más que el receptor, no habrá problemas ( el emisor esperará). El problema sería justamente lo contrario, que el emisor fuese más rápido que el receptor.

Pues bien para producir un retardo entre el envío de cada paquete o cada conjunto de paquetes, hemos desarrollado esta función que le es proporcionada por la librería de nivel_paquete.lib al programa de nivel alto que use la emisión de paquetes. Consiste, simplemente, en un bucle de 75 iteraciones (mediante el registro DATOAUX). Si queremos contar exactamente el retardo que produce sería:

2 ciclos de inicializaciones + 74*3 + 4 = 228 ciclos de instrucción = 228µs

>> Emitir_Paquete Previo a llamar a esta función, el programa invocador tiene que poner en el registro FSR un puntero a la dirección de memoria en la que se encuentra el primer Byte a enviar. Así, esta función enviará un paquete que contenga al byte que indica el FSR y los 2 siguientes Bytes. Para explicar la implementación, vamos a hacer una descripción secuencial de los pasos que sigue esta función para formar y enviar un paquete de 3 bytes:

Inicializaciónes: en primera instancia se borra el registro FalloE para que no haya ninguna interferencia entre el resultado de un envío anterior y el de este envío. Igualmente, se carga en el registro INCRE el número de bytes que formará el paquete, representado por NBytes_Paquete definido en el fichero de Constantes.inc en principio con valor 3 (las explicaciones irán referida a 3, pero saber que en realidad cambiando NBytes_Paquete se puede cambiar el número de bytes de paquete ).

Comienzo del paquete: para encabezar el paquete se envía un “Sincronismo” con EnviarSincronismo, herramienta ya descrita en el nivel bajo. Con esto indicará al receptor que va a comenzar a enviarse un paquete.

LoopEP: en este bucle se va a proceder con leer el Byte apuntado por el FSR (que se encuentra en INDF)y ver si es un cero:

Si no es un cero: se carga ese byte en W y se invoca a EnviarByte para que lo mande. Incrementa el FSR o puntero a memoria, para que en el próximo bucle se envíe el byte contiguo a éste en memoria, y se decrementa INCRE. Por tanto, en última instancia se repetirá este bucle y se mandarán 3 bytes, que son los 3 contiguos en memoria a partir de la posición indicada por FSR.

Si es un cero: si es un cero, se envía igualmente, pero abandona el bucle y va a la etiqueta ExitEP, que básicamente sigue con el decremento de INCRE pero enviando solo ceros. Es decir, a partir del momento que se manda 1 cero, el resto del paquete se rellena con ceros. También se pone el bit 1 del registro FalloE a 1, indicando a niveles superiores ( ya que el registro es global) que este paquete es el último ( ya que según el protocolo, uno o más ceros indican el final del mensaje en un paquete).

Nótese que el puntero a memoria se quedará, como máximo, señalando al siguiente

Byte que no ha sido enviado (la posición inicial + 3). Incluso al enviar ceros se avanza el puntero FSR aunque los ceros no se estén leyendo de memoria. Esto es para que el efecto de EmitirPaquete siempre sea el mismo ( dejar FSR 3 posiciones más avanzadas de la original que se pasó al programa). Hace más sencillo el control en niveles superiores, ya que siempre deja el registro FSR 3 posiciones más adelantado (en general NBytes_Paquete posiciones más adelantado).

Para acceder al contenido de la memoria apuntada por FSR usamos el registro INDF, que posee el valor del byte almacenado en la posición a la que apunta actualmente FSR.

La descripción de los registros usados sería: Registro Descripción

INCRE Solo sirve para contar el número de veces que se tiene que repetir el bucle de EmitirPaquete, que coincide con el número de Bytes que debe tener el paquete.

DATOAUX Solo sirve para contar el número de bucles de la función retarda_paquete. FalloE Registro que señala, si tiene el bit 1 a 1 que en el último paquete hay un cero, por

tanto es el último paquete a enviar del mensaje. Es global y puede ser usado por niveles superiores para controlar cuando se acaba el mensaje y por ello el envío.

FSR & INDF FSR señala a la posición de memoria donde se encuentra el siguiente byte del mensaje a enviar. INDF contiene en todo momento el contenido de la memoria apuntada por FSR.

* 0 FalloE * Nbytes_Paquete INCRE

EnviarSincronismo

No

INDF w

Si

1 FalloE(1)

EnviarByte

FalloE (1) = 1 ¿?

INDF = 0 ¿?

FSR+1 FSR INCRE-1 INCRE

INCRE = 0 ¿?

No

RETURN Si

No

FSR+1 FSR INCRE-1 INCRE INCRE = 0 ¿?

Si

0 W

EnviarByte

No

6.3.2 Receptor_Paquete.asm Este fichero no posee más que una sola función, que es la que exporta: >>Recibir_Paquete En este caso, el registro FSR debe de contener la posición de memoria a parir de la cual se irán almacenando los bytes que se vayan recibiendo ( hasta un máximo de Nbytes_Paquete, que es la misma constante definida en el fichero Constantes.inc, en principio con valor 3 ( el receptor irá almacenando 3 bytes en posiciones contiguas de memoria a partir de la posición original a la que apunta FSR). Su implementación contiene los siguientes hitos:

1) Inicializaciones: primeramente, se borra el registro Fallo, para que el resultado de envíos anteriores no puedan ser confundidos con el del envío de este paquete. Obsérvese lo siguiente: El registro de Falloen principio, indica en su bit menos significativo con 1 si ha habido un fallo al transmitir un byte. ¿No convendría mejor borrar Fallo cada vez que se recibe un byte, para que se sepa en qué byte hubo un error? En tal caso, el borrado del Fallo lo hubiésemos hecho en el comienzo de la función de nivel bajo RecibePaquete.

La respuesta es la siguiente: a nosotros no nos interesa en que byte se produjo el error, solo si el paquete tubo un error, por que el sistema de corrección de errores ( la redundancia) no distingue en que byte se produjo el fallo. Como dijimos en el protocolo, solo nos interesa conocer si hubo fallo en alguno de los bytes o no del paquete, para desechar todo el paquete y tomar el siguiente envío de los paquetes redundantes ( a nivel alto). Es por ello que el registro Fallo solo se borra en este nivel de paquetes.

Si al hacer llamadas al RecibirByte alguna de las 3 llamadas ( 3 bytes por paquete) o más de una o otras poseen error, con que solo 1 tenga error, ya el registro de Fallo tendrá el bit menos significativo a 1 ( gracias a que no se borró en el nivel bajo). Si hubiésemos borrado cada vez que se recibe un Byte, posiblemente en el primero se produjo error, pero en el segundo no, así como cada Byte borra el registro Fallo, solo se sabría si hubo un fallo en el último byte recibido, no en los otros 2. El borrar Fallo en el nivel de paquetes nos permite solventar este problema.

Por otra parte, se inicializa CUENTA con el número de bytes por paquete Nbytes_Paquetes, pues será el contador que cuente cuantos Bytes van recibidos para saber cuando acaba el paquete.

2) Sincronismo: siempre que se desee recibir un paquete, previo a

esto, el receptor tiene que estar sincronizado con el emisor. Por ello todo paquete tiene que venir encabezado por un Sincronismo. RecibirSincronismo nos permite que el receptor no se ponga a recibir el paquete hasta que no reciba el sincronismo que le indique que va a comenzar un paquete.

3) LooR: una vez sincronizados el emisor y receptor se procede con un bucle, en el cual se recibe un byte con RecibirByte se guarda en INDF (por tanto se está guardando en la posición de memoria a la que apunta el registro FSR) se avanza la posición de memoria incrementando FSR para

que el siguiente byte recibido se guarde contiguamente, se decrementa CUENTA y vuelta a empezar al bucle para recibir otro Byte

4) Comprobación final: para terminar devuelve FSR señalando al ultimo Byte que se introdujo en memoria (si en nivel alto se quiere leer el primer Byte que se metió en memoria, basta con restarle a la posición que señale FSR – Nbytes_Paquete). Por último se comprueba que si ese último byte recibido al que señala FSR después de decrementarlo es un cero. Si es un cero entonces es que era el último paquete, luego ser guarda en el registro Fallo, en el bit 1 un 1 si este era el último paquete ( por que contiene al menos 1 cero final). Con eso se avisa a los niveles superiores que este es el último paquete a tratar.

La descripción de los registros usados sería:

Registro Descripción CUENTA

Solo sirve para contar el número de veces que se tiene que repetir el bucle de RecibirPaquete, que coincide con el número de Bytes que debe tener el paquete.

DATOAUX Solo sirve para contar el número de bucles de la función retarda_paquete. Fallo Registro que señala, si tiene el bit 1 a 1 que en el último paquete hay un cero, por

tanto es el último paquete a recibir del mensaje. Es global y puede ser usado por niveles superiores para controlar cuando se acaba el mensaje y por ello la escucha.

FSR & INDF INDF contiene en todo momento el contenido de la memoria apuntada por FSR. Al modificar el contenido de INDFse modifica el de la posición de memoria de que señala FSR.

* 0 Fallo * Nbytes_Paquete CUENTA

RecibirSincronismo

No No

RecibirByte

CUENTA = 0 ¿?

W INF

FSR+1 FSR

CUENTA-1 CUENTA

RETURN

FSR-1 FSR

INDF = 0 ¿?

1 Fallo(1)

Si

Si

6.4 nivel_paquetes_variables.lib

Este es otro caso de librería de nivel medio, que es exactamente igual a la anterior, pero con una sutil diferencia de funcionalidad: en vez de estar predefinido del número de bytes que posee el paquete con Nbytes_Paquete en el fichero Constantes.inc, vamos a hacer que el programa de usuario pueda elegir el tamaño del paquete de forma dinámica. +

Para ello, en el caso del Emisor_Paquete.asm, el cambio a hacer es la introducción de

un registro global, NBPackE, de manera que el programa de nivel alto que invoque a Emitir_Paquete, deberá previamente no solo dar la poción de memoria donde comienzan los Bytes a Enviar, sino también indicar en el registro NBPackE el tamaño del paquete ( un número). Así, si en el momento de invocar a Emitir_Paquete se encuentra un 4 en NBPackE, el paquete que se envíe será de 4 bytes, etc. A nivel de código, la única diferencia es que en vez de cargar en INCRE el valor de Nbytes_Paquete, carga el contenido de NBPackE en ese instante.

En el caso del Receptor_Paquete.asm el cambio será la introducción de un registro

global NBPackR, de forma que al invocar el Receptor de nivel alto a la función Recibir_Paquete, deberá de indicar en NBPackR el número de Bytes que conforma el paquete. Uno de los problemas será ves de que manera puede el Receptor cuál es el número de Bytes que el usuario introdujo en el Emisor. Lo veremos en el nivel alto. Por tanto, a nivel de implementación, la única modificación será que, al cargar en CUENTA el valor de Nbytes_Paquete, en vez de ello, cargar el valor del registro NBPackR. 6.5 Divide_Paquetes_Emisor.asm Este es el primer programa de nivel alto que vamos a ver. Su función será sencillamente dividir un mensaje en paquetes y enviarlos en conjuntos redundantes. El número de veces que se repite cada paquete ( por tanto número de paquetes iguales en un conjunto redundante) viene definido por la constante REDUNDANCIA definida en el fichero Constantes.inc. Este número va a ser predefinido antes de compilación y por defecto tenemos que la redundancia sea de 3. Igualmente, decir que este programa esta preparado para una librería de nivel_paquetes.lib tal que el número de Bytes por Paquetes venga definido por la constante Nbytes_Paquete, tal y como se describió la primera libraría de Nivel_paquetes.lib. Hay que tener cuidado con no usar la librería Nivel_paquetes_variables.lib con este programa de usuario, pues no esta preparado para ello. Más tarde veremos un programa que si está preparado para trabajar con una librería de paquetes variables. Antes de ver el programa principal considerar que se da una tabla que contiene ordenados de mayor índice a menor índice todas las letras que conforman el mensaje. Se pueden acceder a estos Bytes invocando a la función Ristra ( previamente poniendo en w la posición en la que está la letra a extraer de la tabla). Ahora, podemos proceder con explicar el algoritmo:

1) Cargar el Mensaje en Memoria: en todas las librerías hechas se

nos exigen punteros a memoria de Datos en donde se encuentra el mensaje ( a través de FSR). Sin embargo, ahora mismo tenemos la frase en memoria de instrucciones (preferimos hacerlo así para que fuera más fácil de modifica el mensaje. Si hubiésemos introducido el mensaje directamente en memoria de datos hubiese sido más complicado cambiar el mensaje).

Por tanto vamos a pasar por el bucle Bucle, donde un contador CONT inicializado a cero irá sacando Bytes de la Tabla y introduciéndolos en memoria de datos correspondiente con la etiqueta Mensaje. El registro FSR estará inicializado inicialmente a Mensaje, y los bytes que saquemos de la tabla los iremos introduciendo en la memoria de datos a través de INDF. Tras cada byte incrementaremos tanto el contador CONT y también FSR. Así hasta que completemos todas las letras que hay en el la tabla. Véase que el número de bytes de la tabla debe de coincidir con el valor de MaxNBytes_Mensaje en el fichero Constantes.inc, que también coincide con el tamaño del espacio reservado en memoria de datos para Mensaje. Más tarde analizaremos que limitaciones tiene ese espacio.

Tras ello se inicializa el FSR al comienzo del Mensaje ( primera posición de Mensaje).

2) EPPro: aquí se va a trazar primeramente el comienzo del conjunto

redundante. Para ello, inicializa CONT con el valor de REDUNDANCIA o número de veces que se tiene que mandar cada paquete, se emite la primera trama para identificar al conjunto redundante, mediante EmitirTrama, y se borra el registro de FalloE para evitar que el resultado de otros envíos afecte a este.

A partir de este momento en el que se inicializó el conjunto redundante, comienza un bucle en el que se va a repetir tantas veces como indica REDUNDANCIA el siguiente proceso: se llama a EmitirPaquete, de manera que al FSR que señala a la primera posición se le modificará y terminará en una posición FSR + Nbytes_Paquete, según las especificaciones de EmitePaquete que ya analizamos. Por ello, para repetir el mismo paquete, basta con restarle Nbytes_Paquete al FSR devuelto por EmitePaquete, y volver a buclear. Antes de ello, invocamos a retarda_paquete para poder dar tiempo al receptor a tratar la información.

Una vez que ya se ha repetido esto el número de veces indicado por REDUNDANCIA, se le suma al FSR ( recién restado Nbytes_Paquete) el mismo Nbytes_Paquete con el fin de dejarlo apuntando al primer byte que formará parte del siguiente paquete, y se vuelve a EPPro, a no ser que en el registro FalloE se encuentre un 1 en el bit 1, en cuyo caso este conjunto redundante recién enviado era el último y ya puede terminar la emisión.

Registros: La descripción de los registros usados sería:

Registro Descripción CONT

Sirve en cada conjunto redundante para controlar el número de iteraciones del bucle ( en total este número es igual a REDUNDANCIA).

MENSAJE Zona de memoria destinada a guardar el mensaje a enviar. Su tamaño es MaxNBytes_Mensaje

Cargar Ristra en Memoria

Mensaje FSR

REDUNDANCIA CONT

EmitorTrama 0 FalloE

EmitirPaquete

FSR – Nbytes_Paquete FSR

Retarda_Paquete

CONT-1 CONT

CONT = 0 ¿? No

FalloE (1) = 0 ¿?

Si

Si

END.

No

FSR+Nbytes_Paquete FSR

6.6 Divide_Paquetes_Receptor.asm Simétrico al anterior, se va a encargar de tomar paquetes de tamaño fijo

(Nbytes_Paquete), de manera que si no se produce ningún fallo, los imprime en la LCD y espera a la siguiente Trama ( es decir el siguiente conjunto redundante, ignora las respeticiones del mismo paquete). Sin embargo, en caso de fallo tiene en total tantas oportunidades como el número que indica REDUNDANCIA en el fichero CONSTANTES.inc, ( común con el emisor). Por tanto, ignorará el paquete recién recibido y se pondrá a la escucha del siguiente. Si finalmente pierde todas las oportunidades, no imprime nada en la LCD, y se pone a la escucha del siguiente conjunto redundante. Ha perdido un paquete definitivamente, pero por lo menos no pierde todos los del mensaje.

Una descripción detallada sería la siguiente:

1) Inicializaciones: nada más comenzar el programa se configura e inicializa la LCD.

2) Bucle de recepción de paquetes: Primeramente se comienza en

Recibe a recibir el conjunto redundante recibiendo una Trama con RecibeTrama, y se carga en AUXILIAR el número REDUNDANCIA, que van a ser el número de oportunidades de capturar el paquete, o lo que es lo mismo, el número de veces que se envía el mismo paquete en el conjunto redundante.

A partir de aquí, se inicia un bucle interno para tratar todo el conjunto redundante. En este bucle interno comienza en Reb_Paq_again.

Se inicializa FSR a recent_Paquete que va a ser la zona de memoria donde se van a almacenar los Bytes recibidos que contiene el paquete. Véase que en la declaración de recent_paquete, el tamaño del mismo coincide con el número de bytes del paquete Nbytes_Paquete.

Se invoca a RecibirPaquete para guardarlo en la memoria, y se revisa el registro Fallo por si se produjo algún error. Si se produjo algún error, se decrementa AUXILIAR y vuelve a Reb_Paq_again. Si sigue dando errores y Auxiliar llega a cero se han agotado las oportunidades, por lo que se acepta que el paquete esta perdido, y se vuelve a Recibe para aunque sea tomar el siguiente conjunto redundante.

Pero en el caso de que no se de Fallo, se va a la etiqueta Seguir, donde se inicializa a FSR para que apunte a los Bytes que se acaban de grabar en memoria (en recent_paquete, ya que este fue el puntero que se le pasó a FSR antes de RecibirPaquete). Acto seguido se van uno a uno escribiendo en la LCD siempre y cuando no sean estos bytes igual a cero. Desde que uno sea cero, se va a la etiqueta final LOOPINACTIVER, donde, antes de dormirse, se escribe un signo = en la LCD, para luego pasar a modo de bajo consumo el microcontrolador, pues ya terminó la emisión.

Registros: La descripción de los registros usados sería:

Registro Descripción AUXILIAR

Sirve simplemente para indicar número de bucles máximos a hacer, tanto a la hora de dar oportunidades en función de Redundancia, como a la hora de contar el número de bytes que hay que escribir en la LCD.

recent_paquete Zona de memoria destinada a guardar el mensaje recibido. Como el número de Bytes por paquete es fijo (Nbytes_Paquete), y solo hace falta guardar los bytes de un paquete en memoria, pues a medida que se van tomando nuevos paquetes se pueden ir volcando los antiguos en la LCD, dejando la memoria libre, solo necesita como espacio Nbytes_Paquete.

Configurar LCD Inicializar LCD

Recent_paquete FSR

RecibirTrama

REDUNCANCIA Auxiliar

RecibirPaquete

Fallo (0) = 0 ¿?

Auxiliar –1 Auxiliar

AUXILIAR = 0 ¿?

No

No

Si

Si

Recent_paquete FSR Nbytes_Paquete AUXILIAR

INDF W

LCD DATO

Auxiliar –1 Auxiliar

INDF = 0 ¿?

FSR +1 FSR

No Si

No

‘=’ W LCD_DATO

END.

Si

AUXILIAR = 0 ¿?

Véase que en el manejo de la LCD, cada vez que se carga un dato en la pantalla, hay que borrar el bit 2 del puerto A, para evitar que se desconfigure al usar el puerto A para recibir Datos también

6.7 Divide_Paquetes_Emisor (2).asm En este caso, vamos a acometer el diseño de un programa emisor que haga uso

de la librería de nivel medio nivel_paquetes_variables.lib, que recordamos presenta la posibilidad de elegir desde el nivel superior cuál es el número de Bytes del Paquete. El programa de nivel alto deberá comunicárselo inicializando el registro global que proporciona el nivel de paquetes NBPackE. Una vez inicializado, cada vez que se emita un paquete se hará con ese número de Bytes especificado.

A continuación vamos a citar solo las modificaciones respecto al programa Emisor principal ya descrito que hemos tenido que hacer para que pudiera funcionar con esta nueva librería:

1) Inicializar pantalla: antes que nada imprime por la pantalla de la LCD la frase que se da en la tabla RistraPantalla, accediendo secuencialmente a cada posición de la tabla. Este mensaje pide al usuario teclear el número de Bytes que se desea que tenga el paquete.

2) Cargar mensaje en memoria de datos: al igual que en emisor antes

descrito, se carga el mensaje que se presenta en memoria de código ( en una tabla) a la memoria de datos ( Mensaje).

3) Tec: aquí comienza un bucle, que hace uso de las librerías del

teclado para poder leer del mismo . Así, va a estar continuamente bucleando hasta que en el teclado se escriba algo (registro Tecla sea distinto de 80h).

Una vez que lee algo , comprueba que esté entre 1 y MaxNBytes_Paquete-1, constante que se encuentra en constantes.incy que debido a que vale 5, permite paquetes de hasta 4 bytes. ( de 1 a 4 bytes). Si la tecla pulsada no esta entre estas, vuelve a Tec. Sino continua, lo guarda en POS y lo escribe por pantalla. Recuerde que para que el pic pueda leer del teclado es fundamental que el potenciometro del circuito este a algo de resistencia, distinta de cero. Si no va a lograr leer el programa desde el Teclado.

4) Configurar los puestos: el puerto A, como entrada ( para el punto

5). 5) Comienzo manual de transmisión: entra en un bucle en

Bucle_em, de manera que hasta que el usuario no ponga el puerto A, el interruptor más a la derecha a cero, no se va a comenzar con el envió. Recuerde que antes de activar este interruptor, el potenciómetro deberá de volver a su posición inicial, es decir a resistencia 0, para que la intensidad de salida del PIC no se desvíe al teclado.

6) Envío de número de paquetes: uno de los problemas que citamos

antes era el de cómo se podía enterar el Receptor del número de Bytes que tendría cada paquete, para inicializar su registro NBPackR. Pues bien, antes

de comenzar la emisión, el emisor le envía un conjunto redundante, con un paquete de tamaño 1 solo byte, donde lo que se transmite es el contenido de POS , es decir, el número de Bytes del paquete. Tras enviar esto tantas veces como indica REDUNDANCIA, inicializa NBPackE con el valor de Pos, eso es, el número de bytes de paquete.

7) Final: a partir de ahora se hará el envío normal, idéntico que en el

primer Emisor que vimos, pero usando el contenido de NBPackE en vez de Nbytes_paquete.

Este diagrama ayudará a comprenderlo.

Escribir en LCD “N_Paquetes? ”

Cargar mensaje en Ristra en Mensaje(memoria de datos)

Leer de Teclado en Tecla

Tecla = 80h ¿? Si

Llamar a CodsTecla Obtener en POS el número

pulsado

No

0 < Pos < MaxNBytes_Paquete-1 ¿?

No

Configuirar Puerto A como entrada

PORTA = 0 ¿?

1 NBPackE

Emitir POS tantas veces como REDUNDANTE, en un conjunto

redundante.

POS NBPackE Emitir resto de mensaje en conjuntos redundantes de NBPackE bytes cada

paquete (como en el caso base explicado)

Si

No

Si

6.8 Divide_Paquetes_Receptor (2).asm Similar al anterior, ahora el número de paquetes es variable, ( ya que usa la

librería de paquetes variables como nivel de paquetes), por lo que en el protocolo, antes de comenzar la transmisión el Emisor deberá de comunicarle cual es el número de Bytes que tiene cada paquete ( para ponerse de acuerdo ambos).

Una vez esto, simplemente tiene que desarrollarse igual que antes, pero usando la librería de paquetes variables. Las modificaciones básicas al programa original son:

1) Nada más comenzar el programa, se inicializa NBPackR a 1, pues

el primer conjunto redundante, que nos indica el número de bytes por paquete que se van a transmitir, son paquetes con 1 solo byte indicando ese número.

2) En la recepción de dicho conjunto redundante incial (encabezado

pro un RecibirTrama), se dan tantas oportunidades como indique REDUNDANCIA. Desde que una recepción del de RecibirTrama sea válida, ya se puede avanzar en el programa. Esta recepción válida se habrá guardado en NBPackR, por lo que, a partir de ahora, el algoritmo del Receptor es similar al de antes, solo que en vez de usar Nbytes_paquete, usa el contenido de NBPackR.

3) Pero, si en la recepción del primer conjunto redundante que posee

la información sobre el número de bytes de paquete, se agotan todas las oportunidades de redundancia y en todas da fallo, el programa termina automáticamente ( no puede trabajar el receptor si no sabe el número de bytes que va a tener cada paquete).

Un diagrama explicativo sería:

RecibirTrama

Aux2 FSR

No

No

REDUNDANCIA AUXILIAR 1 NBPackR

RecibirPaquete

Fallo (1) = 1 ¿? AUXILIAR-1 AUXILIAR

Auxiliar = 0 ¿?

Si

Escribir en LCD un ‘=’ END

Recibir el resto de conjuntos redundantes, tal como en el algoritmo

anterior

NOTA: en esta sección solo se están comentando el funcionamiento y estrategias de implementación usadas. Para ver más detalladamente los requisitos de cada función y sus efectos en los registro del microcontrolador, leer las explicaciones que se hayan en los mismo fichero *.asm en lo que fueron desarrollados.

6.8 Implementación del nivel bajo en modo rápido. En las siguientes líneas se explicará como se realizó la implementación del emisor y del receptor de forma que la transmisión sea más rápida de lo que lo es con los ficheros de nivel bajo iniciales. Las implementaciones del nivel bajo inicialmente tenían un marco de bit de un tamaño de 200 pulsos (100 pulsos para el período activo y 100 para el inactivo para obtener la interpretación de un bit a 1, y 200 pulsos inactivos para obtener la interpretación de un bit a cero). La idea de agilizar la transmisión surgió porque con los protocolos de transmisión actuales el envío es muy lento debido a la redundancia y al envío de “bytes” especiales que se utilizan para sincronizar al receptor con el emisor. La transmisión de 9 letras se volvía muy lenta, ya que llegaba a durar dos o tres segundos. Pero es verdad que con un marco de bit de 200 pulsos no estábamos llevando al límite la capacidad del sensor, ni mucho menos. Inicialmente decidimos utilizar estos 200 pulsos para tener mucha más holgura (y por lo tanto mucho más robustez, ya que habríamos de tener muchas instrucciones de mas o de menos en el receptor para perder la sincronización) de cara a que no teníamos muy bien medido el tiempo que tardaba el emisor en enviar un byte y prepararse para enviar el siguiente con respecto al tiempo que tarda el receptor en obtener el bit y almacenarlo. Por ejemplo, si el emisor emite un bit en el tiempo 0, y prolonga la emisión de este bit hasta los 200 pulsos (que serían 200 pulsos por 26 microsegundos que tarda cada pulso), tendríamos que al emitir el próximo bit el emisor se retrasará unas 5 instrucciones (por ejemplo) para cargar el bit que tiene que enviar y enviarlo, mientras que el receptor se va a retrasar unas 3 instrucciones (por ejemplo) porque solo tiene que guardar el bit recibido y ponerse nuevamente a la escucha. Para ponerse a la escucha el receptor esperará 200 pulsos para leer el próximo bit. Tenemos que en enviar un bit el emisor tarda los 200 pulsos más las 5 instrucciones mientras que el receptor tarda las 200 instrucciones más loas 3 instrucciones para almacenar el bit. En principio el receptor realizaría 2 instrucciones de menos que el emisor (que serían unos 2 microsegundos frente a los 5200 microsegundos que tarda cada transmisión de un bit), lo que no tendría importancia de cara la sincronización, ya que tenemos bastante holgura (si nos situamos a mitad del bit para leerlo tendríamos una holgura de 2600 instrucciones que podría durar el receptor más o menos que el emisor, lo que nos permitiría recibir 1300 bits sin que se desincronizase el receptor). Con estos 200 pulsos tendríamos una holgura exagerada, ya que solo queremos emitir 8 bits seguidos más el Start bit y los Stop Bits. Es por ello por lo que finalmente decidimos disminuir el marco de bit de forma considerable. Ahora el marco de bit sería de 50 pulsos (la cuarta parte del pulso que teníamos antes), aunque podríamos disminuir el marco de bit hasta

los 30 pulsos, ya que el límite para el sensor está en los 15 pulsos para el tren de pulsos, que sería la mitad del marco de bit. Pero no lo hicimos tan a la ligera. Aunque aún con este marco de bit teníamos una holgura bastante considerable para equivocarnos en un número considerable de instrucciones del emisor frente al receptor, decidimos medir las instrucciones del receptor y del emisor para que no hubiera desfases de uno frente a otro en la transmisión. Si nos faltaban instrucciones en alguno de los dos, en el emisor o en el receptor, le añadíamos instrucciones nops. A continuación se dan las medidas que se han hecho de los dos procedimientos para equilibrar la transmisión. Las cuentas se han hecho de forma que estén ordenadas para saber dónde hay que añadirle las instrucciones nops a los procedimientos, si antes o después de realizar la espera (que normalmente vendrá definido por un 31*200, excepto en el Start Bit que vendrá definido por un bucle de 31*250, ya que realizará un salto de un marco y medio de bit para centrarse a la mitad del primer bit a recibir). Start Bit: Como se puede ver, el emisor y el receptor están claramente desequilibrados en el Start Bit, pero esto es normal. Es normal puesto que lo que debe de hacer el receptor es colocarse a mitad del pulso siguiente cuando recibe el Start Bit, por lo tanto, tendrá que esperar durante más tiempo que el emisor par poder recibir el primer bit. Por ello no haremos modificaciones en el Start Bit. Centrémonos en los puntos importantes, que es donde se irá acumulando el retardo. Estos puntos importantes en los programas son los etiquetados como LeeBit en el receptor y como LeerByte en el emisor.

Se puede ver como el emisor necesita más instrucciones que el receptor para enviar el bit. Por lo tanto habrá que rellenar el receptor mediante nops para que no se retrase en la transmisión. El número de nops que habría que ponerle serían unos tres antes de realizar la espera (antes del 31*200, que sería el marco de bit, el receptor solo tiene 9 instrucciones mientras que el emisor tiene tres instrucciones más que el receptor, unas 12 instrucciones). Además, vemos que realizar la espera el receptor tiene 4 instrucciones menos que el emisor, por lo tanto habría que añadírselas como nops.

(31*250 = 7750) + 2

Receptor

función Pulso

función Pulso1 o Pulso0

2 + ((31*200) + 2 + 2 + 3) + 2 + 2 = 6213

Emisor

Emisor

(5 + 2 + 2 + 31*200 + 3)*8 - 1

Receptor

( 5 + (2 + (3 + 2 + 31*200 + 2 ) + 2) + 3)*8 - 1

Otro punto peligroso es al recibir los stop bits, ya que se recibirán 9 Stop Bits con el consiguiente peligro de una desincronización. Veamos que número de instrucciones hay en el Stop Bit.

Hay un bucle tanto en el emisor como en el receptor para enviar los Stop Bits a cero. En el emisor el bucle es de 11, ya que éste emitirá 11 Stop Bits mientras que el receptor solo comprobará 9 Stop Bits de esos 11 que enviará el emisor. El emisor enviará más Stop Bits de los que realimente se comprobarán para dar tiempo al receptor a prepararse para recibir el siguiente byte. Se puede comprobar que en el Stop Bit, dentro del bucle (lo que se está multiplicando por 11), hay una instrucción más en el emisor antes de la emisión del bit (la emisión del bit se realiza durante el 31*200) que en el receptor antes de realizar la espera (antes del 31*200). Por lo tanto tan solo habría que añadirle una instrucción nop al receptor dentro del bucle (lo que se está multiplicando por 9) antes de realizar la espera. Después de la emisión del bit por parte del emisor y de la espera por parte del receptor hay dos instrucciones más en el emisor que en el receptor, por lo tanto habrá que añadirle dos instrucciones nops más al receptor después de este punto. Con estas modificaciones hechas ya podemos reducir bastante el marco de bit sin miedo a una desincronización, ya que los desfases entre el receptor y el emisor serán prácticamente inexistentes.

6.9 Implementación del nivel bajo con paridad. Para nuestra práctica tenemos que hay un registro denominado “Fallo” en el receptor del nivel bajo que nos indica cuando no se ha recibido correctamente un byte. Pero por que no se haya recibido correctamente un byte nos referimos hasta ahora con que haya habido un fallo de sincronización entre el emisor y el receptor (que el receptor no haya pillado la recepción de un byte desde el principio, produciéndose un fallo que se detecta con el primer Stop Bit, ya que el receptor no recibirá el primer Stop Bit a uno). En nuestro protocolo hemos añadido unos instrumentos que nos permiten resolver situaciones de error en la recepción de un byte cuando no se ha sincronizado correctamente, pero no tenemos herramientas implementadas que nos permitan analizar el byte recibido y decidir si la recepción de ese byte ya obtenido bajo una correcta sincronización es el byte que deberíamos haber recibido o no. Si queremos añadirle robustez a la comunicación entre los dos PICs debemos implementar sistemas que nos permitan analizar el byte recibido a posteriori y decidir si ese byte recibido es o no correcto según el análisis mediante esas herramientas.

4 + ((2 + (3 + 2 + 31*200 + 2) + 2) + 3 )*11 - 1

Emisor Receptor

2 + (4 + 2 + 31*200 + 2 + 3) * 9 - 1

El sistema de este tipo más simple que se puede implementar es el sistema basado en la paridad. Cada vez que recibamos un byte, comprobaremos el bit que recibiremos con él que nos indicará si el número de bits a uno que hemos recibido es un número par o impar. Si lo que nos dice ese bit recibido coincide con la comprobación que hacemos a posteriori significará que hemos recibido, o mejor dicho, hay una alta posibilidad de haber recibido correctamente el byte que estamos analizando. Si por el contrario, el bit recibido no coincide con la paridad de la cuenta de los unos que hemos recibido indicará que hay una alta posibilidad de que lo que hayamos recibido sea incorrecto. En este caso se podría utilizar la redundancia que hemos impuesto en el nivel alto para poder enmendar este error intentando recibir el próximo byte con el mismo contenido que el fallido que envíe el emisor (la redundancia). También se podrían intentar implementar otro tipo de actuaciones sobre los bytes recibidos, ya que lo único que habría que tener en cuenta para que los niveles superiores siguieran funcionando es que los nuevos archivos implementados en el nivel bajo tuvieran la misma interfaz que los archivos originales (mismos nombres de procedimientos, misma utilización de ciertos registros como puede ser el registro Fallo...). En verdad, hemos estado intentando implementar una librería para el nivel bajo que implementara la paridad, pero debido a que se nos echaba la fecha de la entrega encima no hemos podido hacer que funcionaran estos archivos. Pero para que se vea que lo hemos intentado, vamos a explicar un poco la filosofía de estos archivos que hemos creado, aunque no funcionan. Partíamos de los archivos originales, los que usábamos para implementar la librería del nivel bajo. Nuestra intención era añadir la paridad tanto en el receptor como en el emisor sin utilizar muchos registros demás, solo los necesarios. Esta intención ha sido también nuestra desdicha. Creemos que debido a la mala utilización de los registros, que ya utilizábamos antes, pero que intentemos adaptar para poderlos utilizar en la paridad sin quitarles las funcionalidades que tenían antes, todavía no hemos resuelto la práctica. La idea fue la siguiente: 6.9.1 Emisor Como ya se ha explicado, en el programa del emisor hay un registro denominado Banderilla que utilizamos para implementar las alternativas de RecibirSincronismo y de Recibir_Trama. En este registro se especificaba si lo que queríamos que realizara el emisor era un byte, una trama o un Sincronismo. Pues bien, la idea fue que podríamos utilizar este mismo registro para contar la paridad de los bytes que fuéramos a emitir. Para ello primero se emitirían dos pulsos a 1 antes de emitir el byte con todo a 1, si era un sincronismo (bit 0 de banderilla activado. Se puede observar como emitimos dos pulsos y el byte con todo a 1, esto es porque necesitamos diferenciarlo de una recepción de un byte todo a uno con el bit de paridad erróneo, a uno. Debido a esto el Emitir_Trama emitirá 11 bits a 1 en lugar de 10, como en los archivos originales).

Después, se comprobará si el bit 1 del registro Banderilla está activado, en cuyo caso se enviará otro bit a uno para emitir la trama, por lo que si había un tres en banderilla habremos emitido 2 + 1 bits antes de los 8 bits, que sería la trama. Con esto controlaríamos si queremos enviar un byte normal, un sincronismo o una trama y además tendríamos el registro Banderilla listo para ser utilizado para contar la paridad, ya que ya habría servido a su propósito, que era identificar el tipo de byte que se quería enviar. El código sería el siguiente: btfsss Banderilla,0 goto empezar_lec ; si bit 0 =1 --> sincronismo(pulso1) call Pulso1 ; En la paridad tanto el sincronismo como la trama llevan un bit más que sin la paridad. call Pulso1 btfsc Banderilla,1 ; si bit 1 =0 --> toca StopBit call Pulso1 ; si bit 0 =1 --> trama(pulso1). goto LeerByte empezar_lec CLRF Banderilla LeerByte ... Como se puede observar, solo inicializamos el registro banderilla en caso de que haya que enviar un byte, para empezar a contar el número de unos que tiene el byte. No lo inicializamos en los otros casos porque como veremos más adelante, utilizaremos la ventaja de que cuenta los 8 bits a 1 y además a esto le suma el 1 o el 3 a que esté al entrar en el procedimiento para poder diferenciarlo del byte, y decidir si enviar bit de paridad o no, teniendo en cuenta que tanto la trama como el sincronismo habrán dado como resultado en el registro banderilla después de enviar los 8 bits un número de 1 mayor que 8. Si esto ha sucedido, no habrá que enviar paridad. Es por ello por lo que antes de enviar la paridad comprobaremos lo siguiente: ; Si es sincronismo o trama se saltará el bit de paridad BTFSS Banderilla, 3 GOTO BitParidad BTFSS Banderilla, 0 GOTO BitParidad GOTO StopBit Se puede ver como comprobamos si el registro banderilla es mayor que 8 comprobando el bit 3 y el bit 0, ya que al final tendremos que si tenemos que enviar una trama o un sincronismo tendremos un valor para el registro Banderilla de 9 u 11 (ya que comenzábamos con Banderilla a un valor de 1 o 3), para los cuales tanto el bit 0 como el 3 estarán activos. A continuación se muestran los diagramas ASM de estos puntos del código, más el punto del código en el que realiza el conteo de la paridad.

Control de Banderilla: Control de Conteo de la paridad:

Control de envío de paridad:

Bit 0 de Banderilla activado?

No Si

ir a empezar_lec Pulso1

Bit 1 de Banderilla activado?

Pulso1

Ir a Leer_byte Pulso1

No Si

Bit a transmitir es un 1?

No Si

Pulso0 Pulso1

Incrementar Banderilla

Bit 3 de Banderilla activado?

No Si

Ir a BitParidad

Bit 0 de Banderilla activado?

Ir a BitParidad Ir a StopBit

No Si

Bit 0 de Banderilla activado?

No Si

Pulso1 Pulso0

6.9.2 Receptor: El receptor lo implementemos de una forma más simple. En éste no encontremos otra alternativa que utilizar un registro de más que realizara el conteo de la paridad en el byte que se estaba recibiendo. El procedimiento RecibirSincronismo y Recibir_Trama serán totalmente iguales a los originales, excepto por que ahora tendrán que recibir 10 y 11 bit para el sincronismo y la trama respectivamente en lugar de los 9 y 10 que recibían antes. Lo único que tendrá el RecibirByte de diferente es que contará el número de unos que recibe en el byte mientras está recibiendo los bits, almacenándolo en un registro nuevo que se denomina ContPar, y comparará el conteo con el bit de paridad que reciba para comprobar si ha recibido el byte correctamente o si debe activar el bit cero del registro “Fallo” en caso de no haberlo recibido correctamente. Esta vez si que podremos inicializar el registro ContPar desde el principio para poder contar el número de unos que recibe el byte. 7 Manual de instrucciones

A continuación se explicará brevemente como ha de utilizarse el material utilizado en esta práctica. 7.1 implementación física: El diodo emisor de infrarrojos deberá ir conectado al bit 0 del puerto B. El cable que sale del puerto B deberá ir a una resistencia conectada en serie con el diodo. La resistencia deberá de ser de menos de 200 ohmios, y a ser posible deberá ser un potenciómetro, sobretodo si queremos utilizar las librerías en las que el número de bytes en el paquete es variable. La tierra, evidentemente deberá ir conectada a una de las patas del diodo emisor y puede ser la tierra que nos proporciona el bus IR2C. El sensor de infrarrojos deberá de tener su patilla de salida conectada a la entrada del puerto A, al bit 4, con el circuito de acondicionamiento que se ha explicado anteriormente en el apartado del sensor. 7.2 implementación software: Todos los ficheros necesarios los tenemos almacenados en la capeta de entrega. Su estructura es la siguiente: Una carpeta denominada otras librerías y cabeceras, en la cual encontraremos los ficheros cabecera y las librerías de la LCD, del teclado, y un fichero de constantes, denominado constantes.inc, definidas para los procedimientos creados para la comunicación por infrarrojos.

Una carpeta llamada nivel bajo. En ella encontraremos las librerías y los ficheros fuentes y cabecera de las distintas implementaciones para el nivel bajo. En esta carpeta se encuentran las carpetas:

o nivel bajo normal: contiene las librerías, ficheros fuente y cabeceras de los procedimientos del nivel bajo original.

o nivel bajo rápido: Esta carpeta contendrá los ficheros de la versión

rápida del nivel bajo. o nivel bajo paridad: Carpeta que contiene los ficheros fuente de la

versión del nivel bajo con paridad. Estos ficheros no funcionan correctamente y es por ello por lo que no hay ficheros cabecera ni librerías, solo los ficheros fuente.

Una carpeta denominada nivel paquete. En ella se encuentran los dos tipos de procedimientos que hemos implementado para el nivel bajo. Están contenidos en las carpetas:

• nivel paquete N bytes fijo: contendrá la librería y el fichero cabecera del nivel paquete con un número de bytes por paquete determinado en tiempo de compilación mediante el valor NBytes_Paquete especificado en el fichero constantes.inc.

• nivel paquete N bytes no fijo: contendrá la librería y el fichero cabecera del nivel paquete con un número de bytes por paquete determinado en tiempo de ejecución.

Una carpeta a la que hemos llamado Nivel Alto. Esta carpeta contendrá ejemplos de programas que utilizan el nivel paquete y el nivel bajo. En esta carpeta habrá un receptor y emisor que utiliza el nivel paquete con un número de bytes fijo para los paquetes, y otro receptor y emisor que utilizan el nivel paquete con un número de bytes variable. Seguidamente vamos a explicar como preparar el entorno de trabajo para poder utilizar los ficheros implementados. Para cualquier librería que se desee usar de las implementadas deberá incluirse el fichero cabecera pic16f84.inc, puesto que hemos optado por usar los nombres estándares de los registros de propósito específico del microcontrolador, a fin de mejora la legibilidad del código. 1º Implementar un programa utilizando el nivel bajo: Si queremos implementar un programa en nivel bajo, primero debemos saber qué librerías son las que queremos utilizar. Hay dos opciones, utilizar la librería nivel bajo

normal o la librería nivel bajo rápido. Pero la única diferencia entre utilizar una u otra es la librería que se utiliza, ya que los ficheros cabecera son los mismos. Para empezar, deberemos cargar como ficheros cabecera los ficheros “constantes.inc” (que se encuentra en la carpeta “otras librerías y cabeceras”) y “nivel_bajo.inc” (que será el mismo para los dos niveles bajos). A continuación cargaremos la librería que queramos utilizar del nivel bajo, pudiendo elegir entre la librería del nivel bajo rápido o del nivel bajo normal. Con estos ficheros cargados ya podríamos utilizar las funciones especificadas en el fichero cabecera del nivel bajo. 2º Implementar un programa utilizando el nivel paquete: Los ficheros necesarios para crear un programa que utilice los procedimientos del nivel paquete se encuentran en la carpeta Nivel Paquetes. En ellos podemos encontrar las librerías y ficheros cabecera que necesitamos. Se pueden distinguir dos niveles de paquetes distintos: nivel de paquetes con número de bytes seleccionable en ejecución, o nivel de paquetes con número de bytes prefijados en el fichero de constantes.inc. Para utilizar estos procedimientos debemos incluir los ficheros que necesitábamos para usar el nivel bajo más demás la librería y fichero cabecera propios del nivel de paquetes que queramos utilizar.

3º Ejemplo de utilización de recursos de ambos tipos de librerías: Unos ejemplos de implementación de programas que utilizan las funciones que nos ofrece el nivel bajo y el nivel paquete son los ficheros almacenados en la carpeta “Nivel Alto”. En esta carpeta se pueden encontrar dos programas diferentes:

1) Un primer programa usa la librería de nivel de paquetes cuyo tamaño está prefijado de antemano en el fichero cabecera Constantes.inc (mediante NBytes_Paquetes). Es importante, a la hora de elegir una librería de las implementadas, ver como funcionan sus funciones y los requisitos básicos. Leer los comentarios hecho sobre los propios ficheros *.asm.

No debe pasar inadvertido incluir esta serie de ficheros:

Para el nivel bajo hará falta incluir todos los ficheros que antes especificamos, una vez hayamos decidido que tipo de librería de nivel bajo queremos usar.

Para el nivel de paquetes remitirse también a lo antes especificado.

Finalmente, no debe olvidarse incluir las librerías y cabeceras correspondientes al manejo de la LCD, que

podemos encontrar en el directorio “Otras librerías y cabeceras”.

Todo esto, más el fichero correspondiente al programa en sí, incluidos todos en un mismo directorio, ya es apto para poder ser compilado.

2) Un segundo programa usa la librería de paquetes con tamaño o

número de bytes variable. Con este programa tenemos que hacer más de lo mismo a la hora de compilarlo y trabajar con él, pero además deberemos incluir el fichero cabecera y la librería correspondiente al teclado. Fíjese que el programa tiene otro modo de tratar y trabajar con las librerías de nivel paquete. Esto es debido a que cada librería del nivel de paquete tiene sus especificaciones, que deben de ser leídas antes de su uso indiscriminado.

8 Restricciones del sistema de comunicación por infrarrojos.

En el desarrollo de la presente práctica, se han detectado ciertas restricciones en el uso de este sistema de comunicación. A saber:

Los bytes a cero, son utilizados como fin de mensaje. Por tanto ningún mensaje que enviemos podrá incluir un cero ya que el mensaje se cortaría justo por ese punto.

En el emisor, utilizamos la memoria de datos para almacenar mensajes

por tanto el mensaje no puede ser todo lo largo que queramos. Haciendo un recuento del número de registros usados tanto en las librerías del nivel bajo como en las del nivel de paquete más los registros que usamos en nuestro programa, y sabiendo la capacidad de memoria de datos que nos ofrece el PIC. el usuario tendrá que tratar mensajes que quepan en el espacio libre de esa memoria de datos.

En el receptor el problema es menor, pues solo hace falta tanta memoria de datos como el número de bytes que tenga el paquete (que siempre se supondrá menor que el número de bytes que contendrá el paquete). Aun así, también debe calcularse el espacio libre que hay en la memoria de datos para tal fin (tarea del programador de nivel alto).

La pila del microcontrolador PIC contiene un máximo de 8 llamadas a

funciones. Esta estructura modular que hemos propuesto tiene el inconveniente de tener demasiadas llamadas a funciones, por lo que el programador deberá tener cuidado y estudiar cuántos niveles de pila implica la llamada a cada función de cada librería para no socavar dichos límites y encontrarse con comportamientos atípicos.

Hemos diseñado un sistema de comunicación a nivel alto destinado a

enviar un único mensaje ya que ponemos como ejemplo que el receptor

al término del mensaje pasa a un estado de consumo mínimo. Véase que no es una restricción inherente a las librerías, por tanto un uso adecuado de las mismas podría solucionar dicha restricción (modificando el programa a nivel alto).

9 Ejercicios Propuestos

A continuación se proponen una serie de retos abarcables siempre que se haya leído atentamente la presenta práctica: Ejercicio1: Diseñar librerías que doten al sistema de comunicación de detección de errores y corrección mediante el uso del código de Hamming. Ejercicio 2: Diseñar un sistema de comunicación que permita manejar paquetes de tamaño variable. Nótese que en la presente práctica, una vez definido (en tiempo de ejecución o de compilación) el tamaño del paquete todos los envíos de paquetes se hacen con el mismo tamaño. Proponemos que cada paquete diferente pueda poseer tamaño diferente, pero en un conjunto redundante, al ser todos el mismo paquete, deben de ser todos los paquetes del mismo tamaño (solo puede cambiar el tamaño de paquete entre paquetes diferentes). Pista: Las tramas y Sincronizaciones no poseen ninguna información útil. Podría dotárselas de información. Ejercicio 3: Diseñar un sistema de comunicación, usando las librerías proporcionadas en la práctica, que permita enviar “Mensajes Infinitos” (es decir, que sea independiente de la restricción impuesta por el tamaño de la memoria de datos). Ejercicio 4: Diseñar un sistema de comunicación cuya corrección de errores no este basada en la redundancia. Proponer por tanto un sistema de HandShaking, donde el emisor solo tenga que repetir los mensajes que no han llegado bien al receptor, y que lo repita tantas veces como haga falta. Pista: usar dos parejas de emisor de infrarrojo / receptor de infrarrojo, para que cada PIC este dotado de herramientas para leer datos y comunicar.

10 Referencias bibliográficas

Para el desarrollo de la presente práctica hemos empleado básicamente el manual de referencia del microcontrolador PIC 16f84 del que disonemos en la página de la asignatura y en el laboratorio. Así y todo, también hemos usado los siguientes documentos encontrados vía internet: Datasheet TFMS 5360 (sensor receptor infrarrojos)

Tfms.pdf (* Recomendado) Diodo emisor infrarrojos.

Ir97.pdf

Tsip520_.pdf (* Recomendado)

Leds.pdf Páginas webs recomendadas.(referente a protocolos estándares):

Introduction to IrDA

Digital Modulation Schemes