High resolution barometer and altimeter using i2c mode
Fork of ms5611 by
Diff: ms5611.cpp
- Revision:
- 0:f97f410d4a21
- Child:
- 2:05804ed70748
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ms5611.cpp Tue May 07 18:44:06 2013 +0000 @@ -0,0 +1,300 @@ +//! +//! @file an520_I2C.c,v +//! +//! Copyright (c) 2009 MEAS Switzerland +//! +//! +//! +//! @brief This C code is for starter reference only. It is written for the +//! MEAS Switzerland MS56xx pressure sensor modules and Atmel Atmega644p +//! microcontroller. +//! +//! @version 1.0 $Id: an520_I2C.c,v 1.0 +//! +//! @todo + +#include "mbed.h" +#include "ms5611.h" + +//--------------------------------------------------------------------------------------------------------------------------------------// +// Constructor and destructor + +ms5611::ms5611(PinName sda, PinName scl) : _i2c(sda, scl) { + +} + +//******************************************************** +//! @brief send I2C start condition and the address byte +//! +//! @return 0 +//******************************************************** + +int ms5611::m_i2c_start(bool readMode) { + int twst; + _i2c.start(); + if(readMode == true) { + twst = m_i2c_write(MS5611_ADDR_R); + } else { + twst = m_i2c_write(MS5611_ADDR_W); + } + return(twst); +} + +//******************************************************** +//! @brief send I2C stop condition +//! +//! @return none +//******************************************************** + +void ms5611::m_i2c_stop(void) { + _i2c.stop(); +} + +//******************************************************** +//! @brief send I2C stop condition +//! +//! @return remote ack status +//******************************************************** + +unsigned char ms5611::m_i2c_write(unsigned char data) { + int twst = _i2c.write(data); + return(twst); +} + +//******************************************************** +//! @brief read I2C byte with acknowledgment +//! +//! @return read byte +//******************************************************** + +unsigned char ms5611::m_i2c_readAck(void) { + int twst = _i2c.read(1); + return(twst); +} + +//******************************************************** +//! @brief read I2C byte without acknowledgment +//! +//! @return read byte +//******************************************************** + +unsigned char ms5611::m_i2c_readNak(void) { + int twst = _i2c.read(0); + return(twst); +} + +//******************************************************** +//! @brief send command using I2C hardware interface +//! +//! @return none +//******************************************************** + +void ms5611::m_i2c_send(char cmd) { + unsigned char ret; + ret = m_i2c_start(false); // set device address and write mode + if(!(ret)) {//failed to issue start condition, possibly no device found */ + m_i2c_stop(); + } else {// issuing start condition ok, device accessible + ret = m_i2c_write(cmd); + m_i2c_stop(); + } +} + +//******************************************************** +//! @brief send reset sequence +//! +//! @return none +//******************************************************** + +void ms5611::cmd_reset() { + m_i2c_send(MS5611_CMD_RESET); // send reset sequence + wait_ms(4); // wait for the reset sequence timing + loadCoefs(); +} + +//******************************************************** +//! @brief preform adc conversion +//! +//! @return 24bit result +//******************************************************** + +unsigned long ms5611::cmd_adc(char cmd) { + char cobuf[3]; + cobuf[0] = 0; + cobuf[1] = 0; + cobuf[2] = 0; + unsigned int ret; + unsigned long temp = 0; + m_i2c_send(MS5611_CMD_ADC_CONV + cmd); // send conversion command + switch (cmd & 0x0f) { // wait necessary conversion time + case MS5611_CMD_ADC_256 : wait_us(900); break; + case MS5611_CMD_ADC_512 : wait_ms(3); break; + case MS5611_CMD_ADC_1024: wait_ms(4); break; + case MS5611_CMD_ADC_2048: wait_ms(6); break; + case MS5611_CMD_ADC_4096: wait_ms(10); break; + } + m_i2c_send(MS5611_CMD_ADC_READ); + + ret = _i2c.read(MS5611_ADDR_R, cobuf, 3, false); + if(ret) printf("\n*** ms5611 ADC Read Error "); + temp = (cobuf[0] << 16) + (cobuf[1] << 8) + cobuf[2]; + return temp; +} + +//******************************************************** +//! @brief Read calibration coefficients +//! +//! @return coefficient +//******************************************************** + +unsigned int ms5611::cmd_prom(char coef_num) { + char cobuf[2]; + unsigned int ret; + unsigned int rC = 0; + cobuf[0] = 0; + cobuf[1] = 0; + m_i2c_send(MS5611_CMD_PROM_RD + coef_num * 2); // send PROM READ command + ret = _i2c.read(MS5611_ADDR_R, cobuf, 2, false); + if(ret) printf("\n*** ms5611 PROM Read Error "); + rC = cobuf[0] * 256 + cobuf[1]; + return rC; +} + +//******************************************************** +//! @brief calculate the CRC code +//! +//! @return crc code +//******************************************************** + +unsigned char ms5611::crc4(unsigned int n_prom[]) { + //int cnt; // simple counter + unsigned int n_rem; // crc reminder + unsigned int crc_read; // original value of the crc + unsigned char n_bit; + n_rem = 0x00; + crc_read = n_prom[7]; //save read CRC + n_prom[7]=(0xFF00 & (n_prom[7])); //CRC byte is replaced by 0 + for (int cnt = 0; cnt < 16; cnt++) {// operation is performed on bytes // choose LSB or MSB + if (cnt%2 == 1) { + n_rem ^= (unsigned short) ((n_prom[cnt>>1]) & 0x00FF); + } else { + n_rem ^= (unsigned short) (n_prom[cnt>>1]>>8); + } + for (n_bit = 8; n_bit > 0; n_bit--) { + if (n_rem & (0x8000)) { + n_rem = (n_rem << 1) ^ 0x3000; + } else { + n_rem = (n_rem << 1); + } + } + } + n_rem= (0x000F & (n_rem >> 12)); // final 4-bit reminder is CRC code +// printf("n_rem: %x crc_read: %x n_prom[7]: %x\n", n_rem, crc_read, n_prom[7]); + + n_prom[7]=crc_read; // restore the crc_read to its original place + return (n_rem ^ 0x0); +/* +The CRC code is calculated and written in factory with the LSB byte in the prom n_prom[7] set to 0x00 (see +Coefficient table below). It is thus important to clear those bytes from the calculation buffer before proceeding +with the CRC calculation itself: +n_prom[7]=(0xFF00 & (n_prom[7])); //CRC byte is replaced by 0 +As a simple test of the CRC code, the following coefficient table could be used: +unsigned int nprom[] = {0x3132,0x3334,0x3536,0x3738,0x3940,0x4142,0x4344,0x4500}; +the resulting calculated CRC should be 0xB. + +DB 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +Addr +0 16 bit reserved for manufacturer +1 Coefficient 1 (16 bit unsigned) +2 Coefficient 2 (16 bit unsigned) +3 Coefficient 3 (16 bit unsigned) +4 Coefficient 4 (16 bit unsigned) +5 Coefficient 5 (16 bit unsigned) +6 Coefficient 6 (16 bit unsigned) +7 0 0 0 0 CRC(0x0) +*/ +/* + //Returns 0x0b as per AP520_004 + PTbuffer[0] = 0x3132; + PTbuffer[1] = 0x3334; + PTbuffer[2] = 0x3536; + PTbuffer[3] = 0x3738; + PTbuffer[4] = 0x3940; + PTbuffer[5] = 0x4142; + PTbuffer[6] = 0x4344; + PTbuffer[7] = 0x4546; + n_crc = ms.crc4(C); // calculate the CRC + pc.printf("testing CRC: 0x%x\n", n_crc); +*/ + +} +//******************************************************** +//! @brief load all calibration coefficients +//! +//! @return none +//******************************************************** + +unsigned int PTbuffer[8]; // calibration coefficients + +void ms5611::loadCoefs() { + // printf("Getting coefficients... "); + for (int i = 0; i < 8; i++){ + wait_ms(50); + PTbuffer[i] = cmd_prom(i); // read coefficients + // printf("0x%04x ", PTbuffer[i]); + } + // printf("\nCalculate CRC4: "); + unsigned char n_crc = crc4(PTbuffer); // calculate the CRC + // printf(" 0x%02x\n", n_crc); +} + +double P; // compensated pressure value +double T; // compensated temperature value + +//******************************************************** +//! @brief calculate temperature and pressure +//! +//! @return none +//******************************************************** + +void ms5611::calcPT() { + unsigned long D2 = cmd_adc(MS5611_CMD_ADC_D2 + MS5611_CMD_ADC_4096); // read D2 + unsigned long D1 = cmd_adc(MS5611_CMD_ADC_D1 + MS5611_CMD_ADC_4096); // read D1 + // calculate 1st order pressure and temperature (MS5607 1st order algorithm) + + double dT = D2 - (PTbuffer[5] << 8); + //change OFF and SENS scaling factor for ms5611 + double OFF = (PTbuffer[2] << 16) + dT * PTbuffer[4] / (1 << 6); //was OFF = (PTbuffer[2] << 17) + dT * PTbuffer[4] / (1 << 6); + double SENS = (PTbuffer[1] << 15) + dT * PTbuffer[3] / (1 << 7); //was SENS = (PTbuffer[1] << 16) + dT * PTbuffer[3] / (1 << 7); + T = (2000 + (dT * PTbuffer[6]) / (1 << 23)) / 100; + P = (((D1*SENS) / (1 << 21) - OFF) / (1 << 15)) / 100; + +// dT = D2 - PTbuffer[5] * pow(2,8); +// OFF = PTbuffer[2] * pow(2,17) + dT * PTbuffer[4] / pow(2,6); +// SENS = PTbuffer[1] * pow(2,16) + dT * PTbuffer[3] / pow(2,7); +// T = (2000 + (dT * PTbuffer[6]) / pow(2,23)) / 100; +// P = (((D1*SENS) / pow(2,21) - OFF) / pow(2,15)) / 100; + // place to use P, T, put them on LCD, send them trough RS232 interface... +} + +//******************************************************** +//! @brief calculate temperature +//! +//! @return double temperature degC +//******************************************************** + +double ms5611::calcTemp() { + calcPT(); + return(T); +} + +//******************************************************** +//! @brief calculate prssure +//! +//! @return double barometric pressure millibar +//******************************************************** + +double ms5611::calcPressure() { + calcPT(); + return(P); +} \ No newline at end of file