Forum: Compiler & IDEs C Code Frage: subtrahieren in Bitfeld


von c-anfaenger (Gast)


Lesenswert?

Hallo zusammen,

ich habe eine uint8_t counter variable, die ich als 4 separate 2bit 
counter nutzen möchte.
Ich stehe gerade etwas auf dem schlauch wie ich diese counter am besten 
herunterzählen kann.

Bit 0,1 -> Counter 0
Bit 2,3 -> Counter 1
Bit 4,5 -> Counter 2
Bit 6,7 -> Counter 3

jeden Counter möchte ich nun herunterzählen können um "-1", aber auch 
nicht kleiner als 0 werden (Überlauf).

Wie mache ich das am geschicktesten?

Viele Grüße,
C-Anfänger

: Verschoben durch Moderator
von A. S. (Gast)


Lesenswert?

1
struct sCounter;
2
{
3
    uint8_t C0 :2; 
4
    uint8_t C1 :2;
5
    uint8_t C2 :2;
6
    uint8_t C3 :2;
7
};
8
9
struct sCounter Counter4;
10
11
...
12
13
    if(Counter.C3 > 0) {Counter.C3--;}
14
...

von Peter D. (peda)


Lesenswert?

c-anfaenger schrieb:
> Wie mache ich das am geschicktesten?

Man nimmt 4 Byte.
Wenn der MC dafür zu knapp an RAM ist, dann ist er auch zu knapp an 
Flash, um bei jeder Operation aufwendige AND/OR/Schiebe-Orgien 
auszuführen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

c-anfaenger schrieb:
> jeden Counter möchte ich nun herunterzählen können

Alle miteinander in einem Aufwasch?

Das geht bspw. so:

1
uint8_t counter;
2
3
counter = counter & counter<<1 & 0xaa | ~counter & counter>>1 & 0x55;

Mit diesem Ausdruck wird jeder der vier 2-Bit-Zähler dekrementiert,
falls sein aktueller Wert >0 ist. Ist der Wert 0, bleibt er unverändert.

Beispiel:

1
binär:        dezimal:
2
3
C3 C2 C1 C0   C3 C2 C1 C0
4
———————————   ———————————
5
10 11 00 01    2  3  0  1
6
01 10 00 00    1  2  0  0
7
00 01 00 00    0  1  0  0
8
00 00 00 00    0  0  0  0

: Bearbeitet durch Moderator
von Walter T. (nicolas)


Lesenswert?

Yalu X. schrieb:
> uint8_t counter;
>
> counter = counter & counter<<1 & 0xaa | ~counter & counter>>1 & 0x55;

Hast Du Dir das gerade selbst mal eben hergeleitet oder ist das ein 
festes Rezept?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Walter T. schrieb:
> Hast Du Dir das gerade selbst mal eben hergeleitet oder ist das ein
> festes Rezept?

Ersteres. Rezepte kann ich mir leider schlecht merken ;-)

Die Herleitung ist aber nicht schwer:
1. Wertetabelle aufstellen
2. Daraus logische Terme ableiten und vereinfachen
3. Das Ganze noch etwas schöner machen

Hier ist noch eine leicht geänderte Variante, die die beiden
Maskierungsoperationen zu einer zusammenfasst:

1
  uint8_t counter;
2
3
  uint8_t tmp = counter & 0xaa;
4
  counter =  tmp & counter<<1 | tmp>>1 & ~counter;

Irgendwie habe ich das Gefühl, dass man das noch weiter vereinfachen
kann, evtl. unter Verwendung der XOR-Operationen, komme aber nicht
drauf.

von Walter T. (nicolas)


Lesenswert?

Yalu X. schrieb:
> 1. Wertetabelle aufstellen

Achja... ein Schritt, den ich regelmäßig vergesse: Vor dem Programmieren 
einen Schritt weg vom Monitor machen und auf einem Blatt Papier 
kritzeln...

von Andreas R. (daybyter)


Lesenswert?

Wenn Du die Zähler gemeinsam dekrementierst, besser nur einen Zähler 
nehmen und 4x verwenden?  ;-)

von c-anfaenger (Gast)


Lesenswert?

>   uint8_t counter;
>
>   uint8_t tmp = counter & 0xaa;
>   counter =  tmp & counter<<1 | tmp>>1 & ~counter;
>
> Irgendwie habe ich das Gefühl, dass man das noch weiter vereinfachen
> kann, evtl. unter Verwendung der XOR-Operationen, komme aber nicht
> drauf.


das hilft mir schon mal sehr weiter!
Danke und Gruß!

von c-anfaenger (Gast)


Lesenswert?

Andreas R. schrieb:
> Wenn Du die Zähler gemeinsam dekrementierst, besser nur einen
> Zähler
> nehmen und 4x verwenden?  ;-)

es sind halt unabhängige Zähler, die zwar gleichzeitig runtergezählt 
werden aber zu anderen Zeiten gesetzt werden sollen.

Danke erstmal für die Hilfe, ich werde doch nur 2 4bit Zähler nutzen und 
nicht 4 2bit Zähler. Versuche das gerade entsprechend anzupassen!

Gruß,

von A. S. (Gast)


Lesenswert?

c-anfaenger schrieb:
> ich werde doch nur 2 4bit Zähler nutzen und nicht 4 2bit Zähler.

Und warum? Hier RAM zu sparen bleibt blödsinn., Zumindest in 99.9%.

Weiter Idee zur Knobelaufgabe: wirklich subtrahiren.
1
tmp = (counter | counter>>1) & 0x55;
2
counter -= tmp;
3
4
//oder bei 4 Bit:
5
6
tmp = (counter | counter>>2) & 0x33;
7
tmp &= tmp>>1;
8
counter -= tmp;

von A. S. (Gast)


Lesenswert?

counter -= (counter>15?16:0)+!!(counter&15);

von Peter D. (peda)


Lesenswert?

Achim S. schrieb:
> Und warum? Hier RAM zu sparen bleibt blödsinn., Zumindest in 99.9%.

Das würde mich auch mal interessieren, was die Anwendung ist.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Peter D. schrieb:
> Das würde mich auch mal interessieren, was die Anwendung ist.

Vielleicht so etwas Ähnliches wie die vertical Counters in deiner
Entprellroutine? Nur dass die Counters hier horizontal sind, nur 1 statt
2 Bytes belegen und damit in der Anzahl auf 4 beschränkt sind.

von Ingo L. (corrtexx)


Lesenswert?

Achim S. schrieb:
> counter -= (counter>15?16:0)+!!(counter&15);
Der Preis für den geheimnisvollsten und nichtssagensten Code geht an 
Achim S.

von c-anfaenger (Gast)


Lesenswert?

ich habe inzwischen auch das herunterzählen hinbekommen, allerdings ist 
das mir hier zu peinlich den mehrzeiligen Code zu posten ;)

Also die Anwendung ist folgender:
Eine Art Event-System:
Es werden verschiedene Events ausgelöst und dabei jeweils ein Bit von 0 
auf 1  gekippt. Diese sollen nach kurzer Zeit wieder zurückgesetzt 
werden.
Da die Events zu unterschiedlichen Zeitpunkten ausgelöst werden, hat 
jedes Event einen eigenen Counter für das Zurücksetzen.
Das ganze findet in einer Smarthome-Bus Steuerung Anwendung, bei der ich 
einige Events haben möchte, da einfach für alles 8bit zu nehmen würde 
viel mehr Speicher fressen als notwendig.

gruß

von A. S. (Gast)


Lesenswert?

c-anfaenger schrieb:
> ich habe inzwischen auch das herunterzählen hinbekommen, allerdings ist
> das mir hier zu peinlich den mehrzeiligen Code zu posten ;)

und warum nicht straight, mit Bitfeldern?
1
if(counter[n].A) {counter[n].A--;}

> ds einfach für alles 8bit zu nehmen würde
> viel mehr Speicher fressen als notwendig.

Das ist klar. Das ist aber normalerweise nicht die Frage. Es macht 
regelmäßig nur dann Sinn, den Code zu verunstalten, wenn es konkret an 
RAM mangelt. Denn hier hat Peter einfach Recht:

Peter D. schrieb:
> Wenn der MC dafür zu knapp an RAM ist, dann ist er auch zu knapp an
> Flash, um bei jeder Operation aufwendige AND/OR/Schiebe-Orgien
> auszuführen.


Und wenn RAM wirklich knapp sein sollte, dann sind Arrays von 2-4 Bit in 
einem Byte meist zweitbeste Lösung. Wenn es z.B. zu jedem Event-Timer 
noch 2-3 Bits gibt (z.B. enable, prio, ...), dann lieber diese in einem 
Byte gruppieren, um sie geschlossen und konsistent beackern zu können.

von c-anfaenger (Gast)


Lesenswert?

ich werde einen Atmega1284 nehmen, der hat ja 16KB SRAM.

Ich hatte überlegt alle Einstellungen um Events zu erkennen (z.B. auf 
dem genutzten RS485 Bus sendet 0x12345678 den Befehl 0x01 mit Wert 0x04, 
dann setze das Event-Bit auf 1 für Zeit 0x08) im RAM vorzuhalten um 
nicht permanent das EPROM abzufragen.
Somit sind durch die Settings schon ein paar KB RAM belegt.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.