High resolution barometer and altimeter using i2c mode. Adapted to FreeIMU interface
Dependents: FreeIMU FreeIMU_external_magnetometer FreeIMU
Fork of ms5611 by
Diff: MS561101BA.cpp
- 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(); +}