Imported Upstream version 2.9.0
[debian/cc1111] / device / examples / test2.c
1 /******************************************************************************/
2 /*                                                                            */
3 /*   2HACES.C                                                                 */
4 /*                                                                            */
5 /******************************************************************************/
6 void watchdog();
7 unsigned char codigo(unsigned char, unsigned char, unsigned char, unsigned char);
8 void contestar();
9 void inicializar_registros();
10 void bajo_consumo();
11 unsigned char recibir_trama();
12 unsigned char contestar_si_error();
13 void configurar_haz0(unsigned char, unsigned char);
14 void configurar_haz1(unsigned char, unsigned char);
15 void telec_actualizacion();
16 void telec_conformacion(unsigned char);
17 void telec_temperatura();
18 void telec_pet_Estado();
19
20 /* Fichero de definición de direcciones de memoria
21         asignadas a los diferentes registros */
22 #include <8051.h>
23
24 /* Direcciones de los puertos P4 y P5 y del TIMER3*/
25 sfr P4     = 0xC0;
26 sfr P5     = 0xC4;
27 sfr TIMER3 = 0xFF;
28 sfr ADCON  = 0xD8;
29 sfr ADAT   = 0xD7;
30
31 /* Dirección del bit que indica que hay una conversión A/D preparada */
32 #define AD_FIN               0x10
33
34 /*
35   Valores que utilizamos para obtener el código de Hamming de los mensajes.
36   El mensaje es de 29 bits (4 bytes) y la redundancia la tomamos de 11 bits.
37   La matriz de Hamming asociada será por tanto de tamaño (11x29) bits.
38   En las matrices siguiente colocamos la expresión hexadecimal de cada
39   una de las columnas de la matriz de Hamming. La obtención de los bits de
40   redundancia asociados a un mensaje los obtendremos haciendo la operación XOR
41   entre las columnas de la matriz de Hamming correspondientes a los '1' de los 29
42   bits del mensaje. Esto es más eficiente que realizar el producto matricial.
43   Como la memoria está organizada en bytes, utilizamos dos matrices. En una de ellas,
44   "Hamming_H", colocamos las tres primeras filas de la matriz de Hamming y en la otra,
45   "Hamming_L",  las 8 filas restantes.
46 */
47
48 #define Hamming_H0 0x0
49 #define Hamming_H1 0x1
50 #define Hamming_H2 0x2
51 #define Hamming_H3 0x1
52 #define Hamming_H4 0x6
53 #define Hamming_H5 0x5
54 #define Hamming_H6 0x2
55 #define Hamming_H7 0x7
56 #define Hamming_H8 0x8
57 #define Hamming_H9 0x3
58 #define Hamming_H10 0x9
59 #define Hamming_H11 0x6
60 #define Hamming_H12 0x2
61 #define Hamming_H13 0x9
62 #define Hamming_H14 0x4
63 #define Hamming_H15 0x4
64 #define Hamming_H16 0x3
65 #define Hamming_H17 0x8
66 #define Hamming_H18 0x7
67 #define Hamming_H19 0x6
68 #define Hamming_H20 0x0
69 #define Hamming_H21 0x8
70 #define Hamming_H22 0x7
71 #define Hamming_H23 0x6
72 #define Hamming_H24 0x9
73 #define Hamming_H25 0x2
74 #define Hamming_H26 0x3
75 #define Hamming_H27 0x7
76 #define Hamming_H28 0x3
77 #define Hamming_H29 0x3
78 #define Hamming_H30 0x3
79 #define Hamming_H31 0x3
80
81
82 /* Dirección del bit que inicia la conversión A/D */
83 #define AD_INI               0x08
84
85
86 /* Valores booleanos */
87 #define TRUE                 0x01
88 #define FALSE                0x00
89
90
91 /* Máscaras utilizadas para identificar los telecomandos */
92 #define CONFORMACION         0xC0
93           /* Telecomando que procesan todos los Subarrays. Se cambia el apuntamiento de la antena */
94
95 #define ACTUALIZACION        0x80
96           /*    Telecomando en el que se envía a un Subarray el próximo estado de uno de los haces */
97
98 #define TEMPERATURA          0x00
99           /* Telecomando en el que se solicita a un Subarray la temperatura del módulo T/R */
100
101 #define PETICION_ESTADO      0x40
102           /* Telecomando en el que se solicita a un Subarray el estado de programación de un haz */
103
104
105 /* Valores booleanos utilizados en las rutinas de recepción de tramas */
106 #define TIEMPO_EXCEDIDO      0x01
107 #define TIEMPO_NO_EXCEDIDO   0x00
108
109
110 /* Asociamos etiquetas a los identificadores que utiliza el
111         compilador para designar algunos registros del micro */
112 #define ACUMULADOR           ACC
113 #define BUFFER_SERIE         SBUF
114 #define DATO_RECIBIDO        RI
115 #define DATO_ENVIADO         TI
116 #define PARIDAD_ACC          P
117 #define BIT_PARIDAD_REC      RB8
118 #define BIT_PARIDAD_TRA      TB8
119
120
121 /* Sustituimos por etiquetas algunas operaciones sencillas */
122 #define obtener_direc_mensaje  (BYTE[0] & 0x3F)
123 #define direccion_CONFORMACION (BYTE[1] & 0x3F)
124 #define obtener_tipo_mensaje   (BYTE[0] & 0xC0)
125 #define direc_subarray         ((P5 & 0x7E) >> 1)
126
127
128 /* DECLARACION DE VARIABLES */
129 volatile unsigned char int_serie;
130 unsigned char BYTE[5];
131 unsigned char BYTE_MENSAJE[3];
132 volatile unsigned char ERROR_PARIDAD;
133
134
135 /*
136   Matriz que utilizamos para almacenar los estados predefinidos.
137   En las posiciones pares (0,2,4,...) están los códigos de amplitud y en las
138   impares (1,3,5,...) los de fase.
139 */
140 unsigned char tabla_estados[20];
141
142
143 /*
144   Matriz en la que almacenamos los índices a la tabla de estados correspondientes a la programación
145   actual de los dos haces. Los índices tienen 11 bits de longitud. El contenido de la matriz es
146
147                 estado_haces[0] --> haz 0, i10...i3
148                 estado_haces[1] --> haz 0,  i2...i0
149                 estado_haces[2] --> haz 1, i10...i3
150                 estado_haces[3] --> haz 1,  i2...i0
151 */
152 unsigned char estado_haces[4];
153
154
155 /*
156   Función principal
157 */
158 void main()
159 {
160         unsigned char direc_mensaje;
161         unsigned char TIMEOUT;
162         unsigned char mensaje;
163         unsigned char ERROR;
164         unsigned char DIRECCION_SUBARRAY;
165
166         inicializar_registros();
167
168         /* Obtenemos la dirección del subarray */
169         DIRECCION_SUBARRAY = direc_subarray;
170
171         /* bucle del programa */
172         while(1)
173         {
174                 /* Bandera que utilizamos para determinar si la interrupción
175                         que se produce se ha originado en el puerto serie */
176                 int_serie = 0;
177
178                 /* El micro entra en modo de bajo consumo mientras no se
179                         produzca actividad en el puerto serie */
180                 while(int_serie == 0)
181                         bajo_consumo();
182
183                 /* Se ha detectado actividad en el bus de telecomandos. Capturamos la trama */
184                 TIMEOUT = recibir_trama();
185
186                 /* Si no se ha excedido el tiempo límite se procede a identificar el mensaje recibido */
187                 if (TIMEOUT == FALSE)
188                 {
189                         mensaje       = obtener_tipo_mensaje;
190                         direc_mensaje = obtener_direc_mensaje;
191
192                         if (direc_mensaje == DIRECCION_SUBARRAY)
193                                 ERROR = contestar_si_error();
194
195                         /* Si no ha habido error en la recepción se procesa el telecomando */
196                         if (ERROR == FALSE)
197                         {
198                                 switch(mensaje)
199                                 {
200                                         case CONFORMACION    :  telec_conformacion(DIRECCION_SUBARRAY);
201
202                                                                 break;
203
204                                         case ACTUALIZACION   :  if (direc_mensaje == DIRECCION_SUBARRAY)
205                                                                     telec_actualizacion();
206
207                                                                 break;
208
209                                         case TEMPERATURA     :  if (direc_mensaje == DIRECCION_SUBARRAY)
210                                                                     telec_temperatura();
211
212                                                                 break;
213
214                                         case PETICION_ESTADO :  if (direc_mensaje == DIRECCION_SUBARRAY)
215                                                                     telec_pet_Estado();
216
217                                                                 break;
218                                 }
219                         }
220                 }
221         }
222         
223 }
224
225
226 /*
227   Subrutina que actualiza el TIMER3 con el que se implementa la función WATCHDOG.
228   Esta función se habilita conectando un pin externo a nivel bajo, y funciona de
229   forma independiente al código que se ejecuta en el micro. Cuando el TIMER3 llega
230   al final de la cuenta se produce un RESET del micro, por tanto, hay que habilitar
231   un mecanismo de recarga del CONTADOR de forma que entre dos recargas no medie un
232   tiempo mayor al que emplea la cuenta.
233   Este tiempo oscila (para la frecuencia de reloj que utilizamos, 11.059 MHz) entre
234   2.22 ms para una cuenta mínima y 569 ms para una cuenta máxima.
235   Aquí usaremos el CONTADOR con cuenta máxima, para lo cual, dado que la cuenta
236   es ascendente, el valor de recarga es 0.
237   La recarga la haremos utilizando la interrupción que provoca el TIMER0 al llegar al
238   final de la cuenta.
239 */
240 void watchdog()
241 {
242         PCON   |= 0x10;
243         /*TIMER3  = 0x00;*/ 
244 }
245
246
247 /*
248   Subrutina que atiende a la interrupción provocada por la UART del puerto serie
249   cuando se ha recibido o enviado un carácter.
250   En esta subrutina lo único que hacemos es poner el valor 1 en la variable 'int_serie'
251   para indicar que la interrupción ocurrida es debida al puerto serie. Esto es necesario
252   porque el micro sale del estado de bajo consumo mediante cualquier interrupción, y sólo
253   queremos que lo haga cuando haya actividad en el puerto serie, de forma que si se produce
254   alguna otra interrupción el micro chequea esta variable y si está a 0 vuelve a entrar en
255   el estado de bajo consumo.
256
257   La variable 'int_serie' se pone a cero siempre justo antes de entrar en el modo de bajo
258   consumo, y es únicamente en esta subrutina donde se pone a valor uno.
259 */
260 void sint(void) interrupt 4 using 2
261 {
262         int_serie = 1;
263 }
264
265
266 /*
267   Subrutina que atiende a la interrupción provocada por el TIMER0.
268   Utilizamos esta interrupción para recargar el TIMER3 que relacionado con la operación del
269   WATCHDOG.
270   Esta interrupción se produce (para la frecuencia de reloj que utilizamos, 11.059 MHz) cada
271   71.12 ms, lo que supone un margen de seguridad amplio frente a los 569 ms que emplea el
272   TIMER3 en realizar una cuenta.
273 */
274 void tint() interrupt 1 using 1
275 {
276         /* Llamamos a la subrutina que actualiza el TIMER3 del 'watchdog' */
277         watchdog();
278 }
279
280
281 /*
282   Función que implementa la codificación de Hamming (40,32) del mensaje
283   (tc1, tc2, tc3, tc4).
284   La función devuelve la redundancia del mensaje de entrada en las variables "red" 
285   Como ya hemos comentado al declarar la matriz 'Hamming', para no realizar producto de matriz
286   por vector para calcular la redundancia, almacenamos la matriz generadora de Hamming como dos
287   vectores de valores hexadecimales de longitud 29.
288   Para obtener la redundancia lo que hacemos es realizar la operación XOR entre los valores del
289   vectores "Hamming_H" cuyo índice coincide con los índices de los dígitos igual a '1'
290   en la palabra mensaje.
291   Para encontrar estos índices comparamos cada uno de los 4 bytes de que se compone el mensaje
292   con las potencias de dos (2^7, 2^6, ... , 2^1, 2^0).
293 */
294 unsigned char codigo(unsigned char tc1,unsigned char tc2,unsigned char tc3,unsigned char tc4)
295 {
296 //      unsigned char red;
297         unsigned char red = 0;
298
299         if (tc1 != 0x00)
300         {
301                 if (tc1 & 128) red ^= Hamming_H0;
302                 if (tc1 & 64 ) red ^= Hamming_H1;
303                 if (tc1 & 32 ) red ^= Hamming_H2;
304                 if (tc1 & 16 ) red ^= Hamming_H3;
305                 if (tc1 & 8  ) red ^= Hamming_H4;
306                 if (tc1 & 4  ) red ^= Hamming_H5;
307                 if (tc1 & 2  ) red ^= Hamming_H6;
308                 if (tc1 & 1  ) red ^= Hamming_H7;
309         }
310
311         if (tc2 != 0x00)
312         {
313                 if (tc2 & 128) red ^= Hamming_H8;
314                 if (tc2 & 64 ) red ^= Hamming_H9;
315                 if (tc2 & 32 ) red ^= Hamming_H10;
316                 if (tc2 & 16 ) red ^= Hamming_H11;
317                 if (tc2 & 8  ) red ^= Hamming_H12;
318                 if (tc2 & 4  ) red ^= Hamming_H13;
319                 if (tc2 & 2  ) red ^= Hamming_H14;
320                 if (tc2 & 1  ) red ^= Hamming_H15;
321         }
322
323         if (tc3 != 0x00)
324         {
325                 if (tc3 & 128) red ^= Hamming_H16;
326                 if (tc3 & 64 ) red ^= Hamming_H17;
327                 if (tc3 & 32 ) red ^= Hamming_H18;
328                 if (tc3 & 16 ) red ^= Hamming_H19;
329                 if (tc3 & 8  ) red ^= Hamming_H20;
330                 if (tc3 & 4  ) red ^= Hamming_H21;
331                 if (tc3 & 2  ) red ^= Hamming_H22;
332                 if (tc3 & 1  ) red ^= Hamming_H23;
333         }
334
335         if (tc4 != 0x00)
336         {
337                 if (tc4 & 128) red ^= Hamming_H24;
338                 if (tc4 & 64 ) red ^= Hamming_H25;
339                 if (tc4 & 32 ) red ^= Hamming_H26;
340                 if (tc4 & 16 ) red ^= Hamming_H27;
341                 if (tc4 & 8  ) red ^= Hamming_H28;
342                 if (tc4 & 4  ) red ^= Hamming_H29;
343                 if (tc4 & 2  ) red ^= Hamming_H30;
344                 if (tc4 & 1  ) red ^= Hamming_H31;
345         }
346
347         return(red);
348 }
349
350
351 /*
352   Subrutina que implementa el envío de mensajes en respuesta a los telecomandos recibidos.
353   Los mensajes enviados son siempre de dos bytes más el byte de redundancia del código de Hamming.
354   Para utilizar el mismo código en transmisión que en recepción, se añade un tercer byte con valor
355   0 cuando se calcula la redundancia del mensaje a enviar. Esto mismo hace el módulo que recibe
356   los mensajes que envían los subarrays para chequear la corrección de dichos mensajes.
357
358   El proceso de envío es el siguiente.
359
360   - Activar el 'driver' de transmisión.
361
362   - Enviar 3 bytes del mensaje:
363         * Calcular la paridad de cada byte y colarla en el registro de la UART etiquetado como
364           'BIT_PARIDAD_TRA'.
365         * Colocar el byte a transmitir en el registro de salida 'BUFFER_SERIE'.
366                   * Esperar que la UART indique en la bandera 'DATO_ENVIADO' que se ha enviado un byte
367                          antes de enviar el siguiente.
368
369   - Desactivar el 'driver' de transmisión.
370
371   Para evitar que por alguna eventualidad la UART no actualice la bandera 'DATO_ENVIADO' y el
372   programa quede indefinidamente esperando, utilizamos la variable 'CONTADOR' que se incrementa
373   cada vez que hacemos una comprobación de la bandera. Cuando se llega a un valor límite no
374   se hacen mas comprobaciones y se sigue enviando el resto del mensaje.
375   Podríamos haber optado por abortar la transmisión en el caso de se llegue al valor límite de
376   la cuenta, porque esto indicaría que se ha producido un error. Sin embargo continuamos la
377   transmisión dejando que recaiga sobre el módulo que recibe los mensajes la responsabilidad de
378   actuar frente a la detección de un error.
379   Esto no supone ningún riesgo grave porque los mensajes de respuesta a telecomandos no afectan
380   al estado de apuntamiento de la antena.
381 */
382 void contestar()
383 {
384         unsigned char CONTADOR;
385         int num_byte;
386         /*
387           Paridad impar. Metemos el bit de paridad en BIT_PARIDAD_TRA antes de mandar un byte.
388           En el "flag" PARIDAD_ACC tenemos la paridad del dato que hay en el ACUMULADOR.
389           Como la paridad es impar lo negamos
390         */
391
392         /* Activar driver transmisión:
393                 (P3.2 a 0)                 */
394         P3 &= 0xFB;
395
396         for (num_byte = 0; num_byte <3; num_byte ++)
397         {
398         ACUMULADOR      = BYTE_MENSAJE[num_byte];
399                 BIT_PARIDAD_TRA = ~PARIDAD_ACC;
400                 BUFFER_SERIE    = ACUMULADOR;
401
402                 /* Esperamos que el dato sea enviado */
403                 CONTADOR = 0;
404                 while ((DATO_ENVIADO == 0) && (CONTADOR < 254))
405                         CONTADOR++;
406
407                 /* Desactivamos el flag */
408                 DATO_ENVIADO = 0;
409         }
410
411         /* Desactivar driver */
412         P3 |= 0x04;
413 }
414
415
416 /*
417   Subrutina que se ejecuta al comenzar a funcionar el microcontrolador
418   y que adecua al cometido que ha de realizar el micro los valores en
419   algunos registros.
420 */
421 void inicializar_registros()
422 {
423         /* Registro de interrupciones:
424                 habilitamos la interrupción del TIMER0 */
425         IP =0x82;
426
427         /* Puerto serie               */
428         /* fosc = 11.059 MHz          */
429         /* Transmisión a 9600 baudios */
430         PCON = 0x00;
431         TMOD = 0x21;
432         SCON = 0xD0;
433
434         /* Registros de los 'timers'                                   */
435         /* Timer 1 en 'auto-reload' para generar velocidad transmisión */
436         /* Usamos el TIMER0 para recargar el TIMER3 mientras se        */
437         /* espera un nuevo comando                                     */
438         /* El TIMER1 se usa para generar el 'baud-rate'                */
439         /* Los timers son de 16 bits.                                  */
440         TH1 = 0xFD;
441         TL1 = 0x00;
442         TH0 = 0xFF;
443         TL0 = 0x00;
444
445         /* Activamos los 'timers' */
446         TR1 = 0x1;
447         TR0 = 0x1;
448 }
449
450
451 /*
452   Subrutina que hace que el micro entre en estado de bajo consumo
453 */
454 void bajo_consumo()
455 {
456         /* Habilitamos la interrupción del puerto serie */
457         IE    = 0x92;
458
459         /* Activamos el modo 'idle' de bajo consumo. De este modo se sale
460                           cuando se produce alguna interrupción */
461         PCON |= 1;
462
463         /* Esta instrucción se ejecuta una vez que se ha salido del modo 'idle'
464                 Deshabilitamos la interrupción del puerto serie */
465         IE    = 0x82;
466 }
467
468 /*
469   Subrutina que realiza la recepción de los telecomandos. Todos tienen una longitud de 4 bytes
470   más el byte del código de Hamming.
471   Los bytes recibidos los coloca la UART en el registro 'BUFFER_SERIE', y el programa los coloca en las
472   variables 'BYTE0...4'.
473   La recepción de cada byte supone esperar que la bandera 'DATO_RECIBIDO' sea actualizada por la UART.
474   Para evitar que por alguna eventualidad el programa pudiera quedar indefinidamente esperando que se
475   actualizara dicha bandera se utiliza el mismo método que en la subrutina de enviar mensajes, la variable
476   'CONTADOR' se incrementa a cada comprobación que se hace de la bandera. Si se alcanza un valor límite,
477   en este caso, y a diferencia de la subrutina de transmisión, se aborta la recepción y se devuelve el valor
478   'TIEMPO_EXCEDIDO' indicando que ha habido un error en la recepción.
479   En este caso si que es obligado abortar el proceso puesto que de no hacerlo se podría provocar un
480   funcionamiento incorrecto de la antena al ejecutar comandos erróneos.
481
482   A cada byte recibido se le comprueba si la paridad es la correcta. Caso de no serlo se actualiza la variable
483   global 'ERROR_PARIDAD' que evitará que se procesen telecomandos erróneos. Un error de paridad detectado no
484   aborta la recepción. Lo que se hace es que una vez acabada se envía un mensaje indicando el error detectado.
485 */
486 unsigned char recibir_trama()
487 {
488         /*
489                 Variable que utilizaremos para evitar que el programa
490                 se quede esperando si se produce una interrupción en
491                 el puerto serie, pero no llega ningún dato
492         */
493         unsigned char CONTADOR;
494
495         /* Indice del byte que se está recibiendo */
496       unsigned char num_byte;
497
498         /* Ponemos a cero el indicador de error en la paridad de los datos recibidos */
499         ERROR_PARIDAD = 0;
500
501         /* Bucle de recepción */
502         for (num_byte = 0; num_byte < 5; num_byte ++)
503         {
504                 CONTADOR = 0;
505
506                 /* esperamos que haya un dato válido */
507                 while((DATO_RECIBIDO == 0) && (CONTADOR < 254))
508                         CONTADOR++;
509
510                 /* Si no ha llegado ningún dato salimos de la función                   y devolvemos una señal indicándolo */
511                 if (CONTADOR == 254)
512                         return TIEMPO_EXCEDIDO;
513
514                 /* Desactivamos el "flag" */
515                 DATO_RECIBIDO = 0;
516
517                 /* Almacenamos el dato que ha llegado */
518                 BYTE[num_byte] = BUFFER_SERIE;
519                 ACUMULADOR = BYTE[num_byte];
520
521                 /* Determinamos la paridad del dato recibido. 'PARIDAD_ACC' indica si el dato en el acumulador
522                 tiene paridad par. Por tanto, habrá un error de paridad cuando la paridad recibida sea la misma
523                 que indica 'PARIDAD_ACC' */
524                 if (BIT_PARIDAD_REC == PARIDAD_ACC)
525                         ERROR_PARIDAD = 1;
526         }
527
528
529         /* transmisión terminada */
530         return TIEMPO_NO_EXCEDIDO;
531 }
532
533
534 /*
535   Subrutina que envía un mensaje de error si ha ocurrido alguna de las dos situaciones siguientes:
536
537   - Se ha detectado un error de paridad en al recepción de algún byte del último telecomando.
538
539   - El código de Hamming recibido no coincide con el que realmente corresponde a los cuatro bytes
540          del mensaje recibido.
541
542   En cada ocasión únicamente contesta un Subarray puesto que cada telecomando lleve incluido un campo
543   que identifica al Subarray direccionado, incluso cuando el telecomando es global. En este último caso
544   la dirección del Subarray está en el segundo byte del mensaje y en el resto de los casos en el primero.
545   La subrutina devuelve un indicador de si ha habido error.
546 */
547 unsigned char contestar_si_error()
548 {
549     BYTE_MENSAJE[2] = codigo(BYTE[0], BYTE[1], BYTE[2], BYTE[3]);
550
551         if ((ERROR_PARIDAD == 1) || (BYTE[4] != BYTE_MENSAJE[2]))
552         {
553               BYTE_MENSAJE[0] = BYTE_MENSAJE[1] = BYTE_MENSAJE[1] = 0x00;
554                 contestar();
555                 return TRUE;
556         }
557
558         else
559                 return FALSE;
560 }
561
562
563 /**********************************************************************************/
564 /*   MAPEADO DE LAS LINEAS DE CONTROL DE LOS DOS HACES CON LOS PUERTOS DEL MICRO  */
565 /*                                                                                */
566 /*   Simbolización: Haz 1, bit 2 de Amplitud -> H1A2                              */
567 /*                  Haz 0, bit 0 de Fase     -> H0F0                              */
568 /*                                                                                */
569 /*                    | BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7 |   */
570 /*                    |      |      |      |      |      |      |      |      |   */
571 /*   PUERTO 0 -> P0   | H0A9 |      |      |      |      |      |      |      |   */
572 /*                    |      |      |      |      |      |      |      |      |   */
573 /*   PUERTO 1 -> P1   | H1F0 | H1F1 | H1F2 | H1F3 | H1F4 | H0F0 | H0F1 | H0F2 |   */
574 /*                    |      |      |      |      |      |      |      |      |   */
575 /*   PUERTO 2 -> P2   | H0A0 | H0A1 | H0A2 | H0A3 | H0A4 | H0A5 | H0A6 | H0A7 |   */
576 /*                    |      |      |      |      |      |      |      |      |   */
577 /*   PUERTO 3 -> P3   |      |      |      | H1A8 | H1A9 | H0F3 | H0F4 | H0A8 |   */
578 /*                    |      |      |      |      |      |      |      |      |   */
579 /*   PUERTO 4 -> P4   | H1A0 | H1A1 | H1A2 | H1A3 | H1A4 | H1A5 | H1A6 | H1A7 |   */
580 /*                                                                                */
581 /**********************************************************************************/
582
583
584 /*
585   Subrutina que configura el HAZ 0.
586   Los valores de las líneas H9..H0 se obtienen según las siguientes expresiones:
587
588         H9 <-- ~A0
589         H8 <-- (~A3 & A2) | (A3 & A1)
590         H7 <-- (~A3 & A1) | (A3 & A2)
591         H6 <-- A3 | (A1 & ~A2)
592         H5 <-- A3 | A2
593         H4 <-- ~A1
594         H3 <-- ~A2
595         H2 <-- ~A3
596         H1 <-- A4
597         H0 <-- ~A4
598
599   donde, '~', '|' y '&' representan la negación, la operación OR y la operación AND
600   binaria, respectivamente.
601 */
602 void configurar_haz0(unsigned char amplitud, unsigned char fase)
603 {
604         unsigned char conf_P2  = 0x00;
605
606         /* ponemos a cero los bits de los puertos que vamos a cambiar mediante una operación 'OR' */
607         P0 &= 0xFE;
608         P1 &= 0x1F;
609         P3 &= 0x1F;
610
611         /* H0 */
612         conf_P2 |= (~(amplitud & 0x10))* 0x01;  
613
614         /* H1 */
615         conf_P2 |=   (amplitud & 0x10) * 0x02;
616
617         /* H2 */
618         conf_P2 |= (~(amplitud & 0x08))* 0x04;
619
620         /* H3 */
621         conf_P2 |= (~(amplitud & 0x04))* 0x08;
622
623         /* H4 */
624         conf_P2 |= (~(amplitud & 0x02))* 0x10;
625
626         /* H5 */
627         conf_P2 |= ((amplitud & 0x08) | (amplitud & 0x04))* 0x20;
628
629         /* H6 */
630         conf_P2 |= ((amplitud & 0x08) | ((amplitud & 0x02)&(~(amplitud & 0x04))))* 0x40;
631
632         /* H7 */
633         conf_P2 |= (((amplitud & 0x08)&(amplitud & 0x04)) | ((amplitud & 0x02)&(~(amplitud & 0x08))))* 0x80;
634
635         /* H8 */
636         P3 |= (((amplitud & 0x08)&(amplitud & 0x02)) | ((amplitud & 0x04)&(~(amplitud & 0x08))))* 0x80;
637         /* H9 */
638         P0 |= (~(amplitud & 0x01)) * 0x01;
639
640         /* Actualizamos el puerto P2 */
641         P2 = conf_P2;
642
643         /* Actualizamos el puerto P1 */ 
644         P1 |= (fase & 0x07) << 5;
645
646         /* Actualizamos el puerto P3 */
647         P3 |= (fase & 0x18) << 2;
648 }
649
650
651 /*
652   Subrutina que configura el HAZ 1.
653 */
654 void configurar_haz1(unsigned char amplitud, unsigned char fase)
655 {
656         unsigned char conf_P4 = 0x00;
657
658         /* ponemos a cero los bits de los puertos que vamos a cambiar mediante una operación 'OR' */
659         P1 &= 0xE0;
660         P3 &= 0xE7;
661
662         /* H0 */
663         conf_P4 |= (~(amplitud & 0x10))* 0x01;  
664
665         /* H1 */
666         conf_P4 |=   (amplitud & 0x10) * 0x02;
667
668         /* H2 */
669         conf_P4 |= (~(amplitud & 0x08))* 0x04;
670
671         /* H3 */
672         conf_P4 |= (~(amplitud & 0x04))* 0x08;
673
674         /* H4 */
675         conf_P4 |= (~(amplitud & 0x02))* 0x10;
676
677         /* H5 */
678         conf_P4 |= ((amplitud & 0x08) | (amplitud & 0x04))* 0x20;
679
680         /* H6 */
681         conf_P4 |= ((amplitud & 0x08) | ((amplitud & 0x02)&(~(amplitud & 0x04))))* 0x40;
682
683         /* H7 */
684         conf_P4 |= (((amplitud & 0x08)&(amplitud & 0x04)) | ((amplitud & 0x02)&(~(amplitud & 0x08))))* 0x80;
685
686         /* H8 */
687         P3 |= (((amplitud & 0x08)&(amplitud & 0x02)) | ((amplitud & 0x04)&(~(amplitud & 0x08))))* 0x08;
688         /* H9 */
689         P3 |= (~(amplitud & 0x01)) * 0x10;
690
691         /* Actualizamos el puerto P4 */
692         P4 = conf_P4;
693
694         /* Actualizamos el puerto P1 */ 
695         P1 |= fase;     
696 }
697
698
699 /*
700   Telecomando en el que se envía a un Subarray concreto para actualizar uno de los estados de programación
701   predefinidos.
702   El índice del estado consta de bits contenidos en BYTE1
703   Los 10 bits de la palabra del nuevo estado están en:
704                 5 bits de amplitud en BYTE2[4..0]
705                 5 bits de fase en     BYTE3[7..3]
706 */
707 void telec_actualizacion()
708 {
709         unsigned char fase, amplitud, fila, columna, i, p;
710
711         /* Obtenemos la columna correspondiente al estado que se actualiza */
712         /* dentro de la matriztabla_estados                                */
713         columna = 1 << (BYTE[1] & 0x07);
714
715         /* Obtenemos la fila */
716         fila = (BYTE[1] >> 3) * 0x0A;
717
718         /* Obtenemos el valor de la fase */
719         fase = BYTE[3];
720
721         /* Obtenemos el valor de la amplitud */
722         amplitud = BYTE[2];
723
724         /* Actualizamos la tabla                                           */
725         for (i = 0x00, p = 0x01; i < 0x05; i++, p <<= 1)
726         {
727                 /* Colocamos un '0' en todas las posiciones correspondientes */
728                 /* al estado                                                 */
729                 tabla_estados[fila+i  ] &= 0xFF ^ columna;
730                 tabla_estados[fila+i+5] &= 0xFF ^ columna;
731
732                 /* Según el valor de amplitud y fase colocamos un '1' en las */
733                 /* posiciones correspondientes                               */
734                 if (amplitud & p) tabla_estados[fila+i  ] |= columna;
735                 if (fase     & p) tabla_estados[fila+i+5] |= columna;
736         }               
737
738
739         /* Contestamos indicando que no ha habido error.
740                          Los bytes del mensaje de respuesta son 0xFC y 0x00,
741                          y completa la palabra código Hamming el byte 0x76.
742         */
743         BYTE_MENSAJE[0] = 0xFF;
744         BYTE_MENSAJE[1] = 0x00;
745         BYTE_MENSAJE[2] = 0x76;
746         contestar();
747 }
748
749
750 /*
751   Subrutina que actualiza con la nueva programación las líneas de control de la red desfasadora
752   y atenuadora.
753   La variable 'haz_conformado' contiene la información sobre el haz que hay que reprogramar.
754 */
755 void telec_conformacion(unsigned char DIRECCION_SUBARRAY)
756 {
757         unsigned char haz_conformado, amplitud = 0x00, fase = 0x00, fila, columna, i, p;
758
759         /* Obtenemos la columna dentro de la matriz tabla_estados */
760         columna = 1 << (BYTE[1] & 0x07);
761
762         /* Obtenemos la fila */
763         fila = (BYTE[1] >> 3) * 0x0A;
764
765         /* Obtenemos el haz que se reprograma */
766         haz_conformado = BYTE[3];
767
768         /* Obtenemos los valores de las palabras de amplitud y fase */
769         for (i = 0x00, p = 0x01; i < 0x05; i++, p <<= 1)
770         {
771                 if (tabla_estados[fila+i  ] & columna) amplitud |= p;
772                 if (tabla_estados[fila+i+5] & columna) fase     |= p;
773         }               
774         
775         /* Colocamos los valores correspondientes en
776                 las líneas de control */
777         if (haz_conformado == 0x00)
778         {
779                 estado_haces[0] = amplitud;
780                 estado_haces[1] = fase;
781                 configurar_haz0(amplitud, fase);
782         }
783
784         if (haz_conformado == 0x01)
785         {
786                 estado_haces[2] = amplitud;
787                 estado_haces[3] = fase;
788                 configurar_haz1(amplitud, fase);
789         }
790
791         /* Contestar, si corresponde, que no habido error */    
792         if (direccion_CONFORMACION == DIRECCION_SUBARRAY)
793         {
794                 BYTE_MENSAJE[0] = 0xFF;
795                 BYTE_MENSAJE[1] = 0x00;
796                 BYTE_MENSAJE[2] = 0x76;
797                 contestar();
798         }
799 }
800
801
802 /*
803   Subrutina que realiza la adquisición de la temperatura del módulo T/R.
804   La conversión analógica es de 10 bits.
805   El mensaje se compone de dos bytes más el código de Hamming con la siguiente distribución:
806
807                           MSB                                LSB
808         BYTE1 : 0    0    0    0    0    0    T9   T8
809         BYTE2 : T7   T6   T5   T4   T3   T2   T1   T0   
810         BYTE3 : H7   H6   H5   H4   H3   H2   H1   H0
811
812   donde T9..T0 son los 10 bits de la lectura de la temperatura y
813   H7..H0 son los 8 bits de la redundancia de Hamming.
814 */
815 void telec_temperatura()
816 {
817         unsigned char CONTADOR;
818
819         /* Reseteamos el registro de control del conversor A/D */
820         ADCON = 0x00;
821
822         /* Activar la conversión A/D */
823         ADCON = ADCON | AD_INI;
824
825         /* Esperamos a que termine el muestreo.
826                 Con la variable contador establecemos un tiempo límite de espera */
827         CONTADOR = 0;
828         while (((ADCON & AD_FIN) == 0) || (CONTADOR == 250))
829                 CONTADOR++;
830
831         /* Si se ha superado el tiempo de espera enviamos mensaje de error */
832         if (CONTADOR == 250)
833         {   
834                 BYTE_MENSAJE[0] = BYTE_MENSAJE[1] = BYTE_MENSAJE[2] = 0x00;
835                 contestar();
836                 return;
837         }
838
839         /* Realizamos la lectura del conversor */
840         BYTE_MENSAJE[0] = (ADCON & 0xC0) >> 6;
841         BYTE_MENSAJE[1] = ADAT;
842
843         /* Calculamos la palabra código */
844         BYTE_MENSAJE[2] = codigo(BYTE_MENSAJE[0], BYTE_MENSAJE[1], 0x00, 0x00);
845
846         /* Enviamos respuesta */
847         contestar();
848 }
849
850
851 /*
852   Subrutina que implementa dos funciones:
853
854   - Envía un mensaje en respuesta a un telecomando de petición de estado con la
855          programación actual del haz solicitado, información que está contenida en la
856          variable 'tabla_estados' en la forma siguiente:
857         * tabla_haces_prog[2*haz]   <- Amplitud (5bits)
858         * tabla_haces_prog[2*haz+1] <- Fase     (5bits)
859
860          El mensaje se compone de dos bytes más el código de Hamming con la siguiente distribución:
861
862                                   MSB                                LSB
863                 BYTE1 : 0    0    0    A4   A3   A2   A1   A0
864                 BYTE2 : 0    0    0    F4   F3   F2   F1   F0
865                 BYTE3 : H7   H6   H5   H4   H3   H2   H1   H0
866
867   - Provoca un RESET del microcontrolador cuando el telecomando de petición de temperatura
868          tiene el segundo byte igual a 0xFF.
869          Este RESET intencionado se utiliza cuando el módulo que distribuye los mensajes a los distintos
870          subarrays detecta que hay un error en el bus de telemedidas que persiste cuando se dirige a un
871          subarray en particular, o bien hay un subarray que no contesta. Estas anomalías posiblemente son
872          debidas a que un subarray por cualquier motivo desconocido ha sufrido un error en la posición de
873          memoria donde almacena la dirección que lo identifica, de forma que hay dos subarrays con la misma
874          dirección o bien un subarray tiene una dirección no válida (superior a 51, dirección máxima).
875 */
876 void telec_pet_Estado()
877 {
878         unsigned char haz;
879
880         /* Comprobamos si se trata de un comando de reset */
881         /* En caso afirmativo entramos en un bucle vacío  */
882         /* para que se provoque un reset */
883         if (BYTE[1] == 0xFF)
884         {
885                 /* 
886                   Deshabilitamos las interrupciones de forma que el TIMER0 no 
887                   pueda activar la rutina 'watchdog()' y se produzca un RESET.
888                 */
889                 IE = 0x00;
890
891                 /* Bucle infinito */
892                 while(1);
893         }
894
895
896         /* Si el telecomando es efectivamente de petición */
897         /* de estado obtenemos el haz direccionado        */
898         haz = BYTE[2];
899
900         /* Construimos los bytes de respuesta             */
901         BYTE_MENSAJE[0] = estado_haces[2*haz];
902         BYTE_MENSAJE[1] = estado_haces[2*haz+1];
903
904         BYTE_MENSAJE[2] = codigo(BYTE_MENSAJE[0], BYTE_MENSAJE[1], 0x00, 0x00);
905
906         /* Enviamos mensaje de respuesta                  */
907         contestar();
908 }
909
910