High resolution barometer and altimeter using i2c mode. Adapted to FreeIMU interface

Dependents:   FreeIMU FreeIMU_external_magnetometer FreeIMU

Fork of ms5611 by Kevin Braun

Revision:
7:8545a1d1d1e4
Child:
8:f3660f819e54
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MS561101BA.cpp	Sat Nov 02 17:23:33 2013 +0000
@@ -0,0 +1,187 @@
+/*
+MS5611-01BA.cpp - Interfaces a Measurement Specialities MS5611-01BA with Arduino
+See http://www.meas-spec.com/downloads/MS5611-01BA01.pdf for the device datasheet
+
+Copyright (C) 2011 Fabio Varesano <fvaresano@yahoo.it>
+
+Development of this code has been supported by the Department of Computer Science,
+Universita' degli Studi di Torino, Italy within the Piemonte Project
+http://www.piemonte.di.unito.it/
+
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the version 3 GNU General Public License as
+published by the Free Software Foundation.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+*/
+
+#include "mbed.h"
+#include "MS561101BA.h"
+#define CONVERSION_TIME 12000l // conversion time in microseconds
+
+MS561101BA::MS561101BA():i2c(I2C_SDA,I2C_SCL){
+}
+
+MS561101BA::MS561101BA(I2C _i2c):i2c(_i2c) {
+  ;
+}
+
+void MS561101BA::init(uint8_t address) {  
+lastPresConv=0;
+lastTempConv=0;
+  t.start();
+  _addr =  address;
+ 
+  reset(); // reset the device to populate its internal PROM registers
+  wait_ms(500); // some safety time
+  readPROM(); // reads the PROM into object variables for later use
+}
+
+float MS561101BA::getPressure(uint8_t OSR) {
+  // see datasheet page 7 for formulas
+  
+  uint32_t rawPress = rawPressure(OSR);
+  if(rawPress == NULL) {
+    return NULL;
+  }
+  
+  int32_t dT = getDeltaTemp(OSR);
+  if(dT == NULL) {
+    return NULL;
+  }
+  
+  int64_t off  = ((uint32_t)_Cal[1] <<16) + (((int64_t)dT * _Cal[3]) >> 7);
+  int64_t sens = ((uint32_t)_Cal[0] <<15) + (((int64_t)dT * _Cal[2]) >> 8);
+  return ((( (rawPress * sens ) >> 21) - off) >> 15) / 100.0;
+}
+
+float MS561101BA::getTemperature(uint8_t OSR) {
+  // see datasheet page 7 for formulas
+  int64_t dT = getDeltaTemp(OSR);
+  
+  if(dT != NULL) {
+    return (2000 + ((dT * _Cal[5]) >> 23)) / 100.0;
+  }
+  else {
+    return NULL;
+  }
+}
+
+int32_t MS561101BA::getDeltaTemp(uint8_t OSR) {
+  uint32_t rawTemp = rawTemperature(OSR);
+  if(rawTemp != NULL) {
+    return (int32_t)(rawTemp - ((uint32_t)_Cal[4] << 8));
+  }
+  else {
+    return NULL;
+  }
+}
+
+//TODO: avoid duplicated code between rawPressure and rawTemperature methods
+//TODO: possible race condition between readings.. serious headache doing this.. help appreciated!
+
+uint32_t MS561101BA::rawPressure(uint8_t OSR) {
+  uint32_t now = t.read_us();
+  if(lastPresConv != 0 && (now - lastPresConv) >= CONVERSION_TIME) {
+    lastPresConv = 0;
+    pressCache = getConversion(MS561101BA_D1 + OSR);
+  }
+  else {
+    if(lastPresConv == 0 && lastTempConv == 0) {
+      startConversion(MS561101BA_D1 + OSR);
+      lastPresConv = now;
+    }
+  }
+  return pressCache;
+}
+
+uint32_t MS561101BA::rawTemperature(uint8_t OSR) {
+  unsigned long now = t.read_us();
+  if(lastTempConv != 0 && (now - lastTempConv) >= CONVERSION_TIME) {
+    lastTempConv = 0;
+    tempCache = getConversion(MS561101BA_D2 + OSR);
+  }
+  else {
+    if(lastTempConv == 0 && lastPresConv == 0) { // no conversions in progress
+      startConversion(MS561101BA_D2 + OSR);
+      lastTempConv = now;
+    }
+  }
+  return tempCache;
+}
+
+
+// see page 11 of the datasheet
+void MS561101BA::startConversion(uint8_t command) {
+  // initialize pressure conversion
+  i2c.start();
+  i2c.write(_addr<<1);
+  i2c.write(command);
+  i2c.stop();
+}
+
+uint32_t MS561101BA::getConversion(uint8_t command) {
+  uint32_t conversion = 0;
+  
+  // start read sequence
+  /*Wire.beginTransmission(_addr);
+  Wire.write(0);
+  Wire.endTransmission();*/
+  i2c.start();
+  i2c.write(_addr<<1);
+  i2c.write(0);
+  i2c.stop();
+  
+  //Wire.beginTransmission(_addr);
+  //Wire.requestFrom(_addr, (uint8_t) MS561101BA_D1D2_SIZE);
+  char cobuf[3];
+  if (i2c.read((_addr<<1)+1, cobuf, MS561101BA_D1D2_SIZE)!=0) {conversion=0xFFFFFFFF;}else{
+    conversion = (cobuf[0] << 16) + (cobuf[1] << 8) + cobuf[2];
+  }
+  return conversion;
+}
+
+
+/**
+ * Reads factory calibration and store it into object variables.
+*/
+int MS561101BA::readPROM() {
+  for (int i=0;i<MS561101BA_PROM_REG_COUNT;i++) {
+    /*Wire.beginTransmission(_addr);
+    Wire.write(MS561101BA_PROM_BASE_ADDR + (i * MS561101BA_PROM_REG_SIZE));
+    Wire.endTransmission();*/
+    i2c.start();
+    i2c.write(_addr<<1);
+    i2c.write(MS561101BA_PROM_BASE_ADDR + (i * MS561101BA_PROM_REG_SIZE));
+    i2c.stop();
+    
+    char tmp[2];
+    if (i2c.read((_addr<<1)+1, tmp, MS561101BA_PROM_REG_SIZE)!=0) return -1;
+    _Cal[i] = tmp[0]<<8 | tmp[1];
+    wait_ms(200);
+  }
+  return 0;
+}
+
+
+/**
+ * Send a reset command to the device. With the reset command the device
+ * populates its internal registers with the values read from the PROM.
+*/
+void MS561101BA::reset() {
+  /*Wire.beginTransmission(_addr);
+  Wire.write(MS561101BA_RESET);
+  Wire.endTransmission();*/
+  i2c.start();
+  i2c.write(_addr<<1);
+  i2c.write(MS561101BA_RESET);
+  i2c.stop();
+}