mikrocontroller.net

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


Autor: Y. T. (moritzz)
Datum:

Bewertung
0 lesenswert
nicht 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:
void SetzeTemperatur(void)
{
 if (aktuellesBit==1)
 {
  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;}
  }
 }
  if (aktuellesBit==0)
 {
  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;}
  }
 }

}

Kennt jemand eine elegante Bitoperation? Im Artikel "Bitmanipulationen" 
steht dazu nichts.
Danke.

Autor: Huch (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Karl Heeeiiinz? Guckst Du mal bitte?

Autor: Dussel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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:
unsigned char Hilfe=1;
for(unsigned int i=1;i<Bitposition;i++)
     Hilfe<<1;
Temperatur|=Hilfe;
Allerdings wird mit der Lösung das erste Bit gesetzt, wenn Bitposition 0 
oder 1 ist.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Temperatur = Temperatur & ~(1<<Bitposition) | (aktuellesBit<<Bitposition);

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

Autor: Frank Buss (foobar)
Datum:

Bewertung
0 lesenswert
nicht 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:
Temperatur |= 1 << Bitposition;

wenn Bitposition immer im Bereich 0 bis 7 ist.

Autor: Dussel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.)

Autor: Y. T. (moritzz)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Oh das wäre ja dann einfach!

Mir war nicht klar, dass man mit dem Operator
|= 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
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!

Autor: Dussel (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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…

Autor: Frank Buss (foobar)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Y. T. (moritzz)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Vielen Dank auf jeden Fall!!!!

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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
void SetzeTemperatur(void)
{
  static uint8_t Mask[] = { 0x01, 0x02, 0x04, 0x08,
                            0x10, 0x20, 0x40, 0x80 };
  if (aktuellesBit)
    Temperatur |= Mask[Bitposition];
  else
    Temperatur &= ~Mask[Bitposition];
}

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

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht 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

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.