Hallo!
Ich weiss mal wieder nicht weiter!
Ich verwende nen ATMEGA8 (&AVR Studio), um via I²C zyklisch Messwerte
aus dem Kapazitätssensor AD7746 auszulesen. Die Messwerte haben eine
Breite von 24Bit und teilen sich zu jeweils 8Bit auf in:
cdc_HIGH
cdc_MIDD
cdc_LOW
Ich möchte diese Werte zum eigenlichen Ergebnis "zusammenkleben",
um dieses dann auf den UART zu schicken.
Wenn ich statt utoa() ltoa() verwende, kommen da tatsächlich zyklisch
Messwerte auf dem Terminalprog an. Die sind recht hoch und ändern
sich bei kontinuierlicher Änderung der Kapazität leider nur sprunghaft!
Verwende ich utoa() (wie unten), dann bekomme ich nur ein und denselben
Messwert:
65535dec, also 16mal die "1".
Was habe ich falsch gemacht???
Vielen Dank im Voraus!
Um eine Fehlerquelle auszuschließen kannst Du ja statt <X>toa mal die
drei einzelnen Bytes direkt oder in HEX verschicken. Evtl. stimmt ja was
mit dem Sensor oder dem I2C nicht.
Super Jörg!
Das mit dem ACK/NACK hätte ich nie entdeckt!
Jetzt scheint alles zu laufen. Auch das sprunghafte
Verhalten ist weg.
Dein Code ist natürlich eleganter!
Vielen Dank!
Hast Du das aus dem Ärmel geschüttelt, oder durftest
Du Dich auch schon mit dem AD7746 befassen?
Habe ich das mit dem:
char s[32];
richtig gemacht??
@Freizeitbastler
Habe ich so ähnlich gemacht. Habe den Sensor direkt,
ohne µC ausgelesen. Die Messwerte habe ich von Hand
umgerechnet....da hat alles geklappt. Danke trotzdem!
>Vielen Dank!
Bitte
> Hast Du das aus dem Ärmel geschüttelt, oder durftest> Du Dich auch schon mit dem AD7746 befassen?
So funktioniert I2C (mit dem AD7746 hatte ich noch nie zutun)
>Habe ich das mit dem:>char s[32];>richtig gemacht?
Der String muss nur groß genug sein ("-2147483648\0"-> Max. 12 Stellen
bei int32_t; "16777216\0" -> 9, wenn es nur um das (unsigned)
ADC-Ergebnis geht).
hth. Jörg
Hi,
ich möchte diesen fred mal kurz aufwecken, weil ich eine Frage habe, die
genau hierher passt. Ich habe das gleiche Problem, möchte eine 24Bit
Variable vom SPI Bus nehmen in drei 1Byte Schritten. Das funktioniert
auch. Dann lege ich mir eine 32Bit Variable an, packe das HighByte
hinein und shifte 12mal nach links. Genau dabei bekomme ich aber den
gesamten höherwertigen Anteil in meiner 32 Bit Variable auf high
gesetzt. Hexadezimal sieht das dann so aus, nach dem shiften von 0xAB:
0xFFFF0000; Shifte ich nur 8mal: 0xFFFFAB00. Hier seht ihr meinen Code,
ich finde nichts daran...Kann mir bitte jemand erklären, warum meine
32Bit Variable sich entscheidet negativ, also voll besetzt mit Einsen zu
sein, dort wo nichts hinüber geschiftet wird?
1
boolADS1234_getData(int32_t*Data)
2
{
3
uint8_tin1,in2,in3,dummy;
4
int32_tadcData;
5
6
//first off shift in 24 Bits (= 3x8 Bits)
7
in1=0xab;//spi_putc(0xff);
8
in2=0xcd;//spi_putc(0xff);
9
in3=0xef;//spi_putc(0xff);
10
11
/* now begin shifting everything to the left,
12
* then fill up lowest available byte with read bytes
Hallo Karl Heinz,
habe es vorhin schon einmal so probiert, änderte nichts, war aber auch
meine erste Idee. Hab schon Feierabend gemacht und werd's daher einfach
morgen nochmal versuchen.
Grüße
Göck
S. G. schrieb:
> Hallo Karl Heinz,>> habe es vorhin schon einmal so probiert, änderte nichts,
Muss es aber.
in1 << 8L;
in1 ist ein uint8_t. Damit die Operation gemacht werden kann, wird der
uint8_t implizit auf einen int hochgecastet.
Bitmässig passiert dieses
0x00AB << 8L
gibt als Ergebnis 0xAB00
So weit so gut. Nur hat dieses Zwischenergebnis den Datentyp int, also:
Vorzeichenbehaftet.
Wird das einem uint32_t zugewiesen, muss das Zwischenergebnis erstmal
auf 32 Bit aufgeblasen werden. Und da 0xAB00 eine negative Zahl ist, ist
die entsprechende 32-Bit Zahl (ebenfalls vorzeichenbehaftet) 0xFFFFAB00.
Und erst dann gehst in den uint32_t rein.
Du musst den Compiler daran hindern, int als Zwischenstufe zu benutzen.
AM einfachsten geht das wohl, wenn du von vorne herein in1 auf einen
uint32_t castest. Dann gibt es keine Notwendigkeit mehr für einen
Zwischen-int und die Gefahr von Overflows bei in1 << 16 ist auch
gebannt :-)
Na wiegesagt, werde es morgen mal ausprobieren. Es verwirrt mich
allerdings etwas, denn ich hab ja alle beteiligten Variablen als uint
definiert. Nur die "originale" Variable soll int sein. Ich werde morgen
sehn... :-)
S. G. schrieb:
> Na wiegesagt, werde es morgen mal ausprobieren. Es verwirrt mich> allerdings etwas, denn ich hab ja alle beteiligten Variablen als uint> definiert.
Deine Verwirrung rührt daher, dass du eine Zwischenstufe übersehen hast.
Die resultiert aus einer C-Regel, die da grob gesagt lautet:
Gerechnet wird immer mindestens mit int. Datentypen kleiner als int,
werden zunächst auf int hochgehoben.
Und so wird aus deinem uint8_t zum Zwecke des Linksschiebens
zwischendurch ein int. Wohlgemerkt: ein int! kein uint!
Und mit diesem int ist dann ein Vorzeichen ins Spiel gekommen, welches
im Endergebnis seine Spuren hinterlässt.
> Du musst den Compiler daran hindern, int als Zwischenstufe zu benutzen.
Statt
1
adcData=((uint32_t)in1)<<16L;
sollte auch
1
adcData=in1<<16UL;
dasselbe bewirken (long fasst nicht den Wertebereich von unsigned long,
also Promotion zum unsigned Typ). Ob das im erzeugten Code tatsächlich
einen Unterschied ergibt, ist für mich zweifelhaft. Deshalb nur als
kleine Anmerkung.
Morgen,
habe jetzt das Problem gelöst, bekomme 0xAB0000 aus der Verschiebung
heraus - wie ich es erwartet hatte.
Das hier hat nicht funktioniert:
1
adcData=in1<<16UL;
Dieser Weg funktionierte bei mir, Danke nochmal Karl Heinz.
1
adcData=((uint32_t)in1)<<16L;
Eine frage stellt sich mir noch, was hat es mit den "L" und "UL"
Suffixen auf sich. Ich verstehe bspw. diesen Befehl
1
a=a<<2
so, dass es sich um zweimaliges Linksschieben mit Abspeichern in a
handelt. Das L oder UL sagt jetzt etwas über das implizite casten aus?
Danke und Grüße vom Rhein
Göck
S. G. schrieb:
> handelt. Das L oder UL sagt jetzt etwas über das implizite casten aus?
Ja und Nein
Diese Suffixe legen einen Datentyp für die Zahlenkonstante fest.
5 ist ein int
5L ist ein long
5UL ist ein unsigned long
zusammen mit der Regelung, dass die der Datentyp einer Operation und der
Zahlenraum, in welchem die Operation durchgeführt wird, ausschliesslich
von den beteiligten Operanden abhängt, sollte jetzt klar sein, was der
Unterschied zwischen
in1 << 16 und
in1 << 16UL
ist. Das erste wird als int Operation abgehandelt, das zweite als
unsigned long Operation. Und mitlerweile weißt du auch, dass eine
int-Operation an dieser Stelle ein "falsches" Ergebnis bringt.
Karl heinz Buchegger schrieb:
> sollte jetzt klar sein, was der> Unterschied zwischen>> in1 << 16 und> in1 << 16UL>> ist. Das erste wird als int Operation abgehandelt, das zweite als> unsigned long Operation. Und mitlerweile weißt du auch, dass eine> int-Operation an dieser Stelle ein "falsches" Ergebnis bringt.
Sowohl 16L als auch 16UL bringen hier keine Änderung, denn << ist einer
der wenigen Operatoren, wo die Größe der beteiligten Operanden nicht
aneinander angeglichen wird.
Aha, wo finde ich sowas dokumentiert, in der C Referenz?
Anscheinend wird dieses Verhalten aber nicht - wie du schon vermutet
hattest, Hc - von WinAVR so umgesetzt. Unter "normalen" C sollte das
aber wohl so funktionieren .?
S. G. schrieb:
> Aha, wo finde ich sowas dokumentiert, in der C Referenz?
Im C Standard.
S. G. schrieb:
> Unter "normalen" C sollte das aber wohl so funktionieren .?
Meinst du mit "das" dieses?
1
adcData=in1<<16UL;
Nein. Das funktioniert generell nur dann, wenn auf der Architektur ein
int größer als 16 Bit ist (z.B. auf dem PC). Denn wie gesagt, das UL hat
hier keinerlei Einfluss darauf, wie die Operation ausgeführt wird.
Stefan Ernst schrieb:
> Sowohl 16L als auch 16UL bringen hier keine Änderung, denn << ist einer> der wenigen Operatoren, wo die Größe der beteiligten Operanden *nicht*> aneinander angeglichen wird.
Die weiß ich auch nie auswendig.
Drum halte ich mich da einfach immer an den linken Operanden und dann
stimmts wieder für alle Operatoren :-)