Contenido>Indice>Intro CursoC51

ESTRUCTURAS



Las estructuras es quizás lo que hace de C un potente lenguaje que permite crear complejos programas capaces de manejar grandes cantidades de datos. Se trata básicamente de una forma de agrupar datos relacionados bajo un nombre simbólico.

¿Por qué usar estructuras?

Veamos un ejemplo: Un programa en C51 debe realizar la linealización de una señal procedente de una variedad de sensores de presión fabricados por la misma compañía. Cada sensor lleva asociada una señal de entrada con su span y offset, un coeficiente de temperatura, y un amplificador acondicionador de señal de una determinada ganancia. La información de cada sensor puede almacenarse en variables normales así:

unsigned char sensor_type1_gain = 0x30 ; 
unsigned char sensor_type1_offset = 0x50 ; 
unsigned char sensor_type1_temp_coeff = 0x60 ; 
unsigned char sensor_type1_span = 0xC4 ; 
unsigned char sensor_type1_amp_gain = 0x21 ;

unsigned char sensor_type2_gain = 0x32 ; 
unsigned char sensor_type2_offset = 0x56 ; 
unsigned char sensor_type2_temp_coeff = 0x56 ; 
unsigned char sensor_type2_span = 0xC5 ; 
unsigned char sensor_type2_amp_gain = 0x28 ;

unsigned char sensor_type3_gain = 0x20 ; 
unsigned char sensor_type3_offset = 0x43 ; 
unsigned char sensor_type3_temp_coeff = 0x61 ; 
unsigned char sensor_type3_span = 0x89 ; 
unsigned char sensor_type3_amp_gain = 0x29 ;

Como se puede ver, todos los nombres siguen el patrón:

unsigned char sensor_typeN_gain = 0x20 ; 
unsigned char sensor_typeN_offset = 0x43 ; 
unsigned char sensor_typeN_temp_coeff = 0x61 ; 
unsigned char sensor_typeN_span = 0x89 ; 
unsigned char sensor_typeN_amp_gain = 0x29 ;

Donde  'N' es el número del tipo de sensor. Una estructura es la manera ordenada de condensar este tipo de datos. De echo, la información necesaria para describir a cada sensor se reduce a:

unsigned char gain ; 
unsigned char offset ; 
unsigned char temp_coeff ; 
unsigned char span ; 
unsigned char amp_gain ;

El concepto de estructura reside en la idea de utilizar una plantilla general para datos relacionados. Para el ejemplo anterior se declara la estructura sensor_desc:

struct sensor_desc {unsigned char gain ;
              unsigned char offset ;
              unsigned char temp_coeff ;
              unsigned char span ;
              unsigned char amp_gain ; } ;

La declaración de la estructura anterior no realiza ninguna asignación de memoria. Simplemente crea una plantilla que puede utilizarse para poner datos en memoria, como se muestra a continuación:

struct sensor_desc sensor_database ;

Lo cual significa "utilizar la plantilla sensor_desc para reservar un área de memoria para el elemento sensor_database, que se refiere a un conjunto de valores similares a los de la plantilla". De esta forma se crea una estructura formada por un grupo de 5 unsigned char.

El acceso a los elementos individuales de la estructura se realiza así:

sensor_database.gain = 0x30 ; 
sensor_database.offset = 0x50 ; 
sensor_database.temp_coeff = 0x60 ; 
sensor_database.span = 0xC4 ; 
sensor_database.amp_gain = 0x21 ;

Arrays de estructuras

Siguiendo con el ejemplo anterior, la información de varios tipos de sensores se puede agrupar en un array, de igual forma que se hacía con los int y char:

struct sensor_desc sensor_database[4] ;

Esta sentencia crea cuatro estructuras en memoria, cada una de las cuales es semejante a la plantilla creada. El acceso a los elementos del array requiere ahora un índice:

/* Para trabajar con el Sensor 0 (Primer elemento de la estructura */

sensor_database[0].gain = 0x30 ; 
sensor_database[0].offset = 0x50 ; 
sensor_database[0].temp_coeff = 0x60 ; 
sensor_database[0].span = 0xC4 ; 
sensor_database[0].amp_gain = 0x21 ;

/* Para trabajar con el Sensor 1 (Segundo elemento de la estructura */

sensor_database[1].gain = 0x32 ; 
sensor_database[1].offset = 0x56 ;
sensor_database[1].temp_coeff = 0x56 ; 
sensor_database[1].span = 0xC5 ; 
sensor_database[1].amp_gain = 0x28 ;

y así sucesivamente...

Inicialización de estructuras

Al igual que sucede con los arrays, las estructuras pueden inicializarse en su declaración:

struct sensor_desc sensor_database = { 0x30, 0x50, 0x60, 0xC4, 0x21 } ;

Y el caso de un array de estructuras::

struct sensor_desc sensor_database[4] =
 { {0x30,0x50,0x60,0xC4,0x21},
    ...
   {0x32,0x56,0x56,0xC5,0x28}  
 } ;

Ubicación de estructuras en direcciones absolutas

Algunas veces es necesario colocar una estructura en una dirección absoluta. Imaginemos un chip de reloj mapeado en memoria. La estructura RTC sería:

Contenido del fichero RTCBYTES.C
         
struct RTC { unsigned char segundos;
             unsigned char minutos ;
             unsigned char horas; 
             unsigned char dias; 
} ;

struct RTC xdata RTC_chip ;  // Crea una estructura en xdata

 

Aquí se utiliza un fichero con la idea de usar el linker para asignar una dirección absoluta en XRAM a la estructura RTC_chip, tal como se muestra seguidamente:

/* Estructura ubicada en la dirección del chip de reloj */

Fichero MAIN.C

extern xdata struct RTC_chip ;

/* Otros objetos XDATA */

xdata unsigned char time_segs, time_mins ;

void main(void) {

time_segs = RTC_chip.segundos; 
time_mins = RTC_chip.minutos;
}

 

La orden al linker para ubicar la estructura RTC_chip en la dirección del chip de reloj es:

l51 main.obj,rtcbytes.obj XDATA(?XD?RTCBYTES(8000h))

Ver sección 7.6 para otros ejemplos de este método de ubicación.

Punteros a estructuras

Veamos un ejemplo de acceso a estructuras mediante punteros:

/* Definición de puntero a estructura */

struct sensor_desc *sensor_database ;  

/* Uso del puntero para acceder a los elementos de la estructura */

sensor_database->gain = 0x30 ;
sensor_database->offset = 0x50 ; 
sensor_database->temp_coeff = 0x60 ; 
sensor_database->span = 0xC4 ; 
sensor_database->amp_gain = 0x21 ;

 

Es importante destacar el uso del operador '->' para  acceder a los elementos de una estructura mediante un puntero.

Paso a funciones de punteros a estructuras

Los punteros a estructuras se utilizan frecuentemente en las llamadas a funciones, para evitar el paso de la estructura completa. De esta forma, antes de llamar a la función, solo se necesita empilar los 2 ó 3 bytes del puntero, en lugar de los 20 bytes o más que puede ocupar una estructura. El procedimiento sería:

struct sensor_desc *sensor_database ;

sensor_database-> gain = 0x30 ;
sensor_database-> offset = 0x50  ;
sensor_database-> temp_coeff = 0x60 ;
sensor_database-> span = 0xC4 ;
sensor_ database- >amp_gain = 0x21 ;

test_function(*struct_pointer) ;

test_function(struct sensor_desc *received_struct_pointer) {
   received_struct_pointer->gain = 0x20 ;
   received_struct_pointer->temp_coef = 0x40 ;
   }

 

Nota: Cuando se utiliza un puntero en una llamada a función, se permite que la función opere directamente sobre la estructura y no sobre una copia de la misma.

Punteros a estructuras en direcciones absolutas

Algunas veces es necesario colocar una estructura en una dirección absoluta. Imaginemos un chip de reloj mapeado en memoria. Un método alternativo al estudiado en el sección 6.4.4. es acceder al chip de reloj mediante un puntero a estructura.

Una diferencia importante es que en este caso no se reserva memoria para la estructura. La plantilla en este caso sería:

/* Definición de la estructura RTC */

struct RTC {char segundos;
        char minutos;
        char horas;
        char dias; } ;
             
/* Creación de un puntero a estructura */

struct RTC xdata *rtc_ptr ;  // 'xdata' indica a C51 que se trata
                     // de un dispositivo mapeado en memoria.


void main(void) {
   rtc_ptr = (void xdata *) 0x8000 ;  // rtc_ptr apunta
                          // a la dirección 0x8000 de xdata 
                          // donde está el chip de reloj
    rtc_ptr->segundos = 0 ;  // Opera con los elementos
    rtc_ptr->minutos = 0x01 ;
   }

 

Se trata de una técnica general que puede utilizarse en cualquier situación que precise de un puntero sobre una estructura residente en un dispositivo de IO. Sin embargo, es responsabilidad del usuario el asegurarse de que el linker no utilice esas direcciones para ubicar variables en RAM.

Resumiendo, el procedimiento es:

  1. Definir una plantilla
  2. Declarar un puntero a estructura
  3. En tiempo de ejecución, asignar al puntero una dirección absoluta.

   Contenido>Indice>Intro CursoC51