Escribir en conjuntos de dos bits en un registro a la vez en C

2

Estoy tratando de establecer múltiples valores en un registro de 32 bits, y cada valor tiene 2 bits. Tratando de encontrar una manera elegante de hacer esto.

Escribe ahora que tengo algo como esto:

struct MuxRegister {
Uint16 PIN1:2;
Uint16 PIN2:2;
...
}

#define LED PIN1      (in a different file of course)
#define PWM      PIN2
#define Mux_GPIO 1         (value from 0-3 selects different options)
#define Mux_PWM  3
MuxRegister.LED = Mux_GPIO;
MuxRegister.PWM = Mux_PWM;
...

Y quiero hacer algo donde no tenga que ir a través de cada MuxRegister.X y MuxRegister.Y para cada uno de los 16 pines en la estructura.

Mi idea era hacer algo como esto:

#define BIT1 0x00000001
#define BIT2 0x00000002
#define BIT1 0x00000004
#define BIT2 0x00000008
#define LED (BIT1&&BIT2)
#define PWM (BIT3&&BIT4)
MuxRegister |= ((Mux_GPIO && LED) || (Mux_PWM && PWM) || ... ; 

Pero no hay ningún mecanismo para preservar el espaciado del registro Mux (valores entre 0 y 3) más allá de la primera posición. Al igual que en, el valor para Mux_PWM en este ejemplo es 0x00000003 pero el valor para PWM es 0x0000000C, y no interactúan en absoluto ya que no están "superpuestos".

¿Existe un mecanismo con el cual espaciar los valores de los códigos Mux para que interactúen con los valores de pin correctos? Lo siento si no estoy explicando bien la pregunta. No tengo mucha terminología correcta.

    
pregunta Loo Yung

3 respuestas

1

Intentaría usar un uint32_t en lugar de campos de bits. Esto es lo que probablemente haría:

typedef enum
{
    LED,
    PWM,
    ...
} MUX_E;

uint32_t MuxRegister;

void SetMuxOption (uint8_t const option, MUX_E const bits)
{
    MuxRegister &= ~(uint32_t) (3 << 2*bits);
    MuxRegister |= (uint32_t) (option << 2*bits);
}
    
respondido por el Swarles Barkely
0

No hay una forma super-concisa y elegante de hacer esto que yo sepa. Si desea establecer varios bits, necesita, como mínimo, la siguiente información para cada campo:

  1. La posición del bit (cambio)
  2. El número de bits en el campo (máscara)
  3. Los datos que van en el campo

Parece que también quieres dar nombres funcionales a los pines y los valores mux. Eso es mucha información.

Honestamente, creo que tu primer fragmento de código es un buen comienzo. La estructura del campo de bits puede codificar los cambios, máscaras y nombres de pin en el tipo de estructura, lo que hace que el código sea razonablemente conciso. Puede guardar un paso nombrando los campos de bits LED, PWM, etc. en lugar de PIN1, PIN2, etc.

Alternativamente, puede reducir el tamaño del código haciendo una matriz constante de valores mux, y luego cargándolos a través de un bucle for:

//                          P1: LED   P2: PWM   P3: ADC   P4: SW
const Uint32 muxValues[] = {MUX_GPIO, MUX_PWM,  MUX_ADC,  MUX_GPIO, ... //12 more

MuxRegister = 0x00000000;
for (pin = 0; pin < sizeof(muxValues); pin++)
{
    MuxRegister |= muxValues[pin] << 2*pin;
}

O podrías hacer algo como:

MuxRegister = (MUX_LED << 0) | (MUX_PWM << 2) | (MUX_ADC << 4) | (MUX_GPIO << 6) | ...

Puedes obtener lujosas macros para mejorar la legibilidad:

#define PIN(p)  (2 * (p - 1))
MuxRegister = (MUX_LED << PIN(1)) | (MUX_PWM << PIN(2)) | (MUX_ADC << PIN(3)) | (MUX_GPIO << PIN(4)) | ...

Pero no creo que encuentres algo mucho más corto. Tiene dieciséis selecciones de mux únicas, y de una forma u otra, todas tienen que ir en su código. Si quieres nombres de pin únicos, son otros dieciséis valores.

    
respondido por el Adam Haun
0

Los sindicatos pueden ser tus amigos:

typedef union {
    struct {
        Uint32 PIN1:2;
        Uint32 PIN2:2;
        ...
    } bits ;
    Uint32 reg;
} MuxRegister;

Ahora puedes acceder a él como:

MuxRegister muxReg;
...
muxReg.reg = 1234; //As an integer
muxReg.bits.PIN1 = 1; //As bits.

Notarás que hice las entradas de campo de bits Uint32 porque estás usando registros de 32 bits (¿no estás seguro de por qué los hiciste Uint16?). También debe considerar el uso de tipos estándar de <stdint.h> como uint32_t para una mejor portabilidad.

Dependiendo de tu compilador y de si permite estructuras anónimas, puedes hacerlo:

typedef union {
    struct {
        Uint32 PIN1:2;
        Uint32 PIN2:2;
        ...
    };
    Uint32 reg;
} MuxRegister;

A la que se accedería como:

MuxRegister muxReg;
...
muxReg.reg = 1234; //As an integer
muxReg.PIN1 = 1; //As bits.

A veces las uniones pueden ser un problema, pero la mayoría de las veces funcionan bien. La mayoría de las veces el problema se debe a la alineación de las palabras, pero esto es bastante fácil de verificar. Este bit de código realmente útil permite el tiempo de compilación, comprobando que una unión es del tamaño esperado, por lo general, si la alineación se altera, el tamaño cambiará.

#define ASSERT_CONCAT_(a, b) a##b
#define ASSERT_CONCAT(a, b) ASSERT_CONCAT_(a, b)
#define ct_assert(a,e) enum { ASSERT_CONCAT(assert_sizeof_, a) = 1/(!!(sizeof(a) == e)) }

Entonces puedes hacer:

ct_assert(MuxRegister,sizeof(Uint32));

Lo que marcará un error si la unión no es una palabra de 32 bits.

Además, como nota al margen, tenga cuidado con sus operadores.

MuxRegister |= ((Mux_GPIO && LED) || (Mux_PWM && PWM) || ... ; 

Eso haría bit a bit o MuxRegister con boolean true o false. ¿Por qué? Porque estás utilizando operadores lógicos. (Mux_GPIO && LED) resultará en true si ambos valores son distintos de cero o false de lo contrario.

Creo que lo que quisiste decir con esa línea es:

MuxRegister |= ((Mux_GPIO & LED) | (Mux_PWM & PWM) | ... ; 

Esto utiliza operadores bitwise.

    
respondido por el Tom Carpenter

Lea otras preguntas en las etiquetas