/* * Author: Jon Trulson * Copyright (c) 2016 Intel Corporation. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include #include "ms5803.h" #include #include #include // quick binary power of 2 function #define POWB(type, exp) ((type)1 << exp) // For SPI, these are our CS on/off functions, if needed static void ms5803_cs_on(const ms5803_context dev) { assert(dev != NULL); if (dev->gpio) mraa_gpio_write(dev->gpio, 0); } static void ms5803_cs_off(const ms5803_context dev) { assert(dev != NULL); if (dev->gpio) mraa_gpio_write(dev->gpio, 1); } static upm_result_t ms5803_get_adc_value(const ms5803_context dev, MS5803_CMD_T cmd, MS5803_OSR_T dly, uint32_t *value) { assert(dev != NULL); uint8_t buf[3]; if (ms5803_bus_write(dev, cmd, NULL, 0)) { printf("%s: ms5802_bus_write() failed.\n", __FUNCTION__); return UPM_ERROR_OPERATION_FAILED; } // need to delay for the appropriate time upm_delay_ms(dly); // now, get the 3 byte sample if (ms5803_bus_read(dev, MS5803_CMD_ADC_READ, buf, 3)) { printf("%s: ms5802_bus_read() failed.\n", __FUNCTION__); return UPM_ERROR_OPERATION_FAILED; } *value = ((buf[0] << 16) | (buf[1] << 8) | buf[2]); return UPM_SUCCESS; } ms5803_context ms5803_init(unsigned int bus, int address, int cs_pin) { ms5803_context dev = (ms5803_context)malloc(sizeof(struct _ms5803_context)); if (!dev) return NULL; // zero out context memset((void *)dev, 0, sizeof(struct _ms5803_context)); // make sure MRAA is initialized int mraa_rv; if ((mraa_rv = mraa_init()) != MRAA_SUCCESS) { printf("%s: mraa_init() failed (%d).\n", __FUNCTION__, mraa_rv); ms5803_close(dev); return NULL; } if (address > 0) { // we are doing I2C dev->isSPI = false; if (!(dev->i2c = mraa_i2c_init(bus))) { printf("%s: mraa_i2c_init() failed.\n", __FUNCTION__); ms5803_close(dev); return NULL; } if (mraa_i2c_address(dev->i2c, address) != MRAA_SUCCESS) { printf("%s: mraa_i2c_address() failed.\n", __FUNCTION__); ms5803_close(dev); return NULL; } #if defined(UPM_PLATFORM_ZEPHYR) // we seem to need to stick qith 100khz for some reason with // this device on Zephyr. Even at 100Khz, while calibration // data is now read correctly, there are other issues yet to // be determined. But as a first step, 100Khz is a // requirement for this driver on Zephyr. if (mraa_i2c_frequency(dev->i2c, MRAA_I2C_STD)) { printf("%s: mraa_i2c_frequency() failed.\n", __FUNCTION__); ms5803_close(dev); return NULL; } #endif // UPM_PLATFORM_ZEPHYR } else { // we are doing SPI dev->isSPI = true; if (!(dev->spi = mraa_spi_init(bus))) { printf("%s: mraa_spi_init() failed.\n", __FUNCTION__); ms5803_close(dev); return NULL; } // Only create cs context if we are actually using a valid pin. // A hardware controlled pin should specify cs as -1. if (cs_pin >= 0) { if (!(dev->gpio = mraa_gpio_init(cs_pin))) { printf("%s: mraa_gpio_init() failed.\n", __FUNCTION__); ms5803_close(dev); return NULL; } mraa_gpio_dir(dev->gpio, MRAA_GPIO_OUT); ms5803_cs_off(dev); } if (mraa_spi_mode(dev->spi, MRAA_SPI_MODE0)) { printf("%s: mraa_spi_mode() failed.\n", __FUNCTION__); ms5803_close(dev); return NULL; } if (mraa_spi_frequency(dev->spi, 10000000)) { printf("%s: mraa_spi_frequency() failed.\n", __FUNCTION__); ms5803_close(dev); return NULL; } } // read factory coefficients if (ms5803_load_coefficients(dev)) { printf("%s: ms5803_load_coefficients() failed.\n", __FUNCTION__); ms5803_close(dev); return NULL; } // set the default OSR to the highest resolution ms5803_set_temperature_osr(dev, MS5803_OSR_4096); ms5803_set_pressure_osr(dev, MS5803_OSR_4096); return dev; } void ms5803_close(ms5803_context dev) { assert(dev != NULL); if (dev->i2c) mraa_i2c_stop(dev->i2c); if (dev->spi) mraa_spi_stop(dev->spi); if (dev->gpio) mraa_gpio_close(dev->gpio); free(dev); } upm_result_t ms5803_update(const ms5803_context dev) { assert(dev != NULL); // start by getting temperature and then pressure uint32_t rawTemperature; uint32_t rawPressure; // temperature if (ms5803_get_adc_value(dev, dev->temperatureCmd, dev->temperatureDelay, &rawTemperature)) { printf("%s: ms5803_get_adc_value() failed.\n", __FUNCTION__); return UPM_ERROR_OPERATION_FAILED; } // pressure if (ms5803_get_adc_value(dev, dev->pressureCmd, dev->pressureDelay, &rawPressure)) { printf("%s: ms5803_get_adc_value() failed.\n", __FUNCTION__); return UPM_ERROR_OPERATION_FAILED; } // printf("raw T = %u P = %u\n", rawTemperature, rawPressure); // This algorithm comes from the datasheet. // calc 1st order compensated temperature int32_t dT = rawTemperature - dev->C[5] * POWB(int32_t, 8); int32_t TEMP = 2000 + (int64_t)dT * dev->C[6] / POWB(int32_t, 23); // calc compensated temp and pressure int64_t OFF, SENS; int32_t P = 0; // first order compensation OFF = dev->C[2] * POWB(int64_t, 16) + (dev->C[4] * dT)/POWB(int64_t, 7); SENS = dev->C[1] * POWB(int64_t, 15) + (dev->C[3] * dT)/POWB(int64_t, 8); // second order compensation int64_t T2 = 0, OFF2 = 0, SENS2 = 0; if (TEMP >= 2000) { // >=20C T2 = 7 * (((uint64_t)dT * dT) / POWB(int64_t, 37)); OFF2 = ((TEMP - 2000) * (TEMP - 2000)) / POWB(int64_t, 4); SENS2 = 0; } else { T2 = 3 * (((uint64_t)dT * dT) / POWB(int64_t, 33)); OFF2 = 3 * ((TEMP - 2000) * (TEMP - 2000)) / POWB(int64_t, 1); SENS2 = 5 * ((TEMP - 2000) * (TEMP - 2000)) / POWB(int64_t, 3); // further compensation for very low temps if (TEMP < 1500) { // <15C OFF2 = OFF2 + 7 * ((TEMP + 1500) * (TEMP + 1500)); SENS2 = SENS2 + 4 * ((TEMP + 1500) * (TEMP + 1500)); } } // final caculation TEMP = TEMP - T2; OFF = OFF - OFF2; SENS = SENS - SENS2; P = (rawPressure * SENS/POWB(int64_t, 21) - OFF)/POWB(int64_t, 15); dev->temperature = (float)TEMP / 100.0; dev->pressure = (float)P/10.0; return UPM_SUCCESS; } upm_result_t ms5803_load_coefficients(const ms5803_context dev) { assert(dev != NULL); // we will load them all, even though only 6 of them are of use to us uint8_t buffer[2]; for (int i=0; iC[i] = (buffer[0] << 8) | buffer[1]; // printf("C[%d] = %u\n", i, dev->C[i]); } return UPM_SUCCESS; } // i2c bus read and write functions upm_result_t ms5803_bus_read(const ms5803_context dev, uint8_t cmd, uint8_t *data, uint8_t len) { assert(dev != NULL); if (dev->isSPI) { // SPI uint8_t sbuf[len + 1]; memset((void *)sbuf, 0, len + 1); sbuf[0] = cmd; ms5803_cs_on(dev); if (mraa_spi_transfer_buf(dev->spi, sbuf, sbuf, len + 1)) { ms5803_cs_off(dev); printf("%s: mraa_spi_transfer_buf() failed.\n", __FUNCTION__); return UPM_ERROR_OPERATION_FAILED; } ms5803_cs_off(dev); // now copy it into user buffer for (int i=0; ii2c, cmd, data, len) < 0) { printf("%s: mraa_i2c_read_bytes() failed.\n", __FUNCTION__); return UPM_ERROR_OPERATION_FAILED; } } return UPM_SUCCESS; } upm_result_t ms5803_bus_write(const ms5803_context dev, uint8_t cmd, uint8_t *data, uint8_t len) { assert(dev != NULL); if (dev->isSPI) { // SPI uint8_t sbuf[len + 1]; memset((void *)sbuf, 0, len + 1); sbuf[0] = cmd; // copy in the data to write... if (data && len) { for (int i=0; ispi, sbuf, sbuf, len + 1)) { ms5803_cs_off(dev); printf("%s: mraa_spi_transfer_buf() failed.\n", __FUNCTION__); return UPM_ERROR_OPERATION_FAILED; } ms5803_cs_off(dev); } else { // I2C... uint8_t buffer[len + 1]; buffer[0] = cmd; if (data && len) { for (int i=0; ii2c, buffer, len+1); if (rv != MRAA_SUCCESS) { printf("%s: mraa_i2c_write() failed.\n", __FUNCTION__); return UPM_ERROR_OPERATION_FAILED; } } return UPM_SUCCESS; } void ms5803_set_temperature_osr(const ms5803_context dev, MS5803_OSR_T osr) { assert(dev != NULL); switch(osr) { case MS5803_OSR_256: dev->temperatureCmd = MS5803_CMD_CONVERT_D2_OSR_256; break; case MS5803_OSR_512: dev->temperatureCmd = MS5803_CMD_CONVERT_D2_OSR_512; break; case MS5803_OSR_1024: dev->temperatureCmd = MS5803_CMD_CONVERT_D2_OSR_1024; break; case MS5803_OSR_2048: dev->temperatureCmd = MS5803_CMD_CONVERT_D2_OSR_2048; break; case MS5803_OSR_4096: dev->temperatureCmd = MS5803_CMD_CONVERT_D2_OSR_4096; break; default: // can't happen in this universe printf("%s: Internal error, invalid osr value %d\n", __FUNCTION__, (int)osr); return; } dev->temperatureDelay = osr; } void ms5803_set_pressure_osr(const ms5803_context dev, MS5803_OSR_T osr) { assert(dev != NULL); switch(osr) { case MS5803_OSR_256: dev->pressureCmd = MS5803_CMD_CONVERT_D1_OSR_256; break; case MS5803_OSR_512: dev->pressureCmd = MS5803_CMD_CONVERT_D1_OSR_512; break; case MS5803_OSR_1024: dev->pressureCmd = MS5803_CMD_CONVERT_D1_OSR_1024; break; case MS5803_OSR_2048: dev->pressureCmd = MS5803_CMD_CONVERT_D1_OSR_2048; break; case MS5803_OSR_4096: dev->pressureCmd = MS5803_CMD_CONVERT_D1_OSR_4096; break; default: // can't happen in this universe printf("%s: Internal error, invalid osr value %d\n", __FUNCTION__, (int)osr); return; } dev->pressureDelay = osr; } upm_result_t ms5803_reset(const ms5803_context dev) { assert(dev != NULL); upm_result_t rv = ms5803_bus_write(dev, MS5803_CMD_RESET, NULL, 0); upm_delay_ms(5); return rv; } float ms5803_get_temperature(const ms5803_context dev) { assert(dev != NULL); return dev->temperature; } float ms5803_get_pressure(const ms5803_context dev) { assert(dev != NULL); return dev->pressure; }