Forum: Mikrocontroller und Digitale Elektronik Bitweise SPI Abfrage des ADC im Zweierkomplement


von Gerald G. (gerald_g)


Angehängte Dateien:

Lesenswert?

Hallo,

ich habe leider einen kleinen Knoten im Hirn. Bei der ADC-Abfrage 
(18bit) per SPI mit dem STM32F429 habe ich etwas Probleme.
Die Daten werden wie angehängt gesendet.

Ich rufe die Daten per SPI_I2S_ReceiveData(SPI5) (von der STD-Lib) 
8-bitweise ab.
Im ersten Byte habe ich dann D17-D10, im zweiten D9-D2 und im dritten 
Byte habe ich dann D1-D0.
Die Byte sind alle uint8_t
Es gibt doch bestimmt eine einfache Variante die jetzt in eine int32_t 
Variable zu speichern, und dabei das Vorzeichen nicht zu verlieren, 
oder?

Mir fällt da nur eine Variante mit viel schieben und ifs ein.

Kann mir da bitte jemand auf die Sprünge helfen?

von PittyJ (Gast)


Lesenswert?

Was spricht gegen das Schieben?
Sind 3 Zeile. Geht schneller als ein Post hier.

von Gerald G. (gerald_g)


Lesenswert?

ja ok, meine Version ist gerade folgende:
1
  int32_t value;
2
  uint8_t bytes[3];
3
  uint8_t i;
4
  for(i=0;i<3;i++){
5
    bytes[i]=GG_SPI5_SendByte(0x00);
6
  }
7
  if (0x80 & bytes[0]){
8
    value =~(0x3FFFF-((bytes[0]<<10) | (bytes[1]<<2)| (bytes[2]>>6)));
9
  }
10
  else
11
    value = (bytes[0]<<10) | (bytes[1]<<2)| (bytes[2]>>6);

Dachte vielleicht gibt es etwas schöneres.

edit: So muss es sein

von Gerald G. (gerald_g)


Lesenswert?

So, neue Möglichkeit:
1
int32_t value;
2
uint8_t bytes[3];
3
uint8_t i;
4
for(i=0;i<3;i++){
5
bytes[i]=SPI5_SendByte(0x00);
6
}
7
value = (((bytes[0]<<24) | (bytes[1]<<16)| (bytes[2]<<8))>>14);
Voraussetzung hierfür ist allerdings dass negative signed Integer beim 
GCC Compiler beim rechtsshiften mit Einsen aufgefüllt werden und 
positive mit Nullen.

Wahrscheinlich muss ich in der zweiten Zeile:
1
value = (int32_t)(((bytes[0]<<24) | (bytes[1]<<16)| (bytes[2]<<8))>>14);
casten.

von Karl H. (kbuchegg)


Lesenswert?

Gerald G. schrieb:
> So, neue Möglichkeit:
>
1
> int32_t value;
2
> uint8_t bytes[3];
3
> uint8_t i;
4
> for(i=0;i<3;i++){
5
> bytes[i]=SPI5_SendByte(0x00);
6
> }
7
> value = (((bytes[0]<<24) | (bytes[1]<<16)| (bytes[2]<<8))>>14);
8
>
> Voraussetzung hierfür ist allerdings dass negative signed Integer beim
> GCC Compiler beim rechtsshiften mit Einsen aufgefüllt werden und
> positive mit Nullen.

Nicht nur.

bytes[0]<<24
geht schon mal schief.

> Wahrscheinlich muss ich in der zweiten Zeile:
>
1
> value = (int32_t)(((bytes[0]<<24) | (bytes[1]<<16)| (bytes[2]<<8))>>14);
2
>
> casten.

Das ist zu spät gecastet. Wenn du das komplette 16 Bit Ergebnis von 16 
Bit auf 32 Bit hochcastest, dann ist das zwar nett von dir für die 
Zuweisung, ändert abder nichts daran, dass das Ergebnis der ganzen 
Schiebeorgie erst mal 16 Bit war.


In jeder arithmetischen Operation gilt in C die Regel, dass die 
IMplementierung der Operation ausschliesslich von den Datentypen der 
Operanden abhängt. In
1
   c = a + b
bestimmen die Datentypen von a und b, wie die Addition durchgeführt 
wird. Der Datentyp von c ist dazu völlig uninteressant. Bei den 
Schiebeoperationen
1
   c = a << b
ist es sogar nur der Datentyp von a, welcher darüber entscheidet, welche 
Operation da zum Einsatz kommt.

Weiters gilt die Regel: gerechnet wird immer im größten Datentyp der in 
einer (Teil-)Operation vorkommt. Aber nicht kleiner als int. Und int ist 
nun mal auf deinem µC 16 Bit.

von Gerald G. (gerald_g)


Lesenswert?

Karl Heinz schrieb:
> Das ist zu prät gecastet. Wenn du das komplette 16 Bit Ergebnis von 16
> Bit auf 32 Bit hochcastest, dann ist das zwar nett von dir für die
> Zuweisung, ändert abder nichts daran, dass das Ergebnis der ganzen
> Schiebeorgie erst mal 16 Bit war.

Also so:
1
value = (((int32_t)(bytes[0]<<24) | (int32_t)(bytes[1]<<16)| (int32_t)(bytes[2]<<8))>>14);

Oder reicht es nach der ersten Klammer zu casten? Also bezieht sich das 
dann auf jede Variable in der Klammer.
Also:
1
value = ((int32_t)((bytes[0]<<24) | (bytes[1]<<16)| (bytes[2]<<8))>>14);

edit:
hoppl,a hast ja deinen Beitrag editiert, ich lese schnell.

von Karl H. (kbuchegg)


Lesenswert?

Gerald G. schrieb:

> Also so:
>
1
> value = (((int32_t)(bytes[0]<<24) | (int32_t)(bytes[1]<<16)| 
2
> (int32_t)(bytes[2]<<8))>>14);
3
>

Immer noch falsch.

in
1
uint8_t i = 5;
2
uint64_t c;
3
4
  c = i << 5;
bestimmt der Datentyp von i, wie die Schiebeoperation aufgefasst wird. i 
ist ein uint8_t und damit 'kleiner' als ein int. Ergo ist das eine int 
Operation. i wird implizit zu einem int hochgecastet, ist damit 16 Bit 
breit und die Schiebeoperation wird als 16 Bit Schiebeoperation 
implementiert. Ein nachträglicher Cast
1
uint8_t i = 5;
2
uint64_t c;
3
4
  c = (uint64_t)(i << 5);
ändert daran nicht das geringste.
Wenn du den Compiler dazu zwingen willst, die Schiebeoperation als 64 
Bit Operation zu implementieren, dann musst du VOR dem Verschieben i auf 
64 Bit hochcasten. Nicht hinterher.
1
uint8_t i = 5;
2
uint64_t c;
3
4
  c = ((uint64_t)i) << 5);
Jetzt wird zuerst i auf 64 Bit hochgehoben, damit hat das a in
1
    c = a << b
einen 64 Bit Datentyp und damit wird auch die Verschiebeoperation als 64 
Bit Operation implementiert.

von Karl H. (kbuchegg)


Lesenswert?

Karl Heinz schrieb:

> einen 64 Bit Datentyp und damit wird auch die Verschiebeoperation als 64
> Bit Operation implementiert.

Und immer schön darauf achten welche Signedness deine Zwischenergebnisse 
haben. Dann ganz zum SChluss willst du aus dem unsigned ja einen signed 
machen, damit dir das rechts schieben um 14 Positionen das Vorzeichenbit 
entsprechend aufzieht.

Na, wäre eine Abfrage des MSB des 24 Bit Ergebnisses und je nachdem wird 
das höchstwertige Byte auf entweder 0x00 oder 0xFF gesetzt nicht doch 
einfacher gewesen?

von Gerald G. (gerald_g)


Lesenswert?

So, hatt schon meinen ersten Beitrag editiert, aber durfte nicht mehr 
absenden.
Also werde ich die "bytes[]" als 32bit initialisieren.
Nur die Frage wegen dem cast von uint in int, welcher entweder beim 
auslesen passiert oder beim zuweisen an den Wert.
In jedem Fall wird eine Art "signed overflow" geschehen, da bei einem 
negativen Wert am ADC das Masterbit gesetzt ist, der Wert für den µC 
größer als für einen int-Typ zulässig ist. Am liebsten würde ich gerne 
Bitweise kopieren, unabhängig vom Typ.
Ich glaube es läuft auf eine eigene "SPI5_SendByte()" Funktion hinaus, 
die gleich einen signed int8 ausgibt.

edit:
Karl Heinz schrieb:
> Na, wäre eine Abfrage des MSB des 24 Bit Ergebnisses und je nachdem wird
> das höchstwertige Byte auf entweder 0x00 oder 0xFF gesetzt nicht doch
> einfacher gewesen?

Das war ja mein erster Vorschlag oben, der mir so nicht gefällt, mir 
jedoch immer sympathischer wird :D
Das Castingproblem werde ich dort natürlich korrigieren.

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.