Forum: Mikrocontroller und Digitale Elektronik Bosch BMP280: Umrechnungen vereinfachen?


von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Hallo allerseits,

ich beschäftige mich grad etwas mit einem Luftdrucksensor von Bosch, dem 
BMP280.

Im Datenblatt ist die berechnung von Druck und Temperatur angegeben, ich 
beschränke mich mal bei meiner Frage auf die Temperatur. Der Chip hat 
fest eingebrannte Kalibrierungswerte die beim Start ausgelesen werden, 
und mittels derer dann die eigentliche Temperatur berechnet wird. Die 
Kompensationswerte nennen sich dig_T1..dig_T3

Die Berechnung im Datenblatt sieht folgendermaßen aus:
1
// Returns temperature in DegC, double precision. Output value of “51.23” equals 51.23 DegC.
2
// t_fine carries fine temperature as global value
3
BMP280_S32_t t_fine;
4
double bmp280_compensate_T_double(BMP280_S32_t adc_T)
5
{
6
double var1, var2, T;
7
var1 = (((double)adc_T)/16384.0 - ((double)dig_T1)/1024.0) * ((double)dig_T2);
8
var2 = ((((double)adc_T)/131072.0 - ((double)dig_T1)/8192.0) *
9
(((double)adc_T)/131072.0 - ((double) dig_T1)/8192.0)) * ((double)dig_T3);
10
t_fine = (BMP280_S32_t)(var1 + var2);
11
T = (var1 + var2) / 5120.0;
12
return T;
13
}

t_fine wird erst später für den Druck benötigt.

Wenn ich mir die (komplizierte) Berechnung näher ansehe, lässt sich das 
ja eigentlich auf eine quadratische Gleichung der Form T = a * x^2 + b * 
x + c reduzieren; a,b,c lassen sich aus den Kalibrierungswerten 
berechnen. Ich merk mir dann gleich diese Koeffizienten statt der 
Kalibrierungswerte.  Und die Berechnung selbst wird dann auch einfacher 
und schneller, vor allem kommt dann keine Division mehr vor.

Da ich aber davon ausgehe, dass die Leute bei Bosch keine kompletten 
Idioten sind, frage ich mich warum die nicht selbst drauf gekommen sind? 
Könnte es mit einem (wie auch immer entstehenden) Genauigkeitsverlust 
zusammenhängen?

Oder habe ich einen anderweitigen Denkfehler?

Danke für die "Denkfehler-Analyse" :-)

von Wolfgang (Gast)


Lesenswert?

Michael Reinelt schrieb:
> Und die Berechnung selbst wird dann auch einfacher
> und schneller, vor allem kommt dann keine Division mehr vor.

Wenn du dir deine Divisoren mal anguckst, sollte dir auffallen, dass das 
alles 2er-Potenzen sind. Wenn du die Rechenoperation also per 
Festkommaarithmetik und nicht als Fließkommarechnung ausführen würdest, 
liefe das für den µC auf eine simple Schiebeoperation hinaus, die der 
Prozessor mit einem Wimpernschlag erledigt. Bei vernünftiger 
Programmierung bringt es also fast überhaupt nichts, die 
rauszuschmeißen.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Wolfgang schrieb:
> Michael Reinelt schrieb:
>> Und die Berechnung selbst wird dann auch einfacher
>> und schneller, vor allem kommt dann keine Division mehr vor.
>
> Wenn du dir deine Divisoren mal anguckst, sollte dir auffallen, dass das
> alles 2er-Potenzen sind. Wenn du die Rechenoperation also per
> Festkommaarithmetik und nicht als Fließkommarechnung ausführen würdest,
> liefe das für den µC auf eine simple Schiebeoperation hinaus, die der
> Prozessor mit einem Wimpernschlag erledigt. Bei vernünftiger
> Programmierung bringt es also fast überhaupt nichts, die
> rauszuschmeißen.

Das ist zwar richtig, war aber eigentlich nicht meine Frage :-) 
Abgesehen davon dass 32bit-Schiebeoperationen (oder für höchste 
Genauigkeit sogar 64bit) auf einem AVR alles andere als ein 
Wimpernschlag sind, geht es mir hier wirklich um Fließkomma. Ob ichs 
dann im Endeffekt in float/double oder in int mache, steht auf einem 
anderen Blatt.

von Falk B. (falk)


Lesenswert?

@ Michael Reinelt (fisa)

>ich beschäftige mich grad etwas mit einem Luftdrucksensor von Bosch, dem
>BMP280.

Den BMP180 hatten wir hier schon mal.

Beitrag "BMP180 I2C Problem?"

>Da ich aber davon ausgehe, dass die Leute bei Bosch keine kompletten
>Idioten sind, frage ich mich warum die nicht selbst drauf gekommen sind?

Weil dort anscheinend auch ne Menge Praktikanten arbeiten. Der Code für 
die Berechnungen strotzt nur so vor Casts. Und echte Zweierpotenzen als 
Fließkommazahlen für [[Festkommaarithmetik] MIT Fließkommazahlen zu 
nutzen, ist reichlich albern.

>Könnte es mit einem (wie auch immer entstehenden) Genauigkeitsverlust
>zusammenhängen?

Nö, eher mit Unfähigkeit.

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Falk Brunner schrieb:
> Der Code für
> die Berechnungen strotzt nur so vor Casts.

Dann schau dir mal deren "Oringinal-API" an: 
https://github.com/BoschSensortec/BMP280_driver

Selten sowas haarsträubendes gelesen...
1
#define BMP280_SHIFT_BIT_POSITION_BY_01_BIT  (1)
2
#define BMP280_SHIFT_BIT_POSITION_BY_02_BITS  (2)
3
#define BMP280_SHIFT_BIT_POSITION_BY_03_BITS  (3)
4
#define BMP280_SHIFT_BIT_POSITION_BY_04_BITS  (4)
5
...
6
(((u32)(a_data_u8r[BMP280_TEMPERATURE_LSB_DATA])) << BMP280_SHIFT_BIT_POSITION_BY_04_BITS)
hat schon was, oder?

Falk Brunner schrieb:
>>Könnte es mit einem (wie auch immer entstehenden) Genauigkeitsverlust
>>zusammenhängen?
>
> Nö, eher mit Unfähigkeit.

Ich denke auch ;-)

von Wolfgang (Gast)


Lesenswert?

Michael Reinelt schrieb:
> Da ich aber davon ausgehe, dass die Leute bei Bosch keine kompletten
> Idioten sind, frage ich mich warum die nicht selbst drauf gekommen sind?

Zumindest in der Datenblattversion vom Nov 2014 war die ganze 
Fließkommarechnung noch völlig überflüssig. Irgendein Double-Fetischist 
bei Bosch, der den Sinn des Rechenweges anscheinend nicht so richtig 
gepeilt hat, fühlte sich dann wohl bemüßigt, den eigentlich auf 
Fixkommarechnung abgestimmten Algorithmus auf double umzucastet. Den 
Originalalgorithmus findest du im Datenblatt rev. 1.14 im Abschnitt 8.2.

Michael Reinelt schrieb:
> Abgesehen davon dass 32bit-Schiebeoperationen (oder für höchste
> Genauigkeit sogar 64bit) auf einem AVR alles andere als ein
> Wimpernschlag sind

Das "ein" war nicht wörtlich gemeint. Vergleich mal den Aufwand für eine 
Schiebeoperation mit einer Float-Division. Wenn der Compiler natürlich 
schlau ist, könnte er eine Division durch Zweierpotenzen auch mit einem 
Dekrement im Exponenten hinkriegen - aber ob er auf solche Spezialfälle 
getrimmt ist, weiß ich nicht?

von Wolfgang (Gast)


Lesenswert?

p.s.

Michael Reinelt schrieb:
> var2 = ((((double)adc_T)/131072.0 - ((double)dig_T1)/8192.0) *
> (((double)adc_T)/131072.0 - ((double)dig_T1)/8192.0)) *
> ((double)dig_T3);

Schon die doppelte Berechnung von
1
adc_T/131072 - dig_T1/8192
zeugt von einem merkwürdigen Verhältnis des Code-Autors zum Algorithmus, 
oder erwartet er, dass sein Compiler das erkennt und wegoptimiert?

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Wolfgang schrieb:
> p.s.
>
> Michael Reinelt schrieb:
>> var2 = ((((double)adc_T)/131072.0 - ((double)dig_T1)/8192.0) *
>> (((double)adc_T)/131072.0 - ((double)dig_T1)/8192.0)) *
>> ((double)dig_T3);
>
> Schon die doppelte Berechnung von
>
1
adc_T/131072 - dig_T1/8192
> zeugt von einem merkwürdigen Verhältnis des Code-Autors zum Algorithmus,
> oder erwartet er, dass sein Compiler das erkennt und wegoptimiert?

Ich vermute das resultiert aus der integer-arithmetik

von c-hater (Gast)


Lesenswert?

Michael Reinelt schrieb:

> Abgesehen davon dass 32bit-Schiebeoperationen (oder für höchste
> Genauigkeit sogar 64bit) auf einem AVR alles andere als ein
> Wimpernschlag sind

Huch? Vier Takte/SchiebeBit bei 32Bit, acht Takte/SchiebeBit bei 64Bit 
bei rein kanonischer Implementierung. Das ist ist nun nicht soo wild.

Und falls doch, gibt es etliche mögliche Optimierungen für den Fall, daß 
die Zahl der Schiebebits>1 ist, so daß eigentlich nur selten wirklich 
geschoben wird.

von Wolfgang (Gast)


Lesenswert?

Michael Reinelt schrieb:
> Ich vermute das resultiert aus der integer-arithmetik

Was hat die zweimalige Berechnung dieses Ausdrucks mit der 
Integer-Arithmetik zu tun.

Sinnvoll wäre doch wohl eine Zwischenvariable an Stelle der doppelt 
kodierten Subtraktion und erst dann die Berechnung von Var2, also
1
var3 = adc_T/131072 - dig_T1/8192
2
var2 = (var3 * var3) * dig_T3
an Stelle von
1
var2 = ((adc_T/131072 - dig_T1/8192) *
2
        (adc_T/131072 - dig_T1/8192)) * dig_T3;
(den ganzen Double- und Cast-Schnick-Schnack mal weg gelassen).

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Wolfgang schrieb:
> Sinnvoll wäre doch wohl eine Zwischenvariable an Stelle der doppelt
> kodierten Subtraktion und erst dann die Berechnung von Var2, also
>
1
> var3 = adc_T/131072 - dig_T1/8192
2
> var2 = (var3 * var3) * dig_T3
3
>
> an Stelle von
>
1
> var2 = ((adc_T/131072 - dig_T1/8192) *
2
>         (adc_T/131072 - dig_T1/8192)) * dig_T3;
3
>
> (den ganzen Double- und Cast-Schnick-Schnack mal weg gelassen).

Du hast recht, ich sah den Wald vor lauter Klammern nicht mehr :-(

Je mehr ich mir das Ding ansehe, desto mehr zweifle ich an meinem 
Glauben an das gute im Programmierer (speziell: Bosch-Programmierer)

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

c-hater schrieb:
> Michael Reinelt schrieb:
>
>> Abgesehen davon dass 32bit-Schiebeoperationen (oder für höchste
>> Genauigkeit sogar 64bit) auf einem AVR alles andere als ein
>> Wimpernschlag sind
>
> Huch? Vier Takte/SchiebeBit bei 32Bit, acht Takte/SchiebeBit bei 64Bit
> bei rein kanonischer Implementierung. Das ist ist nun nicht soo wild.
>
> Und falls doch, gibt es etliche mögliche Optimierungen für den Fall, daß
> die Zahl der Schiebebits>1 ist, so daß eigentlich nur selten wirklich
> geschoben wird.

Leider optimiert der GCC bei Schiebeoperationen mit >16 bit schlecht bis 
gar nicht, siehe hier: Beitrag "GCC optimiert 32bit Bitoperationen schlecht/gar nicht?"

Deswegen ist man mit viel Geschubse nicht mehr weit floating point 
(solange keine Divs) entfernt, und Divisionen mit konstantem/bekannten 
Divisor lassen sich einfach in Multiplikationen umwandeln.

von c-hater (Gast)


Lesenswert?

Michael Reinelt schrieb:

> Leider optimiert der GCC bei Schiebeoperationen mit >16 bit schlecht bis
> gar nicht, siehe hier: Beitrag "GCC optimiert 32bit Bitoperationen schlecht/gar nicht?"

Das ist aber dann nicht die Schuld des Controllers, sondern des 
Compilers.

Also ist deine Formulierung falsch. Es ist nicht auf dem AVR langsam, 
sondern in C, zumindest wenn der GCC als Compiler verwendet wird.

Daraus kann man ganz zwanglos den logischen Schluß ziehen, daß C im 
Allgemeinen oder zumindest der GCC im Besonderen schlicht die falsche 
Wahl ist, wenn man einen AVR8 programmieren will...

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

c-hater schrieb:
> Daraus kann man ganz zwanglos den logischen Schluß ziehen, daß C im
> Allgemeinen oder zumindest der GCC im Besonderen schlicht die falsche
> Wahl ist, wenn man einen AVR8 programmieren will...

Wieso hatte ich es im Urin, dass es darauf hinausläuft?

Less es hier in "meinem" Thread bitte gut sein, ok?

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

So, ich hab mich etwas mit den verschiedenen Varianten gespielt. 
Interessante Ergebnisse (alle auf einem AVR ATmega328P mit gcc-4.9.2):

originale Fließkomma-Variante lt. Datenblatt: ~2000 Takte
originale Integer-Variante lt. Datenblatt: 614 Takte

wie zu erwarten: Die integer-Arithmetik ist mehr als dreimal so schnell.

Aber jetzt kommts: optimierte Variante mit vorberechneten Koeffizienten, 
aber auch in Fließkomma : 580 Takte!

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Michael Reinelt (fisa)

>Aber jetzt kommts: optimierte Variante mit vorberechneten Koeffizienten,
>aber auch in Fließkomma : 580 Takte!

Not bad!

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Das wird immer haarsträubender.... sogar die originale 
integer-arithmetik ist sowas von daneben...

Erstmal liefert der Sensor die "rohen" Werte für Druck und Temperatur 
mit 20 Bit Auflösung, allerdings linksbündig in 24 Bit, das heißt die 
unteren 4 Bit sind immer 0. Daher muss man schon mal ein >>4 einbauen, 
um mit den originalen Formeln arbeiten zu können.

Dann wird am Beispiel der Temperatur aber dieser raw-Wert nochmal >>3 
bzw. >>4 verrechnet, d.h. die unteren 3 bzw. 4 bit fallen schonmal unter 
den Tisch. Die Ergebnisse werden dann auch nochmal mit >>11 bzw. >>12 
skaliert, was schon wieder signifikante Bits kostet...

Ich gehe davon aus dass die unteren 4 Bit des Temperaturwertes absolut 
bedeutungslos sind. Untermauert wird diese Annahme durch die Tatsache, 
dass der Offset-Kalibrierungsfaktor mit Faktor 16 in die Berechnung 
eingeht, also die besagten unteren 4 Bit überhaupt nicht berücksichtigen 
kann.

Falk hat das Wort "Unfähigkeit" verwendet. Ich glaube da liegt er 
komplett falsch: Das ist keine ganz normale allzu menschliche 
Unfähigkeit, das ist geballte unglaubliche extragalaktische 
Unfähigkeit...

: Bearbeitet durch User
von Falk B. (falk)


Lesenswert?

@ Michael Reinelt (fisa)

>Unfähigkeit, das ist geballte unglaubliche extragalaktische
>Unfähigkeit...

Von Bosch stammt doch der Spruch

"Guten Leuten muss man absagen" . . .

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Letzte Erkenntnis für heute: mit einigermaßen klugen Herangehen an die 
Umrechnung in integer-Arithmetik lässt sich sowohl Size als auch Cycles 
halbieren, bei praktisch keinem Genauigkeitsverlust (ich spreche immer 
noch nur von der Temperatur)

Die ursprünglich mega-komplexe Berechnung reduziert sich auf das 
einfachere und auch wesentlich logischere
1
int16_t ofs = raw - dig_T1;
2
t_fine = ((int32_t) ofs * dig_T2 + (((int32_t) ofs * ofs)>>16) * dig_T3) >> 10;

Es erfolgt zuerst eine Offset-Korrektur mit dem Kalibrierungsfaktur 
dig_T1, anschließend eine Korrektur 2ter Ordnung mit dig_T2 und dig_T3. 
alles in allem eine klassische parabolische 3-Punkt-Kalibrierung, und 
jetzt auch mit freiem Auge erkennbar.

Rechenzeit: 280 Takte.

Allerdings: Alleine die i2c-Kommunikation dauert (bei 12MHz CPU und 
100kHz i2c-Takt) schon gute 10.000 Takte. Im Sinne des  bewährten 
"premature optimization is the root of all evil" und wenn man (so wie 
ich) das Endergebnis sowieso "verfloaten" will, zahlt sich die ganze 
Integer-Bit-Schubserei gar nicht aus.

Die optimierte float-variante braucht "nur" 580 Takte, und (aufgrund der 
Nutzung der libm) unwesentlich mehr Speicher.

Als nächstes gehe ich den Druck an. Bin schon gespannt was sich da für 
Überraschungen oder Abgründe auftun...

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

ich bin durch!

Ich hab jetzt Druck und Temperatur in vier Varianten getestet: jeweils 
den originalen "Bosch-Code" in int32, int64 und float, und meinen 
optimierten Code (nur float)

int64 auf einem AVR ist natürlich unfair, damit plagt er sich ziemlich. 
Dafür ist die Bosch-int32-Variante wirklich ziemlich ungenau, das 
springt schon mal um +/- 5 Pascal, während alle anderen Varianten sich 
erst in der ersten Nachkommastelle unterscheiden.

Bosch/int32:  930 Byte, 2960 Takte
Bosch/int64: 1366 Byte, 6900 Takte
Bosch/float: 1048 Byte, 7750 Takte
Michi/float:  578 Byte, 3300 Takte

Mein Trick besteht darin, sich die Kalibrierungskoeffizienten einmalig 
beim Initialisieren so "herzurichten" dass man später einfach damit 
rechnen kann. Kleiner Nachteil: die Koeffizienten brauchen jetzt 48 Byte 
(12*float) statt 24 byte (12*16bit)

Conclusio: mit etwas "fortschrittlichem Denken" wird der Code halb so 
groß und doppelt so schnell :-)

@Falk: Ein Projekt weniger ;-)

: Bearbeitet durch User
von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Nachtrag: Code!

Teil 1: Aufbereiten der Koeffizienten:
C.* sind die ausgelesenen dig_T*/dig_P*
F.* die aufbereiteten in float
1
    F.T1 = (float) C.T1 * pow(2, 8);
2
    F.T2 = (float) C.T2 * pow(2, -18);
3
    F.T3 = (float) C.T3 * pow(2, -42);
4
5
    F.P1 = (float) C.P1 * (pow(2, 8) / -100000.0);
6
    F.P2 = (float) C.P1 * (float) C.P2 * (pow(2, -27) / -100000.0);
7
    F.P3 = (float) C.P1 * (float) C.P3 * (pow(2, -47) / -100000.0);
8
9
    F.P4 = (float) C.P4 * pow(2, 8) - pow(2, 24);
10
    F.P5 = (float) C.P5 * pow(2, -10);
11
    F.P6 = (float) C.P6 * pow(2, -27);
12
13
    F.P7 = (float) C.P7 * pow(2, -4);
14
    F.P8 = (float) C.P8 * pow(2, -19) + 1.0;
15
    F.P9 = (float) C.P9 * pow(2, -35);
keine Angst vor dem pow(), das ersetzt der Compiler schon durch die 
entsprechende Konstante!

Und die eigentliche Berechnung:
1
void Michi_conv_float(int32_t T_raw, int32_t P_raw, float *T, float *P)
2
{
3
    // Temperature
4
    float ofs = (float) T_raw - F.T1;
5
    float t_fine = (ofs * F.T3 + F.T2) * ofs;
6
    *T = t_fine * (1.0 / 5120.0);
7
8
    // Pressure
9
    float tf = t_fine - 128000.0;
10
    float x1 = (tf * F.P6 + F.P5) * tf + F.P4;
11
    float x2 = (tf * F.P3 + F.P2) * tf + F.P1;
12
13
    if (x2 == 0.0) {
14
        *P = -1.0;              // avoid exception caused by division by zero
15
    } else {
16
        float pf = ((float) P_raw + x1) / x2;
17
        *P = (pf * F.P9 + F.P8) * pf + F.P7;
18
    }
19
}
Die float-Division ist schmerzlich aber die krieg ich nicht weg, da es 
sich um eine gebrochen rationale Funktion handelt.

: Bearbeitet durch User
von Dominik Geisler (Gast)


Lesenswert?

Tag zusammen,

Gerne würde ich mal den Code aus Sicht der Bosch Sensortec erklären; die 
Begeisterung scheint ja hier nicht ungeteilt zu sein :)

Um etwas weiter aus zu holen:

Wir liefern drei verschiedene Korrekturformeln mit um verschiedenen 
Anforderungen gerecht zu werden. Dabei versuchen wir generell das 
debugging und die Anwendung so einfach wie möglich zu halten, was 
teilweise - wie hier auch gefunden - nicht die schnellste Ausführung 
ermöglicht.

Die ausgelesenen Parameter könnte man natürlich schon teilweise 
"vorverwursten", um nachher Rechenleistung zu sparen. Wenn das nachher 
auf dem Handy nicht funktioniert, können wir aber kaum herausfinden ob 
die Parameter richtig gelesen wurden. Zudem müssten wir auch drei 
verschiedene Funktionen für das Auslesen und vorverwursten mitliefern, 
welche dass teilweise falsch gemischt werden würden - alles was man 
falsch machen kann, wird irgendwann auch falsch gemacht.

Nun zu den Anwendungen hier:
Der floating-point-code ist auf keinen Fall dazu gedacht, auf einem 
8-bit-µC optimal zu funktionieren, sondern soll recht ähnlich zum 
fixed-point-code laufen; deswegen auch die Divisionen durch binäre 
Zahlen, welche aber bei floats keine Rechenleistung reduzieren. Im 
Kommentar steht dann auch, dass der Code für C-code auf dem PC oder für 
e.g. Matlab gedacht ist. Wenn jemand unbedingt das auf einem 8-bit-µC 
laufen lassen will, ist ihm Rechenzeit wohl nicht allzu wichtig - bei 
den beschriebenen I2C-Zeiten ist dies wohl auch hier der Fall.

Vielleicht noch ein paar Kommentare zu de Rosinen dieses Threads:

> Weil dort anscheinend auch ne Menge Praktikanten arbeiten. Der Code für
> die Berechnungen strotzt nur so vor Casts. Und echte Zweierpotenzen als
> Fließkommazahlen für [[Festkommaarithmetik] MIT Fließkommazahlen zu
> nutzen, ist reichlich albern.

Die casts sorgen eben dafür, das der Code funktioniert, auch auf 
8-bit-Maschinen. Zeilen wie "adc_T/131072" wie hier oben geschrieben 
sind ein schönes Beispiel für Code wo nicht gecastet wird. Die 
Zweierpotenzen sind tatsächlich unnötig, aber machen das Vergleichen und 
Debuggen leichter.

> Zumindest in der Datenblattversion vom Nov 2014 war die ganze
> Fließkommarechnung noch völlig überflüssig. Irgendein Double-Fetischist
> bei Bosch, der den Sinn des Rechenweges anscheinend nicht so richtig
> gepeilt hat, fühlte sich dann wohl bemüßigt, den eigentlich auf
> Fixkommarechnung abgestimmten Algorithmus auf double umzucastet. Den
> Originalalgorithmus findest du im Datenblatt rev. 1.14 im Abschnitt 8.2.

Hat mit Fetischismus wenig zu tun. Integerrechnung in Matlab oder 
Simulink ist verdammt kompliziert.

> Schon die doppelte Berechnung von
>
>> adc_T/131072 - dig_T1/8192
>
> zeugt von einem merkwürdigen Verhältnis des Code-Autors zum Algorithmus,
> oder erwartet er, dass sein Compiler das erkennt und wegoptimiert?

Nein, er erwartet dass es sich leichter debuggt.

> Erstmal liefert der Sensor die "rohen" Werte für Druck und Temperatur
> mit 20 Bit Auflösung, allerdings linksbündig in 24 Bit, das heißt die
> unteren 4 Bit sind immer 0. Daher muss man schon mal ein >>4 einbauen,
> um mit den originalen Formeln arbeiten zu können.

Nein, man muss die Daten auslesen und in der Auslesefunktion die Daten 
korrekt schieben. Das steht auch klar in dem Kommentar zu den 
Eingangsdaten.
Mit Vermischung der Auslese- und Korrekturfunktion sinkt der Aufwand für 
den µC, aber braucht man komplexere Funktionen, und braucht man auch die 
Datenauslesefunktion in einer int32, int64, und float-Variante. Und dann 
wäre das Geschreie hier im Forum auch wieder groß.

> Dann wird am Beispiel der Temperatur aber dieser raw-Wert nochmal >>3
> bzw. >>4 verrechnet, d.h. die unteren 3 bzw. 4 bit fallen schonmal unter
> den Tisch. Die Ergebnisse werden dann auch nochmal mit >>11 bzw. >>12
> skaliert, was schon wieder signifikante Bits kostet...

Der Genauigkeitsverlust hier ist genau geprüft und akzeptabel. In den 
vorgeschlagenen Einstellungen ist die Temperaturauflösung auch nur 16 
bzw. 17 bit, da eine höhere tatsächlich nicht nötig ist. Das >>11 bzw. 
>>12 passiert nach multiplikationen, die viele Inhaltslose bits 
erzeugen.

> Ich gehe davon aus dass die unteren 4 Bit des Temperaturwertes absolut
> bedeutungslos sind. Untermauert wird diese Annahme durch die Tatsache,
> dass der Offset-Kalibrierungsfaktor mit Faktor 16 in die Berechnung
> eingeht, also die besagten unteren 4 Bit überhaupt nicht berücksichtigen
> kann.

Bitte hier nicht Linearität und Rauscheinfluss einerseits mit 
Abgleichgenauigkeit andererseits verwechseln. Allerdings lohnen sich für 
diesen Sensor nur 16 bzw. 17 bit Temperaturauflösung.

> Falk hat das Wort "Unfähigkeit" verwendet. Ich glaube da liegt er
> komplett falsch: Das ist keine ganz normale allzu menschliche
> Unfähigkeit, das ist geballte unglaubliche extragalaktische
> Unfähigkeit...

Schön dass unsere Produkte gut ankommen! Gerne würde ich Vorschläge 
entgegennehmen die den Code:
a) sehr kompakt,
b) schnell ausführbar, auch auf ungeeigneten Plattformen,
c) leicht debugbar,
d) portabel, und
e) übersichtlich machen.

Sobald Du fertig bist, bitte auf Github einchecken. Dabei gerne noch auf 
deiner Architektur unnötige casts weglassen, und noch eine 
extrafunktion mit casts für andere Architekturen machen. Glaube mir, man 
kann es nie den Kunden recht machen. 90% des Feedbacks auf unsere 
Produkte ist "zu kompliziert", daher sind noch mehr Funktionen in der 
API die fast_ aber nicht _ganz alles integriert machen nicht die 
Lösung.

> Es erfolgt zuerst eine Offset-Korrektur mit dem Kalibrierungsfaktur
> dig_T1, anschließend eine Korrektur 2ter Ordnung mit dig_T2 und dig_T3.
> alles in allem eine klassische parabolische 3-Punkt-Kalibrierung, und
> jetzt auch mit freiem Auge erkennbar.

Gut gemacht, Komplimente. Dass das was passiert nicht direkt erkennbar 
ist, ist aus Herstellersicht aber gar nicht störend :).

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Dominik Geisler schrieb:
> Schön dass unsere Produkte gut ankommen!

Schön dass du den rauhen Ton hier mit Humor nimmst ;-)

Dass sich hier jemand von Bosch meldet hätte, ich nicht erwartet. Du 
hast meinen Respekt!

Deine Erläuterungen sind für mich aber nicht überall schlüssig, sorry, 
und wer sowas schreibt:
1
> #define BMP280_SHIFT_BIT_POSITION_BY_04_BITS  (4)
2
> ...
3
> (((u32)(a_data_u8r[BMP280_TEMPERATURE_LSB_DATA])) << 
4
> BMP280_SHIFT_BIT_POSITION_BY_04_BITS)
der frisst auch kleine Kinder. Ehrlich, Ruhmesblatt ist der Code 
keines...

aber dein letzter Satz sagt eh alles:

Dominik Geisler schrieb:
> Dass das was passiert nicht direkt erkennbar
> ist, ist aus Herstellersicht aber gar nicht störend

Nicht dass ich jetzt der große Sensor-Experte wäre, aber die meisten 
anderen Hersteller mit denen ich bisher gearbeitet habe (IST, Sensirion, 
...) veröffentlichen zuerst die zugrundeliegende Mathematik; ein 
Beispielcode und/oder eine API sind dann eher Zugabe. Die kann man dann 
nehmen, oder sich (aus verschiedensten Gründen) selbst was schnitzen, 
dann stört es auch nicht wenn der mitgelieferte Code 
"Optimierungspotential" hat.

Das passt aber offensichtlich nicht zum Geschäftsmodell von Bosch.


trotzdem Danke für deinen Beitrag!

von Bronco (Gast)


Lesenswert?

Als ich noch beim Bosch war, wurde bei den Automotive-Bausteinen (CJxxx, 
CYxxx) ganz gerne Intelligenz in die Auswertesoftware verschoben, wenn 
dadurch die Bausteine verbilligt werden konnten. Da waren in den 
Treibern manchmal ganz schöne Klimmzüge drinn. Machte aber nichts, weil 
die CPUs in den Steuergeräten genug Rechenleistung hatten.

von Dominik Geisler (Gast)


Lesenswert?

Hi Michael,

Michael Reinelt schrieb:
> Schön dass du den rauhen Ton hier mit Humor nimmst ;-)
> [...]
> wer sowas schreibt [...] der frisst auch kleine Kinder

Gut, dass Du Dich um ein milderes Klima bemühst :).

Michael Reinelt schrieb:
> Deine Erläuterungen sind für mich aber nicht überall schlüssig, sorry,
> und wer sowas schreibt:> #define BMP280_SHIFT_BIT_POSITION_BY_04_BITS
> (4)
>> ...
>> (((u32)(a_data_u8r[BMP280_TEMPERATURE_LSB_DATA])) <<
>> BMP280_SHIFT_BIT_POSITION_BY_04_BITS)
> der frisst auch kleine Kinder. Ehrlich, Ruhmesblatt ist der Code
> keines...

Obwohl wir generell keine Kinder essen, ist der Punkt tatsächlich nicht 
der schönste und wird auch nochmal zurückgenommen.

Aus meiner Sicht sehr unschön ist auch die Breite von 80 Spalten, die 
wird allerdings vom Linux-Kernel gefordert.

Einen freundlichen Gruß und viel Erfolg beim Projekt noch,

Dominik Geisler
Bosch Sensortec GmbH

von Michael R. (Firma: Brainit GmbH) (fisa)


Lesenswert?

Dominik Geisler schrieb:
> Gut, dass Du Dich um ein milderes Klima bemühst :).

Warum? ich mag den Ton hier... (reib dich mal mit Falk, dann weisst du 
was du an mir hast :-)

Dominik Geisler schrieb:
> Obwohl wir generell keine Kinder essen, ist der Punkt tatsächlich nicht
> der schönste und wird auch nochmal zurückgenommen.

Siehst du, hats doch was gebracht (und generell: nix für ungut)

> Aus meiner Sicht sehr unschön ist auch die Breite von 80 Spalten, die
> wird allerdings vom Linux-Kernel gefordert.
Damit kann zumindest ich gut leben.

Dominik Geisler schrieb:
> viel Erfolg beim Projekt noch,

Wenn ich schon mal jemand "von der Quelle" dran hab, sei mir eine Frage 
gestattet: Welche Genauigkeit kann ich von zwei solchen Sensoren für 
eine "Differenzdruckmessung" erwarten?

Derzeit stecken zwei direkt nebeneinander auf einem Breadboard, 
Temperaturdifferenz recht konstant < 1°C, Druckdifferenz rauscht im 
Bereich 2..8Pa (ohne Hekto!)

Für den Hintergrund müsste ich etwas ausholen: Ich habe ein Passivhaus. 
Das zeichnet sich dadurch aus dass es eine Luftdichte Hülle hat, und 
"kontrolliert" belüftet wird, dazu gibt es zwei Ventilatoren (einer 
bläst rein, einer saugt raus). Aufgrund von Toleranzen und vor allem 
Filterverschmutzung kann das aus der Balance geraten (wobei das tlw. 
durchaus erwünscht ist: Hab ich offenes Feuer (Ofen) im Haus, mag ich 
eher Überdruck haben (ich will mir ja kein CO reinsaugen), hab ich 
keinen Ofen mag ich auf gar keinen Fall Überdruck haben (weil sonst 
warme feuchte Luft an "Löchern" in der dichten Hülle nach außen gedrückt 
wird, abkühlt, kondensiert und nette Schimmelherde bildet). Daher möchte 
ich den Differenzdruck monitoren (ist aber nur eine Bastlerei)

Der Differenzdruck bewegt sich im Bereich -10..+10Pa

krieg ich das sinnvoll hin?

ein echter Differenzdruck-Sensor ist nicht wirklich eine Option, weil 
"wireless air" noch nicht erfunden und ich keine Löcher in mein Haus 
bohren will.


lg Michi

: Bearbeitet durch User
von Peter (Gast)


Lesenswert?

Koeffizienten sind falsh bei Michael Reinelt (fisa)

so ist richtig:


...pow(2,  4 );
...pow(2, -14);
...pow(2, -34);

...(pow(2,  4 ) / -100000.0f);
...(pow(2, -31) / -100000.0f);
...(pow(2, -51) / -100000.0f);

...pow(2,  4 ) - pow(2, 20);
...pow(2, -14);
...pow(2, -31);

...pow(2, -4 );
...pow(2, -19) + 1.0f;
...pow(2, -35);

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.