Forum: Mikrocontroller und Digitale Elektronik [AVR] MLX90609 Temperatur berechnen


von Patrick L. (crashdemon)


Angehängte Dateien:

Lesenswert?

Hallo,
ich habe eine Schaltung aufgebaut bei der ich mittels SPI den Gyro 
MLX90609 von Melexis auslesen, das ganze hängt an einem Atmel8, da der 
Drehratensensor einen eingebauten Temperatursensor verfügt der ebenfalls 
per SPI ausgelesen werden kann,  will ich diesen nutzen um bei 
Temperaturschwankunken meine Messwerte anzupassen, nur leider werde ich 
aus dem Datenblatt nicht ganz schlau.

Meine Funktion sieht zurzeit so aus, allerdings kommen damit nur 
unrealistische Temperaturen zustande.
[c]
/******** Gyro Temperatur ermitteln ********/
int16_t GyroTemp(void)
{
  int16_t iTemp = 0; // Berechnete Temperatur
  uint16_t iTempRaw = 0; // Rohdaten des Temperatursensors

  GyroAdcSetMode(1); // 2. Modi setzen, ADC Wandlung (Temp. Messung)
  GyroTempSetMode(&iTempRaw); // 3. Ergebnis der Messung lesen

  iTemp = (iTempRaw - 0x3F0) + 273; // Temperatur des Gyros

  return iTemp;
}
[c]

Vllt. gibt es ja hier jemanden im Forum der diesen Sensor erfolgreich 
einsetzt, der mir dabei helfen könnte.

von Patrick L. (crashdemon)


Lesenswert?

Hat denn keiner eine idee?

von Henry (Gast)


Lesenswert?

int16_t sValue; // das ist der Wert wie er aus dem Sensor kommt
float fValue; // Temperatur in Celsius

fValue = 25 + (sValue - (1408 << 1)) / (2 * 6.4);

von Patrick L. (crashdemon)


Lesenswert?

Henry wrote:
> int16_t sValue; // das ist der Wert wie er aus dem Sensor kommt
> float fValue; // Temperatur in Celsius
>
> fValue = 25 + (sValue - (1408 << 1)) / (2 * 6.4);

Danke für die Antwort, habe es mal mit der Formel ausprobiert hat aber 
leider nicht funktioniert, hab diese ein wenig angepasst, da ich die 
300°/s variante benutze.
Leider werden mir werte wie 9945 angezeigt, die sich auch nicht ändern, 
bei z.B. anpusten des Sensors, könntest du mir vllt. einmal erläutern 
wie sich deine Formel zusammensetzt, bzw wie du auf die Werte kommst, da 
im Datenblatt  ja nur eine Formel für die Berechnung der Temperatur bei 
werten die vom ADC kommen, steht.

fValue = 25 + (sValue - (1408 << 1)) / (2 * 3.2);

von Henry (Gast)


Lesenswert?

Ich habe das einfach nur aus meinem Programm kopiert und das 
funktioniert. Die 300° Variante benutze ich auch. Da rein denken ist mir 
aber zu mühevoll. Ich habe da auch einige Zeit gegrübelt.

Hast du sValue als signed 16 Bit deklariert?
Hast du die Statusbits des Messwerts gelöscht? sValue &= 0x0FFF;
Wartest du bis der Gyro ADC fertig ist?
Das muss (2 * 6.4) heißen.

von Patrick L. (crashdemon)


Lesenswert?

Henry wrote:
> Ich habe das einfach nur aus meinem Programm kopiert und das
> funktioniert. Die 300° Variante benutze ich auch. Da rein denken ist mir
> aber zu mühevoll. Ich habe da auch einige Zeit gegrübelt.
>
> Hast du sValue als signed 16 Bit deklariert?
> Hast du die Statusbits des Messwerts gelöscht? sValue &= 0x0FFF;
> Wartest du bis der Gyro ADC fertig ist?
> Das muss (2 * 6.4) heißen.

So sieht meine funktion aus:
1
/******** Gyro Temperatur ermitteln ********/
2
float GyroTemp(void)
3
{
4
  float iTemp = 0.0; // Berechnete Temperatur
5
  uint16_t iTempRaw = 0; // Rohdaten des Temperatursensors
6
7
  GyroAdcSetMode(1); // 2. Modi setzen, ADC Wandlung (Temp. Messung)
8
  GyroTempSetMode(&iTempRaw); // 3. Ergebnis der Messung lesen
9
10
  //iTemp = (iTempRaw - 0x3F0) + 273; // Temperatur des Gyros in Grad Celsius
11
  iTemp = 25 + (iTempRaw - (1408 << 1)) / (2 * 6.4); // Temperatur des Gyros in Grad Celsius
12
13
  return iTemp;
14
}

Also iTempRaw sind die Messwerte die er roh vom Gyro kriegt, 
entsprechende Funktion sieht so aus:
1
/******** ADC Temperatur Messung starten ********/
2
uint16_t GyroTempSetMode(uint16_t* ptValue)
3
{
4
     uint8_t ucCommand = 0;
5
     uint16_t unStatus = 0;
6
7
  ucCommand = GYRO_ADCC;
8
  ucCommand |= (1 << 2);
9
  ucCommand |= (1 << 3);
10
11
     PORTB &= ~(1 << PB2); // Den entsprechenden Sensor selektieren
12
13
     SPI_SendByte(ucCommand); // Befehl senden
14
     unStatus = SPI_TransferWord(0xFF); // Antwort holen
15
16
  while(!unStatus & (1 << 15))
17
  {
18
    unStatus = SPI_TransferWord(0xFF);  
19
  }
20
21
  PORTB |= (1 << PB2); // Den entsprechenden Sensor deselektieren
22
23
  _delay_us(250); // 250us warten damit der Sensor richtig arbeiten kann
24
25
  //*ptValue = ((unStatus & 0x0FFF) >> 1); // 12Bit ausmaskieren und um 1Bit nach rechts schieben
26
  *ptValue = ((unStatus >> 1) & 0x0FFF); 
27
28
  return unStatus;
29
}

und hier noch die Modi auswahl:
1
/******** ADC Drehraten / Temperatur Messung starten ********/
2
uint16_t GyroAdcSetMode(uint8_t channel) // Wenn channel == 1, Temperaturmessung
3
{
4
     uint8_t ucCommand = 0;
5
     uint16_t unStatus = 0;
6
7
  ucCommand = GYRO_ADCC;
8
  ucCommand |= (1 << 2);
9
10
  if(channel == 1)
11
  {
12
    ucCommand |= (1 << 3);
13
  }
14
15
     PORTB &= ~(1 << PB2); // Den entsprechenden Sensor selektieren
16
17
     SPI_SendByte(ucCommand); // Befehl senden
18
     unStatus = SPI_TransferWord(0xFF); // Antwort holen
19
20
  while(!unStatus & (1 << 15))
21
  {
22
    unStatus = SPI_TransferWord(0xFF);  
23
  }
24
25
  PORTB |= (1 << PB2); // Den entsprechenden Sensor deselektieren
26
27
  _delay_us(250); // 250us warten damit der Sensor richtig arbeiten kann
28
29
     return unStatus;
30
}

Statusbits des Messwerts werden ausmaskiert (ptValue = ((unStatus >> 1) 
& 0x0FFF)).
Ich warte bis ADC umwandlung fertig ist, 3.2 habe ich jetzt wieder in 
6.4 zurückgeändert, jetzt kommt als Temperatur 4985 raus.

von Henry (Gast)


Lesenswert?

Zwei Dinge sehe ich auf die Schnelle.

1.
uint16_t iTempRaw = 0;

iTempRaw muss als int16_t  deklariert sein damit die Berechnung 
funktioniert.

2.
*ptValue = ((unStatus >> 1) & 0x0FFF);

mach das Shift weg und schreibe:

*ptValue = unStatus & 0x0FFF;

oder mach es mit Shift und schreibe:

iTemp = 25 + (iTempRaw - 1408) / (2 * 3.2);

von Patrick L. (crashdemon)


Lesenswert?

Henry wrote:
> Zwei Dinge sehe ich auf die Schnelle.
>
> 1.
> uint16_t iTempRaw = 0;
>
> iTempRaw muss als int16_t  deklariert sein damit die Berechnung
> funktioniert.
>
> 2.
> *ptValue = ((unStatus >> 1) & 0x0FFF);
>
> mach das Shift weg und schreibe:
>
> *ptValue = unStatus & 0x0FFF;
>
> oder mach es mit Shift und schreibe:
>
> iTemp = 25 + (iTempRaw - 1408) / (2 * 3.2);

Habe iTempRaw jetzt als int16_t deklariert kann aber nicht 
nachvollziehen,
warum das so sein muss, ausscheinlich hat das meinen Messwert aber zum 
positiven verbessert, habe auf meiner anzeige jetzt -24 stehen, jetzt 
noch mit -1 multiplizieren dann müsste das ja die richtige Temperatur 
sein, jedoch kommt mir diese Temperaturmessung sehr träge vor, da sich 
der angezeigte Wert auch nach anpusten nicht verändert, ist das bei dir 
auch so?

Was ich noch nicht so ganz verstehe wie du auf den Wert 1408 kommst?

von Henry (Gast)


Lesenswert?

Das mit dem -1 Multiplizieren ist doch nur geraten.

Zeig nochmals den Kode von GyroTempSetMode() und GyroTemp().

Hast du auch meinen 2. Hinweis mit dem Shift realisiert?



Der Sensor verbraucht ziemlich viel Strom und heizt sich auf. Das 
Anpusten ist da wirkungslos. Warum willst du temperaturkompensieren? Das 
wird doch schon intern gemacht.

von Patrick L. (crashdemon)


Lesenswert?

Henry wrote:
> Das mit dem -1 Multiplizieren ist doch nur geraten.
>
> Zeig nochmals den Kode von GyroTempSetMode() und GyroTemp().
>
> Hast du auch meinen 2. Hinweis mit dem Shift realisiert?
>
>
> Der Sensor verbraucht ziemlich viel Strom und heizt sich auf. Das
> Anpusten ist da wirkungslos. Warum willst du temperaturkompensieren? Das
> wird doch schon intern gemacht.
1
/******** ADC Temperatur Messung starten ********/
2
uint16_t GyroTempSetMode(uint16_t* ptValue)
3
{
4
     uint8_t ucCommand = 0;
5
     uint16_t unStatus = 0;
6
7
  ucCommand = GYRO_ADCC;
8
  ucCommand |= (1 << 2);
9
  ucCommand |= (1 << 3);
10
11
     PORTB &= ~(1 << PB2); // Den entsprechenden Sensor selektieren
12
13
     SPI_SendByte(ucCommand); // Befehl senden
14
     unStatus = SPI_TransferWord(0xFF); // Antwort holen
15
16
  while(!unStatus & (1 << 15))
17
  {
18
    unStatus = SPI_TransferWord(0xFF);  
19
  }
20
21
  PORTB |= (1 << PB2); // Den entsprechenden Sensor deselektieren
22
23
  _delay_us(250); // 250us warten damit der Sensor richtig arbeiten kann
24
25
  //*ptValue = ((unStatus & 0x0FFF) >> 1); // 12Bit ausmaskieren und um 1Bit nach rechts schieben
26
  *ptValue = ((unStatus >> 1) & 0x0FFF); 
27
28
  return unStatus;
29
}
1
/******** Gyro Temperatur ermitteln ********/
2
float GyroTemp(void)
3
{
4
  float iTemp = 0.0; // Berechnete Temperatur
5
  int16_t iTempRaw = 0; // Rohdaten des Temperatursensors
6
7
  GyroAdcSetMode(1); // 2. Modi setzen, ADC Wandlung (Temp. Messung)
8
  GyroTempSetMode(&iTempRaw); // 3. Ergebnis der Messung lesen
9
10
  //iTemp = (iTempRaw - 0x3F0) + 273; // Temperatur des Gyros in Grad Celsius
11
  iTemp = 25 + (iTempRaw - 1408) / (2 * 6.4); // Temperatur des Gyros in Grad Celsius
12
13
  return iTemp;
14
}

So hier nochmal der aktuelle Code, habe hoffentlich das Shifting so 
umgesetzt wie du das meintest.
Der MLX ist ja von Haus aus schon recht Temperaturstabil, ich will aber 
möglichst auch eine Sotfwarebasierende Temperaturkompensation einbauen, 
um auf etwaige Abweichungen der Messwerte reagieren zu können.

von Henry (Gast)


Lesenswert?

Deine erste Zeile ist besser:

*ptValue = (unStatus & 0x0FFF) >> 1;
// *ptValue = (unStatus >> 1) & 0x0FFF;


Weil du mit Shift arbeitest muss der Faktor

(2 * 3.2)

heißen.

von Patrick L. (crashdemon)


Lesenswert?

Henry wrote:
> Deine erste Zeile ist besser:
>
> *ptValue = (unStatus & 0x0FFF) >> 1;
> // *ptValue = (unStatus >> 1) & 0x0FFF;
>
>
> Weil du mit Shift arbeitest muss der Faktor
>
> (2 * 3.2)
>
> heißen.

Hat sich schon erledigt lag alles an einem dummen Fehler meinerseits, da 
ich keinen überblich über meine eingesetzten Funktionen hatte, 
GyroAdcSetMode und  GyroTempSetMode habe ein und dasselbe getan, nämlich 
den Modus des Gyros auf Temperatur Sensor Signal zu stellen. An dieser 
stelle dann trotzdem vielen danke für die unterstützung.

von bruecke1982 (Gast)


Lesenswert?

Hallo ich habe eure Artikel Aufmerksam gelesen und habe einige fragen 
zum MLX90609, bezüglich der Initialisierung.
Ich arbeite gerade an meiner Bachelorarbeit und versuche Verzweifelt den 
MLX90609 zum laufen zu bekommen. Ich bekomme leider nur ab und zu 
sinvolle Daten, habe mich nach der Anleitung von Melexis gerichtet. 
Leider habe ich sehr oft Hardware Error Melexis berücksichtigt dies zwar 
in seinen Programmen, sagt aber nicht was man tun soll um diese zu 
vermeiden. Ich habe schon alles ausprobiert, neuinitialisierung, in den 
Schlafmodus schicken->wieder aufwecken.
Ich habe beide methoden probiert Zeitliches abwarten durch eine delay 
funktion oder abfrage des MSB und EOC. Das Ergebniss ist immer dasselbe, 
ab und zu sind mal alle Abfragen "WAHR" doch meist nicht. Es gibt 
nirgens eine vollständige Initiallisierungsbeschreibung.
Meine Hauptfrage ist erstmal funktioniert das gerät reibungslos mit 
eurer init?
Ich wäre euch sehr dankbar wenn ihr mir helfen könnt.

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.