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
voidSetzeTemperatur(void)
2
{
3
if(aktuellesBit==1)
4
{
5
switch(Bitposition)
6
{
7
case0:{Temperatur|=(1<<0);break;}
8
case1:{Temperatur|=(1<<1);break;}
9
case2:{Temperatur|=(1<<2);break;}
10
case3:{Temperatur|=(1<<3);break;}
11
case4:{Temperatur|=(1<<4);break;}
12
case5:{Temperatur|=(1<<5);break;}
13
case6:{Temperatur|=(1<<6);break;}
14
case7:{Temperatur|=(1<<7);break;}
15
}
16
}
17
if(aktuellesBit==0)
18
{
19
switch(Bitposition)
20
{
21
case0:{Temperatur&=~(1<<0);break;}
22
case1:{Temperatur&=~(1<<1);break;}
23
case2:{Temperatur&=~(1<<2);break;}
24
case3:{Temperatur&=~(1<<3);break;}
25
case4:{Temperatur&=~(1<<4);break;}
26
case5:{Temperatur&=~(1<<5);break;}
27
case6:{Temperatur&=~(1<<6);break;}
28
case7:{Temperatur&=~(1<<7);break;}
29
}
30
}
31
32
}
Kennt jemand eine elegante Bitoperation? Im Artikel "Bitmanipulationen"
steht dazu nichts.
Danke.
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
unsignedcharHilfe=1;
2
for(unsignedinti=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.
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.)
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!
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…
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.
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.
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
voidSetzeTemperatur(void)
2
{
3
staticuint8_tMask[]={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.
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