// // FILE: MS5611_SPI.cpp // AUTHOR: Rob Tillaart // VERSION: 0.1.2 // PURPOSE: MS5611 (SPI) Temperature & Pressure library for Arduino // URL: https://github.com/RobTillaart/MS5611_SPI // // HISTORY: see changelog.md #include "MS5611_SPI.h" // datasheet page 10 #define MS5611_CMD_READ_ADC 0x00 #define MS5611_CMD_READ_PROM 0xA0 #define MS5611_CMD_RESET 0x1E #define MS5611_CMD_CONVERT_D1 0x40 #define MS5611_CMD_CONVERT_D2 0x50 ///////////////////////////////////////////////////// // // PUBLIC // MS5611_SPI::MS5611_SPI(uint8_t select, uint8_t dataOut, uint8_t dataIn, uint8_t clock) { // _address = deviceAddress; // TODO _samplingRate = OSR_ULTRA_LOW; _temperature = MS5611_NOT_READ; _pressure = MS5611_NOT_READ; _result = MS5611_NOT_READ; _lastRead = 0; _deviceID = 0; _pressureOffset = 0; _temperatureOffset = 0; _compensation = true; // SPI _select = select; _dataIn = dataIn; _dataOut = dataOut; _clock = clock; _hwSPI = (dataIn == 255) && (dataOut == 255) && (clock == 255); } bool MS5611_SPI::begin() { // print experimental message. // Serial.println(MS5611_SPI_LIB_VERSION); pinMode(_select, OUTPUT); digitalWrite(_select, HIGH); setSPIspeed(_SPIspeed); if(_hwSPI) { #if defined(ESP32) if (_useHSPI) // HSPI { mySPI = new SPIClass(HSPI); mySPI->end(); mySPI->begin(14, 12, 13, _select); // CLK=14 MISO=12 MOSI=13 } else // VSPI { mySPI = new SPIClass(VSPI); mySPI->end(); mySPI->begin(18, 19, 23, _select); // CLK=18 MISO=19 MOSI=23 } #else // generic hardware SPI // Serial.println("HW_SPI"); mySPI = &SPI; mySPI->begin(); // FIX #6 mySPI->end(); mySPI->begin(); #endif delay(1); } else { // Serial.println("SW_SPI"); pinMode(_dataIn, INPUT); pinMode(_dataOut, OUTPUT); pinMode(_clock, OUTPUT); digitalWrite(_dataOut, LOW); digitalWrite(_clock, LOW); } return reset(); } bool MS5611_SPI::isConnected() { int rv = read(); return (rv == MS5611_READ_OK); } bool MS5611_SPI::reset(uint8_t mathMode) { command(MS5611_CMD_RESET); uint32_t start = micros(); // while loop prevents blocking RTOS while (micros() - start < 3000) // increased as first ROM values were missed. { yield(); delayMicroseconds(10); } // initialize the C[] array initConstants(mathMode); // read factory calibrations from EEPROM. bool ROM_OK = true; for (uint8_t reg = 0; reg < 7; reg++) { // used indices match datasheet. // C[0] == manufacturer - read but not used; // C[7] == CRC - skipped. uint16_t tmp = readProm(reg); C[reg] *= tmp; // _deviceID is a simple SHIFT XOR merge of PROM data _deviceID <<= 4; _deviceID ^= tmp; // Serial.println(readProm(reg)); if (reg > 0) { ROM_OK = ROM_OK && (tmp != 0); } } return ROM_OK; } int MS5611_SPI::read(uint8_t bits) { // VARIABLES NAMES BASED ON DATASHEET // ALL MAGIC NUMBERS ARE FROM DATASHEET convert(MS5611_CMD_CONVERT_D1, bits); // NOTE: D1 and D2 seem reserved in MBED (NANO BLE) uint32_t _D1 = readADC(); convert(MS5611_CMD_CONVERT_D2, bits); uint32_t _D2 = readADC(); // Serial.println(_D1); // Serial.println(_D2); // TEST VALUES - comment lines above // uint32_t _D1 = 9085466; // uint32_t _D2 = 8569150; // TEMP & PRESS MATH - PAGE 7/20 float dT = _D2 - C[5]; _temperature = 2000 + dT * C[6]; float offset = C[2] + dT * C[4]; float sens = C[1] + dT * C[3]; if (_compensation) { // SECOND ORDER COMPENSATION - PAGE 8/20 // COMMENT OUT < 2000 CORRECTION IF NOT NEEDED // NOTE TEMPERATURE IS IN 0.01 C if (_temperature < 2000) { float T2 = dT * dT * 4.6566128731E-10; float t = (_temperature - 2000) * (_temperature - 2000); float offset2 = 2.5 * t; float sens2 = 1.25 * t; // COMMENT OUT < -1500 CORRECTION IF NOT NEEDED if (_temperature < -1500) { t = (_temperature + 1500) * (_temperature + 1500); offset2 += 7 * t; sens2 += 5.5 * t; } _temperature -= T2; offset -= offset2; sens -= sens2; } // END SECOND ORDER COMPENSATION } _pressure = (_D1 * sens * 4.76837158205E-7 - offset) * 3.051757813E-5; _lastRead = millis(); return MS5611_READ_OK; } void MS5611_SPI::setOversampling(osr_t samplingRate) { _samplingRate = (uint8_t) samplingRate; } osr_t MS5611_SPI::getOversampling() const { return (osr_t) _samplingRate; }; float MS5611_SPI::getTemperature() const { if (_temperatureOffset == 0) return _temperature * 0.01; return _temperature * 0.01 + _temperatureOffset; }; float MS5611_SPI::getPressure() const { if (_pressureOffset == 0) return _pressure * 0.01; return _pressure * 0.01 + _pressureOffset; }; void MS5611_SPI::setPressureOffset(float offset) { _pressureOffset = offset; }; float MS5611_SPI::getPressureOffset() { return _pressureOffset; }; void MS5611_SPI::setTemperatureOffset(float offset) { _temperatureOffset = offset; }; float MS5611_SPI::getTemperatureOffset() { return _temperatureOffset; }; int MS5611_SPI::getLastResult() const { return _result; }; uint32_t MS5611_SPI::lastRead() const { return _lastRead; }; uint32_t MS5611_SPI::getDeviceID() const { return _deviceID; }; void MS5611_SPI::setCompensation(bool flag) { _compensation = flag; }; bool MS5611_SPI::getCompensation() { return _compensation; }; // EXPERIMENTAL uint16_t MS5611_SPI::getManufacturer() { return readProm(0); } // EXPERIMENTAL uint16_t MS5611_SPI::getSerialCode() { return readProm(7) >> 4; } void MS5611_SPI::setSPIspeed(uint32_t speed) { _SPIspeed = speed; _spi_settings = SPISettings(_SPIspeed, MSBFIRST, SPI_MODE0); }; uint32_t MS5611_SPI::getSPIspeed() { return _SPIspeed; }; bool MS5611_SPI::usesHWSPI() { return _hwSPI; }; #if defined(ESP32) void MS5611_SPI::selectHSPI() { _useHSPI = true; }; void MS5611_SPI::selectVSPI() { _useHSPI = false; }; bool MS5611_SPI::usesHSPI() { return _useHSPI; }; bool MS5611_SPI::usesVSPI() { return !_useHSPI; }; void MS5611_SPI::setGPIOpins(uint8_t clk, uint8_t miso, uint8_t mosi, uint8_t select) { _clock = clk; _dataIn = miso; _dataOut = mosi; _select = select; pinMode(_clock, OUTPUT); pinMode(_dataIn, INPUT); pinMode(_dataOut, OUTPUT); pinMode(_select, OUTPUT); digitalWrite(_clock, HIGH); digitalWrite(_dataOut, LOW); digitalWrite(_select, HIGH); mySPI->end(); // disable SPI and restart mySPI->begin(clk, miso, mosi, select); } #endif ///////////////////////////////////////////////////// // // PRIVATE // void MS5611_SPI::convert(const uint8_t addr, uint8_t bits) { // values from page 3 datasheet - MAX column (rounded up) uint16_t del[5] = {600, 1200, 2300, 4600, 9100}; uint8_t index = bits; if (index < 8) index = 8; else if (index > 12) index = 12; index -= 8; uint8_t offset = index * 2; command(addr + offset); uint16_t waitTime = del[index]; uint32_t start = micros(); // while loop prevents blocking RTOS while (micros() - start < waitTime) { yield(); delayMicroseconds(10); } } uint16_t MS5611_SPI::readProm(uint8_t reg) { // last EEPROM register is CRC - Page 13 datasheet. uint8_t promCRCRegister = 7; if (reg > promCRCRegister) return 0; uint16_t value = 0; digitalWrite(_select, LOW); if (_hwSPI) { mySPI->beginTransaction(_spi_settings); mySPI->transfer(MS5611_CMD_READ_PROM + reg * 2); value += mySPI->transfer(0x00); value <<= 8; value += mySPI->transfer(0x00); mySPI->endTransaction(); } else // Software SPI { swSPI_transfer(MS5611_CMD_READ_PROM + reg * 2); value += swSPI_transfer(0x00); value <<= 8; value += swSPI_transfer(0x00); } digitalWrite(_select, HIGH); return value; } uint32_t MS5611_SPI::readADC() { // command(MS5611_CMD_READ_ADC); uint32_t value = 0; digitalWrite(_select, LOW); if (_hwSPI) { mySPI->beginTransaction(_spi_settings); mySPI->transfer(0x00); value += mySPI->transfer(0x00); value <<= 8; value += mySPI->transfer(0x00); value <<= 8; value += mySPI->transfer(0x00); mySPI->endTransaction(); } else // Software SPI { swSPI_transfer(0x00); value += swSPI_transfer(0x00); value <<= 8; value += swSPI_transfer(0x00); value <<= 8; value += swSPI_transfer(0x00); } digitalWrite(_select, HIGH); // Serial.println(value, HEX); return value; } int MS5611_SPI::command(const uint8_t command) { yield(); digitalWrite(_select, LOW); if (_hwSPI) { mySPI->beginTransaction(_spi_settings); mySPI->transfer(command); mySPI->endTransaction(); } else // Software SPI { swSPI_transfer(command); } digitalWrite(_select, HIGH); return 0; } // simple one mode version uint8_t MS5611_SPI::swSPI_transfer(uint8_t val) { uint8_t clk = _clock; uint8_t dao = _dataOut; uint8_t dai = _dataIn; uint8_t value = 0; for (uint8_t mask = 0x80; mask; mask >>= 1) { digitalWrite(dao,(val & mask)); digitalWrite(clk, HIGH); value <<= 1; if (digitalRead(dai) != 0) value += 1; digitalWrite(clk, LOW); } digitalWrite(dao, LOW); // Serial.print(" # "); // Serial.println(value, HEX); return value; } void MS5611_SPI::initConstants(uint8_t mathMode) { // constants that were multiplied in read() - datasheet page 8 // do this once and you save CPU cycles // // datasheet ms5611 | appNote // mode = 0; | mode = 1 C[0] = 1; C[1] = 32768L; // SENSt1 = C[1] * 2^15 | * 2^16 C[2] = 65536L; // OFFt1 = C[2] * 2^16 | * 2^17 C[3] = 3.90625E-3; // TCS = C[3] / 2^8 | / 2^7 C[4] = 7.8125E-3; // TCO = C[4] / 2^7 | / 2^6 C[5] = 256; // Tref = C[5] * 2^8 | * 2^8 C[6] = 1.1920928955E-7; // TEMPSENS = C[6] / 2^23 | / 2^23 if (mathMode == 1) // Appnote version for pressure. { C[1] = 65536L; // SENSt1 C[2] = 131072L; // OFFt1 C[3] = 7.8125E-3; // TCS C[4] = 1.5625e-2; // TCO } } // -- END OF FILE --