add test script
[fw/sdcc] / 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
298         if (tc1 != 0x00)
299         {
300                 if (tc1 & 128) red ^= Hamming_H0;
301                 if (tc1 & 64 ) red ^= Hamming_H1;
302                 if (tc1 & 32 ) red ^= Hamming_H2;
303                 if (tc1 & 16 ) red ^= Hamming_H3;
304                 if (tc1 & 8  ) red ^= Hamming_H4;
305                 if (tc1 & 4  ) red ^= Hamming_H5;
306                 if (tc1 & 2  ) red ^= Hamming_H6;
307                 if (tc1 & 1  ) red ^= Hamming_H7;
308         }
309
310         if (tc2 != 0x00)
311         {
312                 if (tc2 & 128) red ^= Hamming_H8;
313                 if (tc2 & 64 ) red ^= Hamming_H9;
314                 if (tc2 & 32 ) red ^= Hamming_H10;
315                 if (tc2 & 16 ) red ^= Hamming_H11;
316                 if (tc2 & 8  ) red ^= Hamming_H12;
317                 if (tc2 & 4  ) red ^= Hamming_H13;
318                 if (tc2 & 2  ) red ^= Hamming_H14;
319                 if (tc2 & 1  ) red ^= Hamming_H15;
320         }
321
322         if (tc3 != 0x00)
323         {
324                 if (tc3 & 128) red ^= Hamming_H16;
325                 if (tc3 & 64 ) red ^= Hamming_H17;
326                 if (tc3 & 32 ) red ^= Hamming_H18;
327                 if (tc3 & 16 ) red ^= Hamming_H19;
328                 if (tc3 & 8  ) red ^= Hamming_H20;
329                 if (tc3 & 4  ) red ^= Hamming_H21;
330                 if (tc3 & 2  ) red ^= Hamming_H22;
331                 if (tc3 & 1  ) red ^= Hamming_H23;
332         }
333
334         if (tc4 != 0x00)
335         {
336                 if (tc4 & 128) red ^= Hamming_H24;
337                 if (tc4 & 64 ) red ^= Hamming_H25;
338                 if (tc4 & 32 ) red ^= Hamming_H26;
339                 if (tc4 & 16 ) red ^= Hamming_H27;
340                 if (tc4 & 8  ) red ^= Hamming_H28;
341                 if (tc4 & 4  ) red ^= Hamming_H29;
342                 if (tc4 & 2  ) red ^= Hamming_H30;
343                 if (tc4 & 1  ) red ^= Hamming_H31;
344         }
345
346         return(red);
347 }
348
349
350 /*
351   Subrutina que implementa el envío de mensajes en respuesta a los telecomandos recibidos.
352   Los mensajes enviados son siempre de dos bytes más el byte de redundancia del código de Hamming.
353   Para utilizar el mismo código en transmisión que en recepción, se añade un tercer byte con valor
354   0 cuando se calcula la redundancia del mensaje a enviar. Esto mismo hace el módulo que recibe
355   los mensajes que envían los subarrays para chequear la corrección de dichos mensajes.
356
357   El proceso de envío es el siguiente.
358
359   - Activar el 'driver' de transmisión.
360
361   - Enviar 3 bytes del mensaje:
362         * Calcular la paridad de cada byte y colarla en el registro de la UART etiquetado como
363           'BIT_PARIDAD_TRA'.
364         * Colocar el byte a transmitir en el registro de salida 'BUFFER_SERIE'.
365                   * Esperar que la UART indique en la bandera 'DATO_ENVIADO' que se ha enviado un byte
366                          antes de enviar el siguiente.
367
368   - Desactivar el 'driver' de transmisión.
369
370   Para evitar que por alguna eventualidad la UART no actualice la bandera 'DATO_ENVIADO' y el
371   programa quede indefinidamente esperando, utilizamos la variable 'CONTADOR' que se incrementa
372   cada vez que hacemos una comprobación de la bandera. Cuando se llega a un valor límite no
373   se hacen mas comprobaciones y se sigue enviando el resto del mensaje.
374   Podríamos haber optado por abortar la transmisión en el caso de se llegue al valor límite de
375   la cuenta, porque esto indicaría que se ha producido un error. Sin embargo continuamos la
376   transmisión dejando que recaiga sobre el módulo que recibe los mensajes la responsabilidad de
377   actuar frente a la detección de un error.
378   Esto no supone ningún riesgo grave porque los mensajes de respuesta a telecomandos no afectan
379   al estado de apuntamiento de la antena.
380 */
381 void contestar()
382 {
383         unsigned char CONTADOR;
384         int num_byte;
385         /*
386           Paridad impar. Metemos el bit de paridad en BIT_PARIDAD_TRA antes de mandar un byte.
387           En el "flag" PARIDAD_ACC tenemos la paridad del dato que hay en el ACUMULADOR.
388           Como la paridad es impar lo negamos
389         */
390
391         /* Activar driver transmisión:
392                 (P3.2 a 0)                 */
393         P3 &= 0xFB;
394
395         for (num_byte = 0; num_byte <3; num_byte ++)
396         {
397         ACUMULADOR      = BYTE_MENSAJE[num_byte];
398                 BIT_PARIDAD_TRA = ~PARIDAD_ACC;
399                 BUFFER_SERIE    = ACUMULADOR;
400
401                 /* Esperamos que el dato sea enviado */
402                 CONTADOR = 0;
403                 while ((DATO_ENVIADO == 0) && (CONTADOR < 254))
404                         CONTADOR++;
405
406                 /* Desactivamos el flag */
407                 DATO_ENVIADO = 0;
408         }
409
410         /* Desactivar driver */
411         P3 |= 0x04;
412 }
413
414
415 /*
416   Subrutina que se ejecuta al comenzar a funcionar el microcontrolador
417   y que adecua al cometido que ha de realizar el micro los valores en
418   algunos registros.
419 */
420 void inicializar_registros()
421 {
422         /* Registro de interrupciones:
423                 habilitamos la interrupción del TIMER0 */
424         IP =0x82;
425
426         /* Puerto serie               */
427         /* fosc = 11.059 MHz          */
428         /* Transmisión a 9600 baudios */
429         PCON = 0x00;
430         TMOD = 0x21;
431         SCON = 0xD0;
432
433         /* Registros de los 'timers'                                   */
434         /* Timer 1 en 'auto-reload' para generar velocidad transmisión */
435         /* Usamos el TIMER0 para recargar el TIMER3 mientras se        */
436         /* espera un nuevo comando                                     */
437         /* El TIMER1 se usa para generar el 'baud-rate'                */
438         /* Los timers son de 16 bits.                                  */
439         TH1 = 0xFD;
440         TL1 = 0x00;
441         TH0 = 0xFF;
442         TL0 = 0x00;
443
444         /* Activamos los 'timers' */
445         TR1 = 0x1;
446         TR0 = 0x1;
447 }
448
449
450 /*
451   Subrutina que hace que el micro entre en estado de bajo consumo
452 */
453 void bajo_consumo()
454 {
455         /* Habilitamos la interrupción del puerto serie */
456         IE    = 0x92;
457
458         /* Activamos el modo 'idle' de bajo consumo. De este modo se sale
459                           cuando se produce alguna interrupción */
460         PCON |= 1;
461
462         /* Esta instrucción se ejecuta una vez que se ha salido del modo 'idle'
463                 Deshabilitamos la interrupción del puerto serie */
464         IE    = 0x82;
465 }
466
467 /*
468   Subrutina que realiza la recepción de los telecomandos. Todos tienen una longitud de 4 bytes
469   más el byte del código de Hamming.
470   Los bytes recibidos los coloca la UART en el registro 'BUFFER_SERIE', y el programa los coloca en las
471   variables 'BYTE0...4'.
472   La recepción de cada byte supone esperar que la bandera 'DATO_RECIBIDO' sea actualizada por la UART.
473   Para evitar que por alguna eventualidad el programa pudiera quedar indefinidamente esperando que se
474   actualizara dicha bandera se utiliza el mismo método que en la subrutina de enviar mensajes, la variable
475   'CONTADOR' se incrementa a cada comprobación que se hace de la bandera. Si se alcanza un valor límite,
476   en este caso, y a diferencia de la subrutina de transmisión, se aborta la recepción y se devuelve el valor
477   'TIEMPO_EXCEDIDO' indicando que ha habido un error en la recepción.
478   En este caso si que es obligado abortar el proceso puesto que de no hacerlo se podría provocar un
479   funcionamiento incorrecto de la antena al ejecutar comandos erróneos.
480
481   A cada byte recibido se le comprueba si la paridad es la correcta. Caso de no serlo se actualiza la variable
482   global 'ERROR_PARIDAD' que evitará que se procesen telecomandos erróneos. Un error de paridad detectado no
483   aborta la recepción. Lo que se hace es que una vez acabada se envía un mensaje indicando el error detectado.
484 */
485 unsigned char recibir_trama()
486 {
487         /*
488                 Variable que utilizaremos para evitar que el programa
489                 se quede esperando si se produce una interrupción en
490                 el puerto serie, pero no llega ningún dato
491         */
492         unsigned char CONTADOR;
493
494         /* Indice del byte que se está recibiendo */
495       unsigned char num_byte;
496
497         /* Ponemos a cero el indicador de error en la paridad de los datos recibidos */
498         ERROR_PARIDAD = 0;
499
500         /* Bucle de recepción */
501         for (num_byte = 0; num_byte < 5; num_byte ++)
502         {
503                 CONTADOR = 0;
504
505                 /* esperamos que haya un dato válido */
506                 while((DATO_RECIBIDO == 0) && (CONTADOR < 254))
507                         CONTADOR++;
508
509                 /* Si no ha llegado ningún dato salimos de la función                   y devolvemos una señal indicándolo */
510                 if (CONTADOR == 254)
511                         return TIEMPO_EXCEDIDO;
512
513                 /* Desactivamos el "flag" */
514                 DATO_RECIBIDO = 0;
515
516                 /* Almacenamos el dato que ha llegado */
517                 BYTE[num_byte] = BUFFER_SERIE;
518                 ACUMULADOR = BYTE[num_byte];
519
520                 /* Determinamos la paridad del dato recibido. 'PARIDAD_ACC' indica si el dato en el acumulador
521                 tiene paridad par. Por tanto, habrá un error de paridad cuando la paridad recibida sea la misma
522                 que indica 'PARIDAD_ACC' */
523                 if (BIT_PARIDAD_REC == PARIDAD_ACC)
524                         ERROR_PARIDAD = 1;
525         }
526
527
528         /* transmisión terminada */
529         return TIEMPO_NO_EXCEDIDO;
530 }
531
532
533 /*
534   Subrutina que envía un mensaje de error si ha ocurrido alguna de las dos situaciones siguientes:
535
536   - Se ha detectado un error de paridad en al recepción de algún byte del último telecomando.
537
538   - El código de Hamming recibido no coincide con el que realmente corresponde a los cuatro bytes
539          del mensaje recibido.
540
541   En cada ocasión únicamente contesta un Subarray puesto que cada telecomando lleve incluido un campo
542   que identifica al Subarray direccionado, incluso cuando el telecomando es global. En este último caso
543   la dirección del Subarray está en el segundo byte del mensaje y en el resto de los casos en el primero.
544   La subrutina devuelve un indicador de si ha habido error.
545 */
546 unsigned char contestar_si_error()
547 {
548     BYTE_MENSAJE[2] = codigo(BYTE[0], BYTE[1], BYTE[2], BYTE[3]);
549
550         if ((ERROR_PARIDAD == 1) || (BYTE[4] != BYTE_MENSAJE[2]))
551         {
552               BYTE_MENSAJE[0] = BYTE_MENSAJE[1] = BYTE_MENSAJE[1] = 0x00;
553                 contestar();
554                 return TRUE;
555         }
556
557         else
558                 return FALSE;
559 }
560
561
562 /**********************************************************************************/
563 /*   MAPEADO DE LAS LINEAS DE CONTROL DE LOS DOS HACES CON LOS PUERTOS DEL MICRO  */
564 /*                                                                                */
565 /*   Simbolización: Haz 1, bit 2 de Amplitud -> H1A2                              */
566 /*                  Haz 0, bit 0 de Fase     -> H0F0                              */
567 /*                                                                                */
568 /*                    | BIT0 | BIT1 | BIT2 | BIT3 | BIT4 | BIT5 | BIT6 | BIT7 |   */
569 /*                    |      |      |      |      |      |      |      |      |   */
570 /*   PUERTO 0 -> P0   | H0A9 |      |      |      |      |      |      |      |   */
571 /*                    |      |      |      |      |      |      |      |      |   */
572 /*   PUERTO 1 -> P1   | H1F0 | H1F1 | H1F2 | H1F3 | H1F4 | H0F0 | H0F1 | H0F2 |   */
573 /*                    |      |      |      |      |      |      |      |      |   */
574 /*   PUERTO 2 -> P2   | H0A0 | H0A1 | H0A2 | H0A3 | H0A4 | H0A5 | H0A6 | H0A7 |   */
575 /*                    |      |      |      |      |      |      |      |      |   */
576 /*   PUERTO 3 -> P3   |      |      |      | H1A8 | H1A9 | H0F3 | H0F4 | H0A8 |   */
577 /*                    |      |      |      |      |      |      |      |      |   */
578 /*   PUERTO 4 -> P4   | H1A0 | H1A1 | H1A2 | H1A3 | H1A4 | H1A5 | H1A6 | H1A7 |   */
579 /*                                                                                */
580 /**********************************************************************************/
581
582
583 /*
584   Subrutina que configura el HAZ 0.
585   Los valores de las líneas H9..H0 se obtienen según las siguientes expresiones:
586
587         H9 <-- ~A0
588         H8 <-- (~A3 & A2) | (A3 & A1)
589         H7 <-- (~A3 & A1) | (A3 & A2)
590         H6 <-- A3 | (A1 & ~A2)
591         H5 <-- A3 | A2
592         H4 <-- ~A1
593         H3 <-- ~A2
594         H2 <-- ~A3
595         H1 <-- A4
596         H0 <-- ~A4
597
598   donde, '~', '|' y '&' representan la negación, la operación OR y la operación AND
599   binaria, respectivamente.
600 */
601 void configurar_haz0(unsigned char amplitud, unsigned char fase)
602 {
603         unsigned char conf_P2  = 0x00;
604
605         /* ponemos a cero los bits de los puertos que vamos a cambiar mediante una operación 'OR' */
606         P0 &= 0xFE;
607         P1 &= 0x1F;
608         P3 &= 0x1F;
609
610         /* H0 */
611         conf_P2 |= (~(amplitud & 0x10))* 0x01;  
612
613         /* H1 */
614         conf_P2 |=   (amplitud & 0x10) * 0x02;
615
616         /* H2 */
617         conf_P2 |= (~(amplitud & 0x08))* 0x04;
618
619         /* H3 */
620         conf_P2 |= (~(amplitud & 0x04))* 0x08;
621
622         /* H4 */
623         conf_P2 |= (~(amplitud & 0x02))* 0x10;
624
625         /* H5 */
626         conf_P2 |= ((amplitud & 0x08) | (amplitud & 0x04))* 0x20;
627
628         /* H6 */
629         conf_P2 |= ((amplitud & 0x08) | ((amplitud & 0x02)&(~(amplitud & 0x04))))* 0x40;
630
631         /* H7 */
632         conf_P2 |= (((amplitud & 0x08)&(amplitud & 0x04)) | ((amplitud & 0x02)&(~(amplitud & 0x08))))* 0x80;
633
634         /* H8 */
635         P3 |= (((amplitud & 0x08)&(amplitud & 0x02)) | ((amplitud & 0x04)&(~(amplitud & 0x08))))* 0x80;
636         /* H9 */
637         P0 |= (~(amplitud & 0x01)) * 0x01;
638
639         /* Actualizamos el puerto P2 */
640         P2 = conf_P2;
641
642         /* Actualizamos el puerto P1 */ 
643         P1 |= (fase & 0x07) << 5;
644
645         /* Actualizamos el puerto P3 */
646         P3 |= (fase & 0x18) << 2;
647 }
648
649
650 /*
651   Subrutina que configura el HAZ 1.
652 */
653 void configurar_haz1(unsigned char amplitud, unsigned char fase)
654 {
655         unsigned char conf_P4 = 0x00;
656
657         /* ponemos a cero los bits de los puertos que vamos a cambiar mediante una operación 'OR' */
658         P1 &= 0xE0;
659         P3 &= 0xE7;
660
661         /* H0 */
662         conf_P4 |= (~(amplitud & 0x10))* 0x01;  
663
664         /* H1 */
665         conf_P4 |=   (amplitud & 0x10) * 0x02;
666
667         /* H2 */
668         conf_P4 |= (~(amplitud & 0x08))* 0x04;
669
670         /* H3 */
671         conf_P4 |= (~(amplitud & 0x04))* 0x08;
672
673         /* H4 */
674         conf_P4 |= (~(amplitud & 0x02))* 0x10;
675
676         /* H5 */
677         conf_P4 |= ((amplitud & 0x08) | (amplitud & 0x04))* 0x20;
678
679         /* H6 */
680         conf_P4 |= ((amplitud & 0x08) | ((amplitud & 0x02)&(~(amplitud & 0x04))))* 0x40;
681
682         /* H7 */
683         conf_P4 |= (((amplitud & 0x08)&(amplitud & 0x04)) | ((amplitud & 0x02)&(~(amplitud & 0x08))))* 0x80;
684
685         /* H8 */
686         P3 |= (((amplitud & 0x08)&(amplitud & 0x02)) | ((amplitud & 0x04)&(~(amplitud & 0x08))))* 0x08;
687         /* H9 */
688         P3 |= (~(amplitud & 0x01)) * 0x10;
689
690         /* Actualizamos el puerto P4 */
691         P4 = conf_P4;
692
693         /* Actualizamos el puerto P1 */ 
694         P1 |= fase;     
695 }
696
697
698 /*
699   Telecomando en el que se envía a un Subarray concreto para actualizar uno de los estados de programación
700   predefinidos.
701   El índice del estado consta de bits contenidos en BYTE1
702   Los 10 bits de la palabra del nuevo estado están en:
703                 5 bits de amplitud en BYTE2[4..0]
704                 5 bits de fase en     BYTE3[7..3]
705 */
706 void telec_actualizacion()
707 {
708         unsigned char fase, amplitud, fila, columna, i, p;
709
710         /* Obtenemos la columna correspondiente al estado que se actualiza */
711         /* dentro de la matriztabla_estados                                */
712         columna = 1 << (BYTE[1] & 0x07);
713
714         /* Obtenemos la fila */
715         fila = (BYTE[1] >> 3) * 0x0A;
716
717         /* Obtenemos el valor de la fase */
718         fase = BYTE[3];
719
720         /* Obtenemos el valor de la amplitud */
721         amplitud = BYTE[2];
722
723         /* Actualizamos la tabla                                           */
724         for (i = 0x00, p = 0x01; i < 0x05; i++, p <<= 1)
725         {
726                 /* Colocamos un '0' en todas las posiciones correspondientes */
727                 /* al estado                                                 */
728                 tabla_estados[fila+i  ] &= 0xFF ^ columna;
729                 tabla_estados[fila+i+5] &= 0xFF ^ columna;
730
731                 /* Según el valor de amplitud y fase colocamos un '1' en las */
732                 /* posiciones correspondientes                               */
733                 if (amplitud & p) tabla_estados[fila+i  ] |= columna;
734                 if (fase     & p) tabla_estados[fila+i+5] |= columna;
735         }               
736
737
738         /* Contestamos indicando que no ha habido error.
739                          Los bytes del mensaje de respuesta son 0xFC y 0x00,
740                          y completa la palabra código Hamming el byte 0x76.
741         */
742         BYTE_MENSAJE[0] = 0xFF;
743         BYTE_MENSAJE[1] = 0x00;
744         BYTE_MENSAJE[2] = 0x76;
745         contestar();
746 }
747
748
749 /*
750   Subrutina que actualiza con la nueva programación las líneas de control de la red desfasadora
751   y atenuadora.
752   La variable 'haz_conformado' contiene la información sobre el haz que hay que reprogramar.
753 */
754 void telec_conformacion(unsigned char DIRECCION_SUBARRAY)
755 {
756         unsigned char haz_conformado, amplitud = 0x00, fase = 0x00, fila, columna, i, p;
757
758         /* Obtenemos la columna dentro de la matriz tabla_estados */
759         columna = 1 << (BYTE[1] & 0x07);
760
761         /* Obtenemos la fila */
762         fila = (BYTE[1] >> 3) * 0x0A;
763
764         /* Obtenemos el haz que se reprograma */
765         haz_conformado = BYTE[3];
766
767         /* Obtenemos los valores de las palabras de amplitud y fase */
768         for (i = 0x00, p = 0x01; i < 0x05; i++, p <<= 1)
769         {
770                 if (tabla_estados[fila+i  ] & columna) amplitud |= p;
771                 if (tabla_estados[fila+i+5] & columna) fase     |= p;
772         }               
773         
774         /* Colocamos los valores correspondientes en
775                 las líneas de control */
776         if (haz_conformado == 0x00)
777         {
778                 estado_haces[0] = amplitud;
779                 estado_haces[1] = fase;
780                 configurar_haz0(amplitud, fase);
781         }
782
783         if (haz_conformado == 0x01)
784         {
785                 estado_haces[2] = amplitud;
786                 estado_haces[3] = fase;
787                 configurar_haz1(amplitud, fase);
788         }
789
790         /* Contestar, si corresponde, que no habido error */    
791         if (direccion_CONFORMACION == DIRECCION_SUBARRAY)
792         {
793                 BYTE_MENSAJE[0] = 0xFF;
794                 BYTE_MENSAJE[1] = 0x00;
795                 BYTE_MENSAJE[2] = 0x76;
796                 contestar();
797         }
798 }
799
800
801 /*
802   Subrutina que realiza la adquisición de la temperatura del módulo T/R.
803   La conversión analógica es de 10 bits.
804   El mensaje se compone de dos bytes más el código de Hamming con la siguiente distribución:
805
806                           MSB                                LSB
807         BYTE1 : 0    0    0    0    0    0    T9   T8
808         BYTE2 : T7   T6   T5   T4   T3   T2   T1   T0   
809         BYTE3 : H7   H6   H5   H4   H3   H2   H1   H0
810
811   donde T9..T0 son los 10 bits de la lectura de la temperatura y
812   H7..H0 son los 8 bits de la redundancia de Hamming.
813 */
814 void telec_temperatura()
815 {
816         unsigned char CONTADOR;
817
818         /* Reseteamos el registro de control del conversor A/D */
819         ADCON = 0x00;
820
821         /* Activar la conversión A/D */
822         ADCON = ADCON | AD_INI;
823
824         /* Esperamos a que termine el muestreo.
825                 Con la variable contador establecemos un tiempo límite de espera */
826         CONTADOR = 0;
827         while (((ADCON & AD_FIN) == 0) || (CONTADOR == 250))
828                 CONTADOR++;
829
830         /* Si se ha superado el tiempo de espera enviamos mensaje de error */
831         if (CONTADOR == 250)
832         {   
833                 BYTE_MENSAJE[0] = BYTE_MENSAJE[1] = BYTE_MENSAJE[2] = 0x00;
834                 contestar();
835                 return;
836         }
837
838         /* Realizamos la lectura del conversor */
839         BYTE_MENSAJE[0] = (ADCON & 0xC0) >> 6;
840         BYTE_MENSAJE[1] = ADAT;
841
842         /* Calculamos la palabra código */
843         BYTE_MENSAJE[2] = codigo(BYTE_MENSAJE[0], BYTE_MENSAJE[1], 0x00, 0x00);
844
845         /* Enviamos respuesta */
846         contestar();
847 }
848
849
850 /*
851   Subrutina que implementa dos funciones:
852
853   - Envía un mensaje en respuesta a un telecomando de petición de estado con la
854          programación actual del haz solicitado, información que está contenida en la
855          variable 'tabla_estados' en la forma siguiente:
856         * tabla_haces_prog[2*haz]   <- Amplitud (5bits)
857         * tabla_haces_prog[2*haz+1] <- Fase     (5bits)
858
859          El mensaje se compone de dos bytes más el código de Hamming con la siguiente distribución:
860
861                                   MSB                                LSB
862                 BYTE1 : 0    0    0    A4   A3   A2   A1   A0
863                 BYTE2 : 0    0    0    F4   F3   F2   F1   F0
864                 BYTE3 : H7   H6   H5   H4   H3   H2   H1   H0
865
866   - Provoca un RESET del microcontrolador cuando el telecomando de petición de temperatura
867          tiene el segundo byte igual a 0xFF.
868          Este RESET intencionado se utiliza cuando el módulo que distribuye los mensajes a los distintos
869          subarrays detecta que hay un error en el bus de telemedidas que persiste cuando se dirige a un
870          subarray en particular, o bien hay un subarray que no contesta. Estas anomalías posiblemente son
871          debidas a que un subarray por cualquier motivo desconocido ha sufrido un error en la posición de
872          memoria donde almacena la dirección que lo identifica, de forma que hay dos subarrays con la misma
873          dirección o bien un subarray tiene una dirección no válida (superior a 51, dirección máxima).
874 */
875 void telec_pet_Estado()
876 {
877         unsigned char haz;
878
879         /* Comprobamos si se trata de un comando de reset */
880         /* En caso afirmativo entramos en un bucle vacío  */
881         /* para que se provoque un reset */
882         if (BYTE[1] == 0xFF)
883         {
884                 /* 
885                   Deshabilitamos las interrupciones de forma que el TIMER0 no 
886                   pueda activar la rutina 'watchdog()' y se produzca un RESET.
887                 */
888                 IE = 0x00;
889
890                 /* Bucle infinito */
891                 while(1);
892         }
893
894
895         /* Si el telecomando es efectivamente de petición */
896         /* de estado obtenemos el haz direccionado        */
897         haz = BYTE[2];
898
899         /* Construimos los bytes de respuesta             */
900         BYTE_MENSAJE[0] = estado_haces[2*haz];
901         BYTE_MENSAJE[1] = estado_haces[2*haz+1];
902
903         BYTE_MENSAJE[2] = codigo(BYTE_MENSAJE[0], BYTE_MENSAJE[1], 0x00, 0x00);
904
905         /* Enviamos mensaje de respuesta                  */
906         contestar();
907 }
908
909