Problemas con estructuras y uniones en c

3

Después de muchos años comencé a divertirme para programar en C nuevamente. Ahora tengo un problema con el uso de estructuras y uniones.

La declaración:

typedef struct {
    unsigned char   SOAKTEMP;
    unsigned char   SOAKTIME;
    unsigned char   REFLOWTEMPERATURE;
    unsigned char   REFLOWTIME;
    unsigned char   BAKETEMP;
    unsigned int    BAKETIME;
    unsigned char   COOLDOWNTEMPERATURE;
    unsigned char   COOLDOWNTIME;
    unsigned char   KP;
    unsigned char   KI;
    unsigned char   KD;
    unsigned char   CYCLETIME;    
}EEPROM_DEFAULTS;

La inicialización:

EEPROM_DEFAULTS EEPD = {
                     170,    // SoakTemp
                      90,    // SoakTime   
                     220,    // ReflowTemperature
                      30,    // ReflowTime
                      80,    // BakeTemp
                    7200,   // BakeTime
                      60,    // CooldownTemperature
                       0,    // CooldownTime
                      11,    // Kp
                       5,    // Ki
                       1,    // Kd
                       5     // cycleTime

};

Para escribir y leer estos datos en la EEPROM del PIC16F18877, necesito dividir BAKETIME en un byte alto y uno bajo.

He hecho esto con una Unión.

union{ 
        unsigned int Baketime;
        struct
        {
            unsigned char BakeTimeLowbyte;
            unsigned char BaketimeHighbyte;
        };
        } BT;

Funciona, pero no me gusta la forma en que lo he hecho. He intentado de muchas maneras incorporar la unión en el typedef EEPROM_DEFAULTS pero sin éxito. Así que mi conocimiento se queda corto después de tantos años. La asistencia sería agradable.

    
pregunta Decapod

4 respuestas

5

Para integrar la unión en la estructura typdef, necesita usar el estándar C moderno. En la versión C11 de la norma, se agregaron uniones / estructuras anónimas como una característica. Con un compilador de C estándar puedes hacer esto:

typedef struct {
    unsigned char   SOAKTEMP;
    unsigned char   SOAKTIME;
    unsigned char   REFLOWTEMPERATURE;
    unsigned char   REFLOWTIME;
    unsigned char   BAKETEMP;

    union
    { 
      unsigned int  BAKETIME;
      struct
      {
        unsigned char BakeTimeLowbyte;
        unsigned char BaketimeHighbyte;
      };
    };

    unsigned char   COOLDOWNTEMPERATURE;
    unsigned char   COOLDOWNTIME;
    unsigned char   KP;
    unsigned char   KI;
    unsigned char   KD;
    unsigned char   CYCLETIME;    
}EEPROM_DEFAULTS;

Ahora puedes usar BAKETIME , BakeTimeLowbyte o BaketimeHighbyte como si fueran 3 miembros de estructura diferentes, aunque se refieran a la misma palabra de 16 bits.

Sin embargo , este código es ligeramente problemático ya que aplica el endianess del procesador en su estructura. Si el código solo se ejecutará en un PIC Little Endian, esto es quizás aceptable. Pero suponga que desea hacer lo mismo con una estructura que describe un protocolo de comunicación de datos. El remitente, el protocolo de red y el receptor pueden diferir en su naturaleza.

Por lo tanto, sería mejor mantener el número entero de 16 bits del código inicial, luego acceder a bytes individuales como este:

unsigned char hi = (unsigned char) (eeprom.BAKETIME >> 8);
unsigned char lo = (unsigned char) (eeprom.BAKETIME & 0xFF);

Este código es independiente y portátil.

Buen consejo general / revisión de código:

  • Esta estructura y la unión pueden tener bytes de relleno insertados en cualquier parte interior. El relleno nunca es un problema en MCU de 8 bits, sino en la mayoría de los otros sistemas. Es posible que desee protegerse contra el relleno involuntario al verificar que el tamaño de la estructura sea correcto, utilizando un static_assert .
  • Evite los tipos predeterminados de C, ya que no son portátiles y, a veces, tienen varios efectos secundarios desagradables, como la promoción de tipo implícita. En lugar de unsigned char y unsigned int , use uint8_t y uint16_t de stdint.h
  • Recuerde que la instancia variable de la estructura EEPROM_DEFAULTS debe declararse como volatile , ya que la EEPROM se puede cambiar en cualquier momento.
respondido por el Lundin
4

No necesitas dividir nada. Simplemente descargue la estructura en la EEPROM tal como está.

Deje que EEPD sea la estructura de EEPROM_DEFAULTS y suponga que eepromWrite8 (uint8_t data, int index) es la función de escritura.

Entonces, debería ser algo así:

eepromWrite8(*((uint8_t*)&EEPD)+index),index);

& EEPD es la dirección de EEPD, un tipo de puntero de EEPD *.

Se convierte en el tipo de puntero uint8_t *.

La parte "+ índice" es la pointer arithmetic que calcula el desplazamiento al punto resolución del tipo, por eso se llama aritmética de punteros)

Finalmente, creas un uint8_t desde donde apunta el puntero. (El asterisco de la izquierda)

    
respondido por el Ayhan
3

Lamentablemente, esta es la única forma de lograr lo que quieres en C.

Sin embargo, puedes hacer la siguiente simplificación.

typedef struct  {
    char lo, hi;
} BytePair;

typedef union {
    int intValue;
    BytePair bytes;
} Word;

// for example purposes / your large structure here
typedef struct {
    u8 a;
    u8 b;
    Word c;
} MyStruct;

Supongo que un 'int' en tu sistema tiene 16 bits de ancho?

    
respondido por el PaulHK
0

Yo diría que el método que está más claro es el mejor, veo dos formas en que puedes hacerlo. La primera es la forma en que se describe, solo usaría tipos de tamaño como uint8_t y uint16_t en lugar de unsigned int y char para mostrar explícitamente sus tamaños. La segunda forma es simplemente enmascarar los bytes individuales. Esto puede requerir un poco más de escritura, pero también es bastante claro en mi mente. La idea es hacer bit a bit y sus datos 8 bits a la vez con 8 unos que están justificados a la derecha (el byte bajo) y luego desplazar a la derecha los datos de 8 bits para mover los 8 bits significativos a la posición baja y luego repetirlos.

/* Helper Macros */
#define LOW_BYTE(data16)    ((uint8_t)(data & 0xFF))
#define HIGH_BYTE(data16)   ((uint8_t)((data>>8) & 0xFF) 
/* Actual data manipulation */
uint8_t bakeTimeHighByte = HIGH_BYTE(EEPD.BAKETIME)
uint8_t bakeTimeLowByte  = LOW_BYTE(EEPD.BAKETIME)

/* my test code */
void p(void){
    uint16_t data = 0x1234;
    printf("0x%04X\r\n", data);
    printf("Low Byte  : 0x%02X\r\n", LOW_BYTE(data));
    printf("High Byte : 0x%02X\r\n", HIGH_BYTE(data));
}

/* my output */
0x1234
Low Byte  : 0x34
High Byte : 0x12
    
respondido por el Luke Gary

Lea otras preguntas en las etiquetas