Schreiben Sie in C gleichzeitig in Sätze von zwei Bits in einem Register

Ich versuche, mehrere Werte in ein 32-Bit-Register zu setzen, und jeder Wert hat 2 Bits. Versuchen Sie, dies auf elegante Weise zu tun.

Schreiben Sie jetzt, ich habe so etwas:

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;
...

Und ich möchte etwas tun, bei dem ich nicht jedes MuxRegister.X und MuxRegister.Y für jeden der 16 Pins in der Struktur durchlaufen muss.

Meine Idee war, so etwas zu machen:

#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) || ... ; 

Es gibt jedoch keinen Mechanismus, um den Abstand für das Mux-Code-Register (Werte zwischen 0 und 3) über die allererste Position hinaus beizubehalten. Wie in diesem Beispiel ist der Wert für Mux_PWM 0x00000003, aber der Wert für PWM ist 0x0000000C, und sie interagieren überhaupt nicht, da sie sich nicht "überlappen".

Gibt es einen Mechanismus, mit dem die Werte der Mux-Codes verteilt werden können, damit sie mit den richtigen Pin-Werten interagieren? Sorry, wenn ich die Frage nicht gut erkläre. Ich habe nicht viel von der richtigen Terminologie.

meinst du, dass du 0b0000000000000000000000000000011 anstelle von 0x00000003 verwenden willst? Wenn Sie dies auf eine einfachere Weise tun möchten, können Sie jederzeit LSL und LSR (<< & >>) verwenden, um Ihre Werte, die Sie eingeben/lesen, bitweise zu verschieben
Der &&Betreiber ist nicht der &Betreiber. Ich denke, Sie müssen die Grundlagen sowohl der bitweisen als auch der logischen Operatoren studieren, bevor Sie irgendetwas anderes tun.

Antworten (3)

Ich würde versuchen, nur ein uint32_t anstelle von Bitfeldern zu verwenden. Folgendes würde ich wahrscheinlich tun:

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);
}
Ihr SetMuxBits() funktioniert nicht, wenn das MSB im Zwei-Bit-Feld bereits gesetzt ist.
Wie? Jeder Wert hat 2 Bits, wobei 01 eingeschaltet und 00 ausgeschaltet ist. Es gibt keine Möglichkeit, das MSB jedes Zwei-Bit-Feldes auf diese Weise einzustellen. Wenn das MSB benötigt wird, funktioniert dies nicht. So habe ich die Frage jedenfalls interpretiert.
Sein Wert für Mux_PWM ist 3. Es ist definitiv möglich, dass das MSB eingestellt wird.
Ach das hat man verpasst. Antwort aktualisiert.

Es gibt keine superpräzise, ​​elegante Methode, dies zu tun, die ich kenne. Wenn Sie mehrere Bits setzen möchten, benötigen Sie für jedes Feld mindestens die folgenden Informationen:

  1. Die Bitposition (Shift)
  2. Die Anzahl der Bits im Feld (Maske)
  3. Die Daten, die in das Feld gehen

Es sieht so aus, als ob Sie den Pins und den Mux-Werten auch funktionale Namen geben möchten. Das sind eine Menge Informationen.

Ehrlich gesagt denke ich, dass Ihr erstes Code-Snippet ein guter Anfang ist. Die Bitfeldstruktur kann die Verschiebungen, Masken und Pinnamen im Strukturtyp codieren, was den Code einigermaßen prägnant macht. Man könnte sich einen Arbeitsschritt sparen, indem man statt PIN1, PIN2 etc. die Bitfelder LED, PWM etc. nennt.

Alternativ können Sie die Codegröße verkleinern, indem Sie ein konstantes Array von Mux-Werten erstellen und diese dann über eine for-Schleife laden:

//                          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;
}

Oder du könntest so etwas tun:

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

Sie können Makros verwenden, um die Lesbarkeit zu verbessern:

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

Aber ich glaube nicht, dass Sie etwas massiv Kürzeres finden werden. Sie haben sechzehn eindeutige Mux-Auswahlen, und auf die eine oder andere Weise müssen sie alle in Ihren Code passen. Wenn Sie eindeutige Pin-Namen wünschen, sind das weitere sechzehn Werte.

Gewerkschaften können dein Freund sein:

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

Jetzt können Sie wie folgt darauf zugreifen:

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

Sie werden feststellen, dass ich die Bitfeldeinträge Uint32 gemacht habe, weil Sie 32-Bit-Register verwenden (nicht sicher, warum Sie sie Uint16 gemacht haben?). Sie sollten auch erwägen, Standardtypen von <stdint.h>z. B. uint32_tfür eine bessere Portabilität zu verwenden.

Abhängig von Ihrem Compiler und ob er anonyme Strukturen zulässt, können Sie Folgendes tun:

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

Auf die zugegriffen werden würde als:

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

Gewerkschaften können manchmal ein Problem sein, aber meistens funktionieren sie gut. Meistens liegt das Problem an der Ausrichtung der Wörter, aber das ist ziemlich einfach zu überprüfen. Dieses wirklich nützliche Stück Code ermöglicht es, während der Kompilierung zu überprüfen, ob eine Vereinigung die erwartete Größe hat - normalerweise ändert sich die Größe, wenn die Ausrichtung aus dem Gleichgewicht gerät.

#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)) }

Dann können Sie Folgendes tun:

ct_assert(MuxRegister,sizeof(Uint32));

Dadurch wird ein Fehler angezeigt, wenn die Vereinigung kein 32-Bit-Wort ist.


Seien Sie als Nebenbemerkung auch vorsichtig mit Ihren Operatoren.

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

Das würde das bitweise oder von MuxRegistermit boolean true oder false tun. Warum? weil Sie logische Operatoren verwenden. (Mux_GPIO && LED)ergibt entweder wahr, wenn beide Werte ungleich Null sind, oder andernfalls falsch.

Ich denke, was Sie mit dieser Zeile gemeint haben, ist:

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

Dies verwendet bitweise Operatoren.

Illegale Verwendung einer Union, und Bitfelder sind per Definition unvorhersehbar, also sehr schlecht für so etwas zu verwenden. Im Allgemeinen funktioniert dieser Ansatz einfach, und wenn er es nicht tut, bricht er dramatisch zusammen. Es gibt keinerlei Grund für den Compiler, dies zu unterstützen.
@dwelch Ich bin an die Syntax mit Typedefs gewöhnt, daher ist es durchaus möglich, dass die Syntax falsch war. So oder so habe ich es auf eine Form aktualisiert, von der ich weiß, dass sie korrekt ist.
+1, da Situationen wie diese selten tragbar sein müssen und die Technik nützlich ist; zeigt auch die Macht der Gewerkschaften. Ohne Portabilitätsanforderung sind Compilerbeschränkungen nicht so wichtig. Obwohl das Update weithin portierbar ist, ist es für eingebetteten Code nicht oft erforderlich.
Wenn Sie mehrere Dinge in einer Union haben, können Sie nicht legal von einem zum anderen wechseln oder eines so einstellen, dass es sich im anderen widerspiegelt. normalerweise funktioniert, aber der Compiler muss es nicht zum Laufen bringen. Bitfelder, haben wenig bis gar keinen Wert. Verwenden Sie schließlich niemals eine Struktur über Kompilierungsdomänen hinweg (Hardware ist eine separate Kompilierungsdomäne). Jedes dieser Dinge für sich genommen ist eine schlechte Angewohnheit, die dich eines Tages beißen wird, kombiniere sie alle, und wenn es eines tut, wird der Schmerz stark sein, wenn du eines davon als Gewohnheit machst, dann wirst du eine Menge haben Code zu reparieren.
Die Union, Bitfelder, die Strukturen auf Dinge zeigen, generiert nur Lese-Modifizier-Schreibvorgänge mit Verschiebungen und Masken und oder Bit-Set/Clear-Anweisungen, wenn der Befehlssatz sie unterstützt. Schreiben Sie das Zeug einfach direkt in Ihren Code, es ist portabel, wird Sie am Ende nicht beißen, und der Compiler kann das gesetzte / gelöschte Bit finden, wenn Sie es haben, und das optimieren. ist keine schlechte Angewohnheit und wird Sie eines Tages nicht übel beißen und eine größere Umschreibung Ihres Codes verursachen. für viele dauert es 10-20 Jahre, bis sie gebissen werden, da sie dazu neigen, eine Weile in einem Feld oder einer Reihe von Werkzeugen zu bleiben.