Die effizienteste Methode zum Erstellen eines Bool-Arrays in C - AVR

7

Ich verwende einen Atmega32 , um verschiedene Sensoren mit seinem ADC zu lesen.

Mit digitaler Logik und einigen Multiplexern habe ich die gesamte PORTA gemultiplext, wobei PA0: 6 als Adresse und PA7 als Eingang verwendet wurden. Daher kann ich maximal 128 Eingänge von nur einem PORT haben.

Da der Benutzer das System nun über einen Computer (mit RS232) bedient, können Sensoren hinzugefügt oder entfernt werden. Das System muss daher nachverfolgen, welche Adressen zum Hinzufügen von Sensoren frei sind und von welchen Adressen gelesen werden kann.

Ich dachte daran, ein boolesches 128-Bit-Array als Flags zu verwenden, um anzuzeigen, ob sich an einer bestimmten Adresse ein Sensor befindet.

Während C keine native Unterstützung für Einzelbitvariablen bietet, ist es möglich, Bitfelder zu verwenden, um bis zu 8 "Bool-Variablen" in ein einzelnes vorzeichenloses Zeichen zu packen.

Also habe ich die folgende Struktur erstellt:

typedef struct bool_s{
      uint8_t bit1:1;
}bool_t;

Dann habe ich ein Array vom Typ bool_t mit einer Größe von 128 erstellt, in der Hoffnung, dass alles gut zusammen gepackt wird, aber sizeof()mir wird gesagt, dass die Arraygröße 128 Bytes anstelle der 16 Bytes beträgt, die ich erwartet hatte.

Technisch könnte ich eine einzelne Struktur mit 128 1-Bit-Variablen erstellen:

typedef struct Flag_s{
      uint8_t F1:1;
      uint8_t F2:1;
      uint8_t F3:1;
      [...]
      uint8_t F128:1;
}Flag_t;

Das Problem bei diesem Ansatz ist, dass er zwar die Speichernutzung reduziert, aber nicht sehr praktisch ist und viel zu viel Platz im Code einnimmt.

Gibt es eine einfache Möglichkeit, eine große Anzahl von Flaggen zu erstellen, oder verlange ich zu viel? Ich meine, es ist nicht so, als würde das Speichern von 112 Bytes einen großen Unterschied machen, wenn 2 KB verfügbar sind, aber was ist, wenn ich noch mehr Flags benötige?

Chi
quelle
Sollte dies nicht auf
Stapelüberlauf sein
1
@Joshpbarron Dies ist spezifisch für eingebettetes C, boolesche Variablen existieren in C99 und außerdem ist es nicht so, als würden hier ein paar zusätzliche Bytes verwendet, und es wird einen signifikanten Unterschied machen, wenn Sie GiBs Speicher zur Verfügung haben, und wenn ich sagte, dass ich es bin Mit Mikrocontrollern würden sie mir wahrscheinlich sagen, ich solle zum EE-Stack-Austausch gehen.
Chi

Antworten:

13

Bitfelder funktionieren so nicht. Die Antwort von Cventu zeigt eine korrekte Verwendung, aber in diesem Fall empfehle ich, sie ganz zu vermeiden.

Erstellen Sie stattdessen ein Array mit 8-Bit-Werten und verwenden Sie Shifting und Masking, um darauf zuzugreifen:

uint8_t flags[16];

//Initialize the flags
void init_flags(void)
{
    for (i = 0; i < 16; i++)
    {
        flags[i] = 0x00;
    }
}

//Set one flag based on the address
void set_flag(uint8_t address)
{
    flags[address/8] |= 0x1 << (address % 8);
}

//Clear one flag based on the address
void clear_flag(uint8_t address)
{
    flags[address/8] &= ~(0x1 << (address % 8));
}

//Check whether a flag is set
bool get_flag_state(uint8_t address)
{
    return (flags[address/8] & (0x1 << (address % 0x8))) != 0x00;
}

Dies ist wahrscheinlich das, was der Compiler ohnehin mit Bitfeldzugriffen macht, und es ist einfacher, damit zu arbeiten. Einige Optimierer sind schlecht darin, Bitfelder zu optimieren, sodass der Compiler möglicherweise sogar schlechter abschneidet. Alle Compiler, die ich gesehen habe, wandeln Division und Modul durch eine konstante Zweierpotenz in Rechtsverschiebung und UND-Anweisungen um. Sie können diese Operationen direkt verwenden, wenn Sie sich paranoid fühlen:

flags[address>>3] |= 0x1 << (address & 0x7);

Bitfelder ähneln eher Strukturen als Arrays. Nach meiner Erfahrung sind sie nur für Dinge nützlich, die Namen haben, wie z. B. Registerfelder.

Adam Haun
quelle
1
Auf fortgeschritteneren Prozessoren (wie x86) werden Bitfelder normalerweise mit noch größeren Elementarvariablen wie uint32_t implementiert. Die Idee ist jedoch dieselbe: Bitweises ODER wird zum Setzen der Flags verwendet, bitweises NOT + AND zum Löschen der Flags und bitweises AND zum Überprüfen der Flags, wobei die Maske entweder mit dem <<Operator generiert oder als Konstanten definiert wird mit Makros.
Laszlo Valko
2
Das ist eine teure Arithmetik (Divide & Mod) für Mikros mit geringer Leistung.
John U
1
Während Ihr Code allgemeiner ist, können einige MCUs (z. B. PIC18) einzelne Bits direkt im RAM lesen / setzen. Dies würde die Last stark reduzieren. Dies kann überprüft werden, da Compiler nicht immer erkennen, was Sie dort tun.
Sweber
2
@JohnU Ich habe noch nie eine Division oder Modul-Division durch eine konstante Zweierpotenz gesehen, die in etwas anderes als Shift- und AND-Anweisungen kompiliert wurde. Aber Sie möchten definitiv nicht durch eine Variable teilen oder modifizieren.
Adam Haun
4
Der Grund, warum Bitfelder nicht so funktionieren, ist, dass jedes Element eines Arrays eine Adresse haben und für Zeigerarithmetik zugänglich sein muss, was verhindert, dass ein Element weniger als die Größe von char (dh ein Byte) einnimmt. Mehrere Bitfelder innerhalb einer Struktur können ein Byte "teilen", aber kein solches Glück mit Array-Elementen.
Hobbs
9

Es klingt ein bisschen verwirrend, aber vielleicht kann so etwas helfen:

struct bits_field {
                       unsigned char bit_7 :1;
                       unsigned char bit_6 :1;
                       unsigned char bit_5 :1;
                       unsigned char bit_4 :1;
                       unsigned char bit_3 :1;
                       unsigned char bit_2 :1;
                       unsigned char bit_1 :1;
                       unsigned char bit_0 :1;

                   };

union dt {
             struct bits_field a;
             unsigned char b;
         };

Nachdem Sie diese definiert haben, deklarieren Sie die folgende Variable:

union dt my_data;

Mit my_data.b können Sie eine ganze Gruppe von acht Bits zusammen schreiben

Oder Sie können jedes Bit einzeln schreiben mit: my_data.a.bit_7 (oder 6,5,4,3,2,1,0)

Wenn dies für Sie funktioniert, replizieren Sie dies 16 Mal (mithilfe eines Arrays oder einer Struktur), und Sie können 128 Bit einzeln verarbeiten.

Lassen Sie uns wissen, ob es funktioniert hat! Viel Glück

cventu
quelle
Ich dachte auch darüber nach, eine Struktur mit 8 1-Bit-Variablen zu verwenden, aber dann müsste ich eine Art Funktion schreiben, um die Adressnummer in etwas zu übersetzen, das ich mit einem solchen Array verwenden könnte, was nur Verwirrung und Overhead verursachen würde. Oh, und übrigens, endlich hat mir jemand ein gutes Beispiel gezeigt, wie nützlich der Gewerkschaftsdatentyp sein kann! : P
Chi
Das Hauptproblem hierbei ist, dass Bitfelder eine beliebige Anzahl von Füllbits und Bytes enthalten können, eines von vielen Dingen, die sie unzuverlässig und nicht portierbar machen. Eine Gewerkschaft löst das nicht.
Lundin
0

Erstellen Sie eine Struktur, in der Sie acht Bit-Flags deklarieren, die letztendlich ein Byte sind. Erstellen Sie dann ein Array dieser 8-Bit-Strukturen.

Michael Karas
quelle