Bitmanipulation - Bit schreiben an variable Position ?

OP #2059307
Lesenswert?

Hallo,
Ich verzweifele gerade an einem AVR-GCC Problem: ein Bit, das in einer 
Variablen "aktuellesBit" liegt, an eine Position in einer Variablen 
"Temperatur" schreiben, wobei diese Position wiederum in einer Variablen 
"Bitposition" liegt.
Ich möchte der Einfachheit halber uint8_t-Variablen verwenden.

Bis jetzt sieht das so aus, total unelegant:
1
void SetzeTemperatur(void)
2
{
3
 if (aktuellesBit==1)
4
 {
5
  switch (Bitposition)
6
  {
7
   case 0:  {Temperatur |= (1 << 0);     break;}
8
   case 1:  {Temperatur |= (1 << 1);     break;}
9
   case 2:  {Temperatur |= (1 << 2);     break;}
10
   case 3:  {Temperatur |= (1 << 3);     break;}
11
   case 4:  {Temperatur |= (1 << 4);     break;}
12
   case 5:  {Temperatur |= (1 << 5);     break;}
13
   case 6:  {Temperatur |= (1 << 6);     break;}
14
   case 7:  {Temperatur |= (1 << 7);     break;}
15
  }
16
 }
17
  if (aktuellesBit==0)
18
 {
19
  switch (Bitposition)
20
  {
21
   case 0:  {Temperatur &= ~(1 << 0);     break;}
22
   case 1:  {Temperatur &= ~(1 << 1);     break;}
23
   case 2:  {Temperatur &= ~(1 << 2);     break;}
24
   case 3:  {Temperatur &= ~(1 << 3);     break;}
25
   case 4:  {Temperatur &= ~(1 << 4);     break;}
26
   case 5:  {Temperatur &= ~(1 << 5);     break;}
27
   case 6:  {Temperatur &= ~(1 << 6);     break;}
28
   case 7:  {Temperatur &= ~(1 << 7);     break;}
29
  }
30
 }
31

32
}

Kennt jemand eine elegante Bitoperation? Im Artikel "Bitmanipulationen" 
steht dazu nichts.
Danke.
Gast #2059314
Lesenswert?

Habe ich das richtig verstanden, dass du ein Bit an einer vorgegebenen 
Stelle schreiben willst? Du bekommst also Bitstelle 5 und schreibst 
setzt oder löschst das fünfte Bit?
Das geht zum Beispiel mit einer Schleife. Eine Hilfsvariable wird auf 1 
gesetzt, mit einer Schleife oft genug geschoben und dann logisch mit der 
zu ändernden Variable verknüpft. Dabei musst du allerdings aufpassen, 
dass die Hilfsvariable das richtige Format hat.
Zum Beispiel nur für das Setzen:
1
unsigned char Hilfe=1;
2
for(unsigned int i=1;i<Bitposition;i++)
3
     Hilfe<<1;
4
Temperatur|=Hilfe;
Allerdings wird mit der Lösung das erste Bit gesetzt, wenn Bitposition 0 
oder 1 ist.
#2059319
Lesenswert?

Moritz G. schrieb:
>   switch (Bitposition)
>   {
>    case 0:  {Temperatur |= (1 << 0);     break;}
>    case 1:  {Temperatur |= (1 << 1);     break;}
>    case 2:  {Temperatur |= (1 << 2);     break;}
>    case 3:  {Temperatur |= (1 << 3);     break;}
>    case 4:  {Temperatur |= (1 << 4);     break;}
>    case 5:  {Temperatur |= (1 << 5);     break;}
>    case 6:  {Temperatur |= (1 << 6);     break;}
>    case 7:  {Temperatur |= (1 << 7);     break;}
>   }

kann man so vereinfachen:
1
Temperatur |= 1 << Bitposition;

wenn Bitposition immer im Bereich 0 bis 7 ist.
Gast #2059324
Lesenswert?

Stefan Ernst:
Eleganter, aber meines Wissens langsamer als meine Lösung ;-)
Ein x86 kann in einem Befehl beliebig weit schieben. Da es aber um den 
AVR-GCC geht und das eher nach einer fortgeschrittenen Anfängerfrage 
aussieht, glaube ich, dass es nicht um AVR32 geht. Und die 'normalen' 
AVR können nur um eine Stelle schieben. In deiner Lösung muss der 
Compiler zweimal um Bitstelle schieben und damit zwei Schleifen 
durchlaufen. (Ich gehe mal davon aus, dass der nicht so stark optimiert, 
dass er das, falls möglich, in einer Schleife zusammenbaut.)
OP #2059326
Lesenswert?

Oh das wäre ja dann einfach!

Mir war nicht klar, dass man mit dem Operator
1
|= 1 << a
 auch eine Variable und nicht nur eine Konstante für a nehmen darf!

@Stefan Ernst: Sieht professionell aus, ich verstehe den letzten Teil 
noch nicht, nehme es aber jetzt logisch auseinander...

@Dussel
Ja, das macht Sinn..
Ich müsste schauen  dass die Anfangswerte usw. stimmen.
Bedeutet
1
Hilfe<<1;
, dass aus 00000001 --> 00000010 wird?

(Also "fortgeschrittene Anfängerfrage" ....tssss :-) Ich arbeite mit dem 
ATmega8, und steige gerade von Assembler auf C um. (GPS, SD-Karte, 
Frequenzmesser, etc. haben in Assembler recht lange gedauert zu 
programmieren...))


Danke!
Gast #2059334
Lesenswert?

Moritz G. schrieb:
> @Dussel
> Ja, das macht Sinn..
> Ich müsste schauen  dass die Anfangswerte usw. stimmen.
> Bedeutet
>Hilfe<<1;, dass aus 00000001 --> 00000010 wird?
Nach so einer Frage beschwerst du dich, wenn ich Anfänger schreibe ;-)
Gerade überlege ich, ob es Hilfe<<1 oder 1<<Hilfe heißen muss, aber ich 
glaube, das erste ist für meine Lösung richtig.
Genau das bedeutet es. Damit wird die Variable Hilfe um eins nach links 
geschoben, also praktisch "lsl Hilfe" (in Pseudoassembler). Dabei muss 
man aber den Datentyp beachten, weil eventuell noch ein Vorzeichenbit 
davorsteht.
Jetzt hoffe ich, dass da kein Fehler drin ist, es ist schon spät und ich 
bin den ganzen Tag schon müde…
#2059335
Lesenswert?

Ja, man kann auch eine Variable nehmen, um anzugeben, wieviel Bits es 
geschoben werden soll. Wie das dann compiliert wird, ist natürlich eine 
andere Frage. Wenn es aber nicht zeitkritisch an der Stelle im 
Programmablauf ist, würde ich mir da keine Gedanken drum machen.
#2059351
Lesenswert?

Dussel schrieb:
> Ein x86 kann in einem Befehl beliebig weit schieben. Da es aber um den
> AVR-GCC geht und das eher nach einer fortgeschrittenen Anfängerfrage
> aussieht, glaube ich, dass es nicht um AVR32 geht. Und die 'normalen'
> AVR können nur um eine Stelle schieben. In deiner Lösung muss der
> Compiler zweimal um Bitstelle schieben und damit zwei Schleifen
> durchlaufen. (Ich gehe mal davon aus, dass der nicht so stark optimiert,
> dass er das, falls möglich, in einer Schleife zusammenbaut.)

Genau deshalb habe ich "ohne Anspruch auf Effizienz" drunter 
geschrieben.
#2059657
Lesenswert?

Das Problem ist, dass ein AVR nicht variabel schieben kann.
So gesehen ist dein switch-case Konstrukt nicht sooo schlecht. Die 
Schleifenlösung kaschiert das Problem ein wenig und letzten Endes ist

   1 << variable

auch nur eine versteckt Schleifenlösung

Aber es gibt noch eine Variante
1
void SetzeTemperatur(void)
2
{
3
  static uint8_t Mask[] = { 0x01, 0x02, 0x04, 0x08,
4
                            0x10, 0x20, 0x40, 0x80 };
5
  if (aktuellesBit)
6
    Temperatur |= Mask[Bitposition];
7
  else
8
    Temperatur &= ~Mask[Bitposition];
9
}

Was dann tatsächlich am schnellsten abgearbeitet wird, müsste man sich 
im Assemblercode ansehen und per Taktzähleung feststellen.
#2059863
Lesenswert?

Ich nehme mal an, Du liest nen digitalen Sensor ein, dann brauchst Du 
diese ganze Umstandskrämerei garnicht.

Die Bits kommen ja geordnet rein, MSB (LSB) first.
Also einfach bei jedem Bit das Ergebnis einmal links (rechts) schieben 
und wenn Bit gesetzt mit 0x01 (0x80) verodern.


Peter

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren