Forum: Mikrocontroller und Digitale Elektronik data |= (1<<16) funktioniert nicht


von Jan S. (spongebob)


Lesenswert?

Moin!
Ich versuche gerade das Signal einer Fernbedienung auszulesen.
Funktioniert auch bis jetzt ganz gut. Bis auf eine Sache.
Ich habe mir eine Variable (volatile int32_t data) angelegt, in der ich 
die 27 eingelesenen bits spiechern will.
Das wollte ich nach folgendem Schema tun:
1
if (bit == 1)
2
    {
3
      data |= (1<<(26-count_bits));
4
5
    }    
6
else
7
    {
8
      data &= ~(1<<(26-count_bits));
9
10
    }
11
count_bits++;

Nach langem rätseln, habe ich festgestellt, das alles über (1<<15) nicht 
funktioniert, so als wären die zwei höheren Bytes wo anders im Speicher 
abgelegt, oder so.
Hat von euch schon mal jemand so ein Problem gehabt?
Ich bin für jeden Tipp dankbar!!!

Grüße Jan

von Peter II (Gast)


Lesenswert?

Jan S. schrieb:
> Nach langem rätseln, habe ich festgestellt, das alles über (1<<15) nicht
> funktioniert, so als wären die zwei höheren Bytes wo anders im Speicher
> abgelegt, oder so.
> Hat von euch schon mal jemand so ein Problem gehabt?
> Ich bin für jeden Tipp dankbar!!!

weil der compiler alles mit INT (16bit) rechnet.

data |= ((uint32_t)1<<(26-count_bits));

sollte das Problem beheben.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Probiers mal mit (1UL<<16) oder ((long)1<<16)
Denn eine einfache Konstante ist beim AVR GCC 16 Bit breit...

von Bananen Joe (Gast)


Lesenswert?

Probier es mal mit einem cast:
1
data |= ((int32_t)1<<((int32_t)26-(int32_t)count_bits));
und
1
      data &= ~((int32_t)1<<((int32_t)26-(int32_t)count_bits));
Der Compiler nimmt nicht immer den grøsste Variablentyp, dass musst du 
im durch einen cast mitteilen. Ist ein fieser Stolperstein, ich hoffe 
damit ist dein Problem geløst.

von (prx) A. K. (prx)


Lesenswert?

Bananen Joe schrieb:
>  data |= ((int32_t)1<<((int32_t)26-(int32_t)count_bits));

Kennst du die Programmiersprache Brainf*ck? ;-)

von Jan S. (spongebob)


Lesenswert?

Jawoll!!!
Mit casten klappts. Man. Sowas muss man sich merken!!!
Danke für die schnelle Hilfe und einen schönen Tag noch!

Grüße Jan

von Stefan W. (dl6dx)


Lesenswert?

Peter II schrieb:
> data |= ((uint32_t)1<<(26-count_bits));

Alternativ geht auch:
1
data |= (1L << (26-count_bits));

Bei der Gelegenheit:
Dein Codefragment scheint aus einer Schleife zu stammen. Bei jedem 
Durchlauf muss die Bitmaske (1L << (26 -count_bits)) neu berechnet 
werden.
Die Shiftoperation ist beim AVR eine sehr aufwendige Sache und kostet 
Zeit und Codegröße. Schau mal:
1
#include <stdint.h>
2
#include <avr/io.h>
3
4
// Datenquelle, durch volatile wird in der Simulation der 
5
// Lesezugriff erzwungen
6
volatile uint8_t bit;
7
8
// im Original auch als volatile deklariert
9
volatile int32_t data;
10
11
// Orginaler Aufbau der Schleife
12
// Das zuerst übertragene Bit steht abschließend im LSB von data.
13
14
void shift1(void)
15
  {
16
  uint8_t count_bits;
17
18
  count_bits = 0;
19
  while(count_bits <= 26)
20
  {
21
    if (bit == 1)
22
      {
23
      data |= (1L<<(26-count_bits));
24
      }
25
    else
26
      {
27
      data &= ~(1L<<(26-count_bits));
28
      }
29
    count_bits++;
30
    }
31
  }

ergibt diesen Assemblercode:
1
void shift1(void)
2
  {
3
  ce:  cf 92         push  r12
4
  d0:  df 92         push  r13
5
  d2:  ef 92         push  r14
6
  d4:  ff 92         push  r15
7
  d6:  2a e1         ldi  r18, 0x1A  ; 26
8
  d8:  30 e0         ldi  r19, 0x00  ; 0
9
      {
10
      data |= (1L<<(26-count_bits));
11
      }
12
    else
13
      {
14
      data &= ~(1L<<(26-count_bits));
15
  da:  cc 24         eor  r12, r12
16
  dc:  dd 24         eor  r13, r13
17
  de:  76 01         movw  r14, r12
18
  e0:  c3 94         inc  r12
19
  uint8_t count_bits;
20
21
  count_bits = 0;
22
  while(count_bits <= 26)
23
  {
24
    if (bit == 1)
25
  e2:  80 91 00 01   lds  r24, 0x0100
26
  e6:  81 30         cpi  r24, 0x01  ; 1
27
  e8:  f9 f4         brne  .+62       ; 0x128 <shift1+0x5a>
28
      {
29
      data |= (1L<<(26-count_bits));
30
  ea:  80 91 01 01   lds  r24, 0x0101
31
  ee:  90 91 02 01   lds  r25, 0x0102
32
  f2:  a0 91 03 01   lds  r26, 0x0103
33
  f6:  b0 91 04 01   lds  r27, 0x0104
34
  fa:  b7 01         movw  r22, r14
35
  fc:  a6 01         movw  r20, r12
36
  fe:  02 2e         mov  r0, r18
37
 100:  04 c0         rjmp  .+8        ; 0x10a <shift1+0x3c>
38
 102:  44 0f         add  r20, r20
39
 104:  55 1f         adc  r21, r21
40
 106:  66 1f         adc  r22, r22
41
 108:  77 1f         adc  r23, r23
42
 10a:  0a 94         dec  r0
43
 10c:  d2 f7         brpl  .-12       ; 0x102 <shift1+0x34>
44
 10e:  84 2b         or  r24, r20
45
 110:  95 2b         or  r25, r21
46
 112:  a6 2b         or  r26, r22
47
 114:  b7 2b         or  r27, r23
48
 116:  80 93 01 01   sts  0x0101, r24
49
 11a:  90 93 02 01   sts  0x0102, r25
50
 11e:  a0 93 03 01   sts  0x0103, r26
51
 122:  b0 93 04 01   sts  0x0104, r27
52
 126:  22 c0         rjmp  .+68       ; 0x16c <shift1+0x9e>
53
      }
54
    else
55
      {
56
      data &= ~(1L<<(26-count_bits));
57
 128:  80 91 01 01   lds  r24, 0x0101
58
 12c:  90 91 02 01   lds  r25, 0x0102
59
 130:  a0 91 03 01   lds  r26, 0x0103
60
 134:  b0 91 04 01   lds  r27, 0x0104
61
 138:  b7 01         movw  r22, r14
62
 13a:  a6 01         movw  r20, r12
63
 13c:  02 2e         mov  r0, r18
64
 13e:  04 c0         rjmp  .+8        ; 0x148 <shift1+0x7a>
65
 140:  44 0f         add  r20, r20
66
 142:  55 1f         adc  r21, r21
67
 144:  66 1f         adc  r22, r22
68
 146:  77 1f         adc  r23, r23
69
 148:  0a 94         dec  r0
70
 14a:  d2 f7         brpl  .-12       ; 0x140 <shift1+0x72>
71
 14c:  40 95         com  r20
72
 14e:  50 95         com  r21
73
 150:  60 95         com  r22
74
 152:  70 95         com  r23
75
 154:  84 23         and  r24, r20
76
 156:  95 23         and  r25, r21
77
 158:  a6 23         and  r26, r22
78
 15a:  b7 23         and  r27, r23
79
 15c:  80 93 01 01   sts  0x0101, r24
80
 160:  90 93 02 01   sts  0x0102, r25
81
 164:  a0 93 03 01   sts  0x0103, r26
82
 168:  b0 93 04 01   sts  0x0104, r27
83
 16c:  21 50         subi  r18, 0x01  ; 1
84
 16e:  30 40         sbci  r19, 0x00  ; 0
85
void shift1(void)
86
  {
87
  uint8_t count_bits;
88
89
  count_bits = 0;
90
  while(count_bits <= 26)
91
 170:  8f ef         ldi  r24, 0xFF  ; 255
92
 172:  2f 3f         cpi  r18, 0xFF  ; 255
93
 174:  38 07         cpc  r19, r24
94
 176:  09 f0         breq  .+2        ; 0x17a <shift1+0xac>
95
 178:  b4 cf         rjmp  .-152      ; 0xe2 <shift1+0x14>
96
      {
97
      data &= ~(1L<<(26-count_bits));
98
      }
99
    count_bits++;
100
    }
101
  }
102
 17a:  ff 90         pop  r15
103
 17c:  ef 90         pop  r14
104
 17e:  df 90         pop  r13
105
 180:  cf 90         pop  r12
106
 182:  08 95         ret

Du kannst den variablen Shift aber ohne größere Probleme vermeiden. Der 
nachfolgende Code sollte funktionsgleich sein. (Ist ungetestet, also 
besser mal im Simulator durchspielen!)

Das jeweils "neue" Bit wird "links" (auf Bitposition 26) eingetragen und 
ein einziger 32Bit-Shift (nach rechts) gemacht. Damit steht am Ende 
das zuerst übertragene Bit auch im LSB von data.
1
// Optimierter Algorithmus
2
// Die variable Shift-Operation ist durch eine zur Übersetzungszeit 
3
// feststehende Konstante ersetzt
4
// Das zuerst übertragene Bit steht weiterhin im LSB von data.
5
6
void shift2(void)
7
{
8
  uint8_t count_bits;
9
10
  count_bits = 0;
11
  while(count_bits <= 26)
12
  {
13
    data = data >> 1;
14
    if (bit == 1)
15
    {
16
      data |= (1L << 26);
17
    }
18
    else
19
    {
20
      data &= ~(1L << 26);
21
    }
22
    count_bits++;
23
  }
24
}
ergibt
1
00000184 <shift2>:
2
// Die variable Shift-Operation ist durch eine zur Übersetzungszeit feststehende Konstante ersetzt
3
// Das zuerst übertragene Bit steht weiterhin im LSB von data.
4
5
6
void shift2(void)
7
{
8
 184:  2b e1         ldi  r18, 0x1B  ; 27
9
  uint8_t count_bits;
10
11
  count_bits = 0;
12
  while(count_bits <= 26)
13
  {
14
    data = data >> 1;
15
 186:  80 91 01 01   lds  r24, 0x0101
16
 18a:  90 91 02 01   lds  r25, 0x0102
17
 18e:  a0 91 03 01   lds  r26, 0x0103
18
 192:  b0 91 04 01   lds  r27, 0x0104
19
 196:  b5 95         asr  r27
20
 198:  a7 95         ror  r26
21
 19a:  97 95         ror  r25
22
 19c:  87 95         ror  r24
23
 19e:  80 93 01 01   sts  0x0101, r24
24
 1a2:  90 93 02 01   sts  0x0102, r25
25
 1a6:  a0 93 03 01   sts  0x0103, r26
26
 1aa:  b0 93 04 01   sts  0x0104, r27
27
    if (bit == 1)
28
 1ae:  80 91 00 01   lds  r24, 0x0100
29
 1b2:  81 30         cpi  r24, 0x01  ; 1
30
 1b4:  91 f4         brne  .+36       ; 0x1da <shift2+0x56>
31
    {
32
      data |= (1L << 26);
33
 1b6:  80 91 01 01   lds  r24, 0x0101
34
 1ba:  90 91 02 01   lds  r25, 0x0102
35
 1be:  a0 91 03 01   lds  r26, 0x0103
36
 1c2:  b0 91 04 01   lds  r27, 0x0104
37
 1c6:  b4 60         ori  r27, 0x04  ; 4
38
 1c8:  80 93 01 01   sts  0x0101, r24
39
 1cc:  90 93 02 01   sts  0x0102, r25
40
 1d0:  a0 93 03 01   sts  0x0103, r26
41
 1d4:  b0 93 04 01   sts  0x0104, r27
42
 1d8:  11 c0         rjmp  .+34       ; 0x1fc <shift2+0x78>
43
    }
44
    else
45
    {
46
      data &= ~(1L<<26);
47
 1da:  80 91 01 01   lds  r24, 0x0101
48
 1de:  90 91 02 01   lds  r25, 0x0102
49
 1e2:  a0 91 03 01   lds  r26, 0x0103
50
 1e6:  b0 91 04 01   lds  r27, 0x0104
51
 1ea:  bb 7f         andi  r27, 0xFB  ; 251
52
 1ec:  80 93 01 01   sts  0x0101, r24
53
 1f0:  90 93 02 01   sts  0x0102, r25
54
 1f4:  a0 93 03 01   sts  0x0103, r26
55
 1f8:  b0 93 04 01   sts  0x0104, r27
56
 1fc:  21 50         subi  r18, 0x01  ; 1
57
void shift2(void)
58
{
59
  uint8_t count_bits;
60
61
  count_bits = 0;
62
  while(count_bits <= 26)
63
 1fe:  19 f6         brne  .-122      ; 0x186 <shift2+0x2>
64
    {
65
      data &= ~(1L<<26);
66
    }
67
    count_bits++;
68
  }
69
}
70
 200:  08 95         ret

In der Codegröße (126 Bytes im Vergleich zu 182 Bytes vorher) ist die 
Funktion bereits kleiner.
Der Unterschied in der Ausführungszeit ist noch dramatischer: Dein 
Original macht in jedem Durchlauf (26 - bit_count) 32Bit-Shifts, das 
sind insgesamt 351. Der veränderte Algrithmus kommt mit einem 
32Bit-Shift pro Durchlauf aus (also 27).

Grüße

Stefan

PS: Als Optimierung war -O1 eingestellt. Mit -Os würde es zwar noch 
etwas kleiner, das geschilderte Problem bleibt aber.

von Jan S. (spongebob)


Lesenswert?

Das ist denke ich echt ein guter Hinweis!
Werde mich morgen mal in ruhe damit auseinandersetzen ;-)

Grüße Jan

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Jan S. schrieb:

> Mit casten klappts. Man. Sowas muss man sich merken!!!
> Danke für die schnelle Hilfe und einen schönen Tag noch!

Du kannst auch Warnungen lesen und beachten.  Der Compiler meckert 
nämlich, wenn man die 1 aus dem Bereich eines int schiebt.

Es ist einfacher Warnungen zu beheben als Fehler zur Laufzeit.  Aber 
jeder hat eben sein Steckenpferd ;-)

von hmm... (Gast)


Lesenswert?

Auf einem 32 bitter würde das übrigens mit den meisten Compilern so 
gehen. Ohne Casting wird eben immer vom Standart Datentyp int 
ausgegangen und der ist beim Avr nunnmal 16 Bit.

Ein Integer ist in der Regel so groß wie die Allzweckregister der CPU, 
mindestens aber 16 Bit.

von Rolf M. (rmagnus)


Lesenswert?

hmm... schrieb:
> Ein Integer ist in der Regel so groß wie die Allzweckregister der CPU,
> mindestens aber 16 Bit.

Aber auch auf 64-Bit-Plattformen selten 64 Bit.

von pico embedded (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Aber auch auf 64-Bit-Plattformen selten 64 Bit.

Für AVR-GCC war dies der wichtigste und treffendste Hinweis.

von Rolf M. (rmagnus)


Lesenswert?

Das war ja auch nur der Vollständigkeit halber, weil mein Vorredner über 
die Größe von int auch auf anderen Plattformen als 8-Bitter gespriochen, 
aber dieses Detail nicht erwähnt hat.
Abgesehen davon habe ich damit zum Thema mehr beigetragen als du!

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.