Forum: Mikrocontroller und Digitale Elektronik I2C TWI BMP180 C#


von Holzmade (Gast)


Angehängte Dateien:

Lesenswert?

Hallo liebe pure C# Programmierer,
ich habe von meiner Techniker Schule die Vorgabe in C# ein Projekt zu 
machen.
Dieser Code wollte ich mit euch teilen und ein Problem von der Rechnung 
der Temperatur und Luftdruck besprechen.
Die Rechnung habe ich aus dem Datenblatt genommen und abgetippt.
Jedoch kommen noch die richtigen Werte raus.
Darum kümmere ich mich noch.
Trotzdem würde ich mich über Verbesserungsvorschläge freuen.

Vielen Dank
Eure Made

von Holzmade (Gast)


Lesenswert?

Der Rechnungsteil um den es geht:
1
int32_t BMP_180_Temperatur_Rechnen(int32_t UT, uint16_t AC5, uint16_t AC6, int16_t MC, int16_t MD)
2
// Rechnet Temperatur aus
3
// Eingabeparameter:            UT, AC5, AC6, MC, MD
4
// Rückgabewert:                T  
5
{
6
  int64_t X1, X2, B5, t;
7
  int32_t  T;
8
  X1 = (((UT - AC6) * AC5) / 32768);
9
  X2 = ((MC * 2048) / (X1 + MD));
10
  B5 = (X1 + X2);
11
  t = ((B5 + 8) / 16);
12
  T=t;
13
  return T;   // Temperatur in 0,1°C
14
}
1
int32_t BMP_180_Luftdruck_Rechnen(int32_t UP, int32_t UT ,int16_t AC1, int16_t AC2, int16_t AC3,uint16_t AC4, uint16_t AC5,uint16_t AC6, int16_t B1, int16_t B2, int16_t MB, int16_t MC, int16_t MD)
2
// Rechnet Temperatur aus
3
// Eingabeparameter:            UT, AC5, AC6, MC, MD
4
// Rückgabewert:                T  
5
{
6
  int64_t B5, B6, X1, X2, X3, B3, p;
7
  uint32_t B4, B7;
8
  X1 = ((UT - AC6) * AC5) >> 15;// / 32768;
9
  X2 = MC * 2048 / (X1 + MD);
10
  B5 = X1 + X2;
11
12
  B6 = B5 - 4000;
13
  X1 = (B2 * ((B6 * B6) / 4096)) / 2048;
14
  X2 = (AC2 * B6) / 2048;
15
  X3 = X1 + X2;
16
  B3 = (((AC1 * 4 + X3)<<oss)+2)/4;
17
18
  X1 = (AC3 * B6) / 8192;
19
  X2 = (B1 * (B6 * B6 / 4096)) / 65536;
20
  X3 = ((X1 + X2) + 2) / 4;
21
  B4 = AC4 * (X3 + 32768) / 32768;
22
  B7 = (UP - B3) * (50000 >> oss);
23
  if (B7 < 0x80000000)
24
  {
25
    p = (B7 * 2) / B4;
26
  }
27
  else
28
  {
29
    p = (B7 / B4) * 2;
30
  }
31
  X1 = (p / 256) * (p / 256);
32
  X1 = (X1 * 3038) / 65536;
33
  X2 = (-7357 * p) / 65536;
34
  p = p + (X1 + X2 + 3791) / 16;
35
36
  return p;   // Luftdruck in Pa
37
}

von Nils L. (Firma: Trier) (holzmade)


Lesenswert?

Da dieses Projekt noch nicht fertig ist wäre es nett wenn der Code nicht 
ohne nachfragen bis August 2021 weiterveröffentlicht wird.
Ab dann ist dieser Code ganz Open Source ;)

Copyright by Holzmade

von nicht"Gast" (Gast)


Lesenswert?

Moin,


1. Das ist kein C#. Das ist einfach nur C.

2. Du solltest Variablen ordentlich benennen. Z.Bsp. X2 als Name geht 
einfach gar nicht. Wer soll denn da durchblicken.

3. Du hast ein haufen Wiederholungen drinnen. Du solltest mal 
nachforschen, wie das besser geht.

4. Die BMP_180_EPromData_Lesen() hat viel zu viele Parameter. Die 
solltest du einkürzen.

5. Warum gibt die BMP_180_EPromData_Lesen() Daten auf der Seriellen aus. 
Das gehört da nicht rein. Mach am besten eine getrennte Funktion dafür.

6. Warum sollte jemand so was veröffentlichen wollen. Damit schädigt man 
nur seiner Reputation :)

Das ist nur ein paar Sachen, die mir auf die schnelle Aufgefallen sind.

Grüße

von donvido (Gast)


Lesenswert?

nicht"Gast" schrieb:
> 2. Du solltest Variablen ordentlich benennen. Z.Bsp. X2 als Name geht
> einfach gar nicht. Wer soll denn da durchblicken.

Macht eher keinen Sinn, weil das fast eins zu eins der Code von Bosch 
ist (siehe Datenblatt). Da wirds eher unübersichtlicher, wenn du dir 
noch eigene Namen einfallen lässt.

von nicht"Gast" (Gast)


Lesenswert?

donvido schrieb:
> Macht eher keinen Sinn, weil das fast eins zu eins der Code von Bosch
> ist (siehe Datenblatt). Da wirds eher unübersichtlicher, wenn du dir
> noch eigene Namen einfallen lässt.

Öhm, nö

Nur weil das Datenblatt ebenso kacke ist, heißt es ja nicht, das der 
Code auch so schlimm sein muss. Zumal der geneigte Leser ja gewöhnlich 
nicht das Datenblatt zur Hand hat bzw es beim Lesen vom Code nicht 
brauchen sollte.

Außerdem scheinst du die Fomeln selber nicht zu verstehen. Sonst würde 
es dir keine Schwierigkeiten bereiten vernünftige Namen zu vergeben.

Grüße

von donvido (Gast)


Lesenswert?

nicht"Gast" schrieb:
> scheinst du die Fomeln selber nicht zu verstehen

Aber du hast sie verstanden ja?
Dann lass uns bitte teilhaben an deinem Wissen über die 
Berechnungsformeln.

von nicht"Gast" (Gast)


Lesenswert?

donvido schrieb:
> Aber du hast sie verstanden ja?
> Dann lass uns bitte teilhaben an deinem Wissen über die
> Berechnungsformeln.

Hab mir nicht die Mühe gemacht mich da durchzufriemeln. Wozu auch? 
Übrigens kannst du dir unnütze Angriffe auf meine Person sparen.

von Max M. (maxmicr)


Lesenswert?

nicht"Gast" schrieb:
> Hab mir nicht die Mühe gemacht mich da durchzufriemeln. Wozu auch?
> Übrigens kannst du dir unnütze Angriffe auf meine Person sparen.

Es macht einfach keinen Sinn sich neue Variablennamen auszudenken wenn 
der Hintergrund eine komplexe Formel aus dem Datenblatt ist.

von Peter D. (peda)


Lesenswert?

Der Code ist grauenhaft. Da sind haufenweise Codewiederholungen drin, 
das macht es unnötig unleserlich.
Z.B. aus
1
    while (bit_is_clear(UCSR0A,5)) {}         // UCSR0A warten bis gesendet wurde
2
    UDR0 = 48;                                // UDR0 Zahl senden
usw. macht man eine Unterfunktion:
1
void uputchar(char c)
2
  while (bit_is_clear(UCSR0A,5));
3
  UDR0 = c;
4
}
5
// ...
6
uputchar('0');
Dann kann man auch später ganz einfach die Ausgabe auf UART mit 
Interrupt und FIFO verbessern oder auf ein LCD umleiten.

Ebenso diese Wiederholungen:
1
        *AC1 = (TWIM_ReadAck () << 8);
2
        *AC1 += TWIM_ReadAck ();
3
    UART_Sende_Zahl(*AC1);
4
    UART_Sende_Tab();
Das kann man auch sehr schön modularisieren:
1
uint16_t read_twi_u16(void)
2
{
3
  uint16_t val = TWIM_ReadAck() << 8;
4
  return val | TWIM_ReadAck();
5
}
6
7
void usend_val_tab(unit16_t val)
8
{
9
  char buf[10];
10
  sprintf(buf, "%u\t", val);
11
  uputs(buf);
12
}
13
//..
14
  usend_val_tab(*AC1 = read_twi_u16());
15
  usend_val_tab(*AC2 = read_twi_u16());
16
// usw.

Vorzugsweise macht man aus allen ähnlichen Sequenzen, die >=2 mal 
auftreten, eine Unterfunktion.
Code sollte immer so kompakt wie möglich sein. Niemand möchte ähnliche 
Sequenzen wieder und immer wieder lesen müssen. Das erschwert nur das 
Verstehen der eigentlichen Funktion.

Und warum machst Du die Zahlenausgabe zu Fuß und nimmst nicht sprintf()?

von nicht"Gast" (Gast)


Lesenswert?

Max M. schrieb:
> Es macht einfach keinen Sinn sich neue Variablennamen auszudenken wenn
> der Hintergrund eine komplexe Formel aus dem Datenblatt ist.

Mittlerweile habe ich mir auch mal aus Neugier das komplette Datenblatt 
angeschaut.

So einen Mist hab ich schon lange nicht mehr als Datenblatt gesehen. 
Keine Erklärungen, die Kalibrierwerte nur als Kürzel. Die Formeln nicht 
erklärt und mit Magic Numbers versehen.

Da kann ich verstehen, dass der TE das einfach kopiert hat.

von Cyblord -. (cyblord)


Lesenswert?

nicht"Gast" schrieb:
> So einen Mist hab ich schon lange nicht mehr als Datenblatt gesehen.
> Keine Erklärungen, die Kalibrierwerte nur als Kürzel. Die Formeln nicht
> erklärt und mit Magic Numbers versehen.

Das ist eine Formel welche die Kalibrierwerte nutzt um echte Daten aus 
dem Sensor zu erhalten. Um die Formel zu verstehen müsste man also die 
internas des Sensors kennen.

> Da kann ich verstehen, dass der TE das einfach kopiert hat.

Die Formel selbst soll man ja auch einfach kopieren, man muss die aber 
halt schon auf seine Programmiersprache anpassen. Und die muss man dafür 
halt kennen. Nicht den Sinn der Formel.

von nicht"Gast" (Gast)


Lesenswert?

Cyblord -. schrieb:
> Das ist eine Formel welche die Kalibrierwerte nutzt um echte Daten aus
> dem Sensor zu erhalten. Um die Formel zu verstehen müsste man also die
> internas des Sensors kennen.


Na ja, aber die Kalibrierwerte einem so hinzurotzen ist schon ziemlich 
mies. Wenn man die Namen vernünftig gewählt hätte und den Magic Numbers 
auch aussagekräftige Namen gegeben hätte und noch ein paar Kommentare 
zur Berechnung hinterlassen hätte, wäre schon viel erreicht. So ist das 
einfach nur Faul vom Dokumenteur.

(ganz schön viele 'hätte' im Text).

Ändert nicht viel dran, dass das Datenblatt Rotz ist und ein Beispiel, 
wie man es nicht machen sollte. Andere Hersteller bekommen das deutlich 
besser hin.

von nicht"Gast" (Gast)


Lesenswert?

Nachtrag.

Eine vernünftige Vorgehensweise wäre natürlich gewesen, die Kalibrierung 
intern zu machen. Einen Prozessor haben die ja sicherlich eh schon 
drauf, nachdem die ja ADC und I2C und Eeprom drauf haben.

Aber das ist eine andere Diskussion und auch nicht Zielführend.

von donvido (Gast)


Lesenswert?

nicht"Gast" schrieb:
> Wenn man die Namen vernünftig gewählt hätte und den Magic Numbers
> auch aussagekräftige Namen gegeben hätte und noch ein paar Kommentare
> zur Berechnung hinterlassen hätte, wäre schon viel erreicht. So ist das
> einfach nur Faul vom Dokumenteur.

Wenns nun aber einfach nur Koeffizienten eines Polynoms sind um die 
Nichtlinearität des Sensors zu kompensieren, dann kann man halt entweder 
kurze Bezeichner dafür verwenden, oder die Formel über 3 Seiten 
vollkommen unübersichtlich präsentieren.
In der Schule wird doch in der Regel auch nur mit einzelnen Buchstaben 
gerechnet und nicht mit ganzen Wörtern ;).

von Peter D. (peda)


Lesenswert?

Der BMP180 ist EOL, Nachfolger ist der BMP280. In dessen Datenblatt ist 
auch C-Code drin.

https://www.bosch-sensortec.com/media/boschsensortec/downloads/environmental_sensors_2/pressure_sensors_1/bmp280/bst-bmp280-ds001.pdf

von Nils L. (Firma: Trier) (holzmade)


Angehängte Dateien:

Lesenswert?

Danke für all die Antworten.

Ich muss mich für meine Programmierung entschuldigen.
Außerdem habe ich eine neue Version, welche das Datentypenproblem löst.
Nicht schön aber funktioniert.

Kommentare kommen erst am Ende bei Abgabe des Projekt.

Vielen Dank für die Info mit dem Datenblatt vom BMP280.
Werde ich mir dann mal durchlesen.

Druck und Temperatur bekomme ich raus (sind geprüft).
Aber die Höhe kann ich so nicht berechnen:
1
/*
2
  pi = p;
3
  H = 44330.0*(1.0-(pi/101325.0)^(1.0/5.255));
4
  Hi = H*1000;
5
  char AUSGABE_H[] = "Hoehe :";
6
  UART_Sende_Text (AUSGABE_H);
7
  UART_Sende_Tab();
8
  UART_Sende_Zahl(H);
9
*/

Hoch Float-Typen kann C nicht.
Also auch keine x-te Wurzeln.
Es gibt zwar Bibliotheken und Algorithmen aber das Problem wollte ich 
simpel lösen.


Ich bin Anfänger im Programmieren also verzeiht mir meine Fehler aber 
weist mich auch bitte darauf hin.

Vielen Dank
Holzmade

von Patrick (Gast)


Lesenswert?

Der ^ Operator ist in C, C++ und C# der bitweise XOR Operator, also 
macht er nicht das was du erwartest.
Nachdem du die avr-libc verwendest, brauchst du nur math.h inkludieren 
und dann die Funktion pow() nutzen. Das ist der einfachste Weg. Da 
potenzieren für eine CPU/Microkontroller nicht wirkloch so einfach ist 
(schon gar nicht bei floats) gibt es dafür kein Sprachfeature, sondern 
nur plattformabhängige Implementierungen als Funktion.

Und warum schreibst du C++ code, wenn dein Projekt C# sein soll?

von Patrick (Gast)


Lesenswert?

Und noch ein par Verbesserungen:
1.) Inkludiere cstdint für uint8_t und co., dass es so funktioniert ist 
Glück
2.) Mach alle Variablen, die sich nie ändern, und beim Compilieren schon 
einen bekannten Inhalt haben constexpr, dann kann der Compiler besser 
optimieren. Das sind bei dir alle globalen Variablen. Wenn du aus 
welchen Gründen auch immer einen C-Compiler (dann passt der Dateiname 
mit *.cpp aber nicht) oder eine C++ Version die älter als C++14 
verwendest, funktioniert das nicht, und du solltest sie zumindest const 
machen.

von Markus F. (mfro)


Lesenswert?

Ich habe das jetzt nicht im Detail angeschaut, aber BOSCH Sensortec hat 
einen öffentlichen github-Account und dort auch ein Treiber-API für den 
BMP180 veröffentlicht:

https://github.com/BoschSensortec/BMP180_driver

Bevor Du dir also ein Bein rausreisst, solltest Du vielleicht lieber mal 
dort reinschauen.

von Wolfgang (Gast)


Lesenswert?

nicht"Gast" schrieb:
> 2. Du solltest Variablen ordentlich benennen. Z.Bsp. X2 als Name geht
> einfach gar nicht. Wer soll denn da durchblicken.

Soll die Variable vielleicht besser 
int64_Zwischenwert_BoschAlgorithmus_Berechnung_Luftdruck_X2 heißen?

Da es sich um lokale Variablen in der Umrechnungsroutine handelt, ist es 
doch völlig in Ordnung, die Namen aus dem Algorithmus genau wie im 
Datenblatt zu verwenden.

von Roland F. (rhf)


Lesenswert?

Hallo,
Patrick schrieb:
> Nachdem du die avr-libc verwendest, brauchst du nur math.h inkludieren
> und dann die Funktion pow() nutzen.

In C# wäre das dann Math.Pow(x,y).

rhf

von Nils L. (Firma: Trier) (holzmade)


Angehängte Dateien:

Lesenswert?

Hallo

Danke für die Hilfe mit dem Höhenproblem.
Ich habe mir die Funktionen der math.h angeschaut aber habe keine 
Funktion gefunden um eine x-te Wurzel zu ziehen.
Wenn ^ die XOR Funktion ist muss ich diese Berechnung outsourcen.
Könnte das mit einem Raspi als Display verbinden.
Dann kann ich die Umrechnung in Python schreiben.
Die Funktion dort habe ich mir schon angeschaut.

Ich habe zur Höhenberechnung 2 Formeln gefunden.
Die Internationale Höhenformel (Problem: 5,255te-Wurzel ziehen)
Die Barometrische Höhenformel (ungenauer und e^x Funktion)

Was ist außerdem cstdint?
Eine Bibliothek?

Vielen Dank
Ihr helft neuen Programmierern sehr :)

von nicht"Gast" (Gast)


Lesenswert?

Kleiner Tip für beliebige Wurzeln mit pow


von Patrick (Gast)


Lesenswert?

Nils L. schrieb:
> Was ist außerdem cstdint?
> Eine Bibliothek?

Ein Teil der C++ Standardbibliothek

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.