Hallo zusammen,
ich steh grad wieder mal auf dem Schlauch (kein Wunder um die Uhrzeit)
auf einem AVR (ATmega328) krieg ich von einem per SPI angeschlossenen
ADC drei mal 8 bit, die zusammen einen 24 Bit vorzeichenbehafteten Wert
darstellen. Der Wert ist das Ergebnis einer ratiometrischen
Widerstandsmessung, skaliert mit einer (bekannten) Verstärkung.
Mein Problem ist: wie krieg ich mit AVR-GCC die 3x8 bit (wobei nur das
erste byte signed ist) effizient in einen int32_t?
inline assembler muss es grade nicht sein (so zeitkritisch ist es auch
nicht).
der alte und verpönte trick mit der Union? nur - ich hab "nur" 24 bit,
und keine 32. Die höchsten 24 bit verwenden, und dann ein >>8? wird wohl
mit dem signed nicht ganz hinhauen...
bitte um Denkanstöße!
lg Michi
Michael Reinelt schrieb:> bitte um Denkanstöße!
Ist der Wert überhaupt ein Zweierkomplement oder wird er mit Offset
0x800000 vom ADC geliefert?
Ansonsten: warum willst Du den Wert überhaupt rechts shiften. Ich
betrachte ADC-Werte immer als Werte im Bereich +/- 0.0 .. 0.99999 der
Referenzspannung.
Gruß Anja
Michael Reinelt schrieb:> auf einem AVR (ATmega328) krieg ich von einem per SPI angeschlossenen> ADC drei mal 8 bit, die zusammen einen 24 Bit vorzeichenbehafteten Wert> darstellen.> (...)> der alte und verpönte trick mit der Union? nur - ich hab "nur" 24 bit,> und keine 32. Die höchsten 24 bit verwenden, und dann ein >>8? wird wohl> mit dem signed nicht ganz hinhauen...
Guten Morgen!
Wenn du die Daten doch eh byteweise vom SPI einsammeln musst, warum
wirfst du dann nicht einfach jeweils das niederwertigste Byte weg und
setzt die beiden übrigen dann zu einem 16-Bit-signed-integer zusammen?
Anja schrieb:> Ist der Wert überhaupt ein Zweierkomplement oder wird er mit Offset> 0x800000 vom ADC geliefert?
Ist er, definitiv.
> Ansonsten: warum willst Du den Wert überhaupt rechts shiften. Ich> betrachte ADC-Werte immer als Werte im Bereich +/- 0.0 .. 0.99999 der> Referenzspannung.
ja eh :-)
Aber wie am besten/effizientesten mit dem 24-Bit-Wert weiterrechnen bzw.
umwandeln in [0..1]?
Markus Weber schrieb:> Wenn du die Daten doch eh byteweise vom SPI einsammeln musst, warum> wirfst du dann nicht einfach jeweils das niederwertigste Byte weg und> setzt die beiden übrigen dann zu einem 16-Bit-signed-integer zusammen?
Weil ich die niederwertigsten Bits auch bezahlt habe :-)
Michael Reinelt schrieb:> Anja schrieb:>>> Ist der Wert überhaupt ein Zweierkomplement oder wird er mit Offset>> 0x800000 vom ADC geliefert?>> Ist er, definitiv.
Definitiv was?
Wenn es ein Offset ist, dann benutz halt den union Trick und zieh danach
0x800000 davon ab, um 0x800000 zu 0 zu machen und signed Werte zu
erhalten
> Weil ich die niederwertigsten Bits auch bezahlt habe :-)
Und dein elektrischer Aufbau ist so, dass die untersten 8 Bit nicht
einfach nur Zufallszahlen sind?
Für einen 16 Bit ADC muss man sich elektrisch schon ordentlich ins Zeug
legen. Für einen 24Bit ADC gilt das noch viel mehr. Immerhin sind das
bei 5V Referenz schon 0.3 Millionstel Volt Auflösung.
@ Michael Reinelt (fisa)
>ADC drei mal 8 bit, die zusammen einen 24 Bit vorzeichenbehafteten Wert>Mein Problem ist: wie krieg ich mit AVR-GCC die 3x8 bit (wobei nur das>erste byte signed ist) effizient in einen int32_t?
Wo ist das Problem?
1
signedint32resultat=0;
2
unsignedint8byte[3];/* byte[0] ist das niederwertigste Byte */
>der alte und verpönte trick mit der Union? nur - ich hab "nur" 24 bit,>und keine 32.
Ja und? Das Rumgemurkse mit 24 Bit bringt nur Stress. Pack es wie oben
gezeigt vorzeichenrichtig in 32 Bit und fertig.
Karl Heinz Buchegger schrieb:> Michael Reinelt schrieb:>> Anja schrieb:>>>>> Ist der Wert überhaupt ein Zweierkomplement oder wird er mit Offset>>> 0x800000 vom ADC geliefert?>>>> Ist er, definitiv.>> Definitiv was?
Sorry, das war leider mißverständlich: Es ist definitiv ein
Zweierkomplement:
0x000000 => 0, 0x7fffff => +max, 0x800000 => -max
> Wenn es ein Offset ist, dann benutz halt den union Trick und zieh danach> 0x800000 davon ab, um 0x800000 zu 0 zu machen und signed Werte zu> erhalten
siehe oben, es ist kein Offset.
>> Weil ich die niederwertigsten Bits auch bezahlt habe :-)> Und dein elektrischer Aufbau ist so, dass die untersten 8 Bit nicht> einfach nur Zufallszahlen sind?> Für einen 16 Bit ADC muss man sich elektrisch schon ordentlich ins Zeug> legen. Für einen 24Bit ADC gilt das noch viel mehr. Immerhin sind das> bei 5V Referenz schon 0.3 Millionstel Volt Auflösung.
Ist mir klar, es geht mir aber ein Stück weit auch ums Prinzip ;-)
Momentan habe ich folgende Lösung:
Ich mach den 16-Bit-Trick "in die andere Richtung", d.h. ich betrachte
die 24-Bit-Zahl als 32 bit, mit den untersten 8 Bit auf 0 gesetzt. Auf
diese Art kann ich direkt die 4 Byte des int32 setzen, drei vom ADC, das
letzte Byte auf 0.
Mein Wert ist dann zwar um Faktor 256 zu groß, aber das macht nix: ich
lese auf dieselbe Art auch den Referenzwert vom ADC, und da ich sowieso
normalisieren/dividieren muss (ratiometrische Messung), kürzt sich der
Faktor 256 raus.
ich wär aber trotzdem neugierig, obs eine trickreiche Variante gibt mit
solchen 24-bit zahlen zu arbeiten....
lg Michi
> ich wär aber trotzdem neugierig, obs eine trickreiche Variante gibt mit
solchen 24-bit zahlen zu arbeiten
Als man noch alles in Assembler programmierte, hätte man sich eine 24bit
Arithmetik(+,-,*,:) geschrieben.
Da du aber in C programmierts und wahrscheinlich kein Assemblerprogramm
drin haben willst, bleibt nur die 32bit Variante.
Falk Brunner schrieb:> int32_t resultat;> uint8_t byte[3]; /* byte[0] ist das niederwertigste Byte */>> resultat = (int32_t)byte[2]<<16 + (int32_t)byte[1]<<8 +> (int32_t)byte[0];> if (bytes[2] & 0x80) resultat |= 0xFF000000; // Vorzeichen erweitern
Genauso hatte ich das erst angedacht, aber der avr-gcc macht da sehr
suboptimalen Code draus...
Meine Lösung sieht jetzt so aus:
1
uint8_trtd_receive(int32_t*result)
2
{
3
SPI_send(0x12);// read data once
4
*((uint8_t*)result+3)=SPI_receive();
5
*((uint8_t*)result+2)=SPI_receive();
6
*((uint8_t*)result+1)=SPI_receive();
7
*((uint8_t*)result+0)=0;
8
SPI_send(0xff);// send NOP to force DOUT/DRDY high
Die 0 rechts anzuhängen und mit einem zu großen Wert rechnen, ist die
eine Variante.
Die andere ist die wie von Falk Brunner vorgegeschlagen mit einer
Fallunterscheidung anhand des obersten gelesenen Bits.
Allerdings würde ich dann das Feld mit den Bytes nicht 3 groß machen,
sondern 4 und bereits darin das höherwertigste Byte setzen.
Dann vermeidet man die 32-Bit-Rechnerei ganz und bekommt zum Schluß den
fertigen Wert:
1
uint8_tbyte[4];/* byte[0] ist das niederwertigste Byte */
2
// bytes[0] bis bytes[2] irgendwie vom Wandler holen...
Michael Reinelt schrieb:> ich wär aber trotzdem neugierig, obs eine trickreiche Variante gibt mit> solchen 24-bit zahlen zu arbeiten....
Erstens, es gibt Compiler die auch 24-Bit Datentypen haben.
Zweitens, es gibt sogar eine C-Erweiterung für Festkommaarithmetik die
auch 24-Bit Datentypen hat.
Drittens, GCC ist da in beiden Fällen schlecht ausgestattet, obwohl die
Festkommaarithmetik langsam ihren Weg in den Compiler-Source findet.
Viertens, eine vom MSB abwärts zusammengesetzten 32-Bit Variable kann
man selbstverständlich auch durch 256 teilen oder um 8 Bit mit
Sign-Extension nach rechts schieben, damit der Wert dem der 24-Bit Zahl
entspricht.
Fünftens, eine vom LSB an aufwärts zusammengesetzten 32-Bit Variable
muss man sign-extenden. Hier
Beitrag "Re: 24 Bit signed effizient verarbeiten" hat
Falk schon alles gesagt. Natürlich kann man auch den Wilden Mann
markieren und nutzlos in die Trickkiste greifen:
1
struct {
2
int i24:24; // Logisch 24 Bits, physikalisch 24 oder mehr (32) Bits
3
} stemp;
4
5
//
6
// 3x8 Bit von unten in eine 32-Bit Variable schreiben
// Das unhandliche Ding in eine normale 32-Bit Variable umpacken.
12
// Dabei hat der Compiler sich durch den Umweg über das Bitfeld um
13
// die Sign-Extension zu kümmern
14
//
15
int32_t exended = stemp.i24 = temp;
In echtem Code sollte man natürlich alle Kommentare und halbwegs
sprechende Variablenbezeichnungen weglassen, um die Codewartung maximal
zu erschweren.
Gutes neues Jahr an alle hier!
Es wird nun Falk's Vorschlag. Danke euch allen! Ihr seid wunderbar!
Basti schrieb:> Hab den IC auch gerade in der Mangel:
Hast Interesse an Schaltplan- und Code-Austausch?
Außerdem:
Hannes Jaeger schrieb:> In echtem Code sollte man natürlich alle Kommentare und halbwegs> sprechende Variablenbezeichnungen weglassen, um die Codewartung maximal> zu erschweren.
Bitte, das ist eh klar! Ich mach seit 25 Jahren Software (wenn auch
nicht am uC), die Grundregel "real programmers write programs, not
documentation" habe ich schon lange verinnerlicht! :-)
lg Michi
apr schrieb:> Bzw. spricht grundsätzlich etwas dagegen die 24 Bit auf dem avr zu> verwenden oder ist das ganz viel zu unausgereift/böse/nicht portabel?
Geht das auch mit avr-gcc? ich hab mal alles unter /usr/lib/avr nach
int24 gegrept, aber nix in der Form int24 gefunden...
@ apr (Gast)
>Bzw. spricht grundsätzlich etwas dagegen die 24 Bit auf dem avr zu>verwenden
Ja, der nicht vorhandene Standard.
> oder ist das ganz viel zu unausgereift/böse/nicht portabel?
Keine Ahnung. Aber man löst sinnvollerweise keine Probleme, die nicht
das sind. Wen einer handvoll Variablen lohnt es sich nicht, 1 Byte
einzusparen. Darüber macht man sich Gedanken, wenn man hunderte oder
tausende Datensätze verarbeiten will.
http://www.mikrocontroller.net/articles/AVR-GCC-Codeoptimierung#Prinzipien_der_Optimierung
apr schrieb:> Bzw. spricht grundsätzlich etwas dagegen die 24 Bit auf dem avr zu> verwenden oder ist das ganz viel zu unausgereift/böse/nicht portabel?
Das müsste man zuvor klären: es ist gut möglich, dass ein Compiler alles
was > 16 bit und <= 32 bit ist, eben mit 32 bit-Funktionen rechnet, dann
bringt das nichts. Ganz sicher gibt es keine Arithmetik-Library mit
speziellen Rechenfunktionen für jede Zahlenlänge von 4 bis 64 bit.
Gruss Reinhard
>Hast Interesse an Schaltplan- und Code-Austausch?
Hm, so kompliziert ist der IC ja nicht... aber kann ja nicht verkehrt
sein...
Ich schreib dich an...
Grüße
Basti