High resolution barometer and altimeter using i2c mode

Dependents:   upverter_fitbit_clone ReadingMag_HMC5883L_work

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ms5611.cpp Source File

ms5611.cpp

00001 //!
00002 //! @file an520_I2C.c,v
00003 //!
00004 //! Copyright (c) 2009 MEAS Switzerland
00005 //!
00006 //!
00007 //!
00008 //! @brief This C code is for starter reference only. It is written for the
00009 //! MEAS Switzerland MS56xx pressure sensor modules and Atmel Atmega644p
00010 //! microcontroller.
00011 //!
00012 //! @version 1.0 $Id: an520_I2C.c,v 1.0
00013 //!
00014 //! @todo
00015 
00016 #include "mbed.h"
00017 #include "ms5611.h"
00018 
00019 double P;                       // compensated pressure value (mB)
00020 double T;                       // compensated temperature value (degC)
00021 double A;                       // altitude (ft)
00022 double S;                       // sea level barometer (mB)
00023 
00024 //uint32_t C[8];                  //coefficient storage
00025 
00026 //--------------------------------------------------------------------------------------------------------------------------------------//
00027 // Constructor and destructor - default to be compatible with legacy m5611 driver
00028 
00029 ms5611::ms5611(PinName sda, PinName scl)  : _i2c(sda, scl) {
00030         _i2c.frequency(400000);
00031         _i2cWAddr = MS5611_ADDR_W;
00032         _i2cRAddr = MS5611_ADDR_R;
00033 }
00034 
00035 //--------------------------------------------------------------------------------------------------------------------------------------//
00036 // Constructor and destructor - new, to allow for user to select i2c address based on CSB pin
00037 
00038 ms5611::ms5611(PinName sda, PinName scl, CSBpolarity CSBpin)  : _i2c(sda, scl) {
00039         _i2c.frequency(400000);
00040         _i2cWAddr = MS5611_ADDR_W;
00041         _i2cRAddr = MS5611_ADDR_R;
00042         if(CSBpin == CSBpin_1) {
00043             _i2cWAddr -= 2;
00044             _i2cRAddr -= 2;
00045         
00046         }
00047 }
00048 
00049 //********************************************************
00050 //! @brief send I2C start condition and the address byte
00051 //!
00052 //! @return 0
00053 //********************************************************
00054 
00055 int32_t ms5611::m_i2c_start(bool readMode) {
00056     int32_t twst;
00057     _i2c.start();
00058     if(readMode == true) {
00059         twst = m_i2c_write(_i2cRAddr);
00060     } else {
00061         twst = m_i2c_write(_i2cWAddr);
00062     }
00063     return(twst);
00064 }
00065 
00066 //********************************************************
00067 //! @brief send I2C stop condition
00068 //!
00069 //! @return none
00070 //********************************************************
00071 
00072 void ms5611::m_i2c_stop() {
00073     _i2c.stop();
00074 }
00075 
00076 //********************************************************
00077 //! @brief send I2C stop condition
00078 //!
00079 //! @return remote ack status
00080 //********************************************************
00081 
00082 uint8_t ms5611::m_i2c_write(uint8_t data) {
00083     uint8_t twst = _i2c.write(data);
00084     return(twst);
00085 }
00086 
00087 //********************************************************
00088 //! @brief read I2C byte with acknowledgment
00089 //!
00090 //! @return read byte
00091 //********************************************************
00092 
00093 uint8_t ms5611::m_i2c_readAck() {
00094     uint8_t twst = _i2c.read(1);
00095     return(twst);
00096 }
00097 
00098 //********************************************************
00099 //! @brief read I2C byte without acknowledgment
00100 //!
00101 //! @return read byte
00102 //********************************************************
00103 
00104 uint8_t ms5611::m_i2c_readNak() {
00105     uint8_t twst = _i2c.read(0);
00106     return(twst);
00107 }
00108 
00109 //********************************************************
00110 //! @brief send command using I2C hardware interface
00111 //!
00112 //! @return none
00113 //********************************************************
00114 
00115 void ms5611::m_i2c_send(uint8_t cmd) {
00116     uint8_t ret;
00117     ret = m_i2c_start(false);
00118     if(!(ret)) {
00119         m_i2c_stop();
00120     } else {
00121         ret = m_i2c_write(cmd);
00122         m_i2c_stop();
00123     }
00124 }
00125 
00126 //********************************************************
00127 //! @brief send reset sequence
00128 //!
00129 //! @return none
00130 //********************************************************
00131 
00132 void ms5611::cmd_reset() {
00133 #if defined  MS5611i2cLOWLEVEL
00134     m_i2c_send(MS5611_CMD_RESET);
00135 #else
00136     cobuf[0] = MS5611_CMD_RESET;
00137     _i2c.write(_i2cWAddr, cobuf, 1, false);
00138 #endif
00139     wait_ms(4);
00140     loadCoefs();
00141 }
00142 
00143 //********************************************************
00144 //! @brief preform adc conversion
00145 //!
00146 //! @return 24bit result
00147 //********************************************************
00148 
00149 uint64_t ms5611::cmd_adc(uint8_t cmd) {
00150     uint64_t temp = 0;
00151 #if defined  MS5611i2cLOWLEVEL
00152     m_i2c_send(MS5611_CMD_ADC_CONV + cmd);
00153 #else
00154     cobuf[0] = 0;
00155     cobuf[1] = 0;
00156     cobuf[2] = 0;
00157     cobuf[0] = MS5611_CMD_ADC_CONV + cmd;
00158     _i2c.write(_i2cWAddr, cobuf, 1, false);
00159 #endif
00160     switch (cmd & 0x0f) {
00161         case MS5611_CMD_ADC_256 : wait_us(900); break;
00162         case MS5611_CMD_ADC_512 : wait_ms(3); break;
00163         case MS5611_CMD_ADC_1024: wait_ms(4); break;
00164         case MS5611_CMD_ADC_2048: wait_ms(6); break;
00165         case MS5611_CMD_ADC_4096: wait_ms(10); break;
00166     }
00167 #if defined  MS5611i2cLOWLEVEL
00168     m_i2c_send(MS5611_CMD_ADC_READ);
00169     m_i2c_start(true);
00170     temp = m_i2c_readAck();
00171     temp = (temp << 8) | m_i2c_readAck();
00172     temp = (temp << 8) | m_i2c_readNak();
00173     m_i2c_stop();
00174 #else
00175     cobuf[0] = MS5611_CMD_ADC_READ;
00176     _i2c.write(_i2cWAddr, cobuf, 1, true);
00177     cobuf[0] = 0;
00178     _i2c.read(_i2cRAddr, cobuf, 3, false);
00179     temp = (cobuf[0] << 16) | (cobuf[1] << 8) | cobuf[2];
00180 #endif    
00181     return temp;
00182 }
00183 
00184 //********************************************************
00185 //! @brief Read calibration coefficients
00186 //!
00187 //! @return coefficient
00188 //********************************************************
00189 
00190 uint32_t ms5611::cmd_prom(uint8_t coef_num) {
00191     uint32_t rC = 0;
00192 #if defined  MS5611i2cLOWLEVEL
00193     m_i2c_send(MS5611_CMD_PROM_RD + coef_num * 2); // send PROM READ command
00194     m_i2c_start(true);
00195     rC = m_i2c_readAck();
00196     rC = (rC << 8) | m_i2c_readNak();
00197     m_i2c_stop();
00198 #else
00199     cobuf[0] = 0;
00200     cobuf[1] = 0;
00201     cobuf[0] = MS5611_CMD_PROM_RD + coef_num * 2;
00202     _i2c.write(_i2cWAddr, cobuf, 1, true);
00203     cobuf[0] = 0;
00204     _i2c.read(_i2cRAddr, cobuf, 2, false);
00205     rC = (cobuf[0] << 8) | cobuf[1];
00206 #endif
00207     return rC;
00208 }
00209 
00210 //********************************************************
00211 //! @brief calculate the CRC code
00212 //!
00213 //! @return crc code
00214 //********************************************************
00215 
00216 uint8_t ms5611::crc4(uint32_t n_prom[]) {
00217     uint32_t n_rem;
00218     uint32_t crc_read;
00219     uint8_t n_bit;
00220     n_rem = 0x00;
00221     crc_read = n_prom[7];
00222     n_prom[7]=(0xFF00 & (n_prom[7]));
00223     for (int cnt = 0; cnt < 16; cnt++) {
00224             if (cnt%2 == 1) {
00225                 n_rem ^= (uint16_t) ((n_prom[cnt>>1]) & 0x00FF);
00226             } else {
00227                 n_rem ^= (uint16_t) (n_prom[cnt>>1]>>8);
00228             }
00229             for (n_bit = 8; n_bit > 0; n_bit--) {
00230                 if (n_rem & (0x8000)) {
00231                     n_rem = (n_rem << 1) ^ 0x3000;
00232                 } else {
00233                     n_rem = (n_rem << 1);
00234                 }
00235             }
00236         }
00237     n_rem= (0x000F & (n_rem >> 12));
00238     n_prom[7]=crc_read;
00239     return (n_rem ^ 0x0);
00240 }
00241 
00242 /*
00243 The CRC code is calculated and written in factory with the LSB byte in the prom n_prom[7] set to 0x00 (see
00244 Coefficient table below). It is thus important to clear those bytes from the calculation buffer before proceeding
00245 with the CRC calculation itself:
00246 n_prom[7]=(0xFF00 & (n_prom[7])); //CRC byte is replaced by 0
00247 As a simple test of the CRC code, the following coefficient table could be used:
00248 uint32_t nprom[] = {0x3132,0x3334,0x3536,0x3738,0x3940,0x4142,0x4344,0x4500};
00249 the resulting calculated CRC should be 0xB.
00250 
00251 DB  15  14  13  12  11  10  9   8   7   6   5   4   3   2   1   0 
00252 Addr
00253 0               16 bit reserved for manufacturer
00254 1               Coefficient 1 (16 bit unsigned)
00255 2               Coefficient 2 (16 bit unsigned)
00256 3               Coefficient 3 (16 bit unsigned)
00257 4               Coefficient 4 (16 bit unsigned)
00258 5               Coefficient 5 (16 bit unsigned)
00259 6               Coefficient 6 (16 bit unsigned)
00260 7                                   0   0   0   0     CRC(0x0)
00261 */    
00262 /*   
00263     //Returns 0x0b as per AP520_004 
00264     C[0] = 0x3132;
00265     C[1] = 0x3334;
00266     C[2] = 0x3536;
00267     C[3] = 0x3738;
00268     C[4] = 0x3940;
00269     C[5] = 0x4142;
00270     C[6] = 0x4344;
00271     C[7] = 0x4546;
00272     n_crc = ms.crc4(C); // calculate the CRC
00273     pc.printf("testing CRC: 0x%x\n", n_crc);
00274 */
00275 
00276 //********************************************************
00277 //! @brief load all calibration coefficients
00278 //!
00279 //! @return none
00280 //********************************************************
00281 extern RawSerial pc;
00282 
00283 void ms5611::loadCoefs() {
00284     pc.printf("\r\n   - Coefs: ");
00285     for (int i = 0; i < 8; i++){ 
00286         wait_ms(50);
00287         C[i] = cmd_prom(i);
00288         pc.printf("%04x ", C[i]);
00289     }
00290     pc.printf("\r\n");
00291     uint8_t n_crc = crc4(C);
00292 }
00293 
00294 //********************************************************
00295 //! @brief calculate temperature and pressure
00296 //!
00297 //! @return none
00298 //********************************************************   
00299      
00300 void ms5611::calcPT() {
00301     int32_t D2 = cmd_adc(MS5611_CMD_ADC_D2 + MS5611_CMD_ADC_4096); // read D2
00302     int32_t D1 = cmd_adc(MS5611_CMD_ADC_D1 + MS5611_CMD_ADC_4096); // read D1
00303     int64_t dT = D2 - ((uint64_t)C[5] << 8);
00304     int64_t OFF  = ((uint32_t)C[2] << 16) + ((dT * (C[4]) >> 7));     //was  OFF  = (C[2] << 17) + dT * C[4] / (1 << 6);
00305     int64_t SENS = ((uint32_t)C[1] << 15) + ((dT * (C[3]) >> 8));     //was  SENS = (C[1] << 16) + dT * C[3] / (1 << 7);
00306     int32_t TEMP = 2000 + (int64_t)dT * (int64_t)C[6] / (int64_t)(1 << 23);
00307     T = (double) TEMP / 100.0;
00308 
00309     if(TEMP < 2000) { // if temperature lower than +20 Celsius
00310         int64_t T1 = ((int64_t)TEMP - 2000) * ((int64_t)TEMP - 2000);
00311         int64_t OFF1  = (5 * T1) >> 1;
00312         int64_t SENS1 = (5 * T1) >> 2;
00313 
00314         if(TEMP < -1500) { // if temperature lower than -15 Celsius
00315             T1 = ((int64_t)TEMP + 1500) * ((int64_t)TEMP + 1500);
00316             OFF1  +=  7 * T1;
00317             SENS1 += 11 * T1 >> 1;
00318         } 
00319         OFF -= OFF1;
00320         SENS -= SENS1;
00321     }
00322     P = (double)(((((int64_t)D1 * SENS ) >> 21) - OFF) / (double) (1 << 15)) / 100.0;
00323 }
00324 
00325 //********************************************************
00326 //! @brief calculate temperature
00327 //!
00328 //! @return double temperature degC
00329 //********************************************************  
00330 
00331 double ms5611::calcTemp() {
00332     calcPT();
00333     return(T);
00334 } 
00335 
00336 //********************************************************
00337 //! @brief calculate pressure
00338 //!
00339 //! @return double barometric pressure millibar
00340 //********************************************************  
00341 
00342 double ms5611::calcPressure() {
00343     calcPT();
00344     return(P);
00345 } 
00346 
00347 //********************************************************
00348 //! @brief get pressure, no calculation
00349 //!
00350 //! @return double barometric pressure millibar
00351 //********************************************************  
00352 
00353 double ms5611::getPressure() {
00354     calcPT();
00355     return(P);
00356 } 
00357 
00358 //********************************************************
00359 //! @brief get altitude from known sea level barometer, 
00360 //! @      no pre-pressure calculation
00361 //!
00362 //! @enter float sea level barometer
00363 //! @return float altitude in feet
00364 //********************************************************  
00365 
00366 float ms5611::getAltitudeFT(float sea_pressure) {
00367     A = (1 - (pow((P / (double)sea_pressure), 0.190284))) * 145366.45;
00368     return((float)A);
00369 } 
00370 
00371 //********************************************************
00372 //! @brief get sea level pressure from known altitude(ft), 
00373 //! @      no pre-pressure calculation
00374 //!
00375 //! @enter float known altitude in feet
00376 //! @return float seal level barometer in mb
00377 //********************************************************  
00378 
00379 float ms5611::getSeaLevelBaroFT(float known_alt) {
00380     S = pow(pow((P * INHG), 0.190284) + 0.00001313 * known_alt , 5.2553026) * MB;
00381     return((float)S);
00382 } 
00383 
00384 //********************************************************
00385 //! @brief get sea level pressure from known altitude(m), 
00386 //! @      no pre-pressure calculation
00387 //!
00388 //! @enter float known altitude in meters
00389 //! @return float seal level barometer in mb
00390 //********************************************************  
00391 
00392 float ms5611::getSeaLevelBaroM(float known_alt) {
00393     S = pow(pow((P * INHG), 0.190284) + 0.00001313 * known_alt * FTMETERS , 5.2553026) * MB;
00394     return((float)S);
00395 } 
00396