CONTROL DE LA CT6811 DESDE EL PC

Comunicaciones serie, carga de programas y programación desde el PC utilizando el servidor CTSERVER.

© GRUPO J&J. JUNIO 1997

Control de la CT6811 desde el PC

2

Control de la CT6811 desde el PC

INDICE

INTRODUCCION CAPITULO 1: COMUNICACIONES SERIE ASINCRONAS EN EL PC
1.- INTRODUCCION 2.- COMUNICACIONES SERIE ASINCRONAS 2.1.- Tipos de comunicaciones 2.2.- El formato de las tramas: Bit de Start y bits de Stop 2.3.- Parámetros que caracterizan las comunicaciones serie asíncronas 2.4.- Señales implicadas en una comunicación serie asíncrona FULL-DUPLEX 3.- LA NORMA RS-232C 3.1.- Introducción 3.2.- Señales de norma RS-232C 3.3.- Conectores de 25 y 9 pines 3.4.- Características eléctricas de las señales RS-232C 3.5.- La norma RS-232C y el PC. 4.- UARTS: Transmisores/receptores universales 4.1.- Introducción 4.2.- Modelo genérico de una UART 4.3.- Señales de una UART 5.- LA UART DEL PC 5.1.- Introducción 5.2.- Patillaje y conexionado de la UART 8250 5.3.- Diagrama de bloques de la UART 8250 5.4.- Los registros de la UART 8250 5.5.- Acceso a la UART en el PC 6.- RUTINAS DE COMUNICACIONES SERIE PARA EL PC 6.1.- Introducción 6.2.- Interfaz 6.2.1.- Apertura y cierre del puerto serie 6.2.2.- Funciones de configuración 6.2.3.- Funciones de lectura/escritura 6.2.4.- Otras funciones 6.2.5.- El interfaz completo 6.3.- Implementación 6.4.- Manejo de la librería SERIE.C 6.4.1.- Ejemplo 1: Un pequeño terminal de comunicaciones 6.4.2.- Ejemplo 2: Utilización de accion_rxcar y accion_break 6.4.3.- Ejemplo 3: Terminal de comunicaciones con TIMEOUT 6.4.4.- Ejemplo 4: Terminal con reset 6.4.5.- Ejemplo 5: Autodetección de la tarjeta CT6811 7.- LA CONEXION DE LA CT6811 Y EL PC 7.1.- Introducción 7.2.- Las señales de interconexion 7.3.- La señal de RESET 7.4.- Los problemas de interconexión 8.- CONCLUSIONES 11 12 12 13 14 14 15 15 15 16 17 17 19 19 19 19 21 21 21 22 23 25 27 27 27 27 28 28 28 28 31 32 32 33 34 35 37 39 39 39 39 39 40

CAPITULO 2: CARGA DE PROGRAMAS EN LA CT6811

3

Control de la CT6811 desde el PC

1.- INTRODUCCION 2.- EL FORMATO S19 DE MOTOROLA 2.1.- Introducción 2.2.- Estructura interna de los fichero .S19 2.3.- Ejemplos de archivos .S19 3.- LIBRERIA S19.C PARA LA GESTION DE ARCHIVOS .S19 3.1.- Introducción 3.2.- El interfaz de la libreria S19.C 3.3.- Implementación 3.3.1.- El tipo abstracto S19 3.3.2.- Rutinas de conversión de datos: Libreria ASCBIN.C 3.4.- Ejemplo de utilizacion de la libreria S19.C 3.4.1.- Ejemplo 1: Programa LEERS19.C 3.4.2.- Ejemplo 2: Programa DUMPS19.C 3.5.- El interfaz de la libreria S19UTIL.C 3.6.- Ejemplos de manjejo de la libreria S19UTIL.C 4.- CARGA DE PROGRAMAS: EL PROGRAMA BOOTSTRAP 4.1.- Introdución 4.2.- El protocolo implementado en el Bootstrap 4.3.- Listado del programa de arranque Bootstrap 4.4.- Carga de programas en la RAM interna 5.- LIBRERIA BOOTSTRP.C PARA CARGA DE PROGRAMAS EN LA RAM INTERNA 5.1.- Introducción 5.2.- Interfaz 5.3.- Implementación 5.4.- Dependencias de la libreria BOOTSTRAP.C 5.5.- Ejemplos 5.5.1.- Ejemplo 1: Programa LEDP.C 5.5.2.- Ejemplo 2: Programa RAMINT.C 6.- CONCLUSIONES

41 42 42 42 43 44 44 44 45 45 46 47 47 48 49 51 52 52 52 55 56 57 57 57 57 58 58 58 59 60

4

Control de la CT6811 desde el PC

CAPITULO 3: EL SERVIDOR DE PROPOSITO GENERAL CTSERVER
1.- INTRODUCCION 2.- EL SERVIDOR CTSERVER 2.1.- Introducción 2.2.- Los servicios ofrecidos 2.3.- Parámetros de los diferentes servicios 2.4.- Limitaciones del servidor 3.- EL PROTOCOLO JJLINK 3.1.- Introducción 3.2.- Formato de las tramas de diálogo 3.3.- Diálogos entre cliente-servidor 3.4.- Discusión sobre protocolo JJLINK 4.- IMPLEMENTACION DEL CTSERVER 4.1.- Diagramas de flujo 4.1.1.- Bucle principal 4.1.2.- Transferencia de bloques del 68HC11 al PC 4.1.3.- Transferencia de bloques del PC al 68HC11 4.1.4.- Grabación de bloques en la memoria EEPROM 4.1.5.- Servicio de salto y ejecución 4.1.6.- Control de supervivencia 4.2.- Listado del servidor CTSERVER 5.- LIBRERIA CTSERVER.C 5.1.- Introducción 5.2.- Interfaz 5.3.- Dependencias 5.4.- Ejemplos de utilización 5.4.1.- Ejemplo 1: Programa CHECKCON.C 5.4.2.- Ejemplo 2: Programa CTLEDP.C 5.4.3.- Ejemplo 3: Programa CTDUMPEE.C 5.4.4.- Ejemplo 4: Programa CTSAVEE.C 5.4.5.- Ejemplo 5: Programa CTGOEE.C 6.- CONCLUSIONES 61 62 62 62 62 63 63 63 63 65 65 66 66 55 66 67 67 67 67 68 72 72 72 73 73 73 74 75 77 78 79

5

Control de la CT6811 desde el PC

CAPITULO 4: PROGRAMACION DE CLIENTES PARA EL CTSERVER
1.- INTRODUCCION 2.- LA LIBRERIA CTCLIENT.C 2.1.- Introducción 2.2.- Interfaz 2.3.- Clasificación de las funciones 2.3.1.- Funciones que hacen uso de los servicios del CTSERVER 2.3.2.- Funciones de bucle abierto y bucle cerrado 2.4.- Implementación 2.5.- Dependencias 2.6.- Ejemplos 2.6.1.- Ejemplo 1: Programa KLEDP.C 2.6.2.- Ejemplo 2: Programa KAD.C 3.- HERRAMIENTAS DE PROGRAMACION DE CLIENTES 3.1.- Introducción 3.2.- El fichero R6811PC.H 3.3.- La librería CTSERVER.LIB 3.3.1.- Librerías incluidas en el fichero CTSERVER.LIB 3.3.2.- Interfaz completo de la librería CTSERVER.LIB 4.- PROGRAMACION DE CLIENTES 4.1.- Introducción 4.2.- La estructura de los clientes 4.3.- Ejemplos de clientes 4.3.1.- Ejemplo 1: Programa PCLIENT1.C 4.3.2.- Ejemplo 2: Programa PCLIENT2.C 4.3.3.- Ejemplo 3: Programa PCLIENT3.C 4.3.4.- Ejemplo 4: Programa SPI1.C 4.3.5.- Ejemplo 5: Programa SPI2.C 5.- CONCLUSIONES 80 81 81 81 82 82 82 82 82 83 83 84 86 86 86 87 87 87 90 90 90 91 91 92 93 95 97 99

6

Control de la CT6811 desde el PC

APENDICE A: IMPLEMENTACION DE LAS DISTINTAS LIBRERIAS
A.1.- SERIE.C A.2.- ASCBIN.C A.3.- S19.C A.4.- S19UTIL.C A.5.- BOOTSTRP.C A.6.- CTSERVER.C A.7.- CTCLIENT.C 101 107 109 115 118 121 124

7

Control de la CT6811 desde el PC

8

Control de la CT6811 desde el PC

INTRODUCCION
El objetivo de este libro es mostrar cómo es posible controlar la tarjeta CT6811 desde el PC. En muchas aplicaciones lo importante no es la autonomía de los sistemas, sino la monitorización en un PC de lo que está ocurriendo. Con este documento se pretende enseñar una manera de realizar sistemas de control desde el PC. En la tarjeta CT6811 se ejecuta un programa servidor que controla los recursos y ofrece unos servicios a las aplicaciones que corren en el PC. La comunicación entre el PC y la tarjeta CT6811 se realiza a través de la norma RS-232C. En los diferentes capitulos del libro se va explicando detalladamente el modelo presentado en la figura de abajo, así como también se van desarrollando las distintas librerías en lenguaje C para su implementación.
PC

CLIENTE TARJETA CT6811 SERVICIOS Carga de Programas en la RAM interna Comunicaciones serie

SERVIDOR Bootstrap

RECURSOS

RS-232C

El primer problema a resolver es el de la comunicación entre la CT6811 y el PC. En el capítulo 1 se introducen los distintos tipos de comunicaciones y se explica con detalle en qué se basan las comunicaciones serie asíncronas y las norma RS-232C. Se presenta la librería SERIE.C para la gestión de las comunicaciones serie desde el PC. Una vez que es posible transmitir bytes entre el PC y la CT6811 y vice-versa, en el capítulo 2 se aborda el tema de la carga de programas en la RAM interna del 68HC11. Para ello es necesario conocer el formato .S19 de motorola y el modo de arranque BOOTSTRAP del 68HC11. En este capítulo se muestran las librerías S19.C y S19UTIL.C para la gestión de archivos en este formato y la libreria BOOTSTRP.C para la carga de programas en la ram interna de la CT6811. En el capítulo 3 se explica en detalle un servidor de propósito general: el CTSERVER. Este servidor nos proporciona una serie de servicios básicos que nos permiten acceder a los recursos del 68HC11: puertos, memoria, conversores... En este mismo capitulo se explica el formato de las tramas utilizadas para transmitir la información y se muestra la libreria CTSERVER.C para el diálogo con el CTSERVER desde el PC. Finalmente, el capítulo 4 se dedica a la programación de clientes en el PC. Este es el capítulo más interesante y del que el lector puede sacar mayor provecho pues es en el que se puede apreciar la potencia y facilidad de manejo del sistema. A partir de este capítulo el lector ya dispone de todas las herramientas y ejemplos necesarios para la realización de cualquier tipo de clientes. La única limitación es la imaginación.... Todas las librerías se han programado en lenguaje C utilizando el compilador TURBO C++ V.1.00. Se debe configurar el compilador para asumir C++ para todos los programas.

9

Control de la CT6811 desde el PC

10

Control de la CT6811 desde el PC

CAPITULO 1:

COMUNICACIONES SERIE ASINCRONAS EN EL PC

1.- INTRODUCCION
El objetivo final de este capítulo es presentar el funcionamiento de la libreria SERIE.C para realizar comunicaciones serie asíncronas con la tarjeta CT6811 desde el PC. Antes de poder enviar programas a la CT6811, programar la CT6811 desde el PC utilizando el servidor CTSERVER o realizar una red de tarjetas CT6811 será necesario disponer de una librería en el PC que nos permita intercambiar caracteres con la tarjeta CT6811. La librería SERIE.C precisamente lo que nos permite es eso: intercambiar caracteres con la CT6811, además algunas otras funciones. Si el lector sólo está interesado en realizar programas de comunicaciones serie utilizando la librería SERIE.C puede saltar directamente a la sección 6. Si además de saber manejar la librería pretende comprender cómo ha sido implementada deberá leer a partir de la sección 4, donde se explican las UARTS y en particular la UART del PC. En las secciones 2 y 3 se trata sobre los conceptos de comunicaciones serie asíncronos y la norma RS232C. Son dos apartados de ‘culturilla’ general. En la sección 4 se presenta un modelo genérico de la UART más sencilla posible. En el apartado 5 se trata en particular sobre la UART del PC y sobre cómo hay que programarla. La sección 6 contiene el interfaz de la librería SERIE.C así como una serie de ejemplos de utilización. Finalmente en la sección 7 se muestra qué seañes están implicadas al conectar la CT6811 con el PC, cómo se realiza el reset software y que problemas hay derivados de utilizar este sistema de reset.

11

Control de la CT6811 desde el PC

2.- COMUNICACIONES SERIE ASINCRONAS
2.1.- TIPOS DE COMUNICACIONES El problema que se quiere solucionar es el de la comunicación entre dos dispositivos digitales. Estos dispositivos pueden ser cualquiera: ordenadores, calculadoras, impresoras, ratones... Puesto que lo que se quiere es que ambos dispositivos intercambien bits, la clave de las comunicaciones está en cómo transportar estos bits. Así aparecen dos tipos de comunicaciones: las comunicaciones serie y las comunicaciones paralelo. En las primeras, los bytes de información se transmiten bit a bit por un único cable mientras que en las segundas se transmiten varios bits simultaneamente por cables diferentes. En la figura 1 se muestra un ejemplo de la transmisión del byte 10011010 del dispositivo A al B mediante comunicaciones serie y mediante comunicaciones paralelo a 8 cables (un cable por bit).
Bit 7 BYTE A TRANSMITIR 1 Bit 6 0 Bit 5 0 Bit 4 1 Bit 3 1 Bit 2 0 Bit 1 1 Bit 0 0

COMUNICACIONES SERIE Bit 0 DISPOSITIVO A 0 Bit 1 1 Bit 2 0 Bit 3 1 Bit 4 1 Bit 5 0 Bit 6 0 Bit 7 1 DISPOSITIVO B

COMUNICACIONES PARALELO Bit 7 (1) DISPOSITIVO A Bit 6 (0) Bit 5 (0) Bit 4 (1) Bit 3 (1) Bit 2 (0) Bit 1 (1) Bit 0 (0) DISPOSITIVO B

Figura 1: Ejemplo de transmisión del byte 10011010 del dispositivo A al B tanto en serie como en paralelo. Las comunicaciones serie presentan una ventaja muy importante y es que necesitan muchos menos cables que las comunicaciones paralelas. Por el contrario, la velocidad de transmisión es menor y los transmisores y receptores son mas complejos que en el caso paralelo. Si se quiere una comunicación entre los dispositivos A y B en ambas direcciones (full-duplex) se necesitarían 16 cables para el caso de las comunicaciones paralelo: 8 cables para los 8 bits en sentido A-B y 8 cables para los 8 bits en sentido B-A. Las comunicaciones en paralelo se utilizan mucho en la conexión del PC a impresoras. Dentro de las comunicaciones serie existen otros dos tipos: las comunicaciones serie asíncronas y las comunicaciones serie síncronas. En las comunicaciones síncronas debe existir una sincronización entre el emisor y el receptor. Es decir, deben tener una señal de reloj común. El emisor envía una señal cuadrada que le indica al receptor en qué instantes debe capturar la información que le llegan por el canal serie. En el caso de las comunicaciones asíncronas es el propio receptor el que debe extraer la información de sincronización a partir de los datos recibidos. Para conseguir esto, la información se divide en tramas, existiendo unos bits que delimitan el comienzo de la trama y unos bits que determinan el final de la trama. El emisor transmite las tramas con una velocidad binaria que es conocida por el receptor.

12

Control de la CT6811 desde el PC

COMUNICACIONES SERIE SINCRONAS DISPOSITIVO A Datos Bit 0 Bit 1 Bit 2 Bit 3 Bit 4 Bit 5 Bit 6 Bit 7 DISPOSITIVO B Datos

Reloj

Reloj

COMUNICACIONES SERIE ASINCRONAS Bits de Stop Datos XXXX Bit 0 Bit 1 Bit 2 Bits de Datos Bit 3 Bit 4 Bit 5 Bit 6 Bit 7 Bits de Start XXXX

DISPOSITIVO A

DISPOSITIVO B Datos

Figura 2: Transmisión de 8 bits del dispositivo A al B por medio de comunicaciones serie síncronas y asíncronas. En la figura 2 se muestra la diferencia entre estos dos tipos de comunicaciones. En el caso de las comunicaciones síncronas es necesario una línea específica que una el dispositivo A con el B para transportar la señal de reloj. Por cada periodo de esta señal de reloj se transmite un bit. Por ello la frecuencia de esta señal determina la velocidad de transmisión. En el caso de las comunicaciones serie asíncronas no es necesario transportar la señal de reloj. Sin embargo, como se ve en la figura, hay que transmitir primero unos bits de start que determinan el comienzo de la trama y al final unos bits de stop que determinan el final de la trama. Resumiendo lo visto hasta ahora, las comunicaciones serie asíncronas se caracterizan porque los bits se transmiten en serie, uno detrás de otro y los datos se dividen en tramas formadas por bits de start, bits de datos y bits de stop. 2.2.- EL FORMATO DE LAS TRAMAS: Bit de start y bits de stop. Los datos a transmitir se insertan en tramas con la BIT DE BITS DE BITS DE DATOS estructura mostrada en la figura 3. Primero se encuentra el bit de START STOP start, después los bits de datos, que pueden ser 7 ó 8 y finalmente Figura 3: Formato de las tramas en los bits de stop. comunicaciones serie asíncronas El bit de start es siempre un único bit, aunque podrían ser más de uno. Simplemente se utiliza un bit por comodidad. Los bits de datos pueden variar según la aplicación: 5, 6, 7, 8,9.... aunque normalmente lo que se transmiten son 8 bits (un byte). Los bits de stop normalmente son 1 ó 2. En las comunicaciones serie con el PC y con la tarjeta CT6811 siempre se utilizarán 8 bits de datos y 1 bit de stop. En la figura 4 se muestra un ejemplo de la transmisión de los bits 10011010 (8 bits de datos) y un bit de stop. Por ser una transmisión binaria la linea puede estar en dos estados que se han llamado nivel alto y nivel bajo. Según la norma utilizada, cada nivel estará representado por una tensión diferente. Antes de llegar el dato al receptor la linea se encuentra en reposo. Al llegar el bit de start la linea cae a nivel bajo y a partir de ahí se van recibiendo los siguientes bits. Con el bit de stop el receptor detecta el final de la trama y se prepara para recibir la siguiente. Si el bit de stop que se recibe tiene un nivel distinto al que le corresponde se produce un error de serialización.
Bit de Start Bits de Datos Bit de Stop

Linea en reposo Nivel Alto Nivel Bajo

Linea en reposo

1

0

0

1

1

0

1

0

13
Tiempo

Figura 4: Transmisión del dato 10011010 utilizando un bit de stop

Control de la CT6811 desde el PC

En algunas ocasiones el dispositivo que transmite quiere indicar al receptor alguna situación anómala. En este caso se puede enviar una señal de BREAK. Esta señal se envía manteniendo la línea serie a nivel bajo durante más tiempo del que le correspondorería si se hubiese enviado un carácter normal. De esta forma, el receptor detecta que la línea ha estado durante más tiempo del normal a nivel bajo y lo entiende como una señal de BREAK. 2.3.- PARAMETROS QUE CARACTERIZAN LAS COMUINCACIONES SERIE ASINCRONAS Para que la comunicación serie asíncronas entre dos dispositivos tenga éxito, tanto el emisor como el receptor deben conocer los parametros que caracterizan esa comunicación. Estos parámetros son: • VELOCIDAD (BAUDIOS): Especifíca el número de bits por segundo que se van a transmitir. Las velocidades más utilizadas son 1200, 2400 y 9600 baudios. (Al ser transmisiones binarias, un baudio equivale a un bit por segundo). En las comunicaciones con la tarjeta CT6811 también se empleará la velocidad de 7680 Baudios. • NUMERO DE BITS DE STOP: Normalmente se usan 1 ó 2 bits de Stop. En las comunicaciones con el PC y la tarjeta CT6811 se utilizará 1 bit de stop. • NUMERO DE BITS DE DATOS: En las comunicaciones con el PC y la CT6811 se utilizarán siempre 8 bits de datos. • PARIDAD: La paridad se utiliza para detectar errores en la transmisión de datos. Si se está utilizando paridad se añade un bit más a los bits de datos, llamado bit de paridad. Para las comunicaciones serie se puede utilizar paridad par, impar o no utilizar paridad. En las comunicaciones con el PC y la CT6811 no se utilizará ningún bit de paridad. Para especificar todos estos parámetros se suele utilizar la siguiente notación: (Baudios)-(bits de datos)(paridad)(bits de Stop) En las comunicaciones entre el PC y la Tarjeta CT6811 se utilizarán comunicaciones serie asíncronas del tipo: 1200-8N1, 7680-8N1 y 9600-8N1. La N quiere decir que no se usa paridad. 2.4.- Señales implicadas en una COMUNICACION SERIE ASINCRONA FULL-DUPLEX Todos los dispositivos que intercambian información mediante comunicaciones serie asíncronas disponen al menos de un pin de Salida denominado TX y de un pin de entrada denominado RX. Por TX se envían datos al otro dispositivo y por RX se reciben las tramas del otro dispositivo. En la figura 5 se muestra un esquema de conexión del dispositivo A con el B. La señal TX del dispositivo A se conecta a la señal RX del dispositivo B. La señal TX de B se conecta con RX de A. Las masas (GND) de ambos dispositivos deben estar también unidas. Como se muestra en el ejemplo de la figura 5 sólo son necesarios tres cables de interconexión entre los dispositivos A y B. Estos cables siempre van a estar presentes en las comunicaciones serie asincrónas. Sin embargo, en muchas interconexiones entre dispositivos, además de las comunicaciones serie se añaden señales especiales de control. Por ejemplo, al interconectar un MODEM con un PC además de TX, RX y GND se encuentran las señales DTR, CTS, DSR... Estas señales extra no forman partes de las comunicacion serie en sí sino que forma parte de una norma que utiliza comunicaciones serie asíncronas para la transferencia de datos. La norma más utilizada y además soportada por los PC’s es la RS-232C.

14

Control de la CT6811 desde elTX PC

TX Dispositivo B RX

Dispositivo A RX

GND

GND

Figura 5: Conexión serie asíncrona full-duplex entre dos dispositivos A y B

3.- LA NORMA RS-232C
3.1.- INTRODUCCION La norma RS-232C fue definida fundamentalmente para conectar un ordenador con un modem. En la comunicación con un modem además de transmitir los datos de una forma serie asíncrona son necesarias una serie de señales adicionales para controlar el modem y saber en qué estado se encuentra. En la norma RS-232C se especifica cuáles son estas señales adicionales, cómo son los conectores, qué características eléctricas deben cumplir las señales y un ‘sin fin’ de parámetros electromecánicos en los que no se entrará en detalle. El dispositivo que genera los datos a transmitir (Ordenador) se denomina en la norma DTE (Data Terminal Equipment) y el equipo que envía los datos a la línea (Modem) se denomina DCE (Data Comunication Equipment). En la figura 6 se representa la interconexión entre dos ordenadores mediante modems. Los ordenadores se comunican con sus modems correspondientes a través de la norma RS-232C.
Norma RS232C Linea telefónica Norma RS232C

DTE Ordenador A

DCE Modem A

DCE Modem B

DTE Ordenador B

Figura 6: Comunicación entre dos ordenadores(DTE) utilizando modems (DCE) 3.2.- SEÑALES DE LA NORMA RS-232C En esta norma se utilizan comunicaciones serie asíncronas por lo que son precisas las señales TX, RX y GND como se ha indicado en el apartado 2.4. Además de estas señales la norma incluye algunas más para controlar el modem. Las más importantes y más comúnmente utilizadas son: • RTS (Request To Send): Solicitud de envío. Esta señal se utiliza en comunicaciones half-duplex, en las cuales las comunicaciones se pueden realizar en ambos sentidos pero no a la vez. Cada vez que el ordenador tiene algo que transmitir activa esta señal para indicar su intención de transmitir cuando la línea se quede libre. • CTS (Clear To Sendo): Listo para enviar. Indica al ordenador que la línea le ha sido concedida y que puede comenzar a transmitir. (Sólo en comunicaciones half-duplex) • DSR (Data Set Ready): Equipo de datos listo. La activa el modem para indicar que está listo para establecer la conexión con otro modem. • DCD (Data Carrier Detect). Detección de portadora. La activa el modem para indicar que se ha establecido el enlace con otro modem. Permanecerá activa durante toda la conexión. • DTR (Data Terminal Ready). Terminal de Datos listo. El ordenador activa esta señal para indicar al modem que está listo para comunicarse. Al desactivarla se le indica al modem que termine con la conexión en curso. • RI (Ring Indicator). Indicador de Timbre. Se activa cuando aparecen tonos de llamada en la línea. Es decir, que cada vez que se reciba una llamada telefónica y esté conectado el modem se activará esta señal. • TX. Transmisión de datos. Esta señal contiene los datos que se quieren transmitir al ordenador remoto. • RX. Recepción de datos. Por esta señal se reciben los datos del ordenador remotor. • GND: Señal de masa entre modem y ordenador.

15

Control de la CT6811 desde el PC

señales.

En la figura 7 se han representado los sentidos(ordenador-modem; modem-ordenador) de las diferentes

TX RX RTS ORDENADOR CTS DSR DCD DTR RI GND MODEM

Figura 7: Señales más importantes de la norma RS232 y su sentido. 3.3.- CONECTORES DE 25 Y 9 PINES La norma RS-232C es soportada por toda la familia de ordenadores PC. La norma define para el ordenador (DTE) un conector de 25 pines macho. No obstante, existen muchas señales de la norma que para las aplicaciones normales no se utilizan. Por ello existen en los PC’s conectores macho de 9 pines. En la figura 8 se muestra la numeración de los conectores macho de 9 y 25 pines así como la señal que se transporta por cada pin. Estos conectores se denomina DB9 y DB25. La mayoria de los PC’s disponen por lo menos de un conector DB9 en el que se conecta el ratón. En otros existe un segundo conector del tipo DB9 ó DB25. Es conveniente saber que las diferentes señales de la norma RS-232C se pueden utlizar para otros fines distintos de lo que fueron creados. Por ejemplo, la señal DTR será utilizada para realizar un RESET de la tarjeta CT6811 antes de cargar algún programa.

16

Control de la CT6811 desde el PC

CONECTOR DB9 MACHO

CONECTOR DB25 MACHO

1 6

2 7

3 8

4 9

5

1

2

3

4

5

6

7

8

9

10 11 12 13

14 15 16 17 18 19 20 21 22 23 24 25

SEÑAL DCD RX TX DTR GND DSR RTS CTS RI

CONECTOR DB9 1 2 3 4 5 6 7 8 9

CONECTOR DB25 8 3 2 20 7 6 4 5 22

Figura 8: Conectores DB9 y DB25 y las señal que transporta cada pin. 3.4.- CARACTERISTICAS ELECTRICAS DE LAS SEÑALES Las señales eléctricas de la norma RS-232 deben estar dentro del rango de ±15 voltios. Las señales de salida tienen diferentes especificaciones que las de entrada. Para las salidas, las tensiones entre 5 y 15 voltios son consideradas como un ‘1’ lógico. Las tensiones entre -5 y -15 voltios representan un ‘0’ lógico. Las tensiones entre -5 y 5 voltios están indefinidas. Para las señales de entrada se utiliza un margen de ruido de 2 voltios por lo que las tensiones entre 3 y 15 voltios se consideran un ‘1’ lógico y las tensiones entre -3 y -15 un ‘0’ lógico. No es necesario conocer las especificaciones eléctricas de las señales. Sin embargo lo importante es saber que estas señales NO SON TTL. Las señales TTL se define entre 0 y 5 voltios mientras que las señales RS-232C se definen entre -15 y +15 voltios. Existe pues una incompatibilidad eléctrica entre estos dos tipos de señales. Si se quiere conectar un PC, que tiene la norma RS-232C, directamente al microcontrolador 68HC11, que tiene tensiones TTL, será necesario utilizar un convertidor de niveles. En la tarjeta CT6811 en convertidor se encuentra dentro de la propia placa por lo que se puede conectar directamente al PC sin ningún problema. 3.5.- LA NORMA RS-232C Y EL PC En el PC se proporcionan las señales de la norma RS-232 para controlar un modem. Estas señales cumplen las características eléctricas. Sin embargo es posible utilizar las señales para realizar aplicaciones que no cumplan la norma RS-232. Por ejemplo la señal RI (Ring Indicator) es una señal de entrada que activa el modem para indicar al ordenador que están llamando por teléfono. Sería posible utilizar esta señal de entrada para otros propósitos definidos por el usuario. Por tanto en los conectores DB9 y DB25 se dispone de bits de entrada y salida (Ojo! que no son TTL) que pueden ser utilizados por el usuario para conectar diferentes circuitos. Si con estas señales se pretende controlar un modem habrá que seguir las indicaciones de la norma. Si por el contrario se pretende controlar otro periférico diferente de un modem se pueden dar diferentes usos a las señales. Al comunicar el PC con la tarjeta CT6811 se utilizarán los pines TX y RX para las comunicaciones serie asíncronas. La señal DTR se utilizará para realizar un reset de la CT6811. Cuando se envíe un pulso por DTR la tarjeta CT6811 se “reseteará”.

17

Control de la CT6811 desde el PC

A efectos de comunicar el PC con la CT6811 lo único que interesa saber de la norma RS-232C es que los niveles no son TTL y que se van a utlizar los pines TX y RX para las comunicaciones serie asíncronas y la señal DTR para el reset. El resto de las señales de la norma no se van a utilizar.

18

Control de la CT6811 desde el PC

4.- UARTS: TRANSMISORES/RECEPTORES UNIVERSALES
4.1.- INTRODUCCION Hasta ahora se han explicado los fundamentos de las comunicaciones serie asíncronas. Pero realmente ¿Cómo se podría construir un dispositivo que se comunicase con otro utilizando comunicaciones serie asíncronas?. Para poder implementar este tipo de comunicaciones exiten unos circuitos integrados conocidos bajo el nombre de UART (Universal Asynchronous Receiver Transmitter). Estos integrados tienen la ventaja de que nos permiten programar los parámetros relacionados con las comunicaciones serie asíncronas (Apartado 2.3): Velocidad, paridad, bits de stop y bits de datos. Con una misma UART podemos conseguir comunicaciones del tipo 1200-8N1, 9600-7N1 etc... La propia UART también nos informa de si se ha producido algún error en la recepción de datos. 4.2.- MODELO GENERICO DE UNA UART En esta sección se va a presentar un modelo de UART muy simple. En la figura 9 se muestra el diagrama de bloques.

Circuito de Reloj

TX
Interfaz con exterior Transmisor

Receptor

RX

Registros de control Registros de estado Gestión interrupciones

Figura 9: Diagrama de bloques de una UART genérica La UART consta de un circuito transmisor para enviar los datos serie y un circuito receptor para recibir los datos de una línea serie. Un circuito de reloj se encarga de generar la señal cuadrada a partir de la cual se obtendrán las señales de temporización para transmitir y recibir con distintas velocidades. Los registros de control permiten configurar los distintos parámetros de las comunicaciones: paridad, bits de datos, bits de estop, velocidad... Los registro de estado indican en todo momento qué es lo que está pasando: se ha recibido un dato, circuito transmisor listo para enviar siguiente dato, error en la recepción de un dato... Finalmente existe una parte que se encarga de generar interrupciones externas para realizar un control más eficaz de la UART. La clave de las UART’s es la facilidad de manejo. Para programarlas sólo habrá que conocer los registros internos y saber cómo configurarlos. Una vez configurada una UART para enviar un dato simplemente habrá que escribir el dato deseado en un registro determinado. La UART ya se encarga de mandar el dato y añadir los bits necesarios para configurar la trama. (Bits de parada, stop y paridad). Igualmente para recibir

19

Control de la CT6811 desde el PC

datos es muy sencillo. Habrá que leer de un registro el dato recibido. Un bit situado en un registro de estado indicará que se ha recibido un dato nuevo y que está listo para ser leído. Algunas UART’s, como por ejemplo las del PC, además de integrar todo lo anterior para realizar comunicaciones serie asíncronas, incorporan registros de estado y control para controlar un modem mediante la norma RS-232C. Por ello estas UARTS disponen de las señales de la norma RS-232C: DTR, CTS, DCD..... 4.3.- SEÑALES DE UNA UART Una UART normal, como la que se encuentra dentro del microcontrolador 68HC11, dispone únicamente de dos señales: TX para transmitir los datos serie y RX para recibir datos serie. Otras UARTS más complejas, por ejemplo las del PC’s, incorporan las demás señales de la norma RS232C. En la figura 10 se muestra un diagrama con las señales normales y las añadidas por la norma RS-232C.
TX RX GND UART RTS CTS DTR DSR DCD RI Señales de la norma RS-232C Señales en todas las UARTS

Figura 10: Señales en UART’s normales y UART’s que soportan la norma RS-232C.

20

Control de la CT6811 desde el PC

5.- LA UART DEL PC
5.1.- INTRODUCCION Las UARTS que se encuentran en un PC suelen ser alguna de las siguientes: 8250, 8250A, 164650 ó 16550A. La UART más básica es la 8250, siendo las demás compatibles con ella. Cualquier programa realizado para esta UART es compatible con las demás por ello en esta sección se estudiará la UART 8250. La UART 8250 además de comunicaciones serie asincronas dispone de todas las señales de la norma RS-232C necesarias para controlar un modem. Por ello, además de contener los registros necesarios para el control y configuración de las comunicaciones serie dispone de unos registros de control y configuración del modem, que son los que controlan las señales DTR, DSR, CTS... 5.2.- PATILLAJE Y CONEXIONADO DE LA UART 8250 En la figura 11 se muestra el patillaje de la UART 8250. No hace falta conocerlo para programarla. Se incluye aquí para completar la información. En la figura 12 se muestra el conexionado de la UART 8250 en un PC compatible. Obsérvese que la UART es una pastilla TTL por lo que para adaptarse a la norma RS-232 es necesario un adaptador de niveles TTL a RS-232. De la figura 12 hay que destacar lo siguiente. La frecuencia de reloj de la UART es de 1.8432. Con esta frecuencia se pueden conseguir velocidades estándares de comunicación de 1200, 2400 y 9600 baudios. La salida de interrupción de la UART en el PC está controlada por la señal de salida OUT2. Cuando esta señal esté activa las interrupciones estarán permitidas. Cuando la señal esté desactivada no se producirán interrupciones.
D0 D1 D2 D3 D4 D5 D6 D7 RCLK SIN SOUT CS0 CS1 CS2 BAUDOUT XTAL1 XTAL2 DOSTR DOSTR VSS 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 8250 40 39 38 37 36 35 34 33 32 31 30 29 28 27 26 25 24 23 22 21 VCC RI DCD DSR CTS MR OUT 1 DTR RTS OUT2 INTRPT NC A0 A1 A2 ADS CSOUT DDIS DISTR DISTR

Figura 11: Patillaje de la UART 8250

IRQ 3 Ó IRQ 4 D0..D7 A3..A9 Lógica Selección

OUT 2 INTR DTR RTS SOUT CS2 DCD DSR CTS RI SIN BAUDOUT RCLK DTR RTS TX DCD DSR CTS RI RX

Reloj 1.8432 MHZ

XIN

21

Figura 12: Conexiodado de la UART 8250 en el PC

Control de la CT6811 desde el PC

5.3.- DIAGRAMA DE BLOQUES DE LA UART 8250
TX
Transmisor

Interfaz con exterior

Receptor

RX

Registro de control de linea

Registro de estado de línea

Latch generador de baudios

Registro de control del modem Logica control del modem Registro de estado del modem

RTS CTS DTR DSR

DCD RI OUT 1 OUT 2

Registro de activación de interrupciones

Logica de control de interrupcion

Registro de identificación de interrupción

Figura 13: Diagrama de bloques de la UART 8250 Hay dos grupos importantes de registros en la UART 8250. Los registros de línea controlan y configuran las transmisiones serie asíncronas. Los registros del modem actúan sobre las señales del interfaz RS-232. Además de estos registros existe un ‘latch’ de 16 bits que sirve para configurar la velocidad de transmisión, un registro de activación de interrupciones y un registro que indentifica qué interrupción es la que se ha producido en la UART. En la sección 5.4 se explican detalladamente los diferentes registros. 5.4.- LOS REGISTROS DE LA UART

22

Control de la CT6811 desde el PC

La UART 8250 dispone de un total de 10 registros de 8 bits. De ellos se puede acceder directamente a 8. Para acceder a los otros dos registros hay que activar el bit 7 del registro de control de línea, como se explicará más adelante. Los registros se van a ir describiendo uno por uno y ordenados del que ocupa la dirección más baja a la más alta. Las direcciones de los registros se van a dar en forma de desplazamiento (offset). Para obtener la dirección completa habrá que sumar el desplazamiento a la dirección base en la que se encuentra situada la UART (Apartado 5.5). OFFSET 0: Registro buffer de Transmisión/Recepción Todo lo que se escribe en este registro es enviado a través de la salida serie. Todo lo que se recibe a través de la línea serie puede ser leído a través de este registro. OFFSET 1: Registro de activación de interrupciones 7
Siempre 0

6
Siempre 0

5
Siempre 0

4
Siempre 0

3
Entrada RS232

2
Error de receptor o BREAK

1
Buffer de transmisor vacío

0
Dato recibido

Los bits 7,6,5 y 4 siempre se encuentra a 0. Cuando los bits 0,1,2 ó 3 se encuentren a nivel alto se generará una interrupción. • Bit 0: Se genera interrupción cuando se ha recibido un dato nuevo • Bit 1: Se genera interupción cuando la UART está lista para enviar otro carácter • Bit 2: Se genera interrupción cuando se ha producido un error en el receptor o se ha recibido una señal de BREAK. • Bit 3: Se genera interrupción cuando se produzca algún cambio en cualquiera de las entradas RS232. OFFSET 2: Registro de indentificación de interrupciones Cuando aparece una interrupción, este registro se utiliza para identificar cúal es la que se ha producido. Un 0 en el bit 0 indica que la interrupción está pendiente. Un 1 significa que no hay interrupcines pendientes. En la siguiente tabla se muestra el significado de los bits 0,1 y 2. Los demás bits no se utilizan. Bit 2 Bit 1 Bit 0 Prioridad Interrupción producida 0 0 1 Ninguna Ninguna 1 1 0 0 Error de serialización o BREAK 1 0 0 1 Dato recibido 0 1 0 2 Buffer de transmisión vacío 0 0 0 3 Entrada RS-232 Si se producen varias interrupciones a la vez tiene la de prioridad 0 prevalecerá sobre las demás. OFFSET 3: Registro de control de línea 7 6
Control BREAK DLAB 0 = OFF 1 = ON

5
Paridad:

4

3

2
Bits de Stop 0 = 1 bit 1 = 2 Bits

1
Bits de Datos 00 = 5 bits 01 = 6 10 = 7 11 = 8

0

000 = Sin paridad 001= Paridad impar 011= Paridad par 101= Bit a 1 111= Bit a 0

Con este registro se configuran los distintos parámetros de las comunicaciones serie asíncronas: Tipo de paridad, Bits de datos y bits de Stop. Al activar el bit 6 se manda una señal de BREAK. El bit 7 se utiliza para configurar la velocidad de transmisión. Cuando se pone a 1 se utilizan los registros 0 y 1 para almacenar el valor de la velocidad deseada. Más adelante se darán más detalles. OFFSET 4: Registro de control del Modem

23

Control de la CT6811 desde el PC

7
Siempre 0

6
Siempre 0

5
Siempre 0

4
Test Local Loopback

3
Salida de propósito general 2

2
Salida de propósito general 1

1
RTS

0
DTR

Con este registro, además de controlar las señales RTS y DTR de la norma RS-232C, se pueden controlar dos salidas de propósito general. En los ordenadores PC la salida de propósito general 2 (Bit 3) se utiliza para habilitar las interrupciones. El bit 4 se utiliza para realizar pruebas en el software de comunicaciones sin tener que conectar dos ordenadores. No se va a entrar en detaller. OFFSET 5: Registro de estado de línea 7
Siempre 0

6
Transmisor vacío

5
Buffer de transmisor vacío

4
Detección de BREAK

3
Error de trama

2
Error de paridad

1
Error de sobreescritura

0
Dato listo

• • • • • •

Bit 0: Se pone a uno cuando se ha recibido un dato está listo para ser leído Bit 1: Se activa cuando se ha recibido un dato nuevo y no se había leído el anterior Bit 2: Indica que se ha producido un error de paridad Bit 3: Se ha recibido una trama serie con el bit de Stop incorrecto Bit 4: Indica que se ha detectado una señal de Break Bit 5: Indica que se ha desplazado un byte desde el buffer del transmisor al registro de desplazamiento del emisor • Bit 6: No existen bytes en el buffer del transmisor ni en su registro de desplazamiento. OFFSET 6: Registro de estado del modem 7
DCD

6
RI

5
DSR

4
CTS

3
Delta DCD

2
Delta RI

1
Delta DSR

0
Delta CTS

Los bits 4-7 informan sobre el estado de las señales CTS, DSR, RI y DCD. Los bits 0-3 indican si se ha producido algún cambio en el estado de las respectivas señales. La aparición de un 1 indica que la entrada ha sido modificada desde la última vez que se leyó. OFFSET 7: Este registro se reserva para futuras ampliaciones

SELECCION DE LA VELOCIDAD La UART en los ordenadores PC tiene conectado un cristal de 1.8432MHZ. Esta frecuencia es dividida por 16 dentro de la UART. Además, la frecuencia puede ser dividida por un valor de 16 bits contenido en un Latch para obtener la velocidad en baudios deseada para la transmisión y recepción. El valor N que habrá que almacenar en el latch divisor para obtener la velocidad en baudios deseada es: N = 1843200 / (16*baudios) Así, para obener las velocidades de 9600, 2400 y 1200 habrá que almacenar los valores respectivos de N: 12, 48 y 96. El latch divisor es un registros de 16 bits al que se accede de una forma especial. Primero hay que activar el bit DLAB que se encuentra en el bit 7 del registro de control de línea. Después se almacena en el registro 0 el valor menos significativo de N y en el registro 1 el valor más significativo. Finalmente se vuelve a poner a 0 el bit DLAB. En la siguiente tabla se muestran valores del divisor para diferentes velocidades en baudios: Velocidad en baudios 50 75 Valor de DIVISOR 2304 1536

24

Control de la CT6811 desde el PC

110 150 300 600 1200 2400 4800 7680 9600 14400 19200 28800 38400 57600 115200 5.5.- ACCESO A LA UART EN EL PC

1047 768 384 192 96 48 24 15 12 8 6 4 3 2 1

En la siguiente tabla se muestra la dirección base de la UART para los diferentes puertos serie. PUERTO SERIE COM1 COM2 COM3 COM4 DIRECCION BASE UART 0x3F8 0x2F8 0x3E8 0x2E8

Para acceder por ejemplo al registro de control de línea del puerto COM1 habrá que acceder a la dirección del puerto 0x3F8 (dirección base COM1) + 3 (Offset registro control de línea) = 0x3FB Para utilizar de una manera eficiente las comunicaciones serie es muy interesante utilizar las interrupciones. En la tabla siguiente se muestran las interrupciones y los vectores asociados a los distintos puertos serie.

PUERTO SERIE COM1 COM2 COM3 COM4

INTERRUPCION IRQ4 IRQ3 IRQ4 IRQ3

VECTOR INTERRUPCION 0x0B 0x0C 0x0B 0x0C

Para habilitar la interrupción IRQ3 ó IRQ4 hay que modificar el registro de máscara de interrupción del controlador de interrupciones 8259. Este registro se encuentra en el puerto 0x21. Los 8 bits de este puerto habilitan o deshabilitan las interrupciones hardware. Un 1 indica que la interrupción está DESHABILITADA. Un 0 indica que está habilitada. Registro de máscara de interrupción 7 LPT1 6 Disquete 5 LPT2 4 COM1 3 COM2 2 -----1 Teclado 0 Timer

Para habilitar la interrupción del COM1 habrá que un 0 en el bit 4. Para habilitar la del COM2 se pondrá a 0 el bit 3. La información que se ha proporcionado en este apartado es útil cuando se está programando la UART bajo MS-DOS. Si se utiliza LINUX no se podrá acceder directamente a la UART ni se podrán controlar directamente las interrupciones.

25

Control de la CT6811 desde el PC

6.- RUTINAS DE COMUNICACIONES SERIE PARA EL PC
6.1.- INTRODUCCION El objetivo de esta sección es disponer de unas rutinas para manejar el puerto serie del PC para poderse comunicar con la tarjeta CT6811. La implementación se va a hacer utilizando el compilador Turbo C ++ V1.0 bajo MS-DOS. En un futuro se implementará con el GCC bajo LINUX. Para las comunicaciones con la tarjeta CT6811 se van a utilizar 8 bits de datos, ninguno de paridad, 1 bit de Stop y velocidades de 1200, 7680 y 9600 baudios. Por ello las rutinas aquí presentadas son unas rutinas de propósito general sino que está orientadas hacia la comunicación con la CT6811. La implementación final se va a realizar mediante interrupciones. La librería desarrollada se denomina SERIE.C. En el fichero SERIE.H se encuentran todos los prototipos de las funciones de interfaz de la librería SERIE.C así como algunas definiciones. 6.2.- INTERFAZ 6.2.1.- Apertura y cierre del puerto serie Antes de utilizar un puerto serie habrá que abrirlo. Para ello hay que llamar a la función:
• void abrir_puerto_serie(int puerto);

Esta función toma como argumento el número del puerto serie que se quiere utilizar: 1 para COM1, 2 para COM2, 3 para COM3 y 4 para COM4. Al abrir el puerto serie se activan las rutinas de servicio de interrupción del puerto serie especificado y se inicializa el puerto serie para poder funcionar adecuandamente. El puerto se configura para trabajar con 8 bits de datos, 1 bit de Stop y ningún bit de paridad. La velocidad por defecto es de 9600 baudios. La señal DTR por defecto se deja a OFF. Una vez que se ha terminado de trabajar con el puerto serie, se debe cerrar. Al cerrarse el puerto serie se desactivan las rutinas de servicio de interrupción. Si no se sale de un programa sin haber cerrado el puerto serie pueden ocurrir errores imprevisibles. Para cerrar el puerto serie se utiliza la función:
• void cerrar_puerto_serie();

No hace falta especificar el número de puerto serie. Se cierra el último que se ha habierto. Es importante hacer notar que estas rutinas sólo sirven para trabajar con un único puerto abierto a la vez. No sirven para trabajar a la vez por ejemplo con los puertos COM1 y COM2. Siempre hay que abrir un puerto, usarlo y después cerrarlo antes de abrir otro. 6.2.2.- Funciones de configuración Puesto que estas rutinas están pensadas para trabajar con la tarjeta CT6811 sólo se pueden configurar la velocidad a 1200, 7680 y 9600 Baudios. Si se quieren otras velocidades habrá que modificar la función de cambio de velocidad (Tarea que no es difícil). La velocidad se configura con la siguiente función:
• void baudios(int velocidad);

El parámetro velocidad sólo puede tomar los valores 1200, 7680 ó 9600. Si se especifica otro valor se configura el puerto a 9600 baudios. Existe una rutina para limpiar el buffer de recepción. Si existía algún carácter en el buffer es eliminado.
• void vaciar_buffer(void);

6.2.3.- Funciones de lectura/escritura •
void enviar_car(char car);

Esta función envía un carácter por el puerto serie.

26

Control de la CT6811 desde el PC

• •

Indicación de si existen caracteres en el buffer de recepción. Si existen caracteres pendientes de ser leídos la función devuelve un 1. En caso contrario devuelve un 0.
int car_waiting(void);

Leer un carácter del buffer de recepción. Si este buffer se encuentra vacío se espera a que se reciba algún carácter. Esta función es peligrosa puesto que se queda esperando hasta que llegue algún carácter. Si el carácter no llega por alguna razón el nuestro programa quedará bloqueado. Para solucionar esto se puede utilizar la siguiente función:
char leer_car(void);

Leer un carácter con un plazo de tiempo máximo. Si no se ha recibido ningún carácter dentro del plazo previsto se devuelve un 1 en timeout. En caso de que se llegue el carácter se devuelve un 0 en timeout. El plazo se especifica en tics de reloj. Cada tic equivale aproximadamente a 55 milisegundos.
char leer_car_plazo(int plazo,int *timeout);

6.2.4.- Otras funciones • • Con esta función se activa la señal DTR de la norma RS-232. Cuando la señal DTR se encuentra activa la tarjeta CT6811 se encuentra en estado de reset.
void dtr_on(void);

Desactivar la señal DTR. Para realizar un reset completo de la tarjeta CT6811 hay que activar el DTR, mantenerlo a ON durante 200 milisegundos aproximadamente y luego desactivar el DTR.
void dtr_off(void);

Se devuelve un 0 si no se ha recibido una señal de BREAK. Se devuelve un 1 cuando se ha recibido. Cuando se recibe un BREAK se activa una señal interna. Con la función ok_break se devuelve el estado de esta señal. Si la señal estaba activa se desactiva. Por tanto, la función ok_break nos devuelve el estado de la señal de BREAK desde la anterior llamada a ok_break.
int ok_break(void); int wait_break(int plazo);

Esperar a que se reciba una señal de break. Si se sobrepasa el plazo y no se ha recibido la señal de BREAK se devuelve un 1. El plazo está especificado en tics de reloj.

6.2.5.- El interfaz completo A continuación se muestran todas las funciones de interfaz:
/* +-----------------------------+ ¦ ¦ ¦ APERTURA DEL PUERTO SERIE ¦ ¦ ¦ +-----------------------------+ */ void abrir_puerto_serie(int puerto); /* +----------------------------------------------------------------------+ ¦ Antes de comenzar a trabajar con el puerto serie hay que llamar a ¦ ¦ esta función. En el argumento se le pasa el número de COM con el ¦ ¦ que se quiere trabajar. Asi por ejemplo para abrir el COM1 habrá ¦ ¦ que llamar a la función: ¦ ¦ ¦ ¦ abrir_puerto_serie(1); ¦ ¦ ¦ ¦ ¦ ¦ Los puertos soportados son COM1, COM2, COM3 y COM4. Si se especifi- ¦ ¦ ca como argumento un número distinto de 1,2,3 ó 4 se abrirá el ¦ ¦ COM1 ¦ ¦ ¦ ¦ Después de llamar a esta rutina el puerto especificado queda con- ¦ ¦ figurado para trabajar con 8 bits de datos, 1 bit de Stop y ninguno ¦ ¦ de paridad. La velocidad es el 9600 baudios. ¦ ¦ ¦ ¦ ¦ +----------------------------------------------------------------------+*/ /* +-----------------------------+ ¦ ¦ ¦ RUTINAS DE CONFIGURACION ¦ ¦ ¦

27

Control de la CT6811 desde el PC

+-----------------------------+ */ void baudios(int velocidad); /* +----------------------------------------------------------------------+ ¦ Establecer la velocidad en baudios especificada. Sólo se permiten ¦ ¦ tres velocidades: 1200, 7680 y 9600 baudios. Si se especifica otra ¦ ¦ velocidad diferente se tomaran 9600 baudios. ¦ ¦ que llamar a la función: ¦ ¦ ¦ ¦ Ejemplo de utilización: baudios(1200); ¦ ¦ ¦ +----------------------------------------------------------------------+*/ void vaciar_buffer(void); /* +----------------------------------------------------------------------+ ¦ Vaciar el buffer de recepción. Si existían caracteres se desechan ¦ +----------------------------------------------------------------------+*/ /* +-------------------------------+ ¦ ¦ ¦ RUTINAS DE LECTURA/ESCRITURA ¦ ¦ ¦ +-------------------------------+ */ void enviar_car(char car); /* +-----------------------------------------------------+ ¦ Enviar un carácter por el puerto serie abierto. ¦ +-----------------------------------------------------+*/ int car_waiting(void); /* +----------------------------------------------------------------------+ ¦ Esta función devuelve un valor 0 si no existe ningún carácter pen- ¦ ¦ diente de ser leído en el buffer de recepción. Se devuelve un valor ¦ ¦ distinto de 0 si existe algún carácter pendiente de ser leído. ¦ +----------------------------------------------------------------------+*/ char leer_car(void); /* +----------------------------------------------------------------------+ ¦ Leer un carácter del buffer de recepción. Si no hay ningún carácter ¦ ¦ pendiente de ser leído se espera indefinidamente hasta que llegue ¦ ¦ algún carácter. ¦ +----------------------------------------------------------------------+*/ char leer_car_plazo(int plazo,int *timeout); /* +----------------------------------------------------------------------+ ¦ Leer un carácter del buffer de recepción. Si el carácter no se ha ¦ ¦ recibido dentro del plazo establecido se devuelve un valor distin- ¦ ¦ to de 0 en timeout. En caso de llegar un carácter dentro del plazo ¦ ¦ indicado se devuelve el carácter y se devuelve 0 en timeout. ¦ ¦ ¦ ¦ El valor del plazo se especifica en tics de reloj. Cada Tic equi- ¦ ¦ vale aproximadamente a 55ms. ¦ +----------------------------------------------------------------------+*/ /* +-----------------------+ ¦ ¦ ¦ OTRAS RUTINAS ¦ ¦ ¦ +-----------------------+ */ void dtr_on(void); /* +---------------------------+ ¦ Activar la señal DTR ¦

28

Control de la CT6811 desde el PC

+---------------------------+*/ /*void dtr_off(void);*/ /* +---------------------------+ ¦ Desactivar la señal DTR ¦ +---------------------------+*/ int ok_break(void); /* +----------------------------------------------------------------------+ ¦ Se devuelve un valor 0 si no se ha recibido ninguna señal de BREAK ¦ ¦ Se devuelve un valor distinto de 0 si se ha recibo un BREAK ¦ +----------------------------------------------------------------------+*/ int wait_break(int plazo); /* +----------------------------------------------------------------------+ ¦ Esperar a que venga una señal de BREAK en el plazo indicado. ¦ ¦ Si es así se devuelve un valor distinto de 0. Si transcurre el plazo¦ ¦ y no se ha recibido un BREAK se devuelve 0. ¦ ¦ El plazo se especifica en tics de reloj. Cada tic equivale a ¦ ¦ 55 milisegundos. ¦ +----------------------------------------------------------------------+*/ /* +------------------------------+ ¦ ¦ ¦ TERMINAR CON EL PUERTO SERIE ¦ ¦ ¦ +------------------------------+ */ void cerrar_puerto_serie(); /*+-------------------------------------------------------+ ¦ Antes de terminar el programa es necesario llamar ¦ ¦ a este procedimiento para desactivar las rutinas de ¦ ¦ interrupción. ¦ +-------------------------------------------------------+ */

29

Control de la CT6811 desde el PC

6.3.- IMPLEMENTACION En la figura 14 se muestra la estructura de las distintas funciones que conforman el interfaz de la librería SERIE.C
abrir_puerto_serie() cerrar_puerto_serie

TX RX DTR

enviar_car() Control DTR Configuración velocidad dtr_on() dtr_off baudios() accion_rxcar()

UART

vaciar_buffer BUFFER DE RECEPCION SEÑAL BREAK ok_break() wait_break leer_car() leer_car_plazo() car_waiting

Rutina de interrupción

Acción al recibir carácter Acción al recibir BREAK

accion_break()

Figura 14: Estructura de las rutinas de manejo del puerto serie. LIBRERIA SERIE.C El ‘grueso’ de la librería lo forma la rutina de atención a las interrupciones. Cada vez que se abre un puerto serie con la función abrir_puerto_serie() se instala la rutina de servicio de interrupción de las interrupciones de la UART. Esta rutina se va ejecutar cada vez que se reciba un carácter por el puerto serie o una señal de BREAK. Cuando se recibe un carácter lo primero que se hace es llamar a la rutina accion_rxcar() que debe ser definida por el usuario. En esta rutina el usuario programa lo que quiere que ocurra cara vez que se ha recibido un carácter. Por ejemplo en el programa MCBOOT 2.0 cada vez que se recibe un carácter se gira un ‘palito’ situado en la esquina superior derecha de la pantalla. Si el usuario no quiere realizar ninguna acción especial al recibirse un carácter simplemente deberá declarar la función accion_rxcar() y dejarla en blando: void accion_rxcar() { } Después de ejecutarse está rutina se procede a meter el carácter recibido en el buffer de recepción. El tamaño por defecto de este buffer es de 512 bytes. Si el buffer se llenase todos los caracteres recibidos después de que se hubiese llenado serían perdidos. Al recibirse una señal de BREAK lo primero que se hace es ejecutar la rutina de usuario accion_break(). En ella el usuario puede especificar lo que quiere que su programa realiza en cuanto se reciba una señal de BREAK. En el MCBOOT 3.0 cada vez que se recibe alguna señal de este tipo se muestran unos dibujitos en la esquina superior derecha de la pantalla. Después de ejecutar accion_break() se modifica una variable interna para señalizar que se ha detectado BREAK. Las rutinas accion_rxcar() y accion_break() son definibles por el usuario y son totalmente opcionales. Si el usuario dejase estar rutinas ‘en blanco’ no pasaría nada. Todo funcionaría sin problemas. Si el usuario quiere utilizar estas funciones debe tener en cuenta lo siguiente: • Estas funciones son llamadas desde dentro de la rutina de atención a la interrupción. Por ello estas rutinas deben ser lo más cortas y simples posibles. • No se puede acceder a servicios de DOS en estas funciones: Si desde dentro de una rutina de atención a una interrupción se llama a algún servicio del DOS el programa se colgará. Conviene dejar claro que mcuhas de las funciones del compilador de Turbo C acceden a servicios del dos, como por ejemplo: printf, scanf... Por ello estas funciones no se pueden usar.

30

Control de la CT6811 desde el PC

Lo normal es utilizar las rutinas accion_rxcar() y accion_break() para actualizar alguna variable o escribir directamente en la memoria de pantalla para generar algún tipo de animación (Como la animación del MCBOOT de los ‘palitos giratorios’). En el apartado 6.4 se muestran ejemplos de utilización de la libreria SERIE.C y ejemplos de las funciones accion_rxcar() y accion_break(). 6.4.- MANEJO DE LA LIBRERIA SERIE.C En este apartado se pretenden dar ejemplos de la utilización de la librería SERIE.C. Se van a ir realizando distintos terminales de comunicaciones, cada uno con una pequeña diferencia. Para que funcionen los ejemplos mostrados es necesario crear un PROYECT en el que se incluya la libreria SERIE.C junto con el ejemplo concreto. 6.4.1.- Ejemplo 1: Un pequeño terminal de comunicaciones El siguiente programa se trata de un terminal de comunicaciones. Todo lo que se teclee se envía por el puerto serie y todo lo que se reciba por el puerto serie se imprime en pantalla.
/* +--------------------------------------------------------------------------+ ¦ TERMINAL.C (c) GRUPO J&J. Julio 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Ejemplo de manejo de las rutinas de comunicaciones serie. ¦ ¦ El programa hace de terminal de comunicaciones. Todo lo que se lee del ¦ ¦ teclado se envia por el puerto serie y todo lo que se lee del puerto ¦ ¦ serie se imprime en pantalla ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #include "serie.h" #include "conio.h" #include "stdio.h" #define ESC 27

/* +----------------------------------------------------------------------+ ¦ No se realiza ninguna acción especial al recibir un caracter o de- ¦ ¦ tectar una señal de BREAK. ¦ +----------------------------------------------------------------------+*/ void accion_rxcar() { } void accion_break() { } main() { char c; char d; clrscr(); printf ("Terminal de comunicaciones. Pulse ESC para terminar\n\n"); abrir_puerto_serie(COM2); baudios(7680); do { if (kbhit()) { c=getch(); enviar_car(c); } if (car_waiting()) { /* Abrir sesion con puerto serie COM2 */

/* Si se ha pulsado una tecla ... /* Leer tecla pulsada /* Enviar la tecla por el puerto serie /* Si llega caracter por puerto serie

*/ */ */ */

31

Control de la CT6811 desde el PC

} }while (c!=ESC);

d=leer_car(); printf ("%c",d);

/* leer caracter que llega /* Imprimirlo por la pantalla /* Cuando se pulsa ESC se termina /* Cerrar la sesión con el COM2 */

*/ */ */

}

cerrar_puerto_serie(); return 0;

Lo importante de este programa es ver cómo se utilizan las rutinas de comunicaciones serie. Lo primero que se hace es abrir el puerto COM2 que va a ser el puerto de trabajo. Si se quiere utilizar el puerto COM1 habrá que definirse una constante llamada COM1 con el valor 1 y llamar a abrir_puerto_serie con el parámetro COM1. Una vez abierto se establece la velocidad a 7680 baudios, que es la velocidad a la que se configura el 68HC11 al arrancar en modo Bootstrap. Para enviar caracteres a la CT6811 se utiliza la rutina enviar_car. Para detectar si ha llegado algún carácter proveniente de la CT6811 se utiliza la función car_waiting() que devuelve 1 cuando ha llegado algún carácter por el puerto serie y se ha introducido en el buffer de recepción. Los caracteres contenidos en el buffer son leídos mediante la función leer_car(). Finalmente se cierra el puerto serie con la función cerrar_puerto_serie(). 6.4.2.- Ejemplo 2: Utilización de accion_rxcar y accion_break; El siguiente programa es exactamente igual que el anterior pero con la diferencia de que se implementan las rutinas accion_break y accion_rxcar para realizar acciones especiales. Cada vez que se recibe un carácter se hace girar un ‘palito’ situado en la esquina superior derecha de la pantalla. Cada vez que se recibe una señal de BREAK se dibuja un caracter al lado del ‘palito’.
/* +--------------------------------------------------------------------------+ ¦ TERM2.C (c) GRUPO J&J. Julio 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Ejemplo de manejo de las rutinas de comunicaciones serie. ¦ ¦ El programa hace de terminal de comunicaciones. Se han implementado ¦ ¦ las funciones accion_break y accion_rxcar para realizar acciones al re- ¦ ¦ cibir una señal de BREAK y al recibir un caracter por el puerto serie. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #include "serie.h" #include "stdio.h" #include "conio.h" #define ESC 27

/* Puntero para acceder directamente a la memoria de Video */ char far *punt = (char far *) 0xb800009A; void accion_break() /* +----------------------------------------------------------------------+ ¦ Cada vez que se recibe un BREAK se escribe un caracter en la esqui- ¦ ¦ na superior derecha de la pantalla. ¦ +----------------------------------------------------------------------+*/ { static char faseb[4] = {2,1,'o','.'}; static char pfb = 0; *(punt+4)=faseb[pfb]; pfb=(pfb+1) % 4; } void accion_rxcar() /* +----------------------------------------------------------------------+ ¦ Cada vez que se recibe un carácter se hace girar un 'palito' situado ¦ ¦ en la esquina superior derecha de la pantalla. ¦ +----------------------------------------------------------------------+*/ { static char fase[4] = {'-','\','||','/'};

32

Control de la CT6811 desde el PC

static char pf1 = 0; *(punt+4)=' '; *punt=fase[pf1]; pf1= (pf1 + 1) % 4;

}

main() { char c; char d; clrscr(); printf ("Terminal de comunicaciones. Pulse ESC para terminar\n\n"); abrir_puerto_serie(COM2); baudios(7680); do { if (kbhit()) { c=getch(); enviar_car(c); } if (car_waiting()) { d=leer_car(); printf ("%c",d); } }while (c!=ESC); cerrar_puerto_serie(); return 0; /* Abrir sesion con puerto serie COM2 */

/* Si se ha pulsado una tecla ... /* Leer tecla pulsada /* Enviar la tecla por el puerto serie /* Si llega caracter por puerto serie /* leer caracter que llega /* Imprimirlo por la pantalla /* Cuando se pulsa ESC se termina /* Cerrar la sesión con el COM2 */

*/ */ */ */ */ */ */

}

Obsérvese que las dos funciones accion_rxcar() y accion_break() son muy cortas y en ninguna se realizan llamadas a los servicios del DOS. En ellas simplemente hay asignaciones de variables. Para escribir caracteres en pantalla se accede directamente a la memoria de video. 6.4.3.- Ejemplo 3: Terminal de comunicaciones con TIMEOUT El programa TERM3.C es igual que el programa TERMINAL.C del primero ejemplo pero los caracteres procedentes del puerto serie no se leen con la función leer_car() sino que se leen con la función leer_car_plazo(). Si no se recibe ningún carácter dentro del plazo establecido por la constante TIEMPO se imprime en pantalla el carácter ‘T’ indicando que se ha producido TIMEOUT. La mejor forma de probar este programa es cargado con el Downmcu el programa SCITEMP.S19. Este programa envia un mensaje cada 3 segundos aproximadamente.
/* +--------------------------------------------------------------------------+ ¦ TERM3.C (c) GRUPO J&J. Julio 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Ejemplo de manejo de las rutinas de comunicaciones serie. ¦ ¦ El programa hace de terminal de comunicaciones. Se establece un periodo ¦ ¦ de tiempo para recibir un carácter. Si no se recibe nada dentro de ese ¦ ¦ plazo se imprime en pantalla el caracter 'T'indicando TIEMOUT. ¦ ¦ ¦ ¦ Para probar este terminal lo mejor es cargar en la CT6811 primero el ¦ ¦ programa SCITEMP.ASM que emite un mensaje cada 3 segundos aprox. Cuando ¦ ¦ se reciben los caracteres del mensaje, como entran dentro del plazo ¦ ¦ establecido se imprimen. Entre mensaje y mensaje, como no se recibe nada ¦ ¦ dentro del plazo establecido se imprime una 'T' ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #include "serie.h" #include "stdio.h" #include "conio.h" #define ESC 27 /* Plazo de tiempo */

#define TIEMPO 5

33

Control de la CT6811 desde el PC

/* +----------------------------------------------------------------------+ ¦ No se realiza ninguna acción especial al recibir un caracter o de- ¦ ¦ tectar una señal de BREAK. ¦ +----------------------------------------------------------------------+*/ void accion_rxcar() { } void accion_break() { } main() { char c; char d; int timeout; clrscr(); printf ("Terminal de comunicaciones. Pulse ESC para terminar\n\n"); abrir_puerto_serie(COM2); baudios(7680); do { if (kbhit()) { c=getch(); enviar_car(c); } /* Abrir sesion con puerto serie COM2 */

/* Si se ha pulsado una tecla ... /* Leer tecla pulsada /* Enviar la tecla por el puerto serie

*/ */ */

d=leer_car_plazo(TIEMPO,&timeout); /* Leer un carácter con plazo de */ /* tiempo. */ if (timeout) printf ("T"); /* Si no se recibe nada dentro del plazo */ else /* se imprime el carácter 'T' */ printf("%c",d); }while (c!=ESC); cerrar_puerto_serie(); return 0; /* Cuando se pulsa ESC se termina /* Cerrar la sesión con el COM2 */ */

}

6.4.4.- Ejemplo 4: Terminal con RESET Con este ejemplo se pretende mostrar cómo es posible realizar un reset de la tarjeta CT6811. El programa TERM4.C es un terminal de comunicaciones como los anteriores con la salvedad de que al apretar la tecla ‘R’ (mayúsculas) se realiza un RESET de la tarjeta CT6811. Obsérvece que cada vez que se realiza un reset se recibe una señal de BREAK de la CT6811 y por tanto se puede ver cómo se escriben unos caracteres en la esquina superior derecha de la pantalla.
/* +--------------------------------------------------------------------------+ ¦ TERM4.C (c) GRUPO J&J. Julio 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Ejemplo de manejo de las rutinas de comunicaciones serie. ¦ ¦ El programa hace de terminal de comunicaciones. Cada vez que se pulsa ¦ ¦ la tecla 'R' se realiza un reset de la CT6811. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #include #include #include #include "serie.h" "stdio.h" "conio.h" "dos.h" 27

#define ESC

34

Control de la CT6811 desde el PC

/* Puntero para acceder directamente a la memoria de Video */ char far *punt = (char far *) 0xb800009A; void accion_break() /* +----------------------------------------------------------------------+ ¦ Cada vez que se recibe un BREAK se escribe un caracter en la esqui- ¦ ¦ na superior derecha de la pantalla. ¦ +----------------------------------------------------------------------+*/ { static char faseb[4] = {2,1,'o','.'}; static char pfb = 0; *(punt+4)=faseb[pfb]; pfb=(pfb+1) % 4; } void accion_rxcar() /* +----------------------------------------------------------------------+ ¦ Cada vez que se recibe un carácter se hace girar un 'palito' situado ¦ ¦ en la esquina superior derecha de la pantalla. ¦ +----------------------------------------------------------------------+*/ { static char fase[4] = {'-','\','||','/'}; static char pf1 = 0; *(punt+4)=' '; *punt=fase[pf1]; pf1= (pf1 + 1) % 4;

}

void reset(void) /* +---------------------------------+ ¦ Realizar un RESET de la CT6811 ¦ +---------------------------------+*/ { dtr_on(); delay(200); dtr_off(); } main() { char c; char d; clrscr(); printf ("Pequeño terminal de comunicaciones. Pulse ESC para terminar\n\n"); abrir_puerto_serie(COM2); baudios(7680); do { if (kbhit()) { c=getch(); enviar_car(c); if (c=='R') reset(); } if (car_waiting()) { d=leer_car(); printf ("%c",d); } }while (c!=ESC); cerrar_puerto_serie(); return 0; /* Abrir sesion con puerto serie COM2 */

/* /* /* /*

Si se ha pulsado una tecla ... Leer tecla pulsada Enviar la tecla por el puerto serie Si tecla 'R' se hace un reset

*/ */ */ */ */ */ */ */

/* Si llega caracter por puerto serie /* leer caracter que llega /* Imprimirlo por la pantalla /* Cuando se pulsa ESC se termina /* Cerrar la sesión con el COM2 */

}

35

Control de la CT6811 desde el PC

La rutina reset() mantiene la señal DTR activada durante 200ms y luego se desactiva. Esto provoca que la señal de reset del 68HC11 se mantenga a nivel bajo durante ese tiempo con lo que se inicializa. Al volver a colocar el DTR a estado de off el 68HC11 queda listo para funcionar. 6.4.5.- Ejemplo 5: Autodetección de la tarjeta CT6811 El programa CTDETECT.C escanea los puertos COM1 y COM2 en busca de la tarjeta CT6811. El criterio para detectar la CT6811 es muy simple. Se realiza un reset y se espera a que se reciba una señal de BREAK. Si cada vez que se hace el reset se recibe el BREAK es que la tarjeta CT6811 se encuentra conectada a ese puerto. (¡Ojo! La tarjeta CT6811 debe estar configurada en modo BOOTSTRAP o de lo contrario no se detectará).
/* +--------------------------------------------------------------------------+ ¦ CTDETECT.C (c) GRUPO J&J. Julio 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Programa para detectar el puerto en el que se encuentra la tarjeta CT6811¦ ¦ Para ello se exploran los puerto COM1 y COM2. La exploración consiste ¦ ¦ en hacer reset hasta que se detecte que se reciben señales de BREAK. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #include "serie.h" #include "stdio.h" #include "dos.h" /* +----------------------------------------------------------------------+ ¦ No se realiza ninguna accion especial al recibir un caracter o una ¦ ¦ señal de BREAK. ¦ +----------------------------------------------------------------------+*/ void accion_rxcar() { } void accion_break() { } void reset() /* +-----------------------------------------+ ¦ Realizar un RESET de la tarjeta CT6811 ¦ ¦ señal de BREAK. ¦ +-----------------------------------------+*/ { dtr_on(); delay(300); dtr_off(); } main() { int puerto; int ok=0,nok=0; int intento; printf ("Autodetección de la tarjeta CT6811\n\n"); /* Bucle de exploración: COM1 y COM2 */ for (puerto=1; puerto<=2; puerto++) { printf ("Explorando puerto COM%u\n",puerto); abrir_puerto_serie(puerto); enviar_car('J'); delay(100); ok=0; nok=0; /* Abrir sesion con el puerto */

/* Enviar un carácter cualquiera */

/* Se realizan 3 intentos por cada puerto */

36

Control de la CT6811 desde el PC

for (intento=1; intento<=3; intento++) { printf ("Intento %u",intento); reset(); /* Se hace un reset y se espera la señal de BREAK */

} printf ("\n"); if (ok==3) printf ("Tarjeta CT6811 detectada en COM%u\n\n",puerto); cerrar_puerto_serie(); } return 0; /* Cerrar sesión con el puerto */

if (wait_break(5)==0) { /* Timeout */ printf ("....Timeout\n"); nok++; } else { printf ("....OK!\n"); ok++; }

}

37

Control de la CT6811 desde el PC

7.- LA CONEXION DE LA CT6811 Y EL PC
7.1.- INTRODUCCION La conexión entre la CT6811 y el PC se ha ido describiendo a lo largo de todo el capítulo. En esta parte se pretende recapitular toda la información. El objetivo es mostrar qué señales de la norma RS-232 son las utilizadas para controlar la CT6811, cómo se realiza el reset, qué problemas aparecen... 7.2.- LAS SEÑALES DE INTERCONEXION Para la conexión el PC con la tarjeta CT6811 se utilizan 4 señales: 3 señales destinadas a comunicaciones serie asíncronas, TX, RX y GND, y una señal de la norma RS-232: DTR. Esta última señal se utiliza para realizar un RESET de la CT6811. En la figura 15 se muestran las 4 señales que intervienen en la interconexión. El cable empleado por tanto debe ser de 4 hilos, uno para cada señal. Por eso se utiliza un cable de teléfono de 4 hilos. 7.3.- LA SEÑAL DE RESET
TX PC RX DTR GND TX RX Reset GND CT6811

Figura 15: Señales de interconexion entre la CT6811 y el PC

Para que la tarjeta CT6811 funcione al estar conectada al PC la señal DTR debe estar desactivada. El efecto de activar DTR es el mismo que el de dejar apretado el botón de reset de la CT6811: la señal de reset del 68HC11 queda a nivel bajo y el micro queda ‘congelado’. Lo más importante es saber que para el correcto funcionamiento la señal DTR debe estar a desactivada. Esto es importante porque al trabajar con la mayoría de los terminales de comunicaciones la señal DTR permanece activa, con lo que la tarjeta CT6811 permanecerá congelada. En la figura 16 se muestra el cronograma de la señal DTR para realizar un reset correcto de la CT6811. La señal DTR se debe encontrar a OFF para el normal funcionamiento de la CT6811. Para realizar un reset se pone a nivel alto durante un tiempo mínimo de 200ms. Transurrido este tiempo se vuelve a desactivar DTR. El reset ya se ha realizado.
SEÑAL DTR ON

OFF

200ms como mínimo

Figura 16: Cronograma de la señal DTR para realizar un reset 7.4.- LOS PROBLEMAS DE INTERCONEXION La norma RS-232C especifica que para el intercambio de datos entre un modem y un PC la señal DTR debe estar activa. Al trabajar con la CT6811 es justo al contrario: para intercambiar datos con ella DTR debe estar desactivada. Esto conlleva una serie de pequeños problemas a la hora de utilizar la CT6811 con muchos de los programas terminales comerciales: Telix, Procom, terminal del windows, así como con los programas terminales de Linux: Seyon, minicom... En todos estos programas la señal DTR está activa siempre, por lo que la conexión con la CT6811 es imposible. Para solucionar esto es necesario eliminar del cable de interconexión entre el PC y la CT6811 el hilo que lleva la señal de DTR. Al quitar DTR todos los programas de comunicaciones anteriores funcionarán correctamente sin embargo se pierde la posibilidad del reset software. Cada vez que se quiera cargar un programa en la CT6811 habrá que pulsar el botón de reset.

8.- CONCLUSIONES

38

Control de la CT6811 desde el PC

En este capítulo se ha partido de los conceptos básicos de comunicaciones serie asícnronas, norma RS232, UART... y se ha llegado a la realización de la librería SERIE.C que nos permite intercambiar caracteres con la tarjeta CT6811. Una vez comprendido el funcionamiento de esta librería y sabiendo manejar las rutinas de interfaz, el usuario puede hacer ‘abstracción’ de todos los conceptos de bajo nivel (Registros de la Uart, bits de Start, Bits de paridad...) y centrarse sólo en el intercambio de caracteres con la CT6811. Ahora para realizar un buen programa de comunicaciones con la CT6811 se puede partir directamente de las rutinas de la libreria SERIE.C. Por ello, a partir de ahora el lector está situado en un ‘nivel superior’ de abstracción. La tarjeta CT6811 se puede ver ahora como una caja negra que intercambia caracteres con nuestros programas. El nivel abstracto que se ha alcanzado se va a denominar nivel de intercambio de caracteres. En el siguiente capítulo, partiendo del intercambio de datos se va a desarrollar una librería que permitirá enviar y ejecutar programas en la ram interna del 68HC11. Se entrará por tanto en un nuevo nivel de abstracción: el nivel de carga de programas en RAM interna. En este nivel nuestros programas llamarán a una función que directamente cargará en la CT6811 el programa especificado. Se podrá por ejemplo cargar un programa, después otro, y otro.... En este nivel nuestros programas verán la CT6811 como algo que además de ser capaz de intercambiar caracteres es capaz de ejecutar los programas que le indiquemos. En el nivel de cliente, que se desarrollará en el capítulo 4, la CT6811 se verá como un servidor que ofrece una serie de servicios. En el PC se realizarán programas cliente que dialogarán con el servidor. En este nivel se encuentran los programas del tipo CT293 y CTDIALOG. Existe un nivel de RED en el que desde el PC se ‘ve’ un sistema formado por varios servidores diferentes conectados en red. Los programas del PC podrán dialogar con cualquiera de los servidores y los deberán coordinar para realizar la tarea deseada. Este último nivel no se trata en este libro.

39

Control de la CT6811 desde el PC

CAPITULO 2:

CARGA DE PROGRAMAS EN LA TARJETA CT6811

1.- INTRODUCCION
El objetivo de este capítulo es el de comprender cómo se cargan los programas en la RAM interna del 68HC11. Si el lector sólo está interesado en saber cargar programas puede saltar directamente a la sección 5 en donde se explica la libreria BOOTSTRP.C En la sección 2 se explica cómo es el formato S19 de motorola. Es necesario leer esta sección si se quiere comprender cómo se usa la libreria S19.C En la sección 3 se habla sobre la libreria S19.C, diseñada para leer de una forma simple los archivos en formato S19. La sección 4 se dedica al modo de arranque BOOTSTRAP del 68HC11. Este modo de arranque es muy importante y su comprensión es vitual para el resto del libro. Finalmente, en la sección 5 se presenta la librería BOOTSTRP.C y se muestran programas de ejemplo para manejarla.

40

Control de la CT6811 desde el PC

2.- EL FORMATO S19 DE MOTOROLA
2.1.- Introducción El formato S19 sirve para representar de una forma clara y ordenada los datos y el código máquina que componen un programa escrito para el 68HC11. Los archivos que utilizan este formato tienen extensión .S19. Se trata por tanto de archivos que contienen datos y código ejecutable por el microcontrolador 68HC11. Lo interesante de este formato es que está codificado con caracteres ASCII y por tanto se puede editar con cualquier editor de textos ASCII, así como imprimirlo, modificarlo etc... En esta sección se pretende describir el formato de estos archivos para poder comprenderlos y desarrollar en la sección 3 una librería para leerlos y poder cargarlos en la tarjeta CT6811. 2.2.- Estructura interna de los ficheros en formato S19 Estos archivos se dividen en registros. Cada registro se corresponde con una línea de texto (Finalizada por CR y LF). Los registros pueden ser de dos tipos diferentes: Registros del tipo 1 ó registros del tipo 9. Según el tipo, los registros tendrán unos campos u otros. Los registros del tipo 1 son los más importantes y están constituidos por 5 campos: tipo de registro, tamaño, dirección, codigo/datos y checksum. TIPO TAMAÑO DIRECCION CODIGO/DATOS CHECKSUM

Cada byte está codificado como dos digitos hexadecimales en ASCII. Así por ejemplo el byte de valor 255 se codifica en ASCII como ‘FF’. Por tanto, por cada byte se necesitan dos digitos ASCII que ocupan dos bytes. Los campos significan lo siguiente: 1. TIPO: Indica el tipo de registro. Para los registros del tipo 1 este campo contendrá los dígitos ASCII ‘S1’. Para los registros del tipo 9 contendrá ‘S9’. 2. TAMAÑO: Indica el número de digitos ASCII que ocupa el registro completo sin contar el campo de Tipo y el propio campo de tamaño. 3. DIRECCION: Indica la dirección a partir de la cual se encuentra el campo de Código/datos. Las direcciones en el 68HC11 van desde la $0000 hasta la $FFFF. Cada dirección ocupa 2 bytes y como cada byte se codifica con 2 dígitos ASCII este campo ocupa 4 dígitos ASCII. 4. CODIGO/DATOS: Este campo es el más importante y contiene el código máquina y los datos de nuestro programa. Lo mismo que los anteriores, cada byte de código o datos está codificado como dos dígitos ASCII. 5. CHECKSUM: Este campo se utiliza para comprobar que el registro completo es válido. El checksum es un byte (Codificado como dos digitos ASCII) que se obtiene al realizar el complemento a uno de la suma de los bytes correspondiente a los campos de tamaño, dirección y codigo/datos. Si el valor obtenido al calcular el checksum no coincide con el campo de CHECKSUM quiere decir que el registro es incorrecto. Los registros del tipo 9 no contienen el campo de codigo/datos y sirven para indicar el final del fichero S19. TIPO TAMAÑO DIRECCION CHECKSUM

El campo dirección siempre está con el valor ‘0000’. Estos registros siempre van a tener el siguiente aspecto: “S9030000FC”. Su única misión va a ser delimitar el final del archivo.

2.3.- Ejemplos de archivos .S19 A continuación se muestran los archivos LEDP.S19 y LEDPE.S19 correspondientes los dos al mismo programa que hace parpadear el led de la tarjeta CT6811 pero estando el primero compilado para la RAM interna y el segundo para la EEPROM interna.
C:811\CT6811>type ledp.s19 S1190000B610008840B7100018CEFFFF1809188C000026F820EAC0 S9030000FC C:811\CT6811>type ledpe.s19 S119B600B610008840B7100018CE80001809188C000026F820EA88 S9030000FC C:811\CT6811>_

41

Figura 17: Los ficheros LEDP.S19 y LEDPE.S19

Control de la CT6811 desde el PC

Ambos programas están constituidos únicamente por dos registros. En las siguientes tablas se muestran los registros descompuestos en campos: • Programa LEDP.S19 Tipo
S1 S9

Tamaño
19 03

Dirección
0000 0000

Código/Datos
B610008840B7100018CEFFFF1809188C000026F820EA ---------------------------------------------------------------

Checksum
C0 FC

• Programa LEDPE.S19 Tipo
S1 S9

Tamaño
19 03

Dirección
B600 0000

Código/Datos
B610008840B7100018CE80001809188C000026F820EA ---------------------------------------------------------------

Checksum
88 FC

En la figura 18 se muestra el programa SCIHOLA.S19 que contiene 3 registros:
C:811\CT6811>type scihola.s19 S1230000CE10008D0818CE00298D1020F61F2E10FCA62F391F2E80FCA72F3918A600810029 S11E002027068DF0180820F339486F6C6120636F6D6F2065737461732E2E00BD S9030000FC

Dividiendo en campos:
Checksum 29 BD FC

Tipo Tamaño Dirección Código/Datos C:811\CT6811>_ S1 23 0000 CE10008D0818CE00298D1020F61F2E10FCA62F391F2E80FCA72F3918A6008100 Figura 18: El 0020 fichero SCIHOLA.S19 S1 1E 27068DF0180820F339486F6C6120636F6D6F2065737461732E2E00 S9 03 0000 ---------------------------------------------------------------

3.- LIBRERIA S19.C PARA LA GESTION DE ARCHIVOS .S19
3.1.- INTRODUCCION El objetivo de esta libreria es el de poder leer fácilmente los diferentes campos de los registros que componen los archivos en formato S19. Esta librería junto con la librería de comunicaciones serie permite que se puedan leer archivos .S19 y que se puedan enviar a través del puerto serie a la CT6811. El manejo de estas librerías pretende ser lo más simple posible. Los archivos S19 se van a tratar como tipos abstractos de datos. En la librería existen una serie de funciones que nos permiten trabajar con este tipo de datos: abrir ficheros en formato S19, leer los diferentes campos etc... 3.2.- EL INTERFAZ DE LA LIBRERIA S19.C Antes de trabajar con un fichero S19 habrá que abrirlo utilizando la función abrir_s19(). Con las funciones leerdir_s19() y leercod_s19() se pueden leer los campos de dirección y codigo/datos de cualquier registro. Con getnregs19() se obtiene el número de registros totales que componen el archivo S19 especificado y con getnbytes19() el número de bytes que ocupan todos los campos de código/datos. Una vez se ha terminado de trabajar con el archivo S19 habrá que cerrarlo con cerrar_s19(). En todas las funciones se trabaja con un tipo abstracto de datos llamado S19 que se encuentra definido en el fichero S19.H. • int abrir_s19(char *fich,S19 *ss19,int modo); 42

Control de la CT6811 desde el PC

Esta función se utiliza para abrir un fichero en formato S19 con el que trabajar. Los parámetros de entrada son los siguiente: • fich : Nombre del fichero en formato S19. Si se especifica sin extensión por defecto se toma la extensión .S19. Se puede especificar la ruta completa del fichero. • ss19: Variable del tipo S19 que va a ser modificada por la rutina. Si la apertura es correcta esta variable contendrá toda la información del archivo abierto. • modo: Modo de apertura del fichero .S19. Existen dos modos 0 y 1. En el modo 1 se comprueba el checksum de todos los registros del fichero.Si alguno es incorrecto se devolverá un error.. En el modo 0 no se comprueba el checksum. La función devuelve 0 si no ha podido abrir el fichero por existir algún error. En caso de éxito se devuelve un 1. A continuación se muestra un pequeño ejemplo de uso:
S19 f; /* Declarar variable del tipo abstracto S19 */

if (abrir_s19(“LEDP”,&f,1)==0) { printf (“Error al abrir el archivo especificado\n”); exit(0); }

En este ejemplo se abre el archivo LEDP.S19 en el modo 1. Si se produce un error se imprime un mensaje en pantalla. En caso contrario el archivo LEDP.S19 es leído y la información depositada en la variable f del tipo S19. •
void cerrar_s19(S19 ss19);

Dejar de trabajar con el archivo S19 especificado. En el siguiente ejemplo se abre un fichero y luego se cierra:
/* Declarar variable del tipo S19 */

S19 f;

abrir_s19(“LEDP”,&f,0); /* Abrir fichero LEDP.S19 en el modo 0 */ ... ... /* Trabajar con archivo */ ... cerrar_s19(f); /* Cerrar el archivo antes abierto */ • int leerdir_s19(S19 ss19,int nreg, unsigned int *dir);

Leer el campo dirección del fichero especificado. Con el parámetro nreg se especifica el número de registro al que se quiere acceder. En dir se devuelve el campo dirección.
S19 f; unsigned int dir; abrir_s19(“LEDP”,&f,1); leerdir_s19(f,1,&dir); cerrar_s19(f); • /* Abrir fichero S19 */ /* Leer campo dirección del primer registro */

int leercod_s19(S19 ss19,int nreg, unsigned short int *cod, unsigned short int *tam);

Leer el campo codigo/datos del registro especificado. También se devuelve el tamaño de este campo.
S19 f; unsigned int tam; unsigned short int codigo[4); abrir_s19(“LEDP”,&f,1); leercod_s19(f,1,codigo,&tam); cerrar_s19(f);

• • •

unsigned int getnbytes19(S19 ss19);

Devolver el número de bytes de todo el código del fichero S19

especificado.
unsigned int getnregs19(S19 ss19); char *geterrors19();

Devolver el número de registros del fichero S19 especificado.

Devolver la cadena del error producido en la última apertura del fichero S19. Esta función se usa para la presentación en pantalla de los errores.

43

Control de la CT6811 desde el PC

3.3.- IMPLEMENTACION 3.3.1.- El tipo S19 El tipo S19 se utiliza para representar los archivos del tipo S19. Está constituido internamente por una estructura con tres campos: el número de bytes del codigo/datos del archivo, el número de registros del tipo 1 que lo forman y un puntero a la cabeza de una lista que contiene los diferentes registros. En la figura 19 se muestra la estrutura interna del tipo S19.
Nº de Bytes Nº de registros Lista registros Tamaño campo codigo/datos Campo dirección Codigo/datos Siguiente registro REGISTRO 1 Tamaño campo codigo/datos Campo dirección Codigo/datos Siguiente registro REGISTRO 2 Tamaño campo codigo/datos Campo dirección Codigo/datos Siguiente registro REGISTRO N

Figura 19: Estructura interna del tipo S19 Cada registro está constituido por otra estructura con 4 campos: el tamaño del campo codigo/datos, el campo de dirección el campo de código/datos y un puntero al siguiente registro. Todos los registros se encuentran formando una lista encadenada. Al llamar a la función abrir_s19() lo que se hace es leer el archivo .S19 especificado y crear la estructura de la figura 19 con los datos del fichero S19. Las demás funciones de la librería S19.C lo único que hacen es moverse por los diferentes nodos de la lista encadenada y devolver los datos solicitados. 3.3.2.- Rutinas de conversión de datos utilizadas: Libreria ASCBIN.C. Los ficheros S19 se encuentran codificados en ASCII. Por ello es necesario disponer de una serie de funciones que realicen la conversión de ASCII a su correspondiente valor binario (de 1 ó 2 bytes). En la librería ASCBIN.C se encuentran las rutinas que realizan estas conversiones y las inversas. Antes de describir las funciones de la librería ASCBIN.C se va a aclarar la notación Los digitos en ASCII se les asigna el tipo CHARX siendo X el número de digitos ASCII. Así por ejemplo el tipo CHAR1 está constituido por los caracteres ASCII ‘1’,’2’,...,’A’...’F’. El tipo CHAR2 está formado por cadenas de 2 dígitos ASCII: “3F”, “12”, “AB”.... Del tipo CHAR4 son las siguientes cadenas: “FFFF”, “1000”, “0000”, “12DE”.... Finalmente BYTE se refiere a número comprendidos entre 0-255, es decir, número que se representan mediante 2 dígitos hexadecimales y el tipo INT se refiere a números comprendidos entre 0-65525, que son representables mediante 4 dígitos hexadecimales. Las funciones de la librería ASCBIN.C realizan conversiones entre estos tipos. Todas las funciones son enteras y devuelve 0 si no se puede realizar la conversión y 1 si se ha realizado correctamente. • int char1tobyte(char car,unsigned short int *dec); Convertir un dígito ASCII hexadecimal en su número correspondiente (entre 0-15). Ej. unsigned short int x; char1tobyte(‘A’,&x); Se devolvería el valor 10 en x. • int char2tobyte(char *cad,unsigned short int *dec); Convertir una cadena de 2 digitos ASCII hexadecimales en su correspondiente valor entre 0-255. Ej. unsigned short int x; char2tobyte(“FF”,&x);

44

Control de la CT6811 desde el PC

Se devolvería el valor 255 en x. • int char4toint(char *cad,unsigned int *dec); Convertir una cadena de 4 digitos ASCII hexadecimales en su correspondiente valor entre 0-65535. Ej. unsigned int x; char4toint(“123C”,&x); Devolvería en x el número 4668 (que es la representación decimal de 0x123C). • int bytetochar1(unsigned short int byte, char *car); Convertir un número entre 0-15 a su correspondiente dígito ASCII hexadecimal correspondiente. Ej. char cad[2]; bytetochar1(12,cad); Se devuelve la cadena “12” en cad. • int bytetochar2(short num, char *cad); Convertir un numero entre 0-255 a su correspondiente cadena de digitos ASCII hexadecimales. Ej. char cad[3]; bytetochar2(127,cad); Se devuelve en cad la cadena “7F”. • int inttochar4(long num, char *cad); Convertir un número entre 0-65535 en la cadena con digitos ASCII hexadecimales. Ej. char cad[5]; inttochar4(14563,cad); Se devuelve en cad la cadena “38E3”. 3.4.- EJEMPLOS DE UTILIZACION DE LA LIBRERIA S19.C Los ejemplos presentados a continuación para ser probados deben ser incluidos en un PROYECT en el que se encuentren las librerías ASCBIN.C y S19.C. 3.4.1.- Ejemplo 1: Programa leers19.C Este programa abre un archivo .S19 y devuelve una serie de datos: número de bytes, número de registros que lo componen y situación del programa en el mapa de memoria. Para saber si se trata de un programa para la RAM interna, EEPROM o ram externa simplemente se examina el campo dirección del primer registro. Según la dirección se toma una decisión u otra.
/* +--------------------------------------------------------------------------+ ¦ LEERS19 (c) GRUPO J&J. Julio 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Ejemplo de utilizacion de la libreria S19.C. ¦ ¦ ¦ ¦ Este programa lee un archivo en formato .S19 y devuelve el numero de ¦ ¦ bytes que ocupa el código, el numero de registros del tipo 1 y la direc- ¦ ¦ ción de comienzo del codigo/datos. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */

45

Control de la CT6811 desde el PC

#include "stdio.h" /* ---- Librerias del Grupo J&J ---- */ #include "s19.h" main() { S19 mi_s19; char cad[80]; char *caderror; unsigned int dir;

/* /* /* /*

Fichero S19 Nombre del fichero Cadena de error Dirección comienzo

*/ */ */ */

/* ------------------------------- */ /* ---- ABRIR EL FICHERO S19 ----- */ /* --------------------------------*/ printf ("\nNombre fichero: "); gets(cad); if (abrir_s19(cad,&mi_s19,1)==0) { /* Si se ha producido un error */ caderror=geterrors19(); /* Leer la cadena de error */ printf ("Error: %s\n",caderror); /* Imprimir mensaje de error */ return 0; } printf ("\n"); /* -------------------------------------------- */ /* ---- MOSTRAR LA INFORMACION DEL FICHERO ---- */ /* -------------------------------------------- */ printf ("Numero de registros : %u\n",getnregs19(mi_s19)); printf ("Tamaño en Bytes : %u\n",getnbytes19(mi_s19)); leerdir_s19(mi_s19,1,&dir); /* Leer dirección del primer registro */ printf ("Dirección de comienzo: %X\n",dir); printf ("Situación programa : "); if (dir<=0xFF) printf ("RAM INTERNA"); else if (dir<=0xB7FF && dir>=0xB600) printf ("EEPROM INTERNA"); else printf ("RAM EXTERNA"); printf ("\n"); /* ------------------------------- */ /* ---- CERRAR EL FICHERO S19 ---- */ /* ------------------------------- */ cerrar_s19(mi_s19); return 0;

}

3.4.2.- Ejemplo 2: Programa dumps19.C En este ejemplo se abre un archivo S19 y se reproducen los campos de código y dirección en la pantalla. Este programa es importante pues modificándolo un poco se puede envíar el programa al 6811 en vez de volcarlo en pantalla.
/* +--------------------------------------------------------------------------+ ¦ DUMPS19 (c) GRUPO J&J. Julio 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Ejemplo de utilizacion de la libreria S19.C. ¦ ¦ ¦ ¦ Este programa lee un archivo en formato .S19 y vuelca los campos de ¦ ¦ codigo/datos y de dirección. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #include "stdio.h" /* -- Librerias del Grupo J&J -- */ #include "ascbin.h"

46

Control de la CT6811 desde el PC

#include "s19.h" main() { char *caderror; char cad2[5]; unsigned short int codigo[37]; char cad[80]; int i,n; unsigned int dir; unsigned short int tam; unsigned int nreg; S19 mis19; /* Fichero S19 */

/* ------------------------------- */ /* ---- ABRIR EL FICHERO S19 ----- */ /* --------------------------------*/ printf ("\nNombre fichero: "); gets(cad); if (abrir_s19(cad,&mis19,1)==0) { caderror=geterrors19(); printf ("Error: %s\n",caderror); return 0; } /* ---------------------------- */ /* ---- VOLCAR EL FICHERO ---- */ /* ---------------------------- */ nreg=getnregs19(mis19); printf ("\n"); printf ("Reg. Dir. tam. for (i=1; i<=nreg; i++) { leerdir_s19(mis19,i,&dir); leercod_s19(mis19,i,codigo,&tam); printf ("%3u %4X %4u ",i,dir,tam); for (n=0; n<tam; n++) { bytetochar2(codigo[n],cad2); printf ("%s",cad2); } printf ("\n"); } /* ------------------------------- */ /* ---- CERRAR EL FICHERO S19 ---- */ /* ------------------------------- */ cerrar_s19(mis19); return 0;

Codigo/datos\n");

}

3.5.- EL INTERFAZ DE LA LIBRERIA S19UTIL.C La librería S19UTIL.C incluye una serie de utilidades para trabajar con los archivos .S19. La librería S19.C hace que se puedan leer los diferentes campos de los registros S19 de una forma fácila. La libreria S19UTIL.C incorpora una serie de utilidades que van a ser de gran ayuda para la creación de la librería BOOTSTRP.C y muchos programas de utilidad, como el CTMAP. Esta libreria incorpora en su interfaz 4 funciones que se describen con más detaller a continuación: • void s19toramint(S19 f,byte *ramint, byte *ramintoc); Introducir en el array ramint la parte del programa s19 especificado que se encuentra entre las direcciones 0-255. Esta función es de gran utilidad pues permite que los programas para la ram interna queden dentro de un array de 256 bytes. El array ramintoc, denominado matriz de ocupación, debe ser también un array de 256 posiciones. En los elementos de esta matriz se indica con 0 que la posición especificada no contiene código. Con un 1 se indica que sí lo hay.

47

Control de la CT6811 desde el PC

Ejemplo: S19 f; byte ram[256]; byte ramoc[256]; abrir_s19(“LEDP.S19”,&f,1); s19toramint(f,ram,ramoc); cerrar_s19(f); ....... En este ejemplo se abre el archivo LEDP.S19 y se introduce en la matriz ram. Puesto que código del programa LEDP ocupa 22 bytes, después de llamar a la función s19toramint en la matriz ram contendrá en ram[0] el primero byte de LEDP y en ram[21] el último. En las posiciones desde la 22 hasta la 255 se encontrará “basura”. La matriz ramoc se inicializará de la siguiente manera: ramoc[0] contendrá un 1 indicando que en la posición 0 hay código, ramoc[1] contiene 1 ..... ramoc[21] contiene 1, ramoc[22] contiene un 0 indicando que NO HAY código en esa posición, es decir, que es ‘basura’. Las posiciones 22 hasta la 255 de ramoc contendrán 0. • void s19toeeprom(S19 f,byte *eeprom, byte *eepromoc); Esta función es exactamente igual que s19toramint con la salvedad de que en la matriz eeprom se almacenan las posiciones de memoria del archivo S19 contenidas entre las posiciones $B600-$B7FF. La matriz eepromoc es la matriz de ocupación correspondinete. Ambas matriz contienen 512 bytes. • int situacion_progs19(S19 fs19, int *ov); Comprobar la situación del programa especificado. La función devuelve 1 si es para la RAM interna, 2 si es para la EEPROM y 3 si es para la RAM EXTERNA. En los casos 1 y 2, se devuelve ov=1 si se ha producido un overflow, es decir, que el programa no cabe. Se devuelve ov=0 en caso de que sí quepa. Si se utiliza esta función con un programa .S19 para la RAM interna que ocupe 300 bytes se devolverá un 1 indicando que se trata de un programa para la RAM interna pero se activará la variable ov a 1 para indicar que desborda la memoria. Ejemplo: S19 f; int ov; abrir_s19(“LEDP.S19”,&f,1); switch(situacion_progs19(f,&ov)) { case 1 : printf (“RAM interna”); if (ov) printf (“OVERFLOW”); break; case 2: printf (“EEPROM “); if (ov) printf (“OVERFLOW”); break; case 3: printf (“RAM EXTERNA”); break; } • void raminttoc(byte *ramint, char *cadc); Tomar la matriz ramint, que contiene un programa para la ram interna y generar una cadena ASCII que representa la misma matriz pero definida en C. Para comprender esta función lo mejor es ver un ejemplo: #include “S19util.h” char cadc[MAXCADC]; char ram[256]; ..... (Se supone que en la matriz ram se ha metido un programa .S19) ....

48

Control de la CT6811 desde el PC

raminttoc(ram,cadc); printf (“%s”,cadc); Este programa imprimiría en la pantalla lo siguiente, suponiendo que el programa almacenado en la matriz ram haya sido el ledp.S19:
char programint[256]={ 0xB6,0x10,0x00,0x88,0x40,0xB7,0x10,0x00,0x18,0xCE,0xFF,0xFF,0x18,0x09,0x18, 0x8C,0x00,0x00,0x26,0xF8,0x20,0xEA,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,};

¿Que utilidad tiene esta función?. Pues aunque parezca una tonteria tiene una utilidad ENORME. Con esta función es muy fácil realizar un programa que convierta archivos .S19 para la RAM INTERNA en archivos que se pueden incluir directamente en un programa en C. Ir a la sección 3.6 para ver un ejemplo de este programa. 3.6.- EJEMPLO DE MANEJO DE LA LIBRERIA S19UTIL.C A continuación se muestran un ejemplo de manejo de la función raminttoc. El programa VIEWS19C.C tomar un archivo .S19 e imprime en pantalla la matriz correspondiente en C. Es la primera versión del programa S19TOC.C. Para compilarlo es necesario incluir el ejemplo en un PROYECT junto con las librerias S19.C, S19UTIL.C y ASCBIN.C
/* +--------------------------------------------------------------------------+ ¦ VIEWS19C.C (c) GRUPO J&J. AGOSTO 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Programa para convertir archivos .S19 para la RAM INTERNA en su ¦ ¦ representación como una matriz de 256 bytes en C. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #include "stdio.h" /* --- Librerias del Grupo J&J ---- */ #include "s19.h" #include "s19util.h" main() { char cad[80]; S19 mi_s19; char *caderror; byte ramint[256]; byte ramintoc[256]; char cadc[1350]; printf ("\nNombre fichero: "); gets(cad); if (abrir_s19(cad,&mi_s19,1)==0) { caderror=geterrors19(); printf ("Error: %s\n",caderror); return 0; }

/* Si se ha producido un error */ /* Leer la cadena de error */ /* Imprimir mensaje de error */

49

Control de la CT6811 desde el PC

printf ("\n"); s19toramint(mi_s19,ramint,ramintoc); raminttoc(ramint,cadc); printf ("%s",cadc); cerrar_s19(mi_s19); return 0;

}

50

Control de la CT6811 desde el PC

4.- CARGA DE PROGRAMAS: EL PROGRAMA BOOTSTRAP
4.1.- INTRODUCCION El microcontrolador 6811 puede arracar en cuatro modos diferentes: modo bootstrap, modo single chip, modo expanded y modo test. Estos modos de arranque se configuran con los switches 1 y 2 de la tarjeta CT6811. El modo que interesa para cargar programas en el 68HC11 es el modo bootstrap. Este modo se obtiene bajando los switches 1 y 2 de la tarjeta CT6811. Es el modo que hay que emplear para trabajar con la CT6811 tanto en modo entrenador como en modo autónomo. Sólo en el caso de que se expanda la CT6811 con memoria externa y se quiera que el programa arranque desde EPROM externa será necesario configurar el 68HC11 en modo expanded. Una vez que el 68HC11 se encuentra en el modo bootstrap, al realizarse un reset sobre la CT6811, bien apretando el pulsador o bien activando la señal de DTR, el 68HC11 comenzará a ejecutar un programa llamado BOOTSTRAP situado en la memoria ROM interna. Este programa de arranque es el encargado de leer datos a través del puerto, configurar el 68HC11 y cargar programas en la RAM interna. Para poder realizar rutinas de carga de programas en la CT6811 hay que conocer cómo funciona el programa Bootstrap. Si el lector sólo quiere aprender a manejar las librerias para la carga de programas sin preocuparse de los detalles internos de implementación puede saltar directamente a la sección 5: Librería BOOTSTP.C para carga de programas en la RAM interna. 4.2.- EL PROTOCOLO IMPLEMENTADO EN EL BOOTSTRAP Lo primero que hace el programa Bootstrap nada más ejecutarse es enviar una señal de BREAK por la línea serie. Cada vez que la tarjeta CT6811 se enviará esta señal. Este es el principio utilizado para detectar si la tarjeta CT6811 se encuentra conectada en un puerto: se realiza un reset de la CT6811 a través de la señal DTR y se espera a que se reciba la señal de BREAK. Si se recibe es porque se ha ejecutado el programa Bootstrap y la CT6811 se encuentra en ese puerto. Después de enviar la señal de BREAK, el programa Bootstrap se queda esperando a que el PC le envíe un carácter. Previamente el bootstrap ha configurado el 68HC11 para realizar transmisiones serie a la velocidad de 7812 Baudios. Esta velocidad no la soporta exactamente la UART del PC pero sí soporta la velocidad de 7680 baudios, que a todos los efectos sirve que el 68HC11 y el PC se puedan entender con una tasa de error despreciable. Según el código recibido desde el PC el BOOTSTRAP realiza una acción u otra: • • • • Código $FF: El 68HC11 sigue funcionando a la velocidad de 7812 Baudios Código $00 o Break: Saltar al comienzo de la memoria EEPROM Código $55: Saltar a la dirección $0000 Otro Código: El micro se configura para trabajar a 1200 Baudios.

Para cargar programas en la RAM interna habrá que enviar el código $FF (si se quiere transmisitir a 7680Baudios) o cualquier código diferente del $00 y $55 (si se quiere transmitir a 1200 Baudios). En cualquiera de estos dos casos el bootstrap funciona como se describe a continuación: Todo carácter que recibe por la entrada serie lo sitúa en la RAM interna a partir de la dirección $0000 y lo reenvía por el puerto serie, es decir, realiza un eco de todo lo que recibe. Cuando se ha recibido el byte número 256 el Bootstrap salta a la direccion $0000 de la RAM interna para ejecutar el programa recién cargado. El programa del PC deberá enviar secuencialmente los bytes que componen el programa a cargar. Si el programa tiene menos de 256 bytes, que es lo normal, se deberán enviar caracteres cualquiera al 68HC11 hasta completar los 256 bytes enviados.

51

Control de la CT6811 desde el PC

PC
PROGRAMA 1) PC y CT6811 conectadas a través de un cable serie

CT6811

En la figura 20 se muestra gráficamente cómo funciona el protocolo implementado en el Bootstrap. En la figura 21 se ha representado el diagrama de flujo del programa BOOTSTRAP.

Figura 21: Diagrama de flujo

PC
PROGRAMA

RESET

BREAK

PROGRAMA BOOTSRAP CT6811

2) Al realizar un reset, el 68HC11 envía una señal de BREAK Configuración Baudios 68HC11= 7812

PC

CODIGO DE REPUESTA

CT6811

PROGRAMA Enviar BREAK 3) El PC responde enviando un código diferente de $55 y $00

PC
PROGRAMA

Esperar Código BYTE de respuesta 1

CT6811
SI Saltar a la EEPROM

4) El PC envía el byte 1 del programa a cargar
¿Codigo = 00?

PC
PROGRAMA

BYTE 1
NO SI

CT6811
Saltar a la dirección $0000

5) El 68HC11 hace eco del byte 1 ¿Código= $55 ?
NO

PC

BYTE 256 SI
¿Código= $FF?

CT6811
PROGRAMA

6) El PC envía el byte 256 del programa a cargar
NO

PC

Configurar BYTE 256 a 1200 baudios velocidad

CT6811
PROGRAMA

7) El 68HC11 hace eco del byte 256. El programa ha sido cargo y el 68HC11 lo comienza aBucle ejecutar
i= 1 hasta Figura 20: Funcionamiento del protocolo del Bootstrap 256

Esperar byte i

4.3.- LISTADO DEL PROGRAMA ARRANQUE BOOTSTRAP

DE

Enviar byte i

En esta sección se muestra el listado del programa BOOTSTRAP para que el lector profundice si está interesado.

Almacenar byte i

52

NO

¿i = 256?

SI

Saltar a la dirección $0000

Control de la CT6811 desde el PC

; +------------------------------------------------------------------------+ ; ¦ Programa BOOTSRAP. (C) MOTOROLA. ¦ ; +------------------------------------------------------------------------+ ; Offset de los registros de control utilizados PORTD DDRD SPCR BAUD SCCR1 SCCR2 SCSR SCDAT PPROG TEST1 CONFIG EEPSTR EEPEND EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU EQU $08 $09 $28 $2B $2C $2D $2E $2F $3B $3E $3F ; Comienzo de la EEPROM ; Final de la EEPROM

EQU $B600 EQU $B7FF ORG $BF40

begin

LDS #$00FF LDX #$1000 BSET SPCR,X $20 LDAA #$A2 STAA BAUD,X

; Inicializar la pila ; Inicializar X para acceso indexado a registros ; Poner el puerto D en colector abierto ; ; ; ; Establecer velocidad de comunicaciones (Divisor de velocidad 16) Para un cristal de 8MHZ la velocidad configurada es de 7812 Baudios ; Habilitar transmisor y receptor ; Enviar señal de BREAK ; Esperar hasta que se mande bit de start ; Ya no se envían más señales de BREAK ; Esperar a que llegue un carácter ; Leer caracter recibido

LDAA #$0C STAA SCCR2,X BSET SCCR2,X $01 wait

BRSET PORTD,X $01 wait BCLR SCCR2,X $01

waitcar BRCLR SCSR,X $20 waitcar LDAA SCDAT,X BNE nocero JMP $B600 nocero CMPA #$55 BEQ STAR CMPA #$FF BEQ baudok

; Si caracter recibido=$00 o BREAK saltar a la ; memoria EEPROM ; Si caracter recibido=$55, saltar al comienzo de ; la RAM. (Sólo utilizado para pruebas de fabrica) ; Si caracter recibido=$FF, la velocidad de Tx actual ; es correcta.

BSET BAUD,X $33 ; Establecer velocidad de 1200 baudios (Si cristal es ; de 8MHZ baudok LDY #$0000 ; Inicializar puntero waitdat BRCLR SCSR,X $20 waitdat ; Esperar a que se reciba un dato LDAA SCDAT,X ; Leer dato recibido STAA $00,Y ; Almacenar dato en la RAM STAA SCDAT,X ; Hacer eco del dato recibido INY CPY #$0100 ; ¿Se ha alcanzado el final de la RAM? BNE waitdat ; NO --> Leer otro dato STAR JMP $0000 ; Ejecutar el programa cargado

; +------------------------------------------------------------------------+ ; ¦ VECTORES DE INTERRUPCION DEL MODO BOOTSTRAP ¦ ; +------------------------------------------------------------------------+ ORG $BFD4 FDB FDB FDB FDB FDB FDB $00C4 $00C7 $00CA $00CD $00D0 $00D3 ; ; ; ; ; ; SCI SPI Flanco subido en acumulador de pulsos Desbordamiento en acumulador de pulsos Desbordamiento del temporizador Comparador 5

53

Control de la CT6811 desde el PC

FDB FDB FDB FDB FDB FDB FDB FDB FDB FDB FDB FDB FDB FDB FDB END

$00D6 $00D9 $00DC $00DF $00E2 $00E5 $00E8 $00EB $00EE $00F1 $00F4 $00F7 $00FA $00FD #begin

; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

Comparador 4 Comparador 3 Comparador 2 Comparador 1 Capturador 3 Capturador 2 Capturador 1 Interrupción de tiempo real IRQ XIRQ SWI Código de instrucción ilegal Fallo en el sistema COP Monitor del reloj Reset

4.4.- CARGA DE PROGRAMAS EN LA RAM INTERNA

PROGRAMA EN EL PC

Esperar BREAK

En la figura 22 se muestra el diagrama de flujo que debe seguir el programa de carga para la RAM interna. Se utiliza la velocidad de 7680 Baudios. El programa debe esperar a que se reciba la señal de BREAK En la libreria SERIE.C existe la función wait_break que espera a que se reciba esta señal. Después se envía el carácter $FF para dejar configurado el 68HC11 para trabajar a la velocidad de 7812 Baudios. Mediante la librería S19.C se lee el archivo .S19 que se quiere enviar a la RAM interna. Habrá que enviar secuencialmente los bytes de los campos de código del archivo S19. Finalmente si el programa ocupa menos de 256 bytes habrá que completar hasta esa cantidad enviado un byte cualquiera, por ejemplo el byte 0.

Enviar $FF

Bucle i=1 hasta 256

Enviar byte i

Esperar byte i

¿Eco diferente? NO NO

SI

ERROR

¿i =256?

SI Programa cargado

Figura 22: Diagrama de flujo para la carga de programas en la RAM interna

54

Control de la CT6811 desde el PC

5.- LIBRERIA BOOTSTP.C PARA CARGA DE PROGRAMAS EN LA RAM INTERNA
5.1.- INTRODUCCION La librería BOOTSTRP.C permite al usuario dialogar con el 68HC11 a través del protocolo implementado en el programa Bootstrap. Las funciones de esta librería van a permitir cargar programas en la RAM interna, saltar a la memoria eeprom, saltar a la memoria RAM y realizar un reset de la tarjeta CT6811. Con esta librería se cierra la etapa de programación a bajo de nivel de la tarjeta CT6811. Una vez que es posible cargar programas en la CT6811, se pueden enviar programas servidores y en el PC se ejecutan los clientes. Desde el punto de vista software es posible ver la CT6811 como una abstracción y no hace falta conocer sus detalles internos. 5.2.- INTERFAZ Para manejar todas las siguientes funciones el puerto serie DEBE ESTAR ABIERTO por medio de la función abrir_puerto_serie() de la libreria SERIE.C. • void resetct6811(); Función para realizar un reset de la tarjeta CT6811. • int okreset(); Hacer un reset de la CT6811. La funcion devuelve 0 si se ha hecho el reset correctamente (se ha recibido la señal de BREAK). Se devuelve 0 si no se recibe el BREAK. • int jump_eeprom(); Función para saltar al comienzo de la memoria EEPROM. Primero se realiza un reset y luego se envía en código necesario para que el 68HC11 salta a la memoria EEPROM. La función devuelve 1 si se ha recibido el BREAK y por tanto se ha saltado a la EEPROM. Devuelve 0 en caso contrario. • int jump_ram(); Función para saltar al comienzo de la memoria RAM. Primero se realiza un reset y después se envía el codigo necesario para saltar a la RAM. Es responsabilidad del usuario que en la RAM del 68HC11 exista previamente algo cargado. La función devuelve 0 en caso de no recibirse la señal de BREAK al realizar el reset. 1 Si ha saltado correctamente a la RAM. • int cargar_ramint(byte *ramint, void (*car)()); Función para cargar un programa en la ram interna de la tarjeta CT6811. Los 256 bytes del programa se toman de la matriz ramint. Cada vez que se ha enviado un byte a la CT6811 se llama a la función car que se pasa como parámetro. Esto permite que el usuario defina lo que quiera en esa función, como por ejemplo imprimir en pantalla algún tipo de carácter o calcular los porcentajes que quedan para terminar de cargar el programa. • int cargars19_ramint(S19 fs19, void (*car)()); Función para cargar un fichero .S19 en la ram interna de la CT6811. El archivo .S19 debe estar previamente abierto llamando a la función abrir_s19(). Cada vez que se ha enviado un byte a la CT6811 se ejecuta la función car para que el usuario implemente alguna acción. • char *getloaderror(); Devolver la cadena de error del último error producido al llamar a la función cargar_ramint() o cargars19_ramint(). 5.3.- IMPLEMENTACION La función más importante es cargar_ramint(). Esta función va recorriendo la matriz del programa y va enviando los bytes. Los bytes se envían con un timeout de tal forma que si ha transcurrido el tiempo especificado y no se ha recibido eco se señaliza error. Por cada byte enviado se espera recibir un eco igual. Si el eco es distinto es que se ha producido un error en la transmisión. La función cargars19_ramint() comprueba si el programa S19 especificado es para la RAM interna. Si no es así se produce un error. Después se introduce el archivo S19 en una matriz de datos, utilizando la función s19toramint() de la librería S19UTIL.C y finalmente se llama a la función cargar_ramint().

55

Control de la CT6811 desde el PC

5.4.- DEPENDENCIAS DE LA LIBRERIA BOOTSTRP.C En la figura 23 se muestran las librerías de las que dependen la librería BOOTSTRP.C. Por ello si que realiza un programa que utilice la librería BOOTSTRP.C será necesario incluir en el ‘PROYECT’ todas las librerías mostradas en la figura 23: SERIE.C, ASCBIN.C, S19.C, S19UTIL.C y BOOTSTRP.C.
SERIE.C ASCBIN.C

S19.C

5.5.- EJEMPLOS 5.5.1.- Ejemplo 1: Programa LEDP.C

S19UTIL.C

Este programa carga directamente en la TARJETA CT6811 el programa del ledp parpadeante, que no se encuentra en un archivo .S19 sino que se BOOTSTRP.C encuentra insertado dentro del propio código en C. Para ello se ha utilzado el programa S19TOC.EXE que se ha Figura 23: Librerías de las que depende la desarrollado con las librerias S19.C y S19UTIL.C. librería BOOTSTRP.C
/* +--------------------------------------------------------------------------+ ¦ LEDP.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Ejemplo de prueba de la libreria BOOTSTRP.C. ¦ ¦ ¦ ¦ Se carga en la CT6811 un programa que hace parpadear el LED. Este ¦ ¦ programa no se toma de un archivo .S19 sino que se encuentra dentro del ¦ ¦ propio código. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #include "stdio.h" #include "serie.h" #include "bootstrp.h" int i; void accion_rxcar() { } void accion_break() { } void prueba() { static int n=0; static int p=0; if (n==24) { n=0; p+=10; printf ("%3u%%\b\b\b\b",p); } n++; i++;

}

main() { /* Matriz que contiene el programa a enviar a la CT6811 */ unsigned short int programint[256]={ 0xB6,0x10,0x00,0x88,0x40,0xB7,0x10,0x00,0x18,0xCE,0x5F,0xFF,0x18,0x09,0x18, 0x8C,0x00,0x00,0x26,0xF8,0x20,0xEA,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,

56

Control de la CT6811 desde el PC

0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, 0x00,}; abrir_puerto_serie(COM2); /* Abrir sesion con puerto serie COM2 */

printf ("\nCargando programa: "); i=1; if (cargar_ramint(programint,prueba)==0) { printf ("\nError: %s",getloaderror()); cerrar_puerto_serie(); return 0; }; printf ("\nOK!\n"); cerrar_puerto_serie(); return 0; /* Cerrar la sesión con el COM2 */

} 5.5.2.- Ejemplo 2: Programa RAMINT.C Este programa carga el programa LEDP.S19 en la RAM INTERNA de la tarjeta CT6811. Es una versión cutre del conocido programa DOWNMCU.
/* +--------------------------------------------------------------------------+ ¦ RAMINT.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Versión 1.0. ¦ ¦ ¦ ¦ Ejemplo de prueba de la libreria BOOTSTRP.C. Se carga en la ram ¦ ¦ interna de la CT6811 el fichero LEDP.S19. ¦ ¦ ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #include "stdio.h" #include "serie.h" #include "bootstrp.h" typedef unsigned short int byte; typedef unsigned int uint; int i=0; int n=0; void accion_rxcar() { } void accion_break() { } void carga() {

57

Control de la CT6811 desde el PC

if (n==16 || i==255) { n=0; printf ("_"); } i++; n++;

}

main() { char cad[80]; char *caderror; S19 fs19; if (abrir_s19("ledp.s19",&fs19,1)==0) { caderror=(char *)geterrors19(); printf ("Error: %s\n",caderror); return 0; } printf ("\n"); abrir_puerto_serie(COM2); printf ("0% 50% 100%\n"); printf ("________________\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); if (cargars19_ramint(fs19,carga)==0) { printf ("\nError: %s",getloaderror()); } else { printf (" OK!!\n\n"); } cerrar_s19(fs19); cerrar_puerto_serie(); } return 0;

6.- CONCLUSIONES
A partir de este capítulo el lector debe ser capaz de comunicarse perfectamente con la CT6811 así como cargar cualquier programa en la RAM interna. Ahora mismo se el lector podría realizar sus propios programas servidores en el 68HC11 y realizar los correspondientes clientes en el PC. En el siguiente capítulo se va a mostrar un modelo de servidor utilizado con exito en las aplicaciones CTDIALOG y CT293 del Grupo J&J: El servidor CTSERVER. Lo que tiene que quedar claro es que con las herramientas presentadas hasta ahora se puede construir cualquier cliente y cualquier servidor. Lo único que es necesario es crearse un protocolo de diálogo entre el cliente y el servidor. En el capítulo 3 se muestra cómo es el protocolo empleado para el diálogo con el CTSERVER.

58

Control de la CT6811 desde el PC

CAPITULO 3:

EL SERVIDOR DE PROPOSITO GENERAL CTSERVER

1.- INTRODUCCION
Este capítulo está dedicado por entero al servidor CTSERVER. En la sección 2 se presenta el servidor CTSERVER: características, servicios ofrecidos, limitaciones... La sección 3 se dedica al protocolo JJLINK, que especifica cómo se tienen que pasar los parámetros entre cliente y servidor y en qué orden debe efectuarse el diálogo. Todo servidor está constituido por dos partes totalmente independientes: Servicios y protocolo de comunicación con el cliente. Puede haber dos clientes que ofrezcan los mismos servicios pero que el protocolo de comunicación sea diferentes. Igualmente dos servidores pueden utilizar el mismo protocolo pero implementar servicios diferentes. La sección 4 muestra cómo se ha implementado el CTSERVER para el 68HC11. Esta sección puede ser saltada. Finalmente en la sección 5 se introduce la librería CTSERVER.C con la que se puede dialogar con el CTSERVER. En la misma sección se desarrollan ejemplos para probar las distintas funciones de diálogo. El objetivo del capítulo es que se comprenda el funcionamiento del CTSERVER y se puedan apreciar sus ventajas e inconvenientes. Para realizar programas clientes el lector deberá acudir al capítulo 4: Programación de clientes para el CTSERVER. Obsérvese la figura de esta página. En ella está representada el PC y la tarjeta CT6811. En el PC se encuentra el programa cliente que quiere hacer uso de CLIENTE SERVICIOS los servicios ofrecidos por el servidor CTSERVER. Para llamar a los servicios del servidor el cliente se comunica con la libreria CTSERVER.C. Esta libreria convierte la información en tramas del protocolo JJLINK que son CTSERVER.C CTSERVER enviadas al servidor CTSERVER que se encuentra en la CT6811. El CTSERVER lee las tramas, ejecuta el servicio solicitado y devuelve las tramas de respuesta si se requieren. El programa cliente no sabe nada del Protocolo J LINK protocolo JJLINK. Simplemente ‘ve’ el servidor como si fuese un conjunto de funciones situadas en una libreria en C. En este capítulo se explican todos los mecanismos intermedios que existen en el diálogo entre cliente y servidor, que son totalmente transparentes al cliente. El capítulo 4 hace abstracción de los mecanismos de comunicacione y se centra en la programación de CLIENTES. El protocolo JJLINK ya no interesa a los clientes porque ‘no lo ven’. Sólo interesan los servicios que puede ofrecer el CTSERVER.
PC CT6811

59

Control de la CT6811 desde el PC

2.- EL SERVIDOR CTSERVER
2.1.- INTRODUCCION El servidor CTSERVER es un servidor de propósito general diseñado para caber en los 256 bytes de la RAM interna del 68HC11. A través de este servidor es posible escribir bytes en cualquier posición del mapa de memoria, leer posiciones de memoria, grabar bytes en la memoria EEPROM interna y ejecutar programas. El CTSERVER se ha diseñado para cumplir las siguientes características: • Máxima versatilidad: Haciendo que los servicios ofrecidos sean lo más generalistas posibles • Minima ocupación: El servidor debe poder ser cargado dentro de los 256 bytes de la RAM interna del 68HC11 • Fácil utilización: El acceso a los servicios debe ser lo más simple posible. 2.2.- LOS SERVICIOS OFRECIDOS Existen 5 servicios ofrecidos por el servidor CTSERVER a los clientes situados en el PC. 1. Transferencia de bloques de datos del PC al 68HC11. Con este servicio se pueden enviar bloques de hasta 64Kb desde el PC a cualquier dirección dentro del mapa de memoria del 68HC11. Este servicio será utilizado para implementar servicios de carga de programas, escritura en determinadas posiciones de memoria y escritura en los recursos del 68HC11: puertos, comparadores, SPI.... 2. Transferencia de bloques de datos del 68HC11 al PC. Este servicio permite volcar bloques de datos de hasta 64Kb situados en cualquier posición de memoria del 68HC11 hacia el PC. Con él podremos ver el contenido de la memoria y realizar lecturas de los recursos del 68HC11: Lectura de puertos, SPI, conversor A/D.... 3. Grabación de un bloque en la memoria EEPROM. Posibilidad de grabación de bytes aislados o bloques en la EEPROM interna del 68HC11. 4. Saltar a la dirección de memoria especificada. El 68HC11 comenzará a ejecutar el codigo situado en la dirección especificada. Este servicio será usado para ejecutar un programa previamente cargado en la RAM externa. 5. Control de supervivencia. Se trata de un servicio de comprobación del Servidor. Si sigue vivo, el servidor responderá enviando un código determinado. Este servicio se utilizará para comprobar si existe o no conexión entre el cliente y el servidor. En ninguno de los servicios ofrecidos se comprueba la consistencia de los datos enviados. Por ejemplo, en el caso del servicio de grabación de un bloque en la memoria EEPROM el servidor NO comprueba si la dirección especificada es EEPROM o no. De la misma manera a la hora de escribir un bloque en una posición de memoria no se comprueba si se trata de memoria RAM o de si existe RAM o no en esa posición. Todos los controles de consistencia de los datos los debe realizar el programa cliente. 2.3.- PARAMETROS DE LOS DIFERENTES SERVICIOS Para llevar a cabo los servicios solicitados, el CTSERVER precisa de unos parámetros de entrada para cada servicio y se devuelven unos parámetros de salida. En esta sección se muestran los servicios del servidor como si fuesen funciones de C. Lo que se va a hacer es representar esos parametros de entrada y salida utilizando la notación del lenguaje C. El cliente situado en el PC lo que ‘vería’ sería precisamente estas funciones. • void ctsendblok(unsigned int dir, unsigned int tam, unsigned short int *buff); Servicio de transferencia de bloques desde el PC a la tarjeta CT6811. Se envían los datos contenidos en el buffer de datos buff que tienen un tamaño tam. El bloque se debe cargar a partir de la dirección dir. • int ctreceiveblock(unsigned int dir, unsigned int tam, unsigned short int *buff); Servicio de transferencia de bloques desde la CT6811 hasta el PC. Se solicita que se envíen los datos que comienzan a partir de la dirección dir y que ocupan un tamaño tam. Estos datos son depositados en el buffer buff. Igual que con la función anterior, se devuelve 0 en caso de que el servidor no responda y 1 en caso de éxito.

60

Control de la CT6811 desde el PC

• int ctsave_eeprom(unsigned int dir, unsigned int tam, usigned short int *buff); Servicio de grabación de datos en la memoria EEPROM. Los datos indicados en buff y que ocupan un tamaño tam se envían a la memoria EEPROM a partir de la dirección dir. El servidor en ningún momento comprueba si la dirección pasada como parámetro es una dirección cualquiera o pertenece a la RAM interna. La función devuelve 1 en caso de éxito o 0 si el servidor ha fallado o se ha producido algún tipo de error. • void ctexecute(unsigned int dir); Servicio de ejecución. El servidor salta a la dirección indicada y se comienza a ejecutar el código que allí se encuentre. Puesto que el servidor se deja de ejecutar, la conexión se pierde. • int ctalive(); Servicio de control de supervivencia. Se devuelve 1 en caso de que el servidor responda. Se devuelve 0 en caso de que no responda o responda mal. Estas funciones se han utilizado para definir los servicios ofrecidos por el CTSERVER. Las funciones que utilizarán los clientes, que se encuentran en la librería CTSERVER.C, son muy parecidas a estas pero difieren en algunos parámetros. 2.4.- LIMITACIONES DEL SERVIDOR Con el servidor CTSERVER en posible acceder a cualquier recurso del 68HC11. Sin embargo el acceso a los recursos está limitado por la transferencia de información a través de las comunicaciones serie. Por ejemplo, es posible leer datos del conversor A/D. Pero para realizar una lectura hay que llevar a cabo varias transferencias de bytes por el puerto serie, lo que enlentece el proceso y disminuye la frecuencia de muestreo vista por el cliente. Por ello este servidor no es apto para cualquier tipo de aplicaciones. Si se quiere realizar una aplicación de muestreo de señales, será mejor diseñarse un servidor específico. Para aplicaciones que tengan que ver con los sentidos humanos, como por ejemplo observar en la pantalla los cambios que se producen en un sensor conectado a un puerto del 68HC11, el servidor es totalmente adecuado, pues el usuario no apreciará ningún retraso desde que el sensor cambia hasta que es visualizado el cambio en la pantalla del PC. (Ejemplo: Programa CT293.EXE).

3.- EL PROTOCOLO JJLINK
3.1.- INTRODUCCION El servidor CTSERVER dispone de 5 servicios, especificados detalladamente en la sección 2.3. En cada llamada a los servicios hay que pasar unos parámetros y recibir las repuestas en otros parametros. El protocolo JJLINK tiene dos misiones. Por un lado en este protocolo se especifican cómo son las distintas tramas de información intercambiadas entre el cliente y el servidor: tamaño, codificación de los parámetros... y por otro lado se especifica cuál es la secuencia de intercambio de la información: quién habla primero, quién después ... 3.2.- FORMATO DE LAS TRAMAS DE DIALOGO Los parámetros que se envían a los diferentes servicios son de 1 byte y 2 bytes. Los parámetros de 2 bytes (direcciónes y tamaños de los bloques) se codifican en las tramas enviando primero el byte de menos peso y luego el de mayor peso. Cada servicio del servidor queda identificado por un número de servicio. En la siguiente tabla se muestran los número de servicio asocidos a cada servicio. Se han elegido estos número ‘tan extraños’ (¿Por qué no empezar a numerar desde el servicio 0?), porque se corresponden con los caracteres ASCII ‘A’,’B’,’C’,’D’ y ‘E’. SERVICIO Transferencia de bloques del 68HC11 al PC Transferencia de bloques del PC al 68HC11 Salto y ejecución Control de supervivencia Grabación de un bloque en la memoria EEPROM Nº DE SERVICIO 65 66 67 68 69

61

Control de la CT6811 desde el PC

En todas las tramas que se envían desde el PC al CTSERVER solicitando un servicio, el orden de los parámetros pasado dentro de la trama es el siguiente: Primero se envía el byte que indica el número de servicio solicitado. Después el parámetro dirección de 2 bytes, primero el byte menos significativo y después el más significativo, a continuación el parámetro tamaño, también de 2 bytes y finalmente el bloque de información. Seguidamente se muestran las tramas de solicitud de servicio para cada uno de los diferentes servicios. Se han ordenado desde el servicio que requiere más parámetros hasta el que menos: • Transferencia de bloques del PC al 68HC11 66 1 byte DIRECCION 2 Bytes TAMAÑO BLOQUE 2 bytes BLOQUE Maximo 64Kbytes

• Grabación de un bloque en la memoria EEPROM 69 1 byte DIRECCION 2 Bytes TAMAÑO BLOQUE 2 bytes BLOQUE Maximo 64Kbytes

• Transferencia de bloques del 68HC11 al PC 65 1 byte DIRECCION 2 Bytes TAMAÑO BLOQUE 2 Bytes

• Salto y ejecución 67 1 byte DIRECCION 2 Bytes

• Control de supervivencia 68 1 byte

62

Control de la CT6811 desde el PC

3.3.- DIALOGOS CLIENTE-SERVIDOR El cliente al solicitar un servicio envía una trama de solicitud de servicio, cuyos formatos se han descrito en la sección 3.2. El servidor NO DEVUELVE un mensaje de confirmación indicando que ha recibido la trama de solicitud. En todo momento, el cliente puede llamar al servicio de control de supervivencia para determinar si el SERVIDOR sigue vivo o no. Según el servicio solicitado el diálogo será alguno de los siguientes: • Control de supervivencia CLIENTE: Envio de la trama de solicitud. SERVIDOR: Responde enviando el carácter ‘J’ • Transferencia de bloques del 68HC11 al PC CLIENTE: Trama de solicitud. SERVIDOR: Responde enviando el bloque de datos solicitado. • Grabación de un bloque en la memoria EEPROM. CLIENTE: Trama de solicitud SERVIDOR: Hace eco de todos los bytes del bloque enviado Este servicio es un poco diferente. El cliente no envía de golpe todo el bloque que quiere grabar en la EEPROM sino que cada vez que se envía un byte se espera a recibir un eco. Se realiza así para garantizar la sincronización necesaria para poder grabar el byte en la EEPROM. La escritura de un byte en la EEPROM lleva mucho más tiempo que escribir en la RAM normal. • Transferencia de bloques del PC al 68HC11 CLIENTE: Trama de solicitud • Salto y ejecución CLIENTE: Trama de solicitud. En estos dos últimos servicios el servidor no responde. Por ello si el cliente quiere obtener una confirmación de que el servidor sigue vivo deberá realizar una llamada al servicio de control de supervivencia. 3.4.- DISCUSION SOBRE EL PROTOCOLO JJLINK El protocolo JJLINK no es ni mucho menos el ideal de protocolo. Se trata de un protoclo NO FIABLE pues no se realiza detección de errores ni se garantiza que las tramas lleguen a su destino. Al pensar en el protocolo JJLINK hay que tener presente que el objetivo fundamental de diseño fue que entrase en los 256bytes de la RAM interna del 68HC11. Además del protocolo, debe entrar en la memoria la implementación de los 5 servicios. La implementación se ha realizado sin interrupcines para ahorrar memoria. Por ello el PC no puede enviar ‘tramas a lo loco’ pues de lo contrario al servidor no le daria tiempo a leer todos los caracteres. Al no realizar recepción por interrupciones, si llega un carácter machaca al recibido anteriormente, independientemente o no de que le hubiese dado tiempo al servidor a procesarlo. Para las aplicaciones para las que ha sido pensado el CTSERVER estos inconvenientes realmente no lo son. Si se requiere su utilización para alguna aplicación en la que la perdida de datos sea crucial, entonces será recomendable diseñarse otro servidor y otro protocolo.

4.- IMPLEMENTACION DEL CTSERVER
4.1.- DIAGRAMAS DE FLUJO 4.1.1.- Bucle principal

63

Control de la CT6811 desde el PC

INICIALIZACION

Esperar caracter de identificacion de servicio

Según el carácter recibido

¿A? NO

SI

Servicio de transferencia de bloques del 68HC11 al PC

SI ¿B? NO SI Transferencia de bloques del PC al 68HC11

¿C? NO ¿D? NO ¿E? NO

Salto y ejecución

SI

Control de supervivencia

SI

Grabación de bloques en la memoria EEPROM

Después de realizar la inicialización, el CTSERVER espera a recibir alguna de las tramas de solicitud de servicio. Estas tramas siempre comienzan por el código de identificación del servicio. Según el carácter recibido, que indentifica el servicio, se llamará a una u otra subrutina. Figura 24: Diagrama de flujo del bucle principal del CTSERVER En caso de que llegue un código de identificación de servicio erróneo, es ignorado. 4.1.2.- Transferencia de bloques del 68HC11 al PC El diagrama de flujo se muestra en la figura 25. Primero se lee la dirección de comienzo del bloque a enviar y después el tamaño del bloque. Mientras queden caracteres por enviar se repite el mismo bucle: se accede a la dirección especificada, se lee el byte y se envía por el puerto serie. Una vez enviado todo el bloque se vuelve al bucle principal.

64

Control de la CT6811 desde el PC

4.1.3.- Tansferencia de bloques del PC al 68HC11 La implementación de este servicio es similar al servicio anterior. Después de leer el tamaño del bloque, se entra en un bucle en el que se realizan las siguientes operaciones: leer un byte del bloque y situarlo a partir de la dirección especificada. 4.1.4.- Grabación de bloques en la memoria EEPROM Este servicio es exactamente igual que el anterior con la salvedad de que los bytes no se sitúan en memoria RAM sino que hay que grabarlos en la memoria EEPROM. Esta operación es algo más compleja que situar un simple byte en una posición de memoria.
Leer dirección inicio

Leer dirección inicio

Leer tamaño del bloque

Mientras tamaño sea mayor de cero NO leer dirección SI

Enviar byte

Leer tamaño del bloque

4.1.5.- Servicio de salto y ejecución

Decrementar tamaño

Mientras tamaño sea mayor de cero NO Leer byte SI

Grabar byte en EEPROM

En este servicio se lee la dirección Apuntar a la siguiente enviada y se dirección salta directamente a ella. Por ello se Fin del servicio pierde NO SE 25: Diagrama de flujo del VUELVE al Figura bucle principal servicio de tarnsferencia de bloques del del CTSERVER 68HC11 al PC 4.1.6.- Control de supervivencia

Hacer eco del byte recibido

Este servicio es el más fácil de implementar. Unicamente hay que enviar como respuesta el carácter ‘J’.

Apuntar a la siguiente dirección

Fin del servicio

Figura 26: Diagrama de flujo del servicio de grabación de bloques en la memoria EEPROM

65

Control de la CT6811 desde el PC

4.2.- LISTADO DEL SERVIDOR CTSERVER A continuación se reproduce enteramente el programa CTSERVER.ASM.
; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; +------------------------------------------------------------------------+ ¦ CTSERVER. (C) GRUPO J&J. Noviembre 1996 ¦ ¦------------------------------------------------------------------------¦ ¦ Programa servidor para la conexión de tarjetas entrenadoras basadas ¦ ¦ en el 68HC11 con el PC. ¦ ¦ ¦ ¦ El servidor ofrece una serie de servicios a un programa cliente ¦ ¦ que se encuentra en el PC y que se comunica con el servidor a través ¦ ¦ del puerto serie. ¦ ¦ ¦ ¦ Los servicios que ofrece el servidor son los siguientes: ¦ ¦ - Transferencia de bloques de hasta 64K del micro al PC ¦ ¦ - Transferencia de bloques de hasta 64K del PC al micro ¦ ¦ - Grabación de datos en la memoria EEPROM ¦ ¦ - Ejecución de programas. (JUMP & EXECUTE) ¦ ¦ - Control de supervivencia ¦ ¦ ¦ ¦ Para la solicitud de estos servicios existe un protocolo. En primer ¦ ¦ lugar el cliente (programa en el PC) debe enviar un código que ¦ ¦ identifique el servicio que quiere obtener del servidor. Según el ¦ ¦ servicio solicitado, el intercambio de datos entre cliente y servidor ¦ ¦ sera de una forma u otra. ¦ ¦ ¦ +------------------------------------------------------------------------+ +------------------------------------------------------------------------+ ¦ DESCRIPCION DETALLADA DE LOS SERVICIOS DEL SERVIDOR ¦ ¦------------------------------------------------------------------------¦ ¦ CONTROL DE SUPERVIVENCIA: ¦ ¦ ¦ ¦ Este servicio lo utiliza el cliente para saber si el servidor ¦ ¦ se está ejecutando en el micro o no. Si el servidor no responde se ¦ ¦ puede deber a una de las siguientes causas: ¦ ¦ * Placa entrenadora no alimentada ¦ ¦ * Placa entrenadora no conectada al PC ¦ ¦ * El micro está ejecutando un programa que no es el CTSERVER ¦ ¦ * Se ha 'reseteado' el micro ¦ ¦ ¦ ¦ El protocolo entre cliente y servidor es el siguiente: ¦ ¦ ¦ ¦ Cliente: Envía código 68 para solicitar control de supervivencia ¦ ¦ Servidor: Responde enviando el carácter 'J' ¦ ¦------------------------------------------------------------------------¦ ¦ TRANSFERENCIA DE BLOQUES DEL MICRO AL PC ¦ ¦ ¦ ¦ Con este servicio se pueden volcar bloques de longitud comprendida ¦ ¦ entre 1 y 65535 bytes desde el micro al PC. ¦ ¦ ¦ ¦ Protocolo entre cliente y servidor: ¦ ¦ ¦ ¦ Cliente: envía código 65 para solicitar servicio de transferencia ¦ ¦ Cliente: Envía la dirección de comienzo del bloque que se quiere ¦ ¦ Transferir. Las direcciones ocupan 2 bytes. Primero se ¦ ¦ envia el byte de menor peso y luego el de mayor peso. ¦ ¦ Cliente: Envía la longitud del bloque a transferir. Este valor es de ¦ ¦ 2 bytes. Primero se envía el de menor peso y luego el de ¦ ¦ mayor peso. ¦ ¦ Servidor: Envía el bloque de memoria solicitado. Se envían tantos ¦ ¦ bytes como sea el tamaño del bloque solicitado (entre 1 y ¦ ¦ 65535 bytes) ¦ ¦------------------------------------------------------------------------¦ ¦ TRANSFERENCIA DE BLOQUES DEL PC AL MICRO ¦ ¦ ¦ ¦ Con este servicio se pueden volcar bloques de longitud comprendida ¦ ¦ entre 1 y 65535 bytes desde el PC al micro. ¦ ¦ ¦ ¦ Protocolo entre cliente y servidor: ¦ ¦ ¦ ¦ Cliente: Envía código 66 para solicitar servico de transferencia ¦ ¦ Cliente: Envía la dirección de comienzo donde situar el bloque. Las ¦ ¦ direcciones ocupan 2 bytes. Primero se envía el byte de ¦ ¦ menor peso y luego el de mayor peso. ¦ ¦ Cliente: Envía la longitud del bloque a transferir. Este valor es de ¦

66

Control de la CT6811 desde el PC

; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;

¦ 2 bytes. Primero se envía el de menor peso y luego el de ¦ ¦ mayor peso. ¦ ¦ Cliente: Comienza a enviar el bloque de datos. El servidor irá ¦ ¦ leyendo los bytes y los irá colocando a partir de la ¦ ¦ dirección de memoria indicada. ¦ ¦------------------------------------------------------------------------¦ ¦ GRABACION DE UN BLOQUE EN LA MEMORIA EEPROM ¦ ¦ ¦ ¦ Con este servicio se pueden grabar datos en la memoria EEPROM ¦ ¦ interna del micro. Antes de grabar un dato en una dirección de la ¦ ¦ memoria EEPROM se borra primero dicha posición y después se escribe ¦ ¦ el dato. ¦ ¦ ¦ ¦ Protocolo entre cliente y servidor: ¦ ¦ ¦ ¦ Cliente: Envía código 69 para solicitar servicio de grabación EEPROM ¦ ¦ Cliente: Envía la dirección de comienzo donde grabar el bloque. Es ¦ ¦ responsabilidad del programa cliente enviar una dirección ¦ ¦ que se corresponda con la EEPROM. Primero se envía el ¦ ¦ byte de menor peso y luego el de mayor peso. ¦ ¦ Cliente: Envía la longitud del bloque a grabar. Primero se envía el ¦ ¦ byte de menor peso y luego el de mayor peso. ¦ ¦ Cliente: Comienza a enviar el bloque de datos. El cliente debe tener ¦ ¦ en cuenta que grabar un byte en la eeprom lleva más tiempo ¦ ¦ que guardarlo en RAM. Por ello el SERVIDOR HACE ECO de ¦ ¦ cada byte enviado una vez grabado. ¦ ¦------------------------------------------------------------------------¦ ¦ EJECUCION DE PROGRAMAS ¦ ¦ ¦ ¦ Con este servicio se salta a programas que se encuentren en otras ¦ ¦ direcciones de memoria. Hay que tener en cuenta que cuando se ejecute ¦ ¦ otro programa, el servidor se deja de ejecutar y por tanto el programa¦ ¦ cliente perderá la conexión con el servidor. ¦ ¦ ¦ ¦ Protocolo entre cliente y servidor: ¦ ¦ ¦ ¦ Cliente: Envía código 67 para solicitar servicio de ejecución ¦ ¦ Cliente: Envía la dirección a la que saltar. Primero se envía el ¦ ¦ byte de menor peso y luego el de mayor peso. ¦ ¦ ¦ +------------------------------------------------------------------------+ ORG $0000

;---- Definición de los servicios TMPC EQU 65 ; Transferencia de bloques del micro al PC TPCM EQU 66 ; Transferencia de bloques del PC al micro JEXE EQU 67 ; Servicio JUMP & EXECUTE ALIVE EQU 68 ; Servicio de supervivencia GBEE EQU 69 ; Grabar un bloque en la EEPROM OKALIVE EQU 'J' ; --- Registros del SCI BAUD SCCR1 SCCR2 SCSR SCDR equ equ equ equ equ $2B $2C $2D $2E $2F -----

; ---- REGISTROS DE LA EEPROM PPROG CONFIG equ $103b equ $103f BRA inicializar dirini DW longbloq DW codigo DB 0 0 0

; Control de programacion de la EEPROM ; Registro de Configuración del MICRO

; Dirección inicio ; Tamaño bloque ; Código de servicio solicitado

inicializar LDX #$1000 LDY #$FFFF wait DEY

67

Control de la CT6811 desde el PC

CPY #0 BNE wait LDAA #$30 STAA BAUD,X begin_server LDX #$1000 BSR leer_car STAA codigo CMPA #ALIVE BEQ serv_alive CMPA #JEXE BEQ serv_jexec CMPA #TMPC BEQ serv_tmpc CMPA #TPCM BEQ serv_tpcm CMPA #GBEE BEQ serv_gbee JMP begin_server serv_alive ; Servicio de supervivencia LDAA #OKALIVE BSR enviar JMP begin_server serv_tpcm INC codigo BRA comun serv_tmpc CLR codigo comun BSR leer_dir STY dirini BSR leer_dir ; Leer tamaño del bloque STY longbloq ; Almacenar tamaño bloque bucle_tmpc CPY #0 ; ¿Bloque transferido? BEQ fin_ser_tmpc ; si -> fin LDY dirini LDAA codigo TSTA BNE tpcm ; Transferencia micro->pc LDAA 0,Y ; Leer un byte BSR enviar ; Enviar byte BRA sigue ; Transferencia pc->micro tpcm BSR leer_car ; Leer un byte STAA 0,Y ; Almacenar byte sigue INY STY dirini LDY longbloq DEY STY longbloq BRA bucle_tmpc fin_ser_tmpc JMP begin_server serv_jexec BSR leer_dir STY dirini JMP 0,Y ; ; ; ; ; ; ; Apuntar siguiente dirección ; Un byte menos queda por enviar ; Configurar velocidad transmisión a 9600 baudios ; Comienzo del servidor ; Esperar a que el PC solicite un servicio ; Almacenar temporalmente el código ; ¿Servicio de supervivencia? ; ¿Servicio JUMP & EXECUTE? ; ¿Servicio Transmisión micro-PC? ; ¿Servicio Transmision PC-micro? ; ¿Servicio Grabacion de EEPROM?

; Leer dirección de inicio ; Almacenar dirección inicio

*********************************************************** * Rutina para leer un carácter del puerto serie (SCI) * * La rutina espera hasta que llega algún carácter * * ENTRADAS: Ninguna * * SALIDAS: El acumulador A contiene el carácter recibido * *********************************************************** ; Esperar hasta que llegue un carácter

leer_car BRCLR SCSR,X $10 leer_car LDAA SCDR,X RTS

68

Control de la CT6811 desde el PC

; ; ; ; ;

****************************************************************** * Enviar un carácter por el puerto serie (SCI). * * ENTRADAS: El acumulador A contiene el carácter a enviar * * SALIDAS: Ninguna * ****************************************************************** BRCLR SCSR,X $80 enviar STAA SCDR,X RTS

enviar

; ************ ; * LEER_DIR * ; ************ ; Leer una dirección por el SCI y devolverla en el registro Y ; ** ENTRADAS: Ninguna ; ** SALIDAS: El registro Y contiene la dirección leida leer_dir PSHA ; Salvar registros utilizados PSHB BSR leer_car TAB BSR leer_car XGDY PULB PULA RTS serv_gbee BSR STY BSR STY bucle_gbee CPY BEQ LDY LDAB STAB STAB LDAB STAB BSR LDAB STAB BSR BSR STAA LDAB STAB BSR leer_dir dirini leer_dir longbloq #0 fin_ser_gbee dirini #$16 PPROG $0,Y #$17 PPROG PAUSA ; ; ; ; Leer byte bajo de la dirección B contiene byte bajo dirección A contiene byte alto de la dirección Ahora Y contiene la dirección leida

; Recuperar registros utilizados

; Leer tamaño del bloque ; Almacenar tamaño bloque ; ¿Bloque transferido? ; si -> fin

; ponemos modo borrar BYTE ; ; Activo programación ; Realizo la pausa de 10ms

#$02 PPROG ; ponemos EELAT=1 (EEPGM=0) leer_car enviar $0,Y #$03 ; PPROG ; Activo programación EEPGM=1 PAUSA ; Realizo la pausa de 10ms

(EELAT=1)

INY STY dirini ; Apuntar siguiente dirección LDY longbloq DEY STY longbloq ; Un byte menos queda por enviar BRA bucle_gbee fin_ser_gbee CLR PPROG ; Desactivo programacion y pongo modo READ JMP begin_server ;************************************************* ;* Realizar una pausa prefijada * ;************************************************* PAUSA SIGUE PSHY LDY #$0D10 DEY CPY #$00 BNE SIGUE PULY RTS

69

Control de la CT6811 desde el PC

END

70

Control de la CT6811 desde el PC

5.- LA LIBRERIA CTSERVER.C
5.1.- INTRODUCCION En la librería CTSERVER.C se han implementado las llamadas a los servicios proporcionados por el servidor de propósito general CTSERVER. Además de los 5 servicios ofrecidos por el servidor, se han implementado dos nuevos servicios: lectura y escritura de un byte. Estos servicios son una particularización de los servicios de lectura y escritura de bloques de datos cuando el tamaño del bloque es 1. En esta librería no se controla si la conexión está o no establecida con el servidor. Si un servicio solicita unos datos y el servidor no responde, se retorna un valor indicando que el servidor no responde, pero todo el control de errores se deja al programa cliente. Por ello, en el capítulo 4 se va a desarrollar la libreria CTCLIENT.C que haciendo uso de la libreria CTSERVER.C implementa la gestión de errores. El interfaz ofrecido por la librería CTSERVER.C no es el interfaz final que verá el usuarioprogramador. El interfaz definitivo es el de la libreria CTCLIENT.C. 5.2.- INTERFAZ En las definiciones de las funciones de interfaz se van a usar mucho los siguientes tipos:
typedef unsigned short int byte; typedef unsigned int uint;

Las funciones de interfaz son las siguientes: • int ctalive(); Servicio de control de supervivencia. Se comprueba si existe conexión con el CTSERVER. En caso afirmativo la función devuelve un 1. Si no hay conexión se devuelve un 0.
• void ctsend_block(uint dir, uint tam, byte *buff, void (*sendcar)());

Servicio de transferencia de bloques desde el PC al 68HC11. Con esta función se envía un bloque de datos desde el PC a la memoria del 68HC11. El bloque a enviar se encuentra almacenado en el buffer apuntado por buff . Tam determina el tamaño del bloque a enviar y dir la dirección en donde se debe situar el primer byte del bloque. Cada vez que se envía un byte se llama a la función apuntada por sendcar. Si no se quiere hacer nada especial simplemente se pasa la función nula, que no hace nada. Esta función no devuelve ningún valor. Al terminar de ejecutarse no tenemos la certeza de que el servidor siga funcionando. Puede haber ocurrido que en la mita de la transmisión del bloque el 68HC11 se haya reseteado y se haya perdido la conexión. Por ello después de utilizar esta función es conveniente comprobar si la conexión sigue establecida.

Servicio de transferencia de bloques desde el 68HC11 hasta el PC. Con esta función se envían datos desde la memoria del 68HC11 hasta el PC. Igual que con la función anterior, el bloque recibido se almacena en el buffer buff. Se quiere recibir el bloque que tiene un tamaño tam y que comienza a partir de la dirección dir en el mapa de memoria del 68HC11. Cada vez que se recibe un byte se llama a la función apuntada por reccar. Esta función sí devuelve el estado del servidor. Al llamar a la función se solicita un bloque de datos al CTSERVER. Si el CTSERVER devuelve el bloque completo, entonces es que la conexión no se ha perdido. Si por el contrario sólo se recibe parte del bloque, se puede asegurar que la conexión se ha perdido. El estado de la conexión es retornado por esta función: 1 si la conexión sigue establecida y por tanto el bloque se ha recibido totalmente y 0 si la conexión se ha perdido y el bloque no se ha recibido. • int ctsave_eeprom(uint dir,uint tam,byte *buff, void (*eeprcar)()); Servicio de grabación de bloques en la EEPROM. Esta función es idéntica a la función ctsend_block con la salvedad de que los datos se almacenan en la EEPROM en vez de en la RAM. La única diferencia es que con esta función sí se comprueba el estado del servidor puesto que todos los bytes enviados al servidor para ser grabados en la EEPROM son devueltos por éste como confirmación. La función devuelve 1 si se ha grabado el bloque completo en la EEPROM y 0 en caso de que se haya perdido la conexión. • void ctexecute(uint dir); Servicio de salto y ejecución. El servidor CTSERVER salta a ejecutar el código situado a partir de la dirección dir. El usuario es el responsable de que en dicha dirección exista código que ejecutar.

int ctreceive_block(uint dir, uint tam, byte *buff, void (*reccar)());

71

Control de la CT6811 desde el PC

• void ctsend_byte(uint dir, byte valor); Servicio de grabación de un byte. El byte indicado se sitúa en la dirección dir del mapa de memoria del 68HC11. Este servicio no está soportado directamente por el CTSERVER sino que está implementado llamando al servicio de transferencia de bloques del PC al micro utilizando un tamaño de bloque de 1 byte. • int ctreceive_byte(uint dir,byte *valor); Servicio de lectura de un byte. Leer un byte de la posición de memoria indicada. Igual que el servicio anterior, no está soportado directamente por el servidor CTSERVER. En su implementación se llama al servicio de transferencia de bloques del 68HC11 al PC con un tamaño de bloque igual a 1. La función devuelve 1 si el byte se ha recibido con éxito, 0 en caso de que se haya perdido la conexión. 5.3.- IMPLEMENTACION La implementación de la libreria CTSERVER.C no tiene mayor complicación. Hay que entender el protocolo JJLINK y enviar las tramas correspondientes. Sin embargo, al enviar las tramas hay que tener cuidado. No se puede enviar la trama ‘a lo bestia’. Entre carácter y carácter es necesario esperar un cierto tiempo para que al CTSERVER le de tiempo a procesar el carácter previamente recibido. Si la implementación del CTSERVER hubiese sido mediante interrupciones, esto no haría falta y se ganaría en eficiencia, pero debido a la limitación de memoria a 256bytes no ha sido posible la implementación mediante interrupciones.
Trama del protocolo JJLINK Caracteres

Trama real Caracteres

Tiempo entre caracteres

Por tanto, las tramas que realmente se Figura 27: Tramas del protocolo JJLINK y las tramas envían tienen una longitud efectiva mayor que realmente implementadas. las especificadas en el protocolo JJLINK. Esto hace que se esté desperdiciando ancho de banda. Esto habrá que tenerlo en cuenta a la hora de implementar servidores cuando la memoria disponible sea mayor: es mucho más eficiente implementar la recepción de caracteres mediante interrupciones. 5.4.- DEPENDENCIAS La librería CTSERVER.C sólo hace uso de la libreria SERIE.C 5.5.- EJEMPLOS DE UTILIZACION El objetivo de estos ejemplos es mostrar cómo se usan las funciones implementadas en la librería CTSERVER.C. Estos ejemplos no se deben tomar como base para la programación de clientes. Simplemente sirven para aprender a usar las funciones de llamadas al CTSERVER y explorar las posibilidades que se pueden obtener al controlar la CT6811 desde el PC. Todos los ejemplos se han realizado para el COM2. Si se quiere utilizar el COM1 habrá que llamar a la función abrir_puerto_serie con el parámetro 1 en vez de con el 2. 5.5.1.- Ejemplo 1: Programa CHECKCON.C Ejemplo de funcionamiento de la función ctalive(). El programa se queda monitorizando la conexión con el servidor. Mientras la conexión exista se hará girar un ‘palito’. Si la conexión se pierde se imprime un mensaje en pantalla.
/* +--------------------------------------------------------------------------+ ¦ CHECKCON.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Ejemplo de prueba de las rutinas de llamada a los servicios del CTSERVER ¦ ¦ ¦

72

Control de la CT6811 desde el PC

¦ Este programa comprueba constantemente la conexión con el servidor. ¦ ¦ El estado de la conexión se visualiza en pantalla. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #include "stdio.h" #include "dos.h" #include "conio.h" /* Librerias del Grupo J&J */ #include "serie.h" #include "ctserver.h" void accion_rxcar() { } void accion_break() { } main() { char fase[5]={'-','\','|','/'}; int pf=0; abrir_puerto_serie(2); baudios(9600); printf ("\nComprobación de la conexión con el servidor\n"); printf ("Pulse una tecla para finalizar\n\n"); printf ("CONEXION: "); do { if (ctalive()) { /* Si la conexión existe */

} else { }

printf ("\b\b\b\b\b\b\b\b\b\b\bEstablecida"); printf (" %c\b\b",fase[pf]); /* Dibujar el palito que gira */ pf=(pf+1) % 4; delay(50); /* Pausa para ver como gira el palito */ /* Conexión perdida */ Perdida");

printf ("\b\b\b\b\b\b\b\b\b\b\b

} while (!kbhit()); printf ("\n\n"); cerrar_puerto_serie(); return 0;

}

5.5.2.- Ejemplo 2: Programa CTLEDP.C Este ejemplo se trata del famolo led parpadeante pero esta vez ejecutado a través del servidor. Se trata de un ejemplo de la función ctsend_byte. Para hacer parpadear el led simplemente se envía a la dirección $1000 (PUERTO A) valores con el bit 6 complementado.
/* +--------------------------------------------------------------------------+ ¦ CTLEDP.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Ejemplo de prueba de las rutinas de llamada a los servicios del CTSERVER ¦ ¦ ¦ ¦ Este programa hace parpadear el LED de la CT6811. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #include "stdio.h" #include "dos.h"

73

Control de la CT6811 desde el PC

#include "conio.h" /* Librerias del Grupo J&J */ #include "serie.h" #include "ctserver.h" #define PORTA 0x1000 /* Dirección del puerto A en la CT6811 */

void accion_rxcar() { } void accion_break() { } main() { byte valor; abrir_puerto_serie(2); baudios(9600); printf ("\n\nHacer parpadear el LED de la CT6811 a través del CTSERVER\n"); printf ("Pulse una tecla para finalizar...\n\n"); if (!ctalive()) { printf("No hay conexión"); cerrar_puerto_serie(); return 0; } valor=0xFF; do { ctsend_byte(PORTA,valor); valor^=0x40; delay(100); } while (!kbhit()); getch(); ctsend_byte(PORTA,0x00); /* Apagar el led */ cerrar_puerto_serie(); return 0;

}

Obsérvese que la conexión con el servidor sólo se comprueba al principio. Si se pierde durante el transcurso del programa no se hace nada al respecto. 5.5.3.- Ejemplo 3: Programa CTDUMPEE.C Este ejemplo trata de ilustrar el funcionamiento de la función ctreceive_block. Se vuelca el contenido de la memoria EEPROM en ASCII.
/* +--------------------------------------------------------------------------+ ¦ CTDUMPEE.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Programa ejemplo de las llamadas a los servicios del CTSERVER. ¦ ¦ ¦ ¦ El programa vuelca en contenido de la EEPROM en ASCII ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ typedef unsigned short int byte; #define EEPROM 0xB600 #include "stdio.h" #include "conio.h" /* Librerias utilizadas del Grupo J&J */

74

Control de la CT6811 desde el PC

#include "serie.h" #include "ctserver.h" void accion_rxcar() { } void accion_break() { } void car_rec() { static nbyte=0; nbyte++; if (nbyte==8) { printf ("_"); nbyte=0; }

}

main() { byte eeprom[512]; byte ok; int x,y; int i; abrir_puerto_serie(2); baudios(9600); printf ("\nPrograma para volcar la EEPROM en ASCII\n\n"); if (!ctalive()) { printf("No hay conexión"); cerrar_puerto_serie(); return 0; } printf ("Leyendo: "); x=wherex(); y=wherey(); printf ("................................................................"); gotoxy(x,y); /* ---- Leer memoria eeprom y meterla en el buffer ----- */ ok=ctreceive_block(EEPROM,512,eeprom,car_rec); if (ok) { /* Si no ha habido errores .. Imprimir */ printf ("\n\n"); x=0; for (i=0; i<512; i++) { if (eeprom[i]>31 && eeprom[i]<127) printf ("%c",eeprom[i]); else printf ("."); x++; if (x==32) { x=0; printf ("\n"); } } printf ("\nVolcado completado\n"); } else printf ("\nConexión PERDIDA\n\n"); cerrar_puerto_serie(); return 0;

}

5.5.4.- Ejemplo 4: Programa CTSAVEE.C En este ejemplo se hace uso de la función ctsave_eeprom para grabar una cadena en la memoria EEPROM.
/* +--------------------------------------------------------------------------+

75

Control de la CT6811 desde el PC

¦ CTSAVEEE.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Programa de prueba de las rutinas de llamada a los servicios del CTSERVER¦ ¦ ¦ ¦ El programa graba una cadena en la memoria EEPROM. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #define EEPROM 0xB600 typedef unsigned short int byte; #include "stdio.h" #include "conio.h" #include "serie.h" #include "ctserver.h" void accion_rxcar() { } void accion_break() { } void car_grab() { static nbyte=0; nbyte++; if (nbyte==8) { printf ("_"); nbyte=0; }

}

main() { byte cad[512]; byte ok; byte tam; int i; int x,y; abrir_puerto_serie(2); baudios(9600); if (!ctalive()) { printf("No hay conexión"); cerrar_puerto_serie(); return 0; } printf ("\nGrabar una cadena en la memoria EEPROM\n\n"); printf ("Introduzca la cadena a grabar (Maximo 512 bytes!!):\n "); i=0; tam=0; /*---------- Leer la cadena y meterla en cad ----------*/ do { cad[i]=getch(); if (cad[i]==13) { cad[i]=0; break; } printf("%c",cad[i]); i++; tam++; } while (i<512); if (tam>0) { printf ("\n\nGrabando: "); x=wherex();

76

Control de la CT6811 desde el PC

y=wherey(); printf ("................................................................"); gotoxy(x,y); ok=ctsave_eeprom(EEPROM,tam,cad,car_grab); if (ok) { printf ("\nGrabación completada"); } else printf ("\nConexion perdida");

}

}

cerrar_puerto_serie(); return 0;

Lo más destacable de este programa es que NO SE HA EMPLEADO la función GETS para leer la cadena introducida por el usuario. Esto es debido a que GETS sólo trabaja con caracteres de tipo CHAR, mientras que el bloque que hay que pasar a la función ctsave_eeprom es del tipo byte. 5.5.5.- Ejemplo 5: Programa CTGOEE.C
/* +--------------------------------------------------------------------------+ ¦ CTGOEE.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Programa de prueba de las rutinas de llamada a los servicios del CTSERVER¦ ¦ ¦ ¦ El programa salta a la EEPROM. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ void accion_rxcar() { } void accion_break() { } main() { abrir_puerto_serie(2); baudios(9600); if (!ctalive()) { printf("No hay conexión"); cerrar_puerto_serie(); return; } ctexecute(0xB600); printf ("\nEjecutando programa de la EEPROM\n"); cerrar_puerto_serie(); return;

Finalmente este ejemplo hace uso de la función ctexecute para saltar a la memoria EEPROM.

}

6.- CONCLUSIONES
En todo sistema cliente-servidor existen dos partes: Los servicios ofrecidos por el servidor y el protocolo de comunicación. Este capítulo ha intentado explicar con detalle cúales son los servicios ofrecidos por el CTSERVER y cómo es el protocolo de comunicación. Conocer el mecanismo de comunicación es interesante para poder evaluar cuál va a ser el retardo de las aplicaciones clientes. No obstante, lo importante para la programación de los clientes son los servicios ofrecidos por el servidor. A partir del capítulo 4 el lector se situará en un nuevo nivel de abstracción. Ahora ya no hay que saber nada de comunicaciones serie ni de los protocolos utilizados. La tarjeta CT6811 se ve ahora

77

Control de la CT6811 desde el PC

como un mapa de memoria en el que podemos realizar lecturas y escrituras de bloques de datos. ¡Es posible acceder a todos los recursos del 68HC11 desde el PC!

78

Control de la CT6811 desde el PC

CAPITULO 4:

PROGRAMACION DE CLIENTES PARA EL CTSERVER

1.- INTRODUCCION
En este último capítulo se aborda el apasionante mundo de los clientes. En la sección 2 se presenta la libreria CTCLIENT.C con la que se programarán los clientes. En la sección 3 se muestras todas las herramientas para la programación: el fichero R6811PC.H con las definiciones de todos los registros del 68HC11 y la libreria CTSERVER.LIB que contiene todas las librerías de este libro en un único archivo. En la sección 4 se habla brevemente sobre la estructura de los clientes y las diferentes políticas empleadas en caso de pérdida de conexión. En esta misma sección se presentan unos ejemplos cortitos de clientes. Con las herramientas presentadas en la sección 3 y los ejemplos de la sección 4 el lector debe ser capaz de realizar cualquier tipo de cliente.

79

Control de la CT6811 desde el PC

2.- LA LIBRERIA CTCLIENT.C
2.1.- INTRODUCCION La programación definitiva de clientes para el CTSERVER se realiza utilizando las funciones proporcionadas por la libreria CTCLIENT.C. Estas funciones proporcionan los mismos servicios que las de la libreria CTSERVER.C: escritura y lectura de bytes, escritura y lectura de bloques, control de supervivencia y grabación de bloques en la EEPROM. La diferencia se encuentra en la gestión de los errores y en el interfaz final. En la libreria CTSERVER.C la gestión de los errores se deja por completo al usuario, lo cual no es apropiado porque conduciría a la realización de clientes poco robustos frente a pérdidas de conexión con el servidor. En la libreria CTCLIENT.C los errores son autocontrolados, de tal forma que si se ha perdido la conexión no se realizan llamadas a los servicios del CTSERVER. El interfaz es mucho más cómo y está basado en instrucciones LOAD/STORE, de forma similar a las instrucciones LDAA y STAA del 68HC11. 2.2.- INTERFAZ En interfaz completo de la librería se presenta a continuación. Los tipos byte e unit se definen como a continuación: typedef unsigned short int byte; typedef uint unsigned int; • int load(byte *valor, uint dir) Leer un byte de la posición de memoria especificada del mapa de memoria del 68HC11 e introducirlo en valor. Se devuelve 0 si el CTSERVER no ha respondido, 1 en caso de éxito. • int load_block(byte *buff, uint tam, uint dir, void (*reccar)()) Leer un bloque de datos del mapa de memoria del 68HC11. El bloque solicitado tiene un tamaño tam y comienza a partir de la dirección dir. Es almacenado en el buffer buff. Cada vez que se recibe un carácter de datos se llama a la función reccar. La función devuelve 0 en caso de que no haya conexión con el CTSERVER. Devuelve un 1 si la operación se ha realizado con éxito. • void store(byte valor, uint dir) Almacenar el byte especificado en la dirección de memoria dir del mapa de memoria del 68HC11. • void store_block(byte *buff, uint tam, uint dir, void (*sendcar)()) Almacenar el bloque apuntado por buff, que tiene un tamaño tam a partir de la dirección dir del mapa de memoria del 68HC11. Cada vez que se envía un byte de datos se llama al procedimiento sendcar.

Grabar un bloque de datos en la memoria EEPROM. El bloque se encuentra en el buffer buff, que tiene un tamaño tam. Se sitúa a partir de la dirección indicada. Cada vez que se envía un byte se llama al procedimiento eepromcar. • int check_conexion() Comprobar si hay conexión con el CTSERVER. Para ello se llama al servicio de control de supervivencia del CTSERVER. Se devuelve 0 si el servidor no ha respondido y 1 si sí ha respondido. • void execute(uint dir) Llamar al servicio de salto y ejecución del CTSERVER. • int hay_conexion() Determinar el estado de la última conexión con el CTSERVER. Esta función NO dialoga con el CTSERVER por lo que no consume ancho de banda.

int store_block_eeprom(byte *buff, uint tam, uint dir, void (*eeprcar)())

2.3.- CLASIFICACION DE LAS FUNCIONES 2.3.1.- Funciones que hacen uso de los servicios del CTSERVER.

80

Control de la CT6811 desde el PC

Todas las funciones de la librería CTCLIENT.C excepto hay_conexion() llaman a los servicios del servidor CTSERVER por lo que ‘gastan’ ancho de banda en las comunicaciones. Por ello para hacer clientes rápidos es conveniente utilizar mucho la función hay_conexión() que no realiza ninguna llamada al servidor CTSERVER. 2.3.1.- Funciones de bucle abierto y bucle cerrado Se define las funciones de bucle cerrado como aquellas funciones que llaman a algún servicio del CTSERVER y que ESPERAN RESPUESTA. Al tener que esperar para recibir una respuesta del CTSERVER se comprueba si la conexión se ha perdido o no. Las funciones de bucle cerrado son: • • • • Load() Load_block() Store_block_eeprom() check_conexion()

Se definen las funciones de bucle abierto como aquellas que llaman a algún servicio del CTSERVER pero no esperan ninguna respuesta. Por ello con estas funciones no se sabe si la conexión se ha perdido o no y por tanto si el servicio solicitado se ha ejecutado o no. Las funciones de bucle abierto son: • Store() • Store_block() • execute() 2.4.- IMPLEMENTACION En el módulo CTCLIENT.C existe una variable global llamada conexión en la que se guarda el estado de la última conexión con el servidor. Esta variable se actualiza cada vez que se hace uso de alguna de las funciones de bucle cerrado. En todas las funciones que hacen llamadas a servicios del CTSERVER, lo primero que se hace es comprobar el estado de la variable conexion. Si no había conexión en la anterior llamada al CTSERVER entonces no se realiza una nueva llamada. La única función que se comunica con el CTSERVER independientemente del estado de la conexión es check_conexion(). Precisamente es esta función la que se utiliza para comprobar si la conexión ha sido o no reestablecida en caso de pérdida. La función hay_conexión() devuelve el estado de la variable conexion. 2.5.- DEPENDENCIAS La librería CTCLIENT.C sólo necesita de las librerías SERIE.C y CTSERVER.C para poder compilarse adecuadamente.
SERIE.C

CTSERVER.C

CTCLIENT.C

Figura 28: Dependencias de la librería CTCLIENT.C 2.6.- EJEMPLOS Los programas de ejemplo que se muestran a continuación están preparados para trabajar con el COM 2. Han sido probados realizando un proyect en el que se incluye la libreria CTSERVER.LIB que se comenta en la sección 4. También se incluye el archivo R6811PC.H con todas las definiciones de los registro del 68HC11. Este fichero es tratado en la sección 4. 2.6.1.- Ejemplo 1: Programa KLEDP.C

81

Control de la CT6811 desde el PC

Se trata del típico ejemplo del led parpadeante. Para ello se utiliza la función store(). Como esta función es de bucle abierto, es necesario llamar a check_conexion para asegurarse que la conexión sigue existiendo.
/* +--------------------------------------------------------------------------+ ¦ KLEDP.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Ejemplo de prueba de la libreria CTCLIENT.C ¦ ¦ ¦ ¦ Se trata del tipico programa del ledp parpadeante. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ typedef unsigned short int byte; #include "conio.h" #include "stdio.h" #include "dos.h" /* Librerias del Grupo J&J */ #include "R6811pc.h" #include "serie.h" #include "ctclient.h" void accion_rxcar() { } void accion_break() { } main() { byte valor; char c; abrir_puerto_serie(2); baudios(9600); clrscr(); check_conexion(); /* Comprobar conexion */ if (!hay_conexion()) { printf("No hay conexión"); cerrar_puerto_serie(); return 0; } valor=0xFF; /* ---- Bucle principal --- */ do { store(valor,PORTA); /* Mandar valor al puerto A */ delay(100); valor^=0xFF; check_conexion(); /* Comprobar conexión */ } while (hay_conexion() && !kbhit()); if (kbhit()) getch(); if (!hay_conexion()) printf ("Conexión perdida.."); cerrar_puerto_serie(); return 0;

}

2.6.2.- Ejemplo 2: Programa KAD.C

82

Control de la CT6811 desde el PC

En este ejemplo se muestran en la pantalla 4 canales analógicos de entrada del 68HC11. Para leer los 4 canales se utiliza la función load_block. Se podría leer cada canal individualmente con la instrucción load pero al utilizar la instrucción anterior ahorramos ancho de banda.
/* +--------------------------------------------------------------------------+ ¦ KAD.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Ejemplo de prueba de la libreria CTCLIENT.C ¦ ¦ ¦ ¦ Se muestran en pantalla 4 canales analógicos de entrada del 68HC11. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ typedef unsigned short int byte; #include "conio.h" #include "stdio.h" #include "R6811pc.h" #include "serie.h" #include "ctclient.h" void accion_rxcar() { } void accion_break() { } void nada() { } main() { byte ad; byte muestra[4]; byte x; byte error; int i; abrir_puerto_serie(2); baudios(9600); clrscr(); check_conexion(); /* Comprobar conexion */ if (!hay_conexion()) { printf("No hay conexión"); cerrar_puerto_serie(); return 0; } store(0x80,OPTION); /* Encender conversor */ store(0x30,ADCTL); do { load(&ad,ADCTL); /* Comprobar si conversion realizada */ if ((ad & 0x80)==0x80) { load_block(muestra,4,ADR1,nada); /* Leer muestras */ /* --- Dibujar vumetros --- */ for (i=0; i<4; i++) { gotoxy(5,5+i*2); muestra[i]=muestra[i]&0xFF; printf ("%2X",muestra[i]); muestra[i]=muestra[i]>>2; gotoxy(5,6+i*2); for (x=0; x<64; x++) { if (x<=muestra[i]) printf ("_"); else printf ("."); } }

83

Control de la CT6811 desde el PC

} } while (!kbhit() && hay_conexion()); if (!hay_conexion()) printf ("\nConexion perdida..."); getch(); cerrar_puerto_serie(); return 0;

}

84

Control de la CT6811 desde el PC

3.- HERRAMIENTAS DE PROGRAMACION DE CLIENTES
3.1.- INTRODUCCION Para la fácil realización de clientes se utilizan dos herramientas fundamentales: El fichero R6811PC.H y la librería CTSERVER.LIB. Con estas dos herramientas el usuario podrá realizar cualquier tipo de clientes. 3.2.- EL FICHERO R6811PC.H Para hacer más fácil la programación de clientes, en el fichero R6811PC.H se encuentran las definiciones de todos los registros de control del 68HC11, con los mismos nombres que tienen asignados en el manual de referencia de motorola. A continuación se muestra el archivo completo
/* +------------------------------------------------------------------------+ ¦ R6811pc.H (C) GRUPO J&J. AGOSTO 1997 ¦ ¦------------------------------------------------------------------------¦ ¦ Fichero que contiene todos los registros internos del 68HC11. ¦ ¦ Version para el PC. ¦ +------------------------------------------------------------------------+ */ #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define #define PORTA PIOC PORTC PORTB PORTCL DDRC PORTD DDRD PORTE CFORC OC1M OC1D TCNT TIC1 TIC2 TIC3 TOC1 TOC2 TOC3 TOC4 TOC5 TCTL1 TCTL2 TMSK1 TFLG1 TMSK2 TFLG2 PACTL PACNT SPCR SPSR SPDR BAUD SCCR1 SCCR2 SCSR SCDR ADCTL ADR1 ADR2 ADR3 ADR4 OPTION COPRST PPROG HPRIO INIT TEST1 CONFIG 0x1000 0x1002 0x1003 0x1004 0x1005 0x1007 0x1008 0x1009 0x100A 0x100B 0x100C 0x100D 0x100E 0x1010 0x1012 0x1014 0x1016 0x1018 0x101A 0x101C 0x101E 0x1020 0x1021 0x1022 0x1023 0x1024 0x1025 0x1026 0x1027 0x1028 0x1029 0x102A 0x102B 0x102C 0x102D 0x102E 0x102F 0x1030 0x1031 0x1032 0x1033 0x1034 0x1039 0x103A 0x103B 0x103C 0x103D 0x103E 0x103F

3.3.- LA LIBRERIA CTSERVER.LIB

85

Control de la CT6811 desde el PC

3.3.1.- Librerias incluidas en el fichero CTSERVER.LIB En la librería CTSERVER.LIB se han incluido todas las librerias incluidas en este libro: ASCBIN.C, SERIE.C, S19.C, S19UTIL.C, BOOTSTRP.C, CTSERVER.C y CTCLIENT.C. En el fichero CTSERVER.LIB se encuentran los ficheros .OBJ de los programas en C antes mencionados. Para la realización de clientes se debe realizar un PROYECT que incorpore el fichero CTSERVER.LIB y los ficheros .C del usuario. De esta forma, el usuario sólo tienen que conocer el interfaz de la libreria CTSERVER.LIB y no hace falta que conozca la implementación. En la siguiente figura se muestran las dependencias de las distintas librerias .C presentadas a lo largo del libro.
CTSERVER.LIB

SERIE.C

ASCBIN.C

S19.C

CTSERVER.C

S19UTIL.C

CTCLIENT.C

BOOTSTRP.C

Programa cliente del USUARIO

Figura 29: Dependencias de las librerias que componen el fichero CTSERVER.LIB 3.3.2.- Interfaz completo de la libreria CTSERVER.LIB En esta sección se ha recapitulado el interfaz de todas las librerías que componen el fichero CTSERVER.LIB. La mayoría de este interfaz no es “útil” para un programa final del usuario, sino que es utilizado por otras librerías. El usuario sobre todo utilizará el interfaz proporcionado por las librerias CTCLIENT.C , BOOTSTRP.C y S19.C. 1.-LIBRERIA ASCBIN.C: Conversion de datos • • • •
int char1tobyte(char car,unsigned short int *dec); Convertir un digito hexadecimal ASCII en un numero entre 0-15. int char2tobyte(char *cad,unsigned short int *dec); Convertir una cadena de dos digitos hexadecimales ASCII en un numero de 8 bits. int char4toint(char *cad,unsigned int *dec); hexadecimales ASCII en un numero de 16 bits. Convertir una cadena de 4 digitos

int bytetochar1(unsigned short int byte, char *car); Convertir un número entre 0-15 a su correspondiente caracter hexadecimal ASCII.

86

Control de la CT6811 desde el PC

• •

int bytetochar2(short num, char *cad); Convertir un numero de 8 bits en 2 caracteres ASCII hexadecimales int inttochar4(long num, char *cad); Convertir un numero de 16 bits en 4 caracteres ASCII hexadecimales

2.- LIBRERIA SERIE.C: Transmisiones serie asincronas • • • • • • • • • • • •
void abrir_puerto_serie(int comunicaciones serie. puerto); Preparar el puerto especificado para realizar

void baudios(int velocidad); Configurar la velocidad de transmisión. void vaciar_buffer(void); Vaciar el buffer de recepción. void enviar_car(char car); Enviar un carácter por el puerto serie. int car_waiting(void); Determinar si hay algun carácter pendiente de ser leido. char leer_car(void); Leer un caracter del buffer de recepción. char leer_car_plazo(int plazo,int *timeout); Leer un caracter con plazo. Si no llega dentro del plazo especificado se devuelve error de timeout. void dtr_on(void); Activar la señal DTR void dtr_off(void); Desactivar la señal DTR int ok_break(void); Detectar si se ha recibido una señal de BREAK, int wait_break(int plazo); Esperar hasta que se reciba un BREAK dentro del plazo de tiempo establecido. void cerrar_puerto_serie(); Dejar de trabajar con el puerto serie previamente abierto.

3.- LIBRERIA S19.C: Lectura de archivos S19 • • • • • • • •
TIPO S19: Tipo abstracto empleado para representar los archivos S19 int abrir_s19(char *fich,S19 *ss19,int modo); Abrir un fichero S19 void cerrar_s19(S19 ss19); Cerrar un archivo S19 previamente abierto int leerdir_s19(S19 ss19,int nreg, unsigned int *dir); Leer el campo direccion del registro especificado del fichero S19 int leercod_s19(S19 ss19,int nreg, unsigned short int *cod, unsigned short int *tam); Leer el campo codigo/datos del registro especificado del archivo S19 unsigned int getnbytes19(S19 ss19); Devolver el numero de bytes del fichero S19 unsigned int getnregs19(S19 ss19); Devolver el número de registros del fichero S19 char *geterrors19(); Devolver la cadena de error de la última apertura del fichero .S19

4.- LIBRERIA S19UTIL.C: Utilidades para fichero S19 • • •
void s19toramint(S19 f,byte *ramint, byte interna en una matriz de 256 bytes. *ramintoc); Meter programa S19 para la ram

void s19toeeprom(S19 f,byte *eeprom, byte *eepromoc); Meter un programa S19 para la eeprom interna en una matriz de 512 bytes. int situacion_progs19(S19 fs19, int *ov); Comprobar la situación del programa S19 : RAM interna, EPROM o RAM externa

87

Control de la CT6811 desde el PC

void raminttoc(byte *ramint, char *cadc); Convertir una matriz de 256 bytes en su representación en lenguaje C.

5.- LIBRERIA BOOTSTRP.C: Carga de programas y manejo del Bootstrap • • • • • • •
void resetct6811(); Realizar un reset software de la tarjeta CT6811 int jump_eeprom(); Saltar a la memoria EEPROM. int jump_ram(); Saltar al comienzo de la RAM. int okreset(); Realizar reset y comprobar que se ha realizado int cargar_ramint(byte *ramint, void (*car)()); Cargar un programa en la RAM interna. El programa debe estar contenido en una matriz de datos de 256 bytes. int cargars19_ramint(S19 fs19, void (*car)()); Cargar un fichero S19 en la RAM interna. char *getloaderror(); Devolver la cadena del error producido en la última carga de programas en la RAM interna

6.- LIBRERIA CTSERVER.C: Servicio de dialogo con el CTSERVER • • • • • •
int ctalive(); Servicio de control de supervivencia void ctsend_block(uint dir, uint tam, byte *buff, void (*sendcar)()); Servicio de envio de datos del PC al 68HC11 int ctreceive_block(uint dir, uint tam, byte *buff, void (*reccar)()); Servicio de envio de datos del 68HC11 al PC. int ctsave_eeprom(uint dir,uint tam,byte *buff, void (*eeprcar)()); Servicio de grabación de datos en la EEPROM. void ctexecute(uint dir); Servicio de salto y ejecución. void ctsend_byte(uint dir, byte valor); Transferencia de un byte desde el PC al 68HC11 de un byte desde el 68HC11 al PC

• int ctreceive_byte(uint dir,byte *valor); Transferencia 7.- LIBRERIA CTCLIENT.C: Programacion de clientes • • • • • • • •

int load(byte *valor, uint dir); Leer un byte de una posición de memoria del 68HC11 int load_block(byte *buff, uint tam, uint dir, void (*reccar)()); Leer un bloque de bytes del mapa de memoria del 68HC11 void store(byte valor, uint dir); Almacenar un valor en una dirección del mapa de memoria del 68HC11 void store_block(byte *buff, uint tam, uint dir, void (*sendcar)()); Almacenar un bloque de datos en el mapa de memoria del 68HC11 int store_block_eeprom(byte *buff, uint tam, uint dir, void (*eeprcar)()); Grabar un bloque de datos en la memoria eeprom int check_conexion(); Comprobar si existe conexion con el CTSERVER void execute(uint dir); Ejecutar codigo a partir de la direccion especificada int hay_conexion(); Comprobar el estado de la conexion desde la ultima vez que se realizo una llamada a un funcion en ‘bucle cerrado’.

88

Control de la CT6811 desde el PC

4.- PROGRAMACION DE CLIENTES
4.1.- INTRODUCCION * Conceptors de Robustez * Detección del estado de la conexión: Al inicio, periodicamente * Distintas politicas de gestión 4.2.- LA ESTRUCTURA DE LOS CLIENTES Para garantizar un mínimo de robustez los clientes debe comprobar la conexión con el servidor antes de comenzar y periodicamente. En la figura 30 se muestra la estructura de los programas clientes. Estos constan de una Comprobacion comprobación y un bucle principal. Dentro del bucle principal se inicial debe realizar una comprobación de la conexión con el servidor. Al realizar la comprobación inicial si la conexión no se encuentra es debido a alguna de las siguientes causas: • No hay conexión física con la CT6811: Puede ser debido a BUCLE las siguientes causas: PRINCIPAL 1. CT6811 no alimentada 2. Cable no conectado 3. CT6811 no se encuentra en modo Bootstrap • Tarjeta conectada a otro puerto • CTSERVER no se esta ejecutando: • Porque no ha sido cargado • Porque se ha reseteado la CT6811 En cualquiera de los dos primeros casos es imposible establecer la conexión. Sin embargo en el último caso sí es Figura 30: Estructura de los clientes posible establecer la conexión si se carga el CTSERVER. Por ello existen varias políticas a seguir en caso de no detectar la conexión al inicio del cliente: 1. Politica “informe y fin”: Consiste en informar al usuario de que no se ha establecido la conexión y abortar el programa cliente. 2. Política “Cargar sevidor”: Si no se establece conexión se carga el servidor en la CT6811 En el caso de la comprobación periódica de la conexión, en el bucle principal, el cliente tiene la certeza de que la conexión existía antes de perderse. No es como en el caso inicial en el que que no se sabe a priori nada sobre el estado de la CT6811. Dentro del bucle principal se sabe que por lo menos inicialmente la CT6811 estaba bien alimentada y correctamente conectada al puerto serie utilizado. Si se ha perdido la conexión, la causa fundamental es que la CT6811 se haya reseteado. Por supuesto puede ocurrir que la CT6811 “se queme”, alguien corte el cable de conexión etc... En caso de pérdida de conexión existen también varias políticas: 1. Politica “informe y fin”: Informar al usuario y terminar el cliente. 2. Política “Rearranque del servidor”: Saltar al comienzo de la memoria RAM para que el servidor se vuelva a ejecutar. 3. Política “Cargar servidor”: Volver a cargar el servidor. 4. Política “Informe y espera”: Informar de conexión perdida y “sondear” la CT6811 para ver si se recupera la conexión. Las políticas más utilizadas son la 1 y la 4. La política número 2 es también muy atractiva, pero todavía no ha sido muy “investigada”. 4.3.- EJEMPLOS DE CLIENTES 4.3.1.- Ejemplo 1: Programa PCLIENT1.C
FIN Instrucciones

Comprobacion periódica

Intrucciones

89

Control de la CT6811 desde el PC

En este primer ejemplo se muestra la política de “informe y fin”. Se trata del popular programa del LEDP. Si no hay conexión con el servidor se imprime un mensaje en pantalla y se aborta.
/* +--------------------------------------------------------------------------+ ¦ PCLIENT1.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Ejemplo de programacion de clientes ¦ ¦ ¦ ¦ En este ejemplo se emplea la política de "informe y fin" tanto en ¦ ¦ la inicialización del cliente como en el bucle principal. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ typedef unsigned short int byte; #include "stdio.h" #include "dos.h" #include "conio.h" /* Librerias del Grupo J&J */ #include #include #include #include "serie.h" "ctclient.h" "bootstrp.h" "R6811PC.H"

void accion_rxcar() { } void accion_break() { } main() { byte valor; abrir_puerto_serie(2); baudios(9600); /* Abrir sesion con puerto serie COM2 */

check_conexion(); if (!hay_conexion()) { printf ("\nConexión no establecida\n"); cerrar_puerto_serie(); return 0; } valor=0x40; do { store(valor,PORTA); delay(100); valor^=0xFF; check_conexion(); } while (hay_conexion() && !kbhit()); if (!hay_conexion()) { printf ("Conexión perdida\n"); } else getch(); cerrar_puerto_serie(); return 0; } /* Cerrar la sesión con el COM2 */

4.3.2.- Ejemplo 2: Programa PCLIENT2.C En este ejemplo se utiliza la política de “carga del servidor” en la inicialización y la política de informe y fin en el bucle principal. Si inicialmente no se establece la conexión con el servidor se carga el programa CTSERVER.S19. 90

Control de la CT6811 desde el PC

/* +--------------------------------------------------------------------------+ ¦ PCLIENT2.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Ejemplo de programacion de clientes ¦ ¦ ¦ ¦ En este ejemplo se emplea la política de busqueda del servidor en la ¦ ¦ inicialización y la politica de "informe y fin" en el bucle principal. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #include "stdio.h" #include "dos.h" #include "conio.h" /* Librerias del Grupo J&J */ #include #include #include #include "serie.h" "ctclient.h" "bootstrp.h" "R6811PC.H"

typedef unsigned short int byte; void accion_rxcar() { } void accion_break() { } void carga() { static byte n=0; static byte i=0; if (n==16 || i==255) { n=0; printf ("_"); } i++; n++;

}

main() { byte valor; char c; S19 fs19; char *caderror; abrir_puerto_serie(2); baudios(9600); /* Abrir sesion con puerto serie COM2 */

check_conexion(); if (!hay_conexion()) { printf ("NO HAY CONEXION\n"); if (abrir_s19("ctserver.s19",&fs19,1)==0) { caderror=(char *)geterrors19(); printf ("Error: %s\n",caderror); return 0; } printf ("\n"); printf ("0% 50% 100%\n"); printf ("________________\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); resetct6811(); if (cargars19_ramint(fs19,carga)==0) { printf ("\nError: %s",getloaderror()); }

91

Control de la CT6811 desde el PC

}

else { printf (" OK!!\n\n"); } cerrar_s19(fs19); delay(1000); baudios(9600);

check_conexion(); if (!hay_conexion()) { printf ("Conexion no establecida\n"); cerrar_puerto_serie(); return 0; } printf ("CONEXION ESTABLECIDA\n"); valor=0x40; do { store(valor,PORTA); valor^=0xFF; check_conexion(); delay(100); } while (hay_conexion() && !kbhit()); if (!hay_conexion()) { printf ("Conexión perdida\n"); } else getch(); cerrar_puerto_serie(); return 0; } /* Cerrar la sesión con el COM2 */

4.3.3.- Ejemplo 3: Programa PCLIENT3.C Este programa es igual que el anterior pero en vez de cargarse el programa CTSERVER.S19 se carga el servidor desde el propio programa. El programa CTSERVER.S19 se encuentra almacenado en una matriz dentro del cliente.
/* +--------------------------------------------------------------------------+ ¦ PCLIENT3.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Ejemplo de programacion de clientes ¦ ¦ ¦ ¦ En este ejemplo se emplea la política de busqueda del servidor en la ¦ ¦ inicialización y la politica de "informe y fin" en el bucle principal. ¦ ¦ ¦ ¦ La diferencia con el programa PCLIENT2.C estriba en que el servidor ¦ ¦ se encuentra almacenado en una matriz en vez de en un fichero .S19 ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #include "stdio.h" #include "dos.h" #include "conio.h" /* Librerias del Grupo J&J */ #include #include #include #include "serie.h" "ctclient.h" "bootstrp.h" "R6811PC.H"

void accion_rxcar() { } void accion_break() { }

92

Control de la CT6811 desde el PC

void carga() { static byte n=0; static byte i=0; if (n==16 || i==255) { n=0; printf ("_"); } i++; n++;

}

unsigned short int programint[256]={ 0x20,0x05,0x00,0x00,0x00,0x00,0x00,0xCE,0x10,0x00,0x18,0xCE,0xFF,0xFF,0x18, 0x09,0x18,0x8C,0x00,0x00,0x26,0xF8,0x86,0x30,0xA7,0x2B,0xCE,0x10,0x00,0x8D, 0x66,0x97,0x06,0x81,0x44,0x27,0x13,0x81,0x43,0x27,0x54,0x81,0x41,0x27,0x17, 0x81,0x42,0x27,0x0E,0x81,0x45,0x27,0x6A,0x7E,0x00,0x1A,0x86,0x4A,0x8D,0x50, 0x7E,0x00,0x1A,0x7C,0x00,0x06,0x20,0x03,0x7F,0x00,0x06,0x8D,0x4A,0x18,0xDF, 0x02,0x8D,0x45,0x18,0xDF,0x04,0x18,0x8C,0x00,0x00,0x27,0x23,0x18,0xDE,0x02, 0x96,0x06,0x4D,0x26,0x07,0x18,0xA6,0x00,0x8D,0x28,0x20,0x05,0x8D,0x1D,0x18, 0xA7,0x00,0x18,0x08,0x18,0xDF,0x02,0x18,0xDE,0x04,0x18,0x09,0x18,0xDF,0x04, 0x20,0xD7,0x7E,0x00,0x1A,0x8D,0x14,0x18,0xDF,0x02,0x18,0x6E,0x00,0x1F,0x2E, 0x10,0xFC,0xA6,0x2F,0x39,0x1F,0x2E,0x80,0xFC,0xA7,0x2F,0x39,0x36,0x37,0x8D, 0xEE,0x16,0x8D,0xEB,0x18,0x8F,0x33,0x32,0x39,0x8D,0xF2,0x18,0xDF,0x02,0x8D, 0xED,0x18,0xDF,0x04,0x18,0x8C,0x00,0x00,0x27,0x34,0x18,0xDE,0x02,0xC6,0x16, 0xF7,0x10,0x3B,0x18,0xE7,0x00,0xC6,0x17,0xF7,0x10,0x3B,0x8D,0x28,0xC6,0x02, 0xF7,0x10,0x3B,0x8D,0xBD,0x8D,0xC2,0x18,0xA7,0x00,0xC6,0x03,0xF7,0x10,0x3B, 0x8D,0x15,0x18,0x08,0x18,0xDF,0x02,0x18,0xDE,0x04,0x18,0x09,0x18,0xDF,0x04, 0x20,0xC6,0x7F,0x10,0x3B,0x7E,0x00,0x1A,0x18,0x3C,0x18,0xCE,0x0D,0x10,0x18, 0x09,0x18,0x8C,0x00,0x00,0x26,0xF8,0x18,0x38,0x39,0x00,0x00,0x00,0x00,0x00, 0x00,}; main() { byte valor; char c; S19 fs19; char *caderror; int i; abrir_puerto_serie(2); baudios(9600); /* Abrir sesion con puerto serie COM2 */

check_conexion(); if (!hay_conexion()) { printf ("\nNO HAY CONEXION\n"); if (abrir_s19("ctserver.s19",&fs19,1)==0) { caderror=(char *)geterrors19(); printf ("Error: %s\n",caderror); return 0; } printf ("\n"); printf ("0% 50% 100%\n"); printf ("________________\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); resetct6811(); if (cargar_ramint(programint,carga)==0) { printf ("\nError: %s",getloaderror()); cerrar_puerto_serie(); return 0; }; printf (" OK!!\n"); delay(1000); baudios(9600);

}

check_conexion(); if (!hay_conexion()) { printf ("Conexion no establecida\n"); cerrar_puerto_serie(); return 0; }

93

Control de la CT6811 desde el PC

printf ("CONEXION ESTABLECIDA\n"); do { store(valor,PORTA); valor^=0xFF; check_conexion(); delay(100); } while (hay_conexion() && !kbhit()); if (!hay_conexion()) { printf ("Conexión perdida\n"); } else getch(); cerrar_puerto_serie(); return 0; /* Cerrar la sesión con el COM2 */

}

4.3.4.- Ejemplo 4: Programa SPI1.C Ejemplo que muestra cómo utilizar el SPI desde los clientes. El ejemplo utiliza la PCT595. La política que se emplea es la misma que en PCLIENT3.C
/* +--------------------------------------------------------------------------+ ¦ SPI1.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Ejemplo de prueba de la libreria CTCLIENT.C ¦ ¦ ¦ ¦ Programa para leer y recibir dator por el SPI. Para hacer las pruebas se ¦ ¦ utiliza la PCT595 que dispone de 8 led y 8 switches de entrada. En este ¦ ¦ ejemplo se hace parpadear los leds para reproducir el movimiento del ¦ ¦ coche fantástico. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ typedef unsigned short int byte; #include "stdio.h" #include "dos.h" #include "conio.h" /* Librerias utilizadas del Grupo J&J */ #include #include #include #include "serie.h" "ctclient.h" "bootstrp.h" "R6811pc.h"

void accion_rxcar() { } void accion_break() { } byte puertof1(byte d) /* Enviar dato por el puerto f1 */ { byte temp; store(d,SPDR); load(&temp,SPSR); load(&temp,SPDR); store(0xFF,PORTD); store(0x00,PORTD); } return temp;

void carga() { static byte n=0; static byte i=0;

94

Control de la CT6811 desde el PC

if (n==16 || i==255) { n=0; printf ("_"); } i++; n++;

}

unsigned short int programint[256]={ 0x20,0x05,0x00,0x00,0x00,0x00,0x00,0xCE,0x10,0x00,0x18,0xCE,0xFF,0xFF,0x18, 0x09,0x18,0x8C,0x00,0x00,0x26,0xF8,0x86,0x30,0xA7,0x2B,0xCE,0x10,0x00,0x8D, 0x66,0x97,0x06,0x81,0x44,0x27,0x13,0x81,0x43,0x27,0x54,0x81,0x41,0x27,0x17, 0x81,0x42,0x27,0x0E,0x81,0x45,0x27,0x6A,0x7E,0x00,0x1A,0x86,0x4A,0x8D,0x50, 0x7E,0x00,0x1A,0x7C,0x00,0x06,0x20,0x03,0x7F,0x00,0x06,0x8D,0x4A,0x18,0xDF, 0x02,0x8D,0x45,0x18,0xDF,0x04,0x18,0x8C,0x00,0x00,0x27,0x23,0x18,0xDE,0x02, 0x96,0x06,0x4D,0x26,0x07,0x18,0xA6,0x00,0x8D,0x28,0x20,0x05,0x8D,0x1D,0x18, 0xA7,0x00,0x18,0x08,0x18,0xDF,0x02,0x18,0xDE,0x04,0x18,0x09,0x18,0xDF,0x04, 0x20,0xD7,0x7E,0x00,0x1A,0x8D,0x14,0x18,0xDF,0x02,0x18,0x6E,0x00,0x1F,0x2E, 0x10,0xFC,0xA6,0x2F,0x39,0x1F,0x2E,0x80,0xFC,0xA7,0x2F,0x39,0x36,0x37,0x8D, 0xEE,0x16,0x8D,0xEB,0x18,0x8F,0x33,0x32,0x39,0x8D,0xF2,0x18,0xDF,0x02,0x8D, 0xED,0x18,0xDF,0x04,0x18,0x8C,0x00,0x00,0x27,0x34,0x18,0xDE,0x02,0xC6,0x16, 0xF7,0x10,0x3B,0x18,0xE7,0x00,0xC6,0x17,0xF7,0x10,0x3B,0x8D,0x28,0xC6,0x02, 0xF7,0x10,0x3B,0x8D,0xBD,0x8D,0xC2,0x18,0xA7,0x00,0xC6,0x03,0xF7,0x10,0x3B, 0x8D,0x15,0x18,0x08,0x18,0xDF,0x02,0x18,0xDE,0x04,0x18,0x09,0x18,0xDF,0x04, 0x20,0xC6,0x7F,0x10,0x3B,0x7E,0x00,0x1A,0x18,0x3C,0x18,0xCE,0x0D,0x10,0x18, 0x09,0x18,0x8C,0x00,0x00,0x26,0xF8,0x18,0x38,0x39,0x00,0x00,0x00,0x00,0x00, 0x00,}; main() { byte valor; char c; S19 fs19; char *caderror; int i; abrir_puerto_serie(2); baudios(9600); /* Abrir sesion con puerto serie COM2 */

check_conexion(); if (!hay_conexion()) { printf ("\nNO HAY CONEXION\n"); if (abrir_s19("ctserver.s19",&fs19,1)==0) { caderror=(char *)geterrors19(); printf ("Error: %s\n",caderror); return 0; } printf ("\n"); printf ("0% 50% 100%\n"); printf ("________________\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); resetct6811(); if (cargar_ramint(programint,carga)==0) { printf ("\nError: %s",getloaderror()); cerrar_puerto_serie(); return 0; }; printf (" OK!!\n"); delay(1000); baudios(9600);

}

check_conexion(); if (!hay_conexion()) { printf ("Conexion no establecida\n"); cerrar_puerto_serie(); return 0; } printf ("CONEXION ESTABLECIDA\n"); /* configuración SPI */ store(0x38,DDRD);

95

Control de la CT6811 desde el PC

store(0x5C,SPCR); store(0xFF,PORTD); do { for (i=1; i<=0x80; i=i<<1) { puertof1(i); } for (i=0x80; i>0; i=i>>1) { puertof1(i); } } while (!kbhit() && hay_conexion()); if (!hay_conexion()) printf ("\nconexion perdida"); getch(); cerrar_puerto_serie(); return 0; } /* Cerrar la sesión con el COM2 */

4.3.5.- Ejemplo 5: Programa SPI2.C Este es otro ejemplo de manejo del SPI. En el anterior sólo se enviaban bytes por el SPI. En este ejemplo también se reciben.
/* +--------------------------------------------------------------------------+ ¦ SPI2.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Ejemplo de prueba de la libreria CTCLIENT.C ¦ ¦ ¦ ¦ Programa para leer y recibir dator por el SPI. Para hacer las pruebas se ¦ ¦ utiliza la PCT595 que dispone de 8 led y 8 switches de entrada. En este ¦ ¦ ejemplo se imprime en pantalla el estado de los swtiches y se envían a ¦ ¦ los leds. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ typedef unsigned short int byte; #include "stdio.h" #include "dos.h" #include "conio.h" /* Librerias utilizadas del Grupo J&J */ #include "serie.h" #include "ctclient.h" #include "bootstrp.h" #include "R6811pc.h" void accion_rxcar() { } void accion_break() { } byte puertof1(byte d) /* Enviar dato por el puerto f1 */ { byte temp; store(d,SPDR); load(&temp,SPSR); load(&temp,SPDR); store(0xFF,PORTD); store(0x00,PORTD); } return temp;

void puertof2(byte *d) /* Leer un dato del puerto f2 */ { byte temp;

96

Control de la CT6811 desde el PC

store(0x00,PORTD); store(0xFF,PORTD); store(0,SPDR); load(&temp,SPSR); load(d,SPDR);

}

void carga() { static byte n=0; static byte i=0; if (n==16 || i==255) { n=0; printf ("_"); } i++; n++;

}

unsigned short int programint[256]={ 0x20,0x05,0x00,0x00,0x00,0x00,0x00,0xCE,0x10,0x00,0x18,0xCE,0xFF,0xFF,0x18, 0x09,0x18,0x8C,0x00,0x00,0x26,0xF8,0x86,0x30,0xA7,0x2B,0xCE,0x10,0x00,0x8D, 0x66,0x97,0x06,0x81,0x44,0x27,0x13,0x81,0x43,0x27,0x54,0x81,0x41,0x27,0x17, 0x81,0x42,0x27,0x0E,0x81,0x45,0x27,0x6A,0x7E,0x00,0x1A,0x86,0x4A,0x8D,0x50, 0x7E,0x00,0x1A,0x7C,0x00,0x06,0x20,0x03,0x7F,0x00,0x06,0x8D,0x4A,0x18,0xDF, 0x02,0x8D,0x45,0x18,0xDF,0x04,0x18,0x8C,0x00,0x00,0x27,0x23,0x18,0xDE,0x02, 0x96,0x06,0x4D,0x26,0x07,0x18,0xA6,0x00,0x8D,0x28,0x20,0x05,0x8D,0x1D,0x18, 0xA7,0x00,0x18,0x08,0x18,0xDF,0x02,0x18,0xDE,0x04,0x18,0x09,0x18,0xDF,0x04, 0x20,0xD7,0x7E,0x00,0x1A,0x8D,0x14,0x18,0xDF,0x02,0x18,0x6E,0x00,0x1F,0x2E, 0x10,0xFC,0xA6,0x2F,0x39,0x1F,0x2E,0x80,0xFC,0xA7,0x2F,0x39,0x36,0x37,0x8D, 0xEE,0x16,0x8D,0xEB,0x18,0x8F,0x33,0x32,0x39,0x8D,0xF2,0x18,0xDF,0x02,0x8D, 0xED,0x18,0xDF,0x04,0x18,0x8C,0x00,0x00,0x27,0x34,0x18,0xDE,0x02,0xC6,0x16, 0xF7,0x10,0x3B,0x18,0xE7,0x00,0xC6,0x17,0xF7,0x10,0x3B,0x8D,0x28,0xC6,0x02, 0xF7,0x10,0x3B,0x8D,0xBD,0x8D,0xC2,0x18,0xA7,0x00,0xC6,0x03,0xF7,0x10,0x3B, 0x8D,0x15,0x18,0x08,0x18,0xDF,0x02,0x18,0xDE,0x04,0x18,0x09,0x18,0xDF,0x04, 0x20,0xC6,0x7F,0x10,0x3B,0x7E,0x00,0x1A,0x18,0x3C,0x18,0xCE,0x0D,0x10,0x18, 0x09,0x18,0x8C,0x00,0x00,0x26,0xF8,0x18,0x38,0x39,0x00,0x00,0x00,0x00,0x00, 0x00,}; main() { byte valor; char c; S19 fs19; char *caderror; byte i; clrscr(); abrir_puerto_serie(2); baudios(9600); /* Abrir sesion con puerto serie COM2 */

check_conexion(); if (!hay_conexion()) { printf ("\nNO HAY CONEXION\n"); if (abrir_s19("ctserver.s19",&fs19,1)==0) { caderror=(char *)geterrors19(); printf ("Error: %s\n",caderror); return 0; } printf ("\n"); printf ("0% 50% 100%\n"); printf ("________________\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b"); resetct6811(); if (cargar_ramint(programint,carga)==0) { printf ("\nError: %s",getloaderror()); cerrar_puerto_serie(); return 0; };

97

Control de la CT6811 desde el PC

printf (" OK!!\n"); delay(1000); baudios(9600);

}

check_conexion(); if (!hay_conexion()) { printf ("Conexion no establecida\n"); cerrar_puerto_serie(); return 0; } printf ("CONEXION ESTABLECIDA\n"); /* Configurar SPI */ clrscr(); store(0x38,DDRD); store(0x5C,SPCR); store(0xFF,PORTD); do { puertof2(&i); gotoxy(5,5); printf ("%3u",i); puertof1(i); } while (!kbhit() && hay_conexion()); if (!hay_conexion()) printf ("\nconexion perdida"); getch(); cerrar_puerto_serie(); return 0;

}

5.- CONCLUSIONES
Con este capítulo se da fin al libro sobre los clientes. El programar clientes es un tema apasionante y muy divertido, pues se pueden hacer aplicaciones muy chulas en conjunción con la CT6811. El PC deja de ser un “ente” aislado del mundo para convertirse en un auténtico “cerebro” que controla periféricos de la vida real. Observe el lector que a la tarjeta CT6811 se le pueden conectar relés que pueden controlar luces, electrodomésticos... se puede controlar una maqueta de tren, un lector de tarjetas chip... Las aplicaciones son infinitas. Con las herramientas desarrolladas en este libro es posible realizar programas en el PC para controlar todo este tipo de cosas. No obstante, la teoría de servidores presentada en este libro no es más que el comienzo. Es una particularización de una red formada sólo por dos nodos: El PC y la Tarjeta CT6811. La idea de los servidores se puede extender mucho más. Se pueden construir redes de tarjetas CT6811 y PC’s en las que se encuentren servidores y clientes distribuidos por la red. Pero todo esto será el tema de otro apasionante libro.

98

Control de la CT6811 desde el PC

99

Control de la CT6811 desde el PC

APENDICE A: IMPLEMENTACION DE TODAS LAS LIBRERIAS 1.-LIBRERIA SERIE.C
/* +--------------------------------------------------------------------------+ ¦ SERIE.C (c) GRUPO J&J. Julio 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Versión 2.0. Rutinas de comunicaciones serie mediante interrupciones. ¦ ¦ Estas rutinas no son de propósito general sino que están adaptadas para ¦ ¦ trabajar con la tarjeta CT6811. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ /* +------------------------+ ¦ ¦ -----------------------¦ I N T E R F A Z ¦---------------------¦ ¦ +------------------------+ */ void abrir_puerto_serie(int puerto); void baudios(int velocidad); void vaciar_buffer(void); void enviar_car(char car); int car_waiting(void); char leer_car(void); char leer_car_plazo(int plazo,int *timeout); void dtr_on(void); void dtr_off(void); int ok_break(void); int wait_break(int plazo); void cerrar_puerto_serie(); /* +------------------------------+ ¦ ¦ ------------------¦ I M P L E M E N T A C I O N ¦---------------------¦ ¦ +------------------------------+ */ #include "dos.h" #include "conio.h" /* +-----------------------+ ¦ DEFINICIONES ¦ +-----------------------+ */ #define BUFFER_SERIE 512 #define #define #define #define COM1 COM2 COM3 COM4 0x3f8 0x2f8 0x3e8 0x2e8 /* /* /* /* /* 512 bytes de buffer */ Dirección Dirección Dirección Dirección base base base base de de de de la la la la Uart Uart Uart Uart para para para para COM1 COM2 COM3 COM4 */ */ */ */

/* ---- OFFSET de los registros de la UART: ----- */ #define #define #define #define #define #define #define datos act_interrup ident_interrup control_linea control_modem estado_linea estado_modem 0x03 0 1 2 3 4 5 6 /* /* /* /* /* /* /* Registro Registro Registro Registro Registro Registro Registro de datos activacion interrupciones indentificación interrupciones Control de linea Control de modem Estado de Linea Estado del Modem */ */ */ */ */ */ */

#define N81

/* Máscara para configurar la UART con el */ /* formato: 8bits de datos, 1 STOP y no paridad */

100

Control de la CT6811 desde el PC

#define ON 1 #define OFF 0 /* +-----------------------+ ¦ VARIABLES GLOBALES ¦ +-----------------------+ */ int char puerto_actual=COM1; hwint; /* Puerto Actual. Por defecto COM1 */ /* Estado de las interrupciones HW */

/* -- Buffer circular donde almacenar los datos recibidos -- */ char buffer[BUFFER_SERIE]; int cabeza=0; /* Cabeza del buffer */ int cola=0; /* Cola del buffer */ int ncar=0; /* Indica el número de caracteres que hay en el buffer */ char sbreak; /* Indica si se ha recibido o no un BREAK */

long int far *tempo=(long int far *)0x0000046C; /* Temporizador PC */ void interrupt serie(); void accion_rxcar(); void accion_break(); /* Rutina de servicio interrupción de las */ /* interrupciones de la UART */ /* Accion a realizar al recibir un carácter /* Acción a realizar al recibir un BREAK */ */

void interrupt serie(...) /* +---------------------------------------------------------------------------+ ¦ Rutina de servicio de interrupción de la UART. La única interrupción que ¦ ¦ se tiene en cuenta es la de dato recibido. Cada vez que se recibe un ¦ ¦ dato, se activa esta interrupción y se mete el dato en un buffer. ¦ ¦ ¦ ¦ Si se recibe una señal de BREAK se activa la variable global sbreak ¦ ¦ para señalizar que ha venido una señal de BREAK. ¦ +---------------------------------------------------------------------------+*/ { char c; /* Primero se comprueba si se ha recibido una señal de BREAK */ c=inportb(puerto_actual+estado_linea); if ( (c & 0x10)==0x10 ) { sbreak=ON; accion_break(); /* Si BREAK */ /* Señalizar BREAK activo */ /* Realizar una acción */

} else { sbreak=OFF; }

c=inportb(puerto_actual+datos); /* Quitar flag interrupción */ outportb(0x20,0x20); /* Mandar orden EOI al 8259 */ return;

/* No se ha recibido señal de BREAK --> Ha venido un caracter */ c=inportb(puerto_actual+datos); /* Leer dato recibido */ if (ncar<BUFFER_SERIE) { /* Si buffer no está lleno */ buffer[cola]=c; cola=(cola + 1) % BUFFER_SERIE; ncar++; } accion_rxcar(); outportb(0x20,0x20); /* Mandar orden EOI al 8259 */

}

void abrir_puerto_serie(int puerto) /* +---------------------------------------------------------------------------+ ¦ Antes de comenzar a trabajar con el puerto serie hay que llamar a ¦ ¦ esta función. En el argumento se le pasa el número de COM con el ¦ ¦ que se quiere trabajar. Asi por ejemplo para abrir el COM1 habrá ¦ ¦ que llamar a la función: ¦ ¦ ¦ ¦ abrir_puerto_serie(1); ¦

101

Control de la CT6811 desde el PC

¦ ¦ ¦ ¦ ¦ Los puertos soportados son COM1, COM2, COM3 y COM4. Si se especifi¦ ¦ ca como argumento un número distinto de 1,2,3 ó 4 se abrirá el ¦ ¦ COM1 ¦ ¦ ¦ ¦ Después de llamar a esta rutina el puerto especificado queda con¦ ¦ figurado para trabajar con 8 bits de datos, 1 bit de Stop y ninguno ¦ ¦ de paridad. La velocidad es el 9600 baudios. ¦ ¦ ¦ ¦ ¦ ¦ INFORMACION SOBRE LAS INTERRUPCIONES SERIE: ¦ ¦ ¦ ¦ ¦ ¦ - Las interrupciones del puerto serie son interrupciones hardware, por ¦ ¦ tanto habrá que habilitarlas en el registro de máscara de ¦ ¦ interrupción del controlador de interrupciones 8259. ¦ ¦ ¦ ¦ Este registro se encuentra en el puerto 0x21. Los 8 bits de este ¦ ¦ puerto habilitan o deshabilitan distintas interrupciones. ¦ ¦ Un 1 indica que la interrupción está DESHABILITADA. Un 0 indica que ¦ ¦ la interrupción está HABILITADA. El contenido del registro es el ¦ ¦ siguiente: ¦ ¦ ¦ ¦ ¦ ¦ Nº de bit: 7 6 5 4 3 2 1 0 ¦ ¦ ¦ ¦ LPT1 Disketera LPT2 COM1 COM2 ---- TECLADO TIMER ¦ ¦ ¦ ¦ Por tanto, para permitir interrupciones del COM1 hay que poner a ¦ ¦ cero el bit 4. Para el COM2 se pone a cero el bit 3. ¦ ¦ ¦ ¦ - En la UART inicialmente no se permiten interrupciones. Las interrup- ¦ ¦ ciones se activan a través del registro de activación de interrup¦ ¦ ción. Hay 4 causas de interrupción, cada una asociada a un bit del ¦ ¦ registro citado. Para permitir interrupciones cuando ocurra alguna ¦ ¦ de las 4 posibles causas de interrupción hay que poner a 1 el bit ¦ ¦ correspondiente. ¦ ¦ ¦ ¦ - Finalmente, para que las interrupciones salgan de la UART, hay que ¦ ¦ poner a 1 el bit 3 del registro de control del modem. En ¦ ¦ encontrar este puto bit he tardado mucho tiempo, porque la informa- ¦ ¦ ción no se encuentra en ningún lado. ¦ ¦ ¦ +---------------------------------------------------------------------------+*/ { char mask; char vect; char c1,c2; ncar=0; cola=0; cabeza=0; /* Inicializar variables de la rutina de interrupción */

switch(puerto) { case 2 : puerto_actual=COM2; mask=0xF7; vect=0x0b; break; case 3 : puerto_actual=COM3; mask=0xEF; vect=0x0c; break; case 4 : puerto_actual=COM4; mask=0xF7; vect=0x0b; break; default: puerto_actual=COM1; mask=0xEF; vect=0x0c; } /* Configuración para N81 */ outportb(puerto_actual+3,0x03); setvect(vect,serie); /* Cambiar vector de interrupción */

102

Control de la CT6811 desde el PC

hwint=inportb(0x21); c2=hwint & mask; outportb(0x21,c2);

/* Obtener el estado de las interrupcines hw */ /* Habilitar la interrupción correspondiente */

/* --- Permitir la interrupción dato recibido y BREAK ---- */ outportb(puerto_actual+act_interrup,0x05); /* --- Activar GPO2 para habilitar las interrupciones --- */ outportb(puerto_actual+control_modem,0x08); inportb(puerto_actual+datos);

}

void baudios(int baud) /* +----------------------------------------------------------------------+ ¦ Establecer la velocidad en baudios especificada. Sólo se permiten ¦ ¦ tres velocidades: 1200, 7680 y 9600 baudios. Si se especifica otra ¦ ¦ velocidad diferente se tomaran 9600 baudios. ¦ ¦ que llamar a la función: ¦ ¦ ¦ ¦ Ejemplo de utilización: baudios(1200); ¦ ¦ ¦ +----------------------------------------------------------------------+ */ { unsigned char ba=0,bb=0x60; /* Byte alto y byte bajo */ unsigned char control; switch(baud){ case 1200 : ba=0x00; bb=0x60; break; case 7680 : ba=0x00; bb=0x0f; break; case 9600 : ba=0x00; bb=0x0c; break; } default : ba=0x00; bb=0x0c; */

/* Activar bit 7 del registro de control de linea outportb(puerto_actual+control_linea,0x80);

/* Situar los divisores de velocidad en los registros 0 y 1 de la Uart */ outportb(puerto_actual+datos,bb); outportb(puerto_actual+act_interrup,ba); /* Establecer configuración N81 */ outportb(puerto_actual+control_linea,N81);

}

void vaciar_buffer() /* +----------------------------------------------------------------------+ ¦ Vaciar el buffer de recepción. Si existían caracteres se desechan ¦ +----------------------------------------------------------------------+*/ { ncar=0; cabeza=cola; } int listo_enviar() /*+-------------------------------------------------------+ ¦ Devuelve TRUE cuando está listo para enviar datos. ¦ ¦ Para ello se comprueba si el bit 5 del registro de ¦ ¦ estado de línea está activado. ¦ +-------------------------------------------------------+ */ { return inportb(puerto_actual+estado_linea) & 0x20; } void enviar_car(char car) /* +---------------------------------------------+ ¦ Enviar un carácter por el puerto serie. ¦ +---------------------------------------------+ */ { while (!listo_enviar()) /* Esperar hasta que el registro de transmisión ; /* esté vacio } outportb(puerto_actual+datos,car);

*/ */

103

Control de la CT6811 desde el PC

int car_waiting() /*+-------------------------------------------------------+ ¦ Devuelve TRUE si hay un carácter esperando para ser ¦ ¦ leído. Para ello se comprueba el bit 0 del registro ¦ ¦ de estado de línea de la UART. ¦ +-------------------------------------------------------+ */ { return (ncar<BUFFER_SERIE && cabeza==cola) ? 0 : 1; } char leer_car() /* +-------------------------------------------------------+ ¦ Leer un carácter por el puerto serie. Si no hay ¦ ¦ ningún carácter esperando se espera hata que se ¦ ¦ introduzca alguno. ¦ +-------------------------------------------------------+ */ { char c; while (!car_waiting()); /* Esperar hasta que se reciba un byte */

c=buffer[cabeza]; cabeza= (cabeza+1) % BUFFER_SERIE; ncar--; } return c;

char leer_car_plazo(int plazo,int *timeout) /* +----------------------------------------------------------------------+ ¦ Leer un carácter del buffer de recepción. Si el carácter no se ha ¦ ¦ recibido dentro del plazo establecido se devuelve un valor distin- ¦ ¦ to de 0 en timeout. En caso de llegar un carácter dentro del plazo ¦ ¦ indicado se devuelve el carácter y se devuelve 0 en timeout. ¦ ¦ ¦ ¦ El valor del plazo se especifica en tics. Cada Tic equivale a 55ms¦ ¦ ¦ +----------------------------------------------------------------------+*/ { long int tiempo; char c; tiempo=*tempo+1+plazo; while (!car_waiting()) { if (tiempo==*tempo) { *timeout=1; return 0; } } c=buffer[cabeza]; cabeza=(cabeza+1) % BUFFER_SERIE; ncar--; *timeout=0; return c; /* Error de timeout */

}

void dtr_on(void) /* +---------------------------+ ¦ Activar la señal DTR ¦ +---------------------------+ */ { char estado; estado=inportb(puerto_actual+control_modem) | 0x1; outportb(puerto_actual+control_modem,estado); } void dtr_off(void) /* +---------------------------+ ¦ Desactivar la señal DTR ¦ +---------------------------+*/

104

Control de la CT6811 desde el PC

{

}

char estado; estado=inportb(puerto_actual+control_modem) & 0xfe; outportb(puerto_actual+control_modem,estado);

int ok_break(void) /* +----------------------------------------------------------------------+ ¦ Se devuelve un valor 0 si no se ha recibido ninguna señal de BREAK ¦ ¦ Se devuelve un valor distinto de 0 si se ha recibo un BREAK ¦ +----------------------------------------------------------------------+*/ { if (sbreak==ON) { sbreak=OFF; return 1; } else return 0; } int wait_break(int plazo) /* +----------------------------------------------------------------------+ ¦ Esperar a que venga una señal de BREAK en el plazo indicado. ¦ ¦ Si es así se devuelve un valor distinto de 0. Si transcurre el plazo¦ ¦ y no se ha recibido un BREAK se devuelve 0. ¦ +----------------------------------------------------------------------+*/ { long int tiempo; char c; tiempo=*tempo+1+plazo; while (!ok_break()) { /* Mientras no venga BREAK */ if (kbhit()) { /* Si tecla pulsada plazo abortado */ getch(); return 0; } if (tiempo==*tempo) { /* Error de timeout */ return 0; } } return 1; /* Break recibido dentro del plazo */

}

void cerrar_puerto_serie() /*+-------------------------------------------------------+ ¦ Antes de terminar el programa es necesario llamar ¦ ¦ a este procedimiento para desactivar las rutinas de ¦ ¦ interrupción. ¦ +-------------------------------------------------------+ */ { /* --- Desactivar interrupción de Dato recibido --- */ outportb(puerto_actual+act_interrup,0x00); /* --- Desactivar DTR y GPO2 --- */ outportb(puerto_actual+control_modem,0x00); /* --- Dejar las interrupciones HW como al principio --- */ outportb(0x21,hwint);

}

105

Control de la CT6811 desde el PC

2.-LIBRERIA ASCBIN.C
/* +--------------------------------------------------------------------------+ ¦ ASCBIN.C (c) GRUPO J&J. Julio 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Versión 1.0. ¦ ¦ ¦ ¦ Rutinas para la conversión de digitos ASCII a numeros binarios y ¦ ¦ viceversa. ¦ ¦ ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ /* +------------------------------+ ¦ ¦ ------------------¦ I M P L E M E N T A C I O N ¦---------------------¦ ¦ +------------------------------+ */ int char1tobyte(char car,unsigned short int *dec) /* +-------------------------------------------------------------------------+ ¦ Se toma una carácter hexadecimal en ASCII y se convierte a un número ¦ ¦ entero. ¦ ¦ ¦ ¦ Se devuelve 0 si el carácter no es un digito en hexadecimal ¦ ¦ Se devuelve 1 en caso contrario. ¦ ¦ ¦ +-------------------------------------------------------------------------+*/ { if (car>='0' && car<='9') *dec=car-'0'; else if (car<='F' && car>='A') *dec=car-'A'+10; else return 0; /* El dígito no está en hexadecimal */ } return 1; /* No ha habido errores */

int char2tobyte(char *cad,unsigned short int *dec) /* +-------------------------------------------------------------------------+ ¦ Se toma una cadena con dos dígitos hexadecimales ASCII y los convierte ¦ ¦ en un número entero de 8 bits ¦ ¦ ¦ ¦ Se devuelve 0 si algún digito no está en hexadecimal. ¦ ¦ Se devuelve 1 en caso contrario. ¦ ¦ ¦ +-------------------------------------------------------------------------+*/ { unsigned short num1,num2; if (char1tobyte(cad[0],&num1)) /* Si se convierte bien el primer digito */ if (char1tobyte(cad[1],&num2)){/* y se convierte bien el segundo... */ *dec=num1*16+num2; /* Se calcula en numero en decimal */ return 1; } return 0; /* Ha habido algún error en la conversión */

}

int char4toint(char *cad,unsigned int *dec) /* +-------------------------------------------------------------------------+ ¦ Se toma una cadena con 4 dígitos hexadecimales ASCII y los convierte ¦ ¦ en un número entero de 16bits ¦ ¦ ¦ ¦ Se devuelve 0 si algún digito no está en hexadecimal. ¦ ¦ Se devuelve 1 en caso contrario. ¦ ¦ ¦ +-------------------------------------------------------------------------+*/ { unsigned short int num1,num2;

106

Control de la CT6811 desde el PC

if (char2tobyte(&cad[0],&num1)) /* Si se convierten bien 1er par de dig */ if (char2tobyte(&cad[2],&num2)) { /* Y se convierten bien el 2º par.. */ *dec=num1*256 + num2; /* Calcular número decimal resultante */ return 1; } } return 0; /* Error en la conversión */

int bytetochar1(unsigned short int byte, char *car) /* +-------------------------------------------------------------------------+ ¦ Convertir un número entre 0-15 a caracteres hexadecimales en ASCII ¦ ¦ ¦ ¦ Se devuelve un 1 si se ha realizado correctamente la conversión. ¦ ¦ Se devuelve un 0 en caso contrario. ¦ ¦ ¦ +-------------------------------------------------------------------------+*/ { if (byte>15) return 0; if (byte<10) *car='0'+byte; else *car='A'-10+byte; } return 1;

int bytetochar2(short num, char *cad) /*+-----------------------------------------------------------------------+ ¦ Convertir un número entre 0-255 a 2 caracters ASCII hexadecimales. ¦ ¦ ¦ ¦ Se devuelve un 1 si se ha realizado correctamente la conversión. ¦ ¦ Se devuelve un 0 en caso contrario. ¦ ¦ ¦ +-----------------------------------------------------------------------+*/ { if (bytetochar1(num>>4,&cad[0])==0) return 0; /* Digito mayor peso */ if (bytetochar1(num & 0xF,&cad[1])==0) return 0; /* Digito menor peso */ cad[2]=0; return 1; } int inttochar4(long num, char *cad) /*+-----------------------------------------------------------------------+ ¦ Convertir un número entre 0-65535 a 4 caracters ASCII hexadecimales. ¦ ¦ ¦ ¦ Se devuelve un 1 si se ha realizado correctamente la conversión. ¦ ¦ Se devuelve un 0 en caso contrario. ¦ ¦ ¦ +-----------------------------------------------------------------------+*/ { if (bytetochar2(num>>8,cad)==0) return 0; if (bytetochar2(num&0xFF,&cad[2])==0) return 0; cad[4]=0; return 1; }

107

Control de la CT6811 desde el PC

3.-LIBRERIA S19.C

/* +--------------------------------------------------------------------------+ ¦ S19.C (c) GRUPO J&J. Julio 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Versión 2.0. ¦ ¦ ¦ ¦ Rutinas para el procesamiento de ficheros en el formato .S19 de ¦ ¦ Motorola. ¦ ¦ ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #define NULL 0 #include #include #include #include "fcntl.h" "s19.h" "io.h" "alloc.h"

/* --- Librerias del Grupo J&J empleadas --- */ #include "ascbin.h" /* +------------------------------+ ¦ ¦ ------------------¦ I M P L E M E N T A C I O N ¦---------------------¦ ¦ +------------------------------+ */ /* +--------------------------+ ¦ VARIABLES GLOBALES ¦ +--------------------------+*/ int nerror; /* Numero de error que se ha producido */ char error_s19[8][80]={ {"Archivo no encontrado o error al abrir el archivo"}, {"-----------------------------"}, {"No se puede leer el fichero"}, {"Fichero no está en formato .S19"}, {"Checksum erróneo"}, {"No hay memoria suficiente"}, {"Conflicto con las directivas ORG"}, }; /* /* /* /* /* /* 1 */ 2 */ 3 */ 4 */ 5 */ 6 */ /* 7 */

void extensions19(char *fich) /* +--------------------------------------------------------------------------+ ¦ Comprobar la extensión del fichero. Si no tiene ninguna extensión se ¦ ¦ le poner .S19 ¦ +--------------------------------------------------------------------------+*/ { int i; i=0; while (fich[i]!='.' && fich[i]!=0) i++; if (fich[i]==0) { fich[i]='.'; fich[i+1]='S'; fich[i+2]='1'; fich[i+3]='9'; fich[i+4]=0; } /* Recorrer la cadena buscando un . */ */

/* no tiene extensión. Hay que añadirla

}

char *geterrors19() /* +--------------------------------------------------------------------------+ ¦ Devolver la cadena del error producido en la ultima apertura del ¦ ¦ fichero .S19 ¦ +--------------------------------------------------------------------------+*/ {

108

Control de la CT6811 desde el PC

} int getchar2(int f,char *cad) /* +--------------------------------------------------------------------------+ ¦ Se leen 2 caracteres del fichero especificado y se meten en la cadena ¦ ¦ cad. Se devuelve 0 si se ha producido algún error. ¦ +--------------------------------------------------------------------------+*/ { if (read(f,cad,2)==-1) { nerror=3; /* Error leyendo el fichero */ return 0; } cad[2]='###BOT_TEXT###'; return 1; } int getchar4(int f,char *cad) /* +--------------------------------------------------------------------------+ ¦ Se leen 4 caracteres del fichero especificado y se meten en la cadena ¦ ¦ cad. Se devuelve 0 si se ha producido algún error. ¦ +--------------------------------------------------------------------------+*/ { if (read(f,cad,4)==-1) { nerror=3; /* Error leyendo el fichero */ return 0; } cad[4]='###BOT_TEXT###'; return 1; } int getbyte(int f, unsigned short int *num) /* +--------------------------------------------------------------------------+ ¦ Leer un entero del fichero especificado. Se leen 2 digitos ASCII en ¦ ¦ hexadecimal y se devuelve su valor entero. ¦ ¦ ¦ ¦ Se devuelve 0 si se ha producido un error. ¦ ¦ Se devuelve 1 en caso contrario. ¦ ¦ ¦ +--------------------------------------------------------------------------+*/ { char cad[3]; if (getchar2(f,cad)==0) { /* Error leyendo el fichero */ nerror=3; return 0; }; if (char2tobyte(cad,num)==0) { /* Error al convertir los digitos */ nerror=4; return 0; } } return 1;

if (nerror==0) return NULL; return (char *)&error_s19[nerror-1];

int getint(int f, unsigned int *num) /* +--------------------------------------------------------------------------+ ¦ Leer un entero del fichero especificado. Se leen 4 digitos ASCII en ¦ ¦ hexadecimal y se devuelve su valor entero. ¦ ¦ ¦ ¦ Se devuelve 0 si se ha producido un error. ¦ ¦ Se devuelve 1 en caso contrario. ¦ ¦ ¦ +--------------------------------------------------------------------------+*/ { char cad[5]; if (getchar4(f,cad)==0) { nerror=3; /* Error leyendo el fichero */ return 0; } if (char4toint(cad,num)==0) { nerror=4; /* Error al convertir los digitos */ return 0;

109

Control de la CT6811 desde el PC

} } return 1;

int leertipo_reg(int f, unsigned short int *tipo) /* +----------------------------------------------------------------------+ ¦ Leer tipo de registro. Se devuelve en tipo un 1 si se trata de un ¦ ¦ registro del tipo 1 y 9 en caso de que se trate de registro del ¦ ¦ tipo 9. ¦ ¦ ¦ ¦ Se devuelve 0 si ha habido algún error ¦ ¦ Se devuelve 1 si no ha habido errores ¦ ¦ ¦ +----------------------------------------------------------------------+*/ { char cad[3]; if (getchar2(f,cad)==0) return 0; if (cad[0]!='S') { nerror=4; return 0; } if (cad[1]!='1' && cad[1]!='9') { nerror=4; return 0; } if (cad[1]=='1') *tipo=1; if (cad[1]=='9') *tipo=9; return 1;

/* Error en tipo de registro */

/* Error en tipo de registro */

}

void liberar_s19(S19 *ss19) /* +----------------------------------------------------------------------+ ¦ Liberar toda la memoria utilizada por el fichero s19 especificado ¦ +----------------------------------------------------------------------+*/ { struct registros1 *punt; /* Punteros a registros S19 */ struct registros1 *borra; punt=ss19->cabeza; while(punt!=NULL){ borra=punt; punt=punt->sig; free(borra); } /* Apuntar al comienzo de la lista */ /* Apuntar al siguiente registro /* Borrar el registro actual */ */

}

int abrir_s19(char *fich,S19 *ss19,int modo) /* +--------------------------------------------------------------------------+ ¦ Abrir un fichero .S19 y crear una variable de tipo S19. Para trabajar ¦ ¦ con cualquier fichero .S19 habrá que abrirlo primero. ¦ ¦ ¦ ¦ PARAMETROS ENTRADA: ¦ ¦ ¦ ¦ - Fich: Nombre del fichero .S19 a abrir. Si se especifica sin ¦ ¦ extension por defecto se toma extensión.S19. Se puede ¦ ¦ especificar la ruta completa del fichero. ¦ ¦ ¦ ¦ - Modo: Indica el modo de apertura del fichero. Existen dos modos ¦ ¦ ¦ ¦ - Modo 1: Se comprueba el Checksum de todos los registros ¦ ¦ - Modo 0: No se comprueba el checksum. ¦ ¦ ¦ ¦ PARAMETROS DE SALIDA: ¦ ¦ ¦ ¦ - ss19: Se devuelve una variable del tipo S19 que sera necesaria ¦ ¦ para trabajar con el fichero abierto. ¦ ¦ ¦ ¦ La función devuelve 0 si no ha podido abrir el fichero porque se ha ¦ ¦ producido algún error. ¦ ¦ ¦ ¦ ¦ ¦ Si el fichero ha sido abierto correctamente se devuelve un 1 ¦

110

Control de la CT6811 desde el PC

¦ ¦ +--------------------------------------------------------------------------+*/ { char cad[3]; unsigned int dir; /* Campo direccion */ unsigned int maxdir; /* Máxima direccion alcanzada*/ unsigned short int tiporeg; /* Campo tipo de registro */ unsigned short int tam; /* Tamaño campo codigo/datos */ unsigned short int checksum; /* Campo de checksum */ unsigned short int crc; /* Checksum calculado */ unsigned short int codigo[37]; /* Campo de codigo/datos */ struct registros1 *nuevoreg; struct registros1 *regant; unsigned int i; int fi; crc=0; maxdir=0; ss19->nbytes=0; ss19->nreg=0; ss19->cabeza=NULL; nerror=0; /* Puntero a registro S1 /* Registro anterior */ */ */

/* Descriptor del fichero de entrada

/* Inicialiar variable del tipo S19 */ /* Inicialmente no hay errores */

/*---------- ABRIR EL FICHERO S19 -----*/ extensions19(fich); /* Si no tiene extensión poner .S19 */ if ((fi=open(fich,O_RDONLY))==-1) { nerror=1; /* Fichero no encontrado */ liberar_s19(ss19); return 0; } /*------- LEER TIPO DE REGISTRO -------*/ if (leertipo_reg(fi,&tiporeg)==0) { liberar_s19(ss19); return 0; } while(tiporeg==1) { crc=0; /*-------- LEER CAMPO TAMAÑO ---------*/ if (getbyte(fi,&tam)==0) { liberar_s19(ss19); return 0; } crc=tam&0xFF; tam-=3; /* Quitar byte de checksum y 2 bytes de direccion /*-------- LEER CAMPO DIRECCION -------*/ if (getint(fi,&dir)==0) { liberar_s19(ss19); return 0; } if (maxdir>dir){ /* comprobar si la dir. del nuevo reg. */ nerror=7; /* no se solapa con las anteriores */ liberar_s19(ss19); /* Si es asi mensaje de error */ return 0; } maxdir=dir+tam; crc=crc + (unsigned short int)(dir & 0xFF) + (unsigned short int)(dir>>8)&0xFF; /*-------- LEER CAMPO CODIGO/DATOS ----*/ for (i=0; i<tam; i++) { if (getbyte(fi,&codigo[i])==0) { liberar_s19(ss19); return 0; } */ /* Ha habido algún error */

111

Control de la CT6811 desde el PC

}

ss19->nbytes++; crc= (crc+codigo[i])&0xFF;

/*-------- LEER CAMPO CRC ----------*/ if (getbyte(fi,&checksum)==0) { liberar_s19(ss19); return 0; } crc= ~crc & 0xFF; if (modo==1 && crc!=checksum) { /* Comprobar el CRC */ nerror=5; /* Error de CRC */ liberar_s19(ss19); return 0; } /*-------- LEER CR Y LF ------------*/ if (getchar2(fi,cad)==0) { liberar_s19(ss19); return 0; } /* ------- CREAR EL NUEVO REGISTRO S1 LEIDO -------*/ if ( (nuevoreg=(struct registros1 *)malloc(sizeof(struct registros1)))==NULL) { nerror=6; /* No hay suficiente memoria */ liberar_s19(ss19); return 0; } ss19->nreg++; nuevoreg->tam=tam; /* Campo tamaño */ nuevoreg->dir=dir; /* Campo direccion */ for (i=0; i<tam; i++) /* Campo código/datos */ nuevoreg->codigo[i]=codigo[i]; nuevoreg->sig=NULL; if (ss19->nreg==1) ss19->cabeza=nuevoreg; /* Cabeza de la lista */ else regant->sig=nuevoreg; /* Enlazar con registro anterior */ regant=nuevoreg; /*------- LEER TIPO DE REGISTRO -------*/ if (leertipo_reg(fi,&tiporeg)==0) { liberar_s19(ss19); /* Ha habido algún error */ return 0; } } close(fi); return 1;

}

void cerrar_s19(S19 ss19) /* +----------------------------------------------------------------------+ ¦ Dejar de trabajar con el archivo S19 especificado. Se libera toda la¦ ¦ memoria que se habia tomado. ¦ +----------------------------------------------------------------------+*/ { liberar_s19(&ss19); /* Liberar memoria */ } int leerdir_s19(S19 ss19,int nreg, unsigned int *dir) /* +----------------------------------------------------------------------+ ¦ Leer el campo direccion del fichero S19 especificado. ¦ ¦ ¦ ¦ ¦ ¦ ENTRADAS: ¦ ¦ ¦ ¦ - ss19: Fichero S19 abierto con el que trabajar ¦ ¦ - nreg: Numero del registro al que se quiere acceder ¦ ¦ ¦ ¦ SALIDAS: ¦ ¦ ¦ ¦ - dir : Campo direccion del registro especificado ¦ ¦ ¦ ¦ Se devuelve 0 en caso de que no exista el registro pedido. ¦ ¦ Se devuelve 1 en caso de exito. ¦ +----------------------------------------------------------------------+*/ {

112

Control de la CT6811 desde el PC

struct registros1 *punt; int reg; reg=1; /* Comenzar la busqueda por el registro 1 */

punt=ss19.cabeza; while(punt!=NULL) { if (reg==nreg) { *dir=punt->dir; return 1; } punt=punt->sig; reg++; } } return 0; /* Registro pedido no se encuentra */

int leercod_s19(S19 ss19,int nreg, unsigned short int *cod, unsigned short int *tam) /* +----------------------------------------------------------------------+ ¦ Leer el campo codigo/datos del registro especificado. ¦ ¦ ¦ ¦ ENTRADAS: ¦ ¦ ¦ ¦ - ss19 : Fichero S19 abierto con el que trabajar ¦ ¦ - nreg : Numero del registro al que se quiere acceder ¦ ¦ ¦ ¦ SALIDAS: ¦ ¦ ¦ ¦ - cod : Campo código/datos del registro especificado ¦ ¦ - tam : tamaño del campo codigo/datos ¦ ¦ ¦ ¦ Se devuelve 0 en caso de que no exista el registro pedido. ¦ ¦ Se devuelve 1 en caso de exito. ¦ +----------------------------------------------------------------------+*/ { struct registros1 *punt; int reg; int i; reg=1; punt=ss19.cabeza; while(punt!=NULL) { if (reg==nreg) { *tam=punt->tam; /* Devolver tamaño */ for (i=0; i<punt->tam; i++) cod[i]=punt->codigo[i]; /* Devolver codigo/datos */ return 1; } punt=punt->sig; reg++; } return 0;

}

unsigned int getnbytes19(S19 ss19) /* +----------------------------------------------------------------------+ ¦ Devolver el numero de bytes de todo el codigo del fichero S19 ¦ ¦ especificado. ¦ +----------------------------------------------------------------------+*/ { return ss19.nbytes; } unsigned int getnregs19(S19 ss19) /* +--------------------------------------------------------------+ ¦ Devolver el numero de registros del tipo 1 del fichero S19 ¦ ¦ especificado. ¦ +--------------------------------------------------------------+*/ { return ss19.nreg; }

113

Control de la CT6811 desde el PC

4.-LIBRERIA S19UTIL.C

/* +--------------------------------------------------------------------------+ ¦ S19UTIL.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Versión 1.0. ¦ ¦ ¦ ¦ Utilidades para archivos .S19. ¦ ¦ Se utiliza la libreria S19.C y ASCBIN.C ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #include "string.h" /* --- Librerias del Grupo J&J empleadas -- */ #include "s19.h" #include "ascbin.h" typedef unsigned short int byte; void s19toramint(S19 f,byte *ramint, byte *ramintoc) /* +----------------------------------------------------------------------+ ¦ Quedarse con la parte del programa .S19 comprendido entre las ¦ ¦ direccion 0-255. El resto se desprecia. ¦ ¦ ¦ ¦ Estas posiciones se introducen en la matriz ramint que debe tener ¦ ¦ una capacidad minima de 256 posiciones. ¦ ¦ ¦ ¦ En la matriz de ocupación se indica con 0 las posiciones dentro de ¦ ¦ la matriz ramint que no tienen codigo. Con 1 las posiciones que si ¦ ¦ lo tienen. ¦ +----------------------------------------------------------------------+*/ { unsigned int i,n; unsigned int dir; unsigned int nreg; unsigned short int codigo[37]; unsigned short int tam; nreg=getnregs19(f); for (i=0; i<=255; i++) { ramint[i]=0; ramintoc[i]=0; }

/* Inicialmente todas las posiciones vacias */

}

for (i=1; i<=nreg; i++) { leerdir_s19(f,i,&dir); leercod_s19(f,i,codigo,&tam); for (n=0; n<tam; n++) { if ((dir+n)<=0xFF) { ramint[dir+n]=codigo[n]; ramintoc[dir+n]=1; } } }

/* Guardar valor */ /* Indicar posición llena */

void s19toeeprom(S19 f,byte *eeprom, byte *eepromoc) /* +----------------------------------------------------------------------+ ¦ Quedarse con la parte del programa .S19 comprendido entre las ¦ ¦ direccion $B600-$B7FF. El resto del programa se desprecia. ¦ ¦ ¦ ¦ Estas posiciones se introducen en la matriz eeprom que debe tener ¦ ¦ una capacidad minima de 512 posiciones. ¦ ¦ ¦ ¦ En la matriz de ocupación se indica con 0 las posiciones dentro de ¦ ¦ la matriz eeprom que no tienen codigo. Con 1 las posiciones que si ¦ ¦ lo tienen. ¦ +----------------------------------------------------------------------+*/ { unsigned int i,n; unsigned int dir; unsigned int ind; unsigned int nreg;

114

Control de la CT6811 desde el PC

unsigned short int codigo[37]; unsigned short int tam; nreg=getnregs19(f); for (i=0; i<=511; i++) eepromoc[i]=0; /* Inicialmente matriz eeprom desocupada */

}

for (i=1; i<=nreg; i++) { leerdir_s19(f,i,&dir); leercod_s19(f,i,codigo,&tam); for (n=0; n<tam; n++) { if ((dir+n)<=0xB7FF) { ind=dir-0xB600; eeprom[ind+n]=codigo[n]; /* Guardar valor */ eepromoc[ind+n]=1; /* Indicar ocupación */ } } }

int check_eeprom(S19 fs19, int *ov) /* +----------------------------------------------------------------------+ ¦ Comprobar si el archivo S19 especificado tiene todas sus direcciones ¦ ¦ dentro de los 512 bytes de la EEPROM. ¦ ¦ ¦ ¦ Se devuelve 1 en caso de que sea programa para la EEPROM ¦ ¦ Se devuelve 0 en caso contrario. ¦ ¦ ¦ ¦ Si es un programa para la EEPROM pero la desborda se indica con ¦ ¦ un 1 en ov. 0 en caso de no desbordamiento. ¦ +----------------------------------------------------------------------+*/ { unsigned int i; unsigned int dir; unsigned int nreg; unsigned short int codigo[37]; unsigned short int tam; nreg=getnregs19(fs19); if (nreg==0) { *ov=0; return 1; } /* ¿Fichero S19 sin registros? */

leerdir_s19(fs19,1,&dir); if (dir<0xB600 || dir>0xB7FF) { return 0; }

/* Programa NO es para la EEPROM */

}

for (i=2; i<=nreg; i++) { leerdir_s19(fs19,i,&dir); leercod_s19(fs19,i,codigo,&tam); if ( (dir+tam)>0xB7FF) { *ov=1; /* Programa desborda la EEPROM */ return 1; } } *ov=0; return 1; /* Programa es para la EEPROM */

int check_ramint(S19 fs19, int *ov) /* +----------------------------------------------------------------------+ ¦ Comprobar si el archivo S19 especificado tiene todas sus direcciones ¦ ¦ dentro de los 256 bytes de la RAM interna. ¦ ¦ ¦ ¦ Se devuelve 1 en caso de que sea programa para la RAM interna ¦ ¦ Se devuelve 0 en caso contrario. ¦ ¦ ¦ ¦ Si es un programa para la RAM interna pero la desborda se indica ¦ ¦ con un 1 en ov. 0 en caso de no desbordamiento. ¦ +----------------------------------------------------------------------+*/ { int i; unsigned int dir; unsigned int nreg;

115

Control de la CT6811 desde el PC

unsigned short int codigo[35]; unsigned short int tam; nreg=getnregs19(fs19); if (nreg==0) { *ov=0; return 1; } /* ¿Fichero S19 sin registros? */

leerdir_s19(fs19,1,&dir); if (dir>0xFF) { /* Programa NO es para la RAM interna */ return 0; } for (i=2; i<=nreg; i++) { leerdir_s19(fs19,i,&dir); leercod_s19(fs19,i,codigo,&tam); if ( (dir+tam)>0xFF) { *ov=1; /* Programa desborda la ram interna */ return 1; } } *ov=0; return 1; /* Programa es para la RAM interna */

}

int situacion_progs19(S19 fs19, int *ov) /* +----------------------------------------------------------------------+ ¦ Comprobar la situación del programa: RAM interna, EEPROM o RAM ¦ ¦ externa. ¦ ¦ En ov se devuelve si ha habido desbordamiento de la eeprom o ram ¦ ¦ interna. ¦ ¦ ¦ ¦ La función devuelve 1 : Programa para RAM interna ¦ ¦ 2 : Programa para EEPROM ¦ ¦ 3 : Programa para RAM externa. ¦ +----------------------------------------------------------------------+*/ { if (check_ramint(fs19,ov)==1) return 1; /* Programa para RAM interna */ if (check_eeprom(fs19,ov)==1) return 2; /* Programa para EEPROM */ } return 3; /* Programa para RAM externa */

void raminttoc(byte *ramint, char *cadc) /* +----------------------------------------------------------------------+ ¦ Tomar la matriz ramint que contiene un programa para la RAM interna ¦ ¦ y devolver en el array cadc la cadena que representa la misma ¦ ¦ matriz pero definida en C. ¦ +----------------------------------------------------------------------+*/ { char cad[3]; int i; int n; strcpy(cadc,"unsigned short int programint[256]={\n"); n=1; for (i=0; i<=255; i++) { strcat(cadc,"0x"); bytetochar2(ramint[i],cad); cad[2]=0; strcat(cadc,cad); strcat(cadc,","); n++; if (n==16) { strcat(cadc,"\n"); n=1; } } } strcat(cadc,"};");

116

Control de la CT6811 desde el PC

5.-LIBRERIA BOOTSTRP.C
/* +--------------------------------------------------------------------------+ ¦ BOOTSTRP.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Versión 2.0. ¦ ¦ ¦ ¦ Rutinas para la carga de programas en la ram interna del 68HC11 cuando ¦ ¦ arranca en modo bootstrap. Esta libreria necesita de las librerias: ¦ ¦ S19.C, S19UTIL.C y SERIE.C. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #include "dos.h" /* -- Librerias del Grupo J&J Empeadas -- */ #include "serie.h" #include "s19.h" #include "s19util.h"

#define TIMEOUT_BREAK 4 #define TIEMPO 5 #define NULL 0 /* Timeout para recibir el eco */

static int nerror; char errorload[8][80] = { "", /* 1 */ /* Error en .S19 */ "La tarjeta CT6811 no responde", /* 2 */ "Fallo en la transmisión", /* 3 */ "No se recibe señal de BREAK", /* 4 */ "El programa no es para la RAM interna", /* 5 */ "El programa desborda los 256 bytes de la RAM interna", /* 6 */ }; void resetct6811() /* +------------------------------------+ ¦ Realizar un reset de la CT6811. ¦ +------------------------------------+*/ { dtr_on(); delay(300); dtr_off(); } int okreset() /* +----------------------------------------------------------------------+ ¦ Realizar un reset de la CT6811 y esperar la señal de BREAK ¦ ¦ correspondiente. Si no se recibe dentro del plazo establecido la ¦ ¦ funcion devuelve 0. 1 en caso de recibirse el BREAK. ¦ +----------------------------------------------------------------------+*/ { resetct6811(); if (wait_break(TIMEOUT_BREAK)==0) return 0; else return 1; } int jump_eeprom() /* +----------------------------------------------+ ¦ Saltar a la memoria EEPROM. La función de- ¦ ¦ vuelve 1 si se ha recibido el BREAK y se ha ¦ ¦ saltado correctamente. En caso de no reci- ¦ ¦ bir el BREAK se devuelve 0. ¦ +----------------------------------------------+*/ { if (okreset()) { enviar_car(0x00);

117

Control de la CT6811 desde el PC

}

return 1; } return 0;

int jump_ram() /* +----------------------------------------------+ ¦ Saltar a la dirección $0000 de la memoria. ¦ ¦ La funcion devuelve 1 si se ha recibido el ¦ ¦ BREAK y se ha saltado correctamente a la ¦ ¦ RAM. En caso de no recibir el BREAK se ¦ ¦ devuelve 0. ¦ +----------------------------------------------+*/ { if (okreset()) { enviar_car(0x55); return 1; } return 0; } int cargar_ramint(byte *ramint, void (*car)()) /* +----------------------------------------------------------------------+ ¦ Enviar el programa especificado por la matriz ramint a la tarjeta ¦ ¦ CT6811. Cada vez que se ha enviado un byte de código se llama a la ¦ ¦ función car. ¦ ¦ ¦ ¦ La función devuelve 1 si no se ha producido ningún error. Se ¦ ¦ devuelve 0 en caso de error. Si se ha producido un error con la ¦ ¦ función getloaderror se devuelve la cadena que indica el error. ¦ ¦ ¦ ¦ ES NECESARIO QUE SE HAYA ABIERTO EL PUERTO SERIE ANTES DE ENVIAR ¦ ¦ CUALQUIER PROGRAMA. ¦ ¦ ¦ +----------------------------------------------------------------------+*/ { int i; unsigned short int c; int timeout; nerror=0; baudios(7680); vaciar_buffer(); resetct6811(); if (wait_break(40)==0) { nerror=4; return 0; } delay(100); enviar_car(0xFF);

/* No se recibe BREAK */

/* Especificar 7680 baudios */

for (i=0; i<=255; i++) { enviar_car(ramint[i]); c=(unsigned short int)leer_car_plazo(TIEMPO,&timeout)&0xFF; if (timeout) { nerror=2; /* Error de timeout */ return 0; } if (c!=ramint[i]) { nerror=3; /* Se ha recibido un eco diferente */ return 0; /* El último eco no se cuenta... */ } (*car)();

}

} return 1;

int cargars19_ramint(S19 fs19, void (*car)()) /* +----------------------------------------------------------------------+ ¦ Enviar el fichero .S19 especificado a la tarjeta CT6811. El ¦

118

Control de la CT6811 desde el PC

¦ fichero debe estar preparado para la ram interna. Cada vez que se ¦ ¦ envía un carácter se llama a la función car. ¦ ¦ ¦ ¦ La función devuelve 1 si no se ha producido ningún error. Se ¦ ¦ devuelve 0 en caso de error. Si se ha producido un error con la ¦ ¦ función getloaderror se devuelve la cadena que indica el error. ¦ ¦ ¦ ¦ ES NECESARIO QUE SE HAYA ABIERTO EL PUERTO SERIE ANTES DE ENVIAR ¦ ¦ CUALQUIER PROGRAMA. ¦ ¦ ¦ +----------------------------------------------------------------------+*/ { byte ramint[256]; byte ramintoc[256]; int ov; if (situacion_progs19(fs19,&ov)!=1) { nerror=5; /* Programa no es para ram interna */ return 0; } if (ov==1) { nerror=6; /* El programa desborda la RAM interna */ return 0; } s19toramint(fs19,ramint,ramintoc); return cargar_ramint(ramint,car);

}

char *getloaderror() /* +--------------------------------------------------------------------------+ ¦ Devolver la cadena del error producido en la ultima carga de programas ¦ ¦ en la RAM interna ¦ +--------------------------------------------------------------------------+*/ { if (nerror==0) return NULL; if (nerror==1) return (char *)geterrors19(); /* Error en .S19 */ return (char *)&errorload[nerror-1]; /* Error al cargar */ }

119

Control de la CT6811 desde el PC

6.-LIBRERIA CTSERVER.C
/* +--------------------------------------------------------------------------+ ¦ CTSERVER.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Versión 2.0. Rutinas para realizar llamadas remotas a los servicios ¦ ¦ del servidor de propósito general CTSERVER. ¦ ¦ ¦ ¦ Esta libreria se utiliza para implementar CLIENTES para el servidor ¦ ¦ CTSERVER. ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #include "dos.h" /* Librerias del Grupo J&J utilizadas */ #include "serie.h" typedef unsigned short int byte; typedef unsigned int uint; #define TIMEOUT 5 #define EETIMEOUT 30 #define WAIT 4 void hacer_nada() { } int ctalive() /* +----------------------------------------------------------------------+ ¦ Comprobar si existe conexión con el servidor CTSERVER. ¦ ¦ ¦ ¦ Se devuelve: ¦ ¦ 0 --> Servidor no responde ¦ ¦ 1 --> El servidor ha respondido ¦ +----------------------------------------------------------------------+*/ { int timeout; char c; vaciar_buffer(); enviar_car('D'); /* Solicitar servicio de supervivencia */ c=leer_car_plazo(TIMEOUT,&timeout); /* Esperar respuesta */ if (timeout==1) return 0; /* Error de timeout: no responde */ if (c!='J') return 0; /* Error: Servidor ha respondido con un */ /* caracter diferentes del enviado */ return 1;

}

void ctsend_block(uint dir, uint tam, byte *buff, void (*sendcar)()) /*+----------------------------------------------------------------------+ ¦ Enviar un bloque de datos desde el PC al 68HC11. El bloque se encuen-¦ ¦ tran en el buffer buff, que tiene un tamaño tam. El bloque se debe ¦ ¦ situar a patir de la dirección indicada. ¦ ¦ ¦ ¦ Cada vez que se envía un byte se llama a procedimiento sendcar pasado¦ ¦ como argumento. ¦ ¦ ¦ +----------------------------------------------------------------------+*/ { byte dirc[2]; byte tamc[2]; int i; dirc[0]=(byte) (dir & 0xFF); dirc[1]=(byte) (dir>>8 & 0xFF); tamc[0]=(byte) (tam & 0xFF); tamc[1]=(byte) (tam>>8 & 0xFF); enviar_car('B'); delay(WAIT); /* Enviar código de servicio */

120

Control de la CT6811 desde el PC

enviar_car(dirc[0]); enviar_car(dirc[1]); enviar_car(tamc[0]); enviar_car(tamc[1]);

delay(WAIT); delay(WAIT); delay(WAIT); delay(WAIT);

/* /* /* /*

Byte Byte Byte Byte

bajo alto bajo alto

direccion direccion tamaño bloque tamaño bloque

*/ */ */ */

}

i=0; /* Enviar bloque solicitado */ while (tam>0) { enviar_car(buff[i]); delay(WAIT); (*sendcar)(); i++; tam--; }

int ctreceive_block(uint dir, uint tam, byte *buff, void (*reccar)()) /*+----------------------------------------------------------------------+ ¦ Recibir un bloque de datos desde el 68HC11. El bloque tiene un ¦ ¦ tamaño tam y comienza a partir de la dirección dir. El bloque reci- ¦ ¦ bido se sitúa en el buffer buff. Cada vez que se recibe un byte se ¦ ¦ llama al procedimiento reccar pasado como parametro. ¦ ¦ ¦ ¦ La función devuelve un 0 si el CTSERVER no responde. Se envía un ¦ ¦ 1 si la operación se ha realizado con éxito. ¦ ¦ ¦ +----------------------------------------------------------------------+*/ { byte dirc[2]; byte tamc[2]; int i; int timeout; vaciar_buffer(); dirc[0]=(byte) (dir & 0xFF); dirc[1]=(byte) (dir>>8 & 0xFF); tamc[0]=(byte) (tam & 0xFF); tamc[1]=(byte) (tam>>8 & 0xFF); enviar_car('A'); enviar_car(dirc[0]); enviar_car(dirc[1]); enviar_car(tamc[0]); enviar_car(tamc[1]); delay(WAIT); delay(WAIT); delay(WAIT); delay(WAIT); delay(WAIT); /* /* /* /* /* Enviar código de servicio Byte bajo direccion Byte alto direccion Byte bajo tamaño bloque Byte alto tamaño bloque */ */ */ */ */

}

i=0; /* Enviar bloque solicitado */ while (tam>0) { buff[i]=leer_car_plazo(TIMEOUT,&timeout); if (timeout==1) { return 0; /* El servidor no responde */ } (*reccar)(); i++; tam--; } return 1;

int ctsave_eeprom(uint dir,uint tam,byte *buff, void (*eeprcar)()) /*+----------------------------------------------------------------------+ ¦ Grabar un bloque de datos en la memoria EEPROM. El bloque se encuen-¦ ¦ tran en el buffer buff, que tiene un tamaño tam. El bloque se debe ¦ ¦ situar a patir de la dirección indicada. ¦ ¦ ¦ ¦ Cada vez que se envía un byte se llama a procedimiento eepromcar ¦ ¦ pasado como argumento. ¦ ¦ ¦ ¦ La función devuelve 0 si ha ocurido algún error. Devuelve 1 en ¦ ¦ caso de exito. ¦ ¦ ¦ +----------------------------------------------------------------------+*/ { byte dirc[2]; byte tamc[2]; int i; int timeout; byte c;

121

Control de la CT6811 desde el PC

byte temp; dirc[0]=(byte) (dir & 0xFF); dirc[1]=(byte) (dir>>8 & 0xFF); tamc[0]=(byte) (tam & 0xFF); tamc[1]=(byte) (tam>>8 & 0xFF); vaciar_buffer(); enviar_car('E'); enviar_car(dirc[0]); enviar_car(dirc[1]); enviar_car(tamc[0]); enviar_car(tamc[1]); delay(WAIT); delay(WAIT); delay(WAIT); delay(WAIT); delay(WAIT); /* /* /* /* /* Enviar código de servicio Byte bajo direccion Byte alto direccion Byte bajo tamaño bloque Byte alto tamaño bloque */ */ */ */ */

}

i=0; /* Enviar bloque solicitado */ while (tam>0) { temp=buff[i]&0xFF; /* Convertir a tipo Byte */ enviar_car(temp); c=leer_car_plazo(EETIMEOUT,&timeout); if (timeout==1) return 0; /* Error: CTSERVER no responde */ if (c!=temp) return 0; /* Error: Eco incorrecto */ delay(WAIT); (*eeprcar)(); i++; tam--; } return 1;

void ctexecute(uint dir) /* +----------------------------------------------------------------------+ ¦ Llamar al servicio de salto y ejecución del servidor CTSERVER. ¦ +----------------------------------------------------------------------+*/ { byte cad[2]; cad[0]=(byte) (dir & 0xFF); cad[1]=(byte) (dir>>8 & 0xFF); enviar_car('C'); delay(WAIT); enviar_car(cad[0]); delay(WAIT); enviar_car(cad[1]); delay(WAIT);

}

void ctsend_byte(uint dir, byte valor) /*+-------------------------------------------------------+ ¦ Enviar el byte especificado en la dirección indicada. ¦ +-------------------------------------------------------+*/ { byte cad[2]; cad[0]=valor; ctsend_block(dir,1,cad,hacer_nada);

}

int ctreceive_byte(uint dir,byte *valor) /*+----------------------------------------------------------------------+ ¦ Leer un byte de la dirección de memoria especificada. La función ¦ ¦ devuelve 0 si el CTSERVER no responde. 1 en caso contrario. ¦ +----------------------------------------------------------------------+*/ { return ctreceive_block(dir,1,valor,hacer_nada); }

122

Control de la CT6811 desde el PC

7.-LIBRERIA CTCLIENT.C
/* +--------------------------------------------------------------------------+ ¦ CTCLIENT.C (c) GRUPO J&J. Agosto 1997. ¦ ¦--------------------------------------------------------------------------¦ ¦ ¦ ¦ Rutinas para la realizacion de CLIENTES para el servidor CTSERVER. ¦ ¦ ¦ ¦ Se utilizan las librerias SERIE.C y CTSERVER.C ¦ ¦ ¦ +--------------------------------------------------------------------------+ */ #include "ctserver.h" #define SI 1 #define NO 0 typedef unsigned short int byte; typedef unsigned int uint; static conexion = 0; int load(byte *valor, uint dir) /*+----------------------------------------------------------------------+ ¦ Leer un byte de la dirección de memoria especificada. La función ¦ ¦ devuelve 0 si el CTSERVER no responde. 1 en caso contrario. ¦ +----------------------------------------------------------------------+*/ { int ok; if (conexion) { ok=ctreceive_byte(dir,valor); if (!ok) { conexion = NO; return 0; } conexion = SI; return 1; } return 0;

}

int load_block(byte *buff, uint tam, uint dir, void (*reccar)()) /*+----------------------------------------------------------------------+ ¦ Recibir un bloque de datos desde el 68HC11. El bloque tiene un ¦ ¦ tamaño tam y comienza a partir de la dirección dir. El bloque reci- ¦ ¦ bido se sitúa en el buffer buff. Cada vez que se recibe un byte se ¦ ¦ llama al procedimiento reccar pasado como parametro. ¦ ¦ ¦ ¦ La función devuelve un 0 si el CTSERVER no responde. Se envía un ¦ ¦ 1 si la operación se ha realizado con éxito. ¦ ¦ ¦ +----------------------------------------------------------------------+*/ { int ok; if (conexion) { ok=ctreceive_block(dir, tam, buff, reccar); if (!ok) { conexion=NO; return 0; } conexion = SI; return 1; } return 0;

}

void store(byte valor, uint dir) /*+-------------------------------------------------------+ ¦ Enviar el byte especificado en la dirección indicada. ¦ +-------------------------------------------------------+*/ { if (conexion) ctsend_byte(dir, valor); }

123

Control de la CT6811 desde el PC

void store_block(byte *buff, uint tam, uint dir, void (*sendcar)()) /*+----------------------------------------------------------------------+ ¦ Enviar un bloque de datos desde el PC al 68HC11. El bloque se encuen-¦ ¦ tran en el buffer buff, que tiene un tamaño tam. El bloque se debe ¦ ¦ situar a patir de la dirección indicada. ¦ ¦ ¦ ¦ Cada vez que se envía un byte se llama a procedimiento sendcar pasado¦ ¦ como argumento. ¦ ¦ ¦ +----------------------------------------------------------------------+*/ { if (conexion) ctsend_block(dir,tam,buff,sendcar); } int store_block_eeprom(byte *buff, uint tam, uint dir, void (*eeprcar)()) /*+----------------------------------------------------------------------+ ¦ Grabar un bloque de datos en la memoria EEPROM. El bloque se encuen-¦ ¦ tran en el buffer buff, que tiene un tamaño tam. El bloque se debe ¦ ¦ situar a patir de la dirección indicada. ¦ ¦ ¦ ¦ Cada vez que se envía un byte se llama a procedimiento eepromcar ¦ ¦ pasado como argumento. ¦ ¦ ¦ ¦ La función devuelve 0 si ha ocurido algún error. Devuelve 1 en ¦ ¦ caso de exito. ¦ ¦ ¦ +----------------------------------------------------------------------+*/ { int ok; if (conexion) { ok=ctsave_eeprom(dir,tam,buff,eeprcar); if (!ok) { conexion=NO; return 0; } conexion=SI; return 1; } return 0;

}

int check_conexion() /* +----------------------------------------------------------------------+ ¦ Comprobar si existe conexión con el servidor CTSERVER. ¦ ¦ ¦ ¦ Se devuelve: ¦ ¦ 0 --> Servidor no responde ¦ ¦ 1 --> El servidor ha respondido ¦ +----------------------------------------------------------------------+*/ { int ok; ok=ctalive(); if (ok) { conexion=SI; return 1; } else { conexion=NO; return 0; }

}

void execute(uint dir) /* +----------------------------------------------------------------------+ ¦ Llamar al servicio de salto y ejecución del servidor CTSERVER. ¦ +----------------------------------------------------------------------+*/ { if (conexion) ctexecute(dir); } int hay_conexion() /* +----------------------------------------------------------------------+ ¦ Devolver estado conexión de la ultima comunicacion con CTSERVER ¦

124

Control de la CT6811 desde el PC

{ }

+----------------------------------------------------------------------+*/ return conexion;

125