PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : Bitweiser Zugriff uint16_t



shedepe
19.07.2011, 21:10
Hey, da meine Platine zum testen noch nicht fertig ist würde mich kurz interessieren ob folgendes Konstrukt funktionieren würde. Ziel ist es herauszufinden welches Bit der Variable gesetzt ist.



uint16_t test = 0b0000000000000001;

for(uint8_t i = 0; i<16;i++;)
{
if((test & (1<<i))==1)
{
//Schalte Segment an usw....
}

}

radbruch
19.07.2011, 21:15
if((test & (1<<i))==1)

Das funzt so nicht. Endweder auf größer null testen oder auf (1<<i)

if((test & (1<<i)) > 0)
if((test & (1<<i)) == (1<<i))

Ein Semikolon zuviel?
for(uint8_t i = 0; i<16;i++;)

shedepe
19.07.2011, 21:58
Oh, danke. Jetzt wo ich noch mal drüber nachdenke ist es mir klar, dass das mit dem testen auf 1 nicht funktionieren kann.
Das Semikolon ist in der Tat zu viel. Kleiner Tippfehler.

radbruch
19.07.2011, 22:07
Wenn man davon ausgeht, dass (1<<i) immer ungleich false ist, kann man natürlich auch quick&dirty ein gesetztes Bit so prüfen:

if(test & (1<<i))

oberallgeier
20.07.2011, 09:14
... quick&dirty ein gesetztes Bit so prüfen: if(test & (1<<i)) ...Quick´n dirty? Meine Bitprüfungen verschiedener Flags laufen z.B. so (das Beispiel mit uint16_t kann ich im Moment nicht finden):

#define IsBitSet(ADDR,BIT) (((ADDR) & (1<<BIT))?1:0) // Fragt Bit = 1?
...
if ( IsBitSet ( MNUflg, 5 ))
...

und das läuft. Ne sinngemäße Abfrage auf IsBitClr habe ich mir natürlich auch definiert. Vielleicht darf ich aber über quick´n dirty in C als C-Noob kein absolutes Urteil abgeben.

sternst
20.07.2011, 10:00
An "if (test & (1<<i))" ist absolut überhaupt nichts dirty.

PicNick
20.07.2011, 10:33
Du könntest deinen armen µC auch etwas schonen, der shiftet ja sonst herum wie der Böse:


uint16_t test = 0b0000000000000001;

for(uint16_t i = 1; i<=16;i<<= 1)
{
if(test & i)
{
//Schalte Segment an usw....
}
}


(is aber auch noch nicht das effizienteste)

sternst
20.07.2011, 11:37
(is aber auch noch nicht das effizienteste)Vor allem ist es falsch. Es testet ja nur die unteren 5 Bits.
for (uint16_t i = 1; i; i <<= 1)Und falls im weiteren Code die Position als Bitnummer benötigt wird:
for (uint16_t i = 1, uint8_t pos = 0; i; i <<= 1, pos++)

radbruch
20.07.2011, 11:48
Hallo

Das "quick&dirty" bezog sich auf die Annahme, dass der Kompiler intern false als 0 speichert und deshalb jeder Wert größer 0 als true interpretiert wird. Ich wollte das nur mal wieder bewußt machen, denn "im Alltag" beachtet das eh niemand: while(1) sollte eigentlich while(true) lauten.

#define IsBitSet(ADDR,BIT) (((ADDR) & (1<<BIT)) > 0) // Fragt Bit = 1?

So würde das Ergebniss true oder false sein.

Das wollte ich mal genauer untersuchen, weil ich ja was lernen will. Mit C komme ich inwischen recht gut zurecht, aber mit Assembler kenne ich mich noch gar nicht aus. Dieses kleine Testprogramm wurde (ohne Optimierung!) mit 136 Bytes übersetzt:

#define IsBitSet(ADDR,BIT) (((ADDR) & (1<<BIT))?1:0) // Fragt Bit = 1?
#define IsBitSet2(ADDR,BIT) (((ADDR) & (1<<BIT)) > 0)

int main(void)
{
int x, tmp;

if(IsBitSet(x,0)) tmp=0x11; else tmp=0x22;
if(IsBitSet2(x,0)) tmp=0x33; else tmp=0x44;

while(1);
return(0);
}
IsBitSet() wird in zwei Varianten definiert, Variante eins prüft 1 oder 0, Variante zwei prüft true/false.

Aus dem erzeugten Assemblerlisting habe ich die entscheidende Stelle rauskopiert und kommentiert (soweit mir das möglich ist):

.LM2: // if((
ldd r24,Y+3 // x
ldd r25,Y+4 // x ist in Y+3 und Y+4 gespeichert
andi r24,lo8(1) // & (1<<0))
andi r25,hi8(1)
tst r24 // prüft ob ein Bit in R24 gesetzt ist (Lowbyte von x!)
breq .L2 // springt nach L2 wenn R24 null ist
ldi r24,lo8(17) // tmp=0x1111
ldi r25,hi8(17)
std Y+2,r25 // tmp ist in Y+1 und Y*2 gespeichert
std Y+1,r24
rjmp .L3
.L2:
ldi r24,lo8(34) // tmp=0x2222
ldi r25,hi8(34)
std Y+2,r25
std Y+1,r24
.L3:

.LM3:
ldd r24,Y+3
ldd r25,Y+4
andi r24,lo8(1)
andi r25,hi8(1)
cp __zero_reg__,r24 // Vergleicht Lowbyte von x mit null
cpc __zero_reg__,r25 // dito Highbyte mit Carry! (beide Bytes werden ausgewertet)
brge .L4 // Sprung wenn beide Vergleiche null sind
ldi r24,lo8(51)
ldi r25,hi8(51)
std Y+2,r25
std Y+1,r24
rjmp .L5
.L4:
ldi r24,lo8(68)
ldi r25,hi8(68)
std Y+2,r25
std Y+1,r24

.L5: // while(1)
rjmp .L5
Erstaunlicherweise ist die erste Variante kürzer, allerdings habe ich das Gefühl, mit tst r24 wird nur das Lowbyte geprüft. Beschreibung von breq:

"Conditional relative branch. Tests the Zero Flag (Z) and branches relatively to PC if Z is set." Bedingter relativer Sprung, springt relativ zum Progammcounter, wenn ZeroFlag gesetzt ist. Das Z-Flag wird von tst gesetzt:

Z: R7• R6 •R5• R4• R3 •R2• R1• R0
Set if the result is $00; cleared otherwise.

Das ist aber das Ergebniss vom Test mit R24, wo wird geprüft, ob R25 auch null ist? Sehr seltsam.

btw. wird sowohl while(1) wie auch while(1==1) gleich übersetzt:
.L5:
rjmp .L5

Gruß

mic

[Edit]
tst scheint doch richtig zu funktionieren. Bei if(IsBitSet(x,0)) ergibt (1<<0) genau 1, das passt in das Lowbyte. Bei if(IsBitSet(x,15)) übersetzt der Kompiler so:

29 .LM2:
30 000c 8B81 ldd r24,Y+3
31 000e 9C81 ldd r25,Y+4
32 0010 9923 tst r25
33 0012 04F4 brge .L2
34 0014 81E1 ldi r24,lo8(17)
35 0016 90E0 ldi r25,hi8(17)
36 0018 9A83 std Y+2,r25
37 001a 8983 std Y+1,r24
38 001c 00C0 rjmp .L3
39 .L2:
40 001e 82E2 ldi r24,lo8(34)
41 0020 90E0 ldi r25,hi8(34)
42 0022 9A83 std Y+2,r25
43 0024 8983 std Y+1,r24
44 .L3:
45 .LM3:
46 0026 8B81 ldd r24,Y+3
47 0028 9C81 ldd r25,Y+4
48 002a 8070 andi r24,lo8(-32768)
49 002c 9078 andi r25,hi8(-32768)
50 002e 1816 cp __zero_reg__,r24
51 0030 1906 cpc __zero_reg__,r25
52 0032 04F4 brge .L4
53 0034 83E3 ldi r24,lo8(51)
54 0036 90E0 ldi r25,hi8(51)
55 0038 9A83 std Y+2,r25
56 003a 8983 std Y+1,r24
57 003c 00C0 rjmp .L5
58 .L4:
59 003e 84E4 ldi r24,lo8(68)
60 0040 90E0 ldi r25,hi8(68)
61 0042 9A83 std Y+2,r25
62 0044 8983 std Y+1,r24
63 .L5:
64 0046 00C0 rjmp .L5
Genial ;)

[EDIT2]
Erstaunlich was der Kompiler alles beachtet:

#define IsBitSet(ADDR,BIT) (((ADDR) & (1<<BIT))?1:0) // Fragt Bit = 1?

int main(void)
{
int x, tmp;

if(IsBitSet(x,0)) tmp=11; else tmp=22;
if(IsBitSet(x,15)) tmp=33; else tmp=44;

while(1);
return(0);
}

.LM2:
ldd r24,Y+3
ldd r25,Y+4
andi r24,lo8(1)
andi r25,hi8(1)
tst r24
breq .L2
ldi r24,lo8(11)
ldi r25,hi8(11)
std Y+2,r25
std Y+1,r24
rjmp .L3
.L2:
ldi r24,lo8(22)
ldi r25,hi8(22)
std Y+2,r25
std Y+1,r24
.L3:
.LM3:
ldd r24,Y+3
ldd r25,Y+4
tst r25
brge .L4
ldi r24,lo8(33)
ldi r25,hi8(33)
std Y+2,r25
std Y+1,r24
rjmp .L5
.L4:
ldi r24,lo8(44)
ldi r25,hi8(44)
std Y+2,r25
std Y+1,r24
.L5:
rjmp .L5

sternst
20.07.2011, 12:58
Das "quick&dirty" bezog sich auf die Annahme, dass der Kompiler intern false als 0 speichert und deshalb jeder Wert größer 0 als true interpretiert wird.1) Man braucht da nichts "anzunehmen". Das ist vom Standard alles klipp und klar geregelt.
2) Nicht jeder Wert größer Null ist true, sondern jeder Wert ungleich Null.
Nochmal: da ist rein gar nichts "quick&dirty".


Variante zwei prüft true/false.Nein. (siehe oben)