Forum: Mikrocontroller und Digitale Elektronik Bitmanipulation - Bit schreiben an variable Position ?


von Y. T. (moritzz)


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.

von Huch (Gast)


Lesenswert?

Karl Heeeiiinz? Guckst Du mal bitte?

von Dussel (Gast)


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.

von Stefan E. (sternst)


Lesenswert?

1
Temperatur = Temperatur & ~(1<<Bitposition) | (aktuellesBit<<Bitposition);

Direkt Umsetzung der Vorgabe ohne Anspruch auf Effizienz. ;-)

von Frank B. (foobar)


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.

von Dussel (Gast)


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

von Y. T. (moritzz)


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!

von Dussel (Gast)


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…

von Frank B. (foobar)


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.

von Y. T. (moritzz)


Lesenswert?

Vielen Dank auf jeden Fall!!!!

von Stefan E. (sternst)


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.

von Karl H. (kbuchegg)


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.

von Peter D. (peda)


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

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.