diff --git a/CMakeLists.txt b/CMakeLists.txt index ea116b5211996b039464de1848fedba46988f799..8e5162364a0b16ffce590db067bfe8abc5401265 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -232,6 +232,9 @@ sbs_target(test-calibration stm32f407vg_stm32f4discovery) add_executable(test-bme280 src/tests/sensors/test-bme280.cpp) sbs_target(test-bme280 stm32f429zi_stm32f4discovery) +add_executable(test-bmp280 src/tests/sensors/test-bmp280.cpp) +sbs_target(test-bmp280 stm32f429zi_stm32f4discovery) + add_executable(test-bmx160 src/tests/sensors/test-bmx160.cpp) sbs_target(test-bmx160 stm32f429zi_skyward_death_stack_x) diff --git a/cmake/boardcore.cmake b/cmake/boardcore.cmake index 979aa24005d368b63bb645051dc14523a4164089..9d99de5a811713715c005a7a24a917ecdb1ff5ae 100644 --- a/cmake/boardcore.cmake +++ b/cmake/boardcore.cmake @@ -68,6 +68,7 @@ foreach(OPT_BOARD ${BOARDS}) ${SBS_BASE}/src/shared/sensors/ADS131M04/ADS131M04.cpp ${SBS_BASE}/src/shared/sensors/ADS131M04/ADS131M04HighFreq.cpp ${SBS_BASE}/src/shared/sensors/BME280/BME280.cpp + ${SBS_BASE}/src/shared/sensors/BMP280/BMP280.cpp ${SBS_BASE}/src/shared/sensors/BMX160/BMX160.cpp ${SBS_BASE}/src/shared/sensors/BMX160/BMX160WithCorrection.cpp ${SBS_BASE}/src/shared/sensors/calibration/SensorDataExtra.cpp diff --git a/src/shared/sensors/BME280/BME280.cpp b/src/shared/sensors/BME280/BME280.cpp index 907b92432b0c724c122ba60085ff714e262c37d6..4701793dfe05f765013c3a2a672a9f2276933808 100644 --- a/src/shared/sensors/BME280/BME280.cpp +++ b/src/shared/sensors/BME280/BME280.cpp @@ -54,16 +54,6 @@ BME280::BME280(SPISlave spiSlave_, BME280Config config_) bool BME280::init() { - // Check if already initialized - if (initialized) - { - LOG_ERR(logger, "Already initialized"); - - lastError = SensorErrors::ALREADY_INIT; - - return false; - } - // Check WHO AM I if (!checkWhoAmI()) { @@ -99,20 +89,19 @@ bool BME280::init() return false; } - initialized = true; return true; } -void BME280::setHumidityOversampling(Oversampling oversampling) +void BME280::setSensorMode(Mode mode) { - config.bits.oversamplingHumidity = oversampling; + config.bits.mode = mode; setConfiguration(); } -void BME280::setSensorMode(Mode mode) +void BME280::setHumidityOversampling(Oversampling oversampling) { - config.bits.mode = mode; + config.bits.oversamplingHumidity = oversampling; setConfiguration(); } @@ -215,12 +204,6 @@ TemperatureData BME280::readTemperature() return lastSample; } -HumidityData BME280::getHumidity() { return lastSample; } - -PressureData BME280::getPressure() { return lastSample; } - -TemperatureData BME280::getTemperature() { return lastSample; } - unsigned int BME280::calculateMaxMeasurementTime(BME280Config config_) { return ceil(1.25 + (2.3 * config_.bits.oversamplingTemperature) + @@ -286,6 +269,8 @@ bool BME280::checkWhoAmI() uint8_t whoAmIValue = transaction.readRegister(REG_ID); + printf("%2X\n", whoAmIValue); + return whoAmIValue == REG_ID_VAL; } diff --git a/src/shared/sensors/BME280/BME280.h b/src/shared/sensors/BME280/BME280.h index 3dd66cce83dc94db771992ce508560981cdfe1a7..7ab2c6cdd39e4d1973091a4c4c40d24e546369db 100644 --- a/src/shared/sensors/BME280/BME280.h +++ b/src/shared/sensors/BME280/BME280.h @@ -163,12 +163,6 @@ public: */ bool init() override; - /** - * @brief Sets the oversampling for humidity readings, use SKIPPED to - * disable humidity sampling - */ - void setHumidityOversampling(Oversampling oversampling); - /** * @brief Sets the sensor mode * @@ -182,6 +176,12 @@ public: */ void setSensorMode(Mode mode); + /** + * @brief Sets the oversampling for humidity readings, use SKIPPED to + * disable humidity sampling + */ + void setHumidityOversampling(Oversampling oversampling); + /** * @brief Sets the oversampling for pressure readings, use SKIPPED to * disable pressure sampling @@ -220,12 +220,6 @@ public: */ TemperatureData readTemperature(); - HumidityData getHumidity(); - - PressureData getPressure(); - - TemperatureData getTemperature(); - /** * @brief Maximum measurement time formula from datasheet page 51 * @@ -301,8 +295,6 @@ private: BME280Comp compParams; int32_t fineTemperature; // Used in compensation algorithm - bool initialized = false; // Whether the sensor has been initialized - PrintLogger logger = Logging::getLogger("bme280"); }; diff --git a/src/shared/sensors/BME280/BME280Data.h b/src/shared/sensors/BME280/BME280Data.h index 4cfe0e90794942102f34641687ff22763e9d557d..473989e103fe62904a75f1c13a1ee425102f5e4f 100644 --- a/src/shared/sensors/BME280/BME280Data.h +++ b/src/shared/sensors/BME280/BME280Data.h @@ -46,9 +46,8 @@ struct BME280Data : public TemperatureData, static std::string header() { - return "temperatureTimestamp,temp,pressureTimestamp,press,humid_" - "timestamp," - "humid\n"; + return "temperatureTimestamp,temperature,pressureTimestamp,pressure," + "humid_timestamp,humidity\n"; } void print(std::ostream& os) const diff --git a/src/shared/sensors/BMP280/BMP280.cpp b/src/shared/sensors/BMP280/BMP280.cpp new file mode 100644 index 0000000000000000000000000000000000000000..12a1f15b75f826078d5f7dc11a35270d80cd6368 --- /dev/null +++ b/src/shared/sensors/BMP280/BMP280.cpp @@ -0,0 +1,315 @@ +/* Copyright (c) 2021 Skyward Experimental Rocketry + * Author: Alberto Nidasio + * + * 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 "BMP280.h" + +#include <drivers/timer/TimestampTimer.h> +#include <math.h> + +using namespace std; + +namespace Boardcore +{ + +const BMP280::BMP280Config BMP280::BMP280_DEFAULT_CONFIG = { + 0, 0, SLEEP_MODE, SKIPPED, SKIPPED, 0, FILTER_OFF, STB_TIME_0_5}; + +const BMP280::BMP280Config BMP280::BMP280_CONFIG_ALL_ENABLED = {0, + 0, + NORMAL_MODE, + OVERSAMPLING_16, + OVERSAMPLING_2, + 0, + FILTER_COEFF_16, + STB_TIME_0_5}; + +const BMP280::BMP280Config BMP280::BMP280_CONFIG_TEMP_SINGLE = { + 0, 0, FORCED_MODE, SKIPPED, OVERSAMPLING_1, 0, FILTER_OFF, STB_TIME_0_5}; + +BMP280::BMP280(SPISlave spiSlave_, BMP280Config config_) + : spiSlave(spiSlave_), config(config_) +{ +} + +bool BMP280::init() +{ + // Check WHO AM I + if (!checkWhoAmI()) + { + LOG_ERR(logger, "Invalid WHO AM I"); + + lastError = SensorErrors::INVALID_WHOAMI; + + return false; + } + + loadCompensationParameters(); + + // Read once the temperature to compute fineTemperature + setConfiguration(BMP280_CONFIG_TEMP_SINGLE); + miosix::Thread::sleep( + calculateMaxMeasurementTime(BMP280_CONFIG_TEMP_SINGLE)); + readTemperature(); + + setConfiguration(); + + BMP280Config readBackConfig = readConfiguration(); + + // Check if the configration on the device matches ours + if (config.bytes.ctrlPressureAndTemperature != + readBackConfig.bytes.ctrlPressureAndTemperature || + config.bytes.config != readBackConfig.bytes.config) + { + LOG_ERR(logger, "Device configuration incorrect, setup failed"); + + lastError = SensorErrors::NOT_INIT; + + return false; + } + + return true; +} + +void BMP280::setSensorMode(Mode mode) +{ + config.bits.mode = mode; + + setConfiguration(); +} + +void BMP280::setPressureOversampling(Oversampling oversampling) +{ + config.bits.oversamplingPressure = oversampling; + + setConfiguration(); +} + +void BMP280::setTemperatureOversampling(Oversampling oversampling) +{ + config.bits.oversamplingTemperature = oversampling; + + setConfiguration(); +} + +void BMP280::setFilterCoeff(FilterCoeff filterCoeff) +{ + config.bits.filter = filterCoeff; + + setConfiguration(); +} + +void BMP280::setStandbyTime(StandbyTime standbyTime) +{ + config.bits.standbyTime = standbyTime; + + setConfiguration(); +} + +PressureData BMP280::readPressure() +{ + uint8_t buffer[3]; + int32_t adc_P = 0; + + { + SPITransaction transaction(spiSlave); + + transaction.readRegisters(REG_PRESS_MSB, buffer, 3); + } + + adc_P |= ((uint32_t)buffer[0]) << 12; + adc_P |= ((uint32_t)buffer[1]) << 4; + adc_P |= (buffer[2] >> 4) & 0x0F; + + // Compensate pressure + lastSample.pressureTimestamp = TimestampTimer::getInstance().getTimestamp(); + lastSample.pressure = + (float)compensatePressure(adc_P) / 256; // Convert to Pa + + return lastSample; +} + +TemperatureData BMP280::readTemperature() +{ + uint8_t buffer[3]; + int32_t adcTemperature = 0; + + { + SPITransaction transaction(spiSlave); + + transaction.readRegisters(REG_TEMP_MSB, buffer, 3); + } + + adcTemperature |= ((uint32_t)buffer[0]) << 12; + adcTemperature |= ((uint32_t)buffer[1]) << 4; + adcTemperature |= (buffer[2] >> 4) & 0x0F; + + // Compensate temperature + fineTemperature = computeFineTemperature(adcTemperature); + lastSample.temperatureTimestamp = + TimestampTimer::getInstance().getTimestamp(); + lastSample.temperature = (float)compensateTemperature(fineTemperature) / + 100; // Converto to DegC + + return lastSample; +} + +unsigned int BMP280::calculateMaxMeasurementTime(BMP280Config config_) +{ + // TODO: This folrmula is not present in the BMP280's datasheet, it should + // be checked + return ceil(1.25 + (2.3 * config_.bits.oversamplingTemperature) + + (2.3 * config_.bits.oversamplingPressure + 0.575)); +} + +unsigned int BMP280::getMaxMeasurementTime() +{ + return calculateMaxMeasurementTime(config); +} + +bool BMP280::selfTest() { return checkWhoAmI(); } + +BMP280Data BMP280::sampleImpl() +{ + uint8_t buffer[8]; + int32_t adcTemperature = 0; + int32_t adc_P = 0; + int32_t adc_H = 0; + BMP280Data data; + + // TODO: implement selective read! + + // Burst read pressure, temperature and humidity + { + SPITransaction transaction(spiSlave); + + transaction.readRegisters(REG_PRESS_MSB, buffer, 8); + } + + adcTemperature |= ((uint32_t)buffer[3]) << 12; + adcTemperature |= ((uint32_t)buffer[4]) << 4; + adcTemperature |= (buffer[5] >> 4) & 0x0F; + + adc_P |= ((uint32_t)buffer[0]) << 12; + adc_P |= ((uint32_t)buffer[1]) << 4; + adc_P |= (buffer[2] >> 4) & 0x0F; + + adc_H |= ((uint32_t)buffer[6] << 8); + adc_H |= buffer[7]; + + // Compensate temperature + fineTemperature = computeFineTemperature(adcTemperature); + data.temperatureTimestamp = TimestampTimer::getInstance().getTimestamp(); + data.temperature = (float)compensateTemperature(fineTemperature) / + 100; // Converto to DegC + + // Compensate pressure + data.pressureTimestamp = TimestampTimer::getInstance().getTimestamp(); + data.pressure = (float)compensatePressure(adc_P) / 256; // Convert to Pa + + return data; +} + +bool BMP280::checkWhoAmI() +{ + SPITransaction transaction(spiSlave); + + uint8_t whoAmIValue = transaction.readRegister(REG_ID); + + printf("%2X\n", whoAmIValue); + + return whoAmIValue == REG_ID_VAL; +} + +void BMP280::setConfiguration() { setConfiguration(config); } + +void BMP280::setConfiguration(BMP280Config config_) +{ + SPITransaction transaction(spiSlave); + + transaction.writeRegister(REG_CONFIG & 0x7F, config_.bytes.config); + transaction.writeRegister(REG_CTRL_MEAS & 0x7F, + config_.bytes.ctrlPressureAndTemperature); +} + +BMP280::BMP280Config BMP280::readConfiguration() +{ + BMP280Config tmp; + SPITransaction transaction(spiSlave); + + transaction.readRegisters(REG_STATUS, (uint8_t *)&tmp, 3); + + return tmp; +} + +void BMP280::loadCompensationParameters() +{ + // Read compensation parameters + { + SPITransaction transaction(spiSlave); + + transaction.readRegisters(REG_CALIB_0, (uint8_t *)&compParams, 25); + } +} + +int32_t BMP280::computeFineTemperature(int32_t adcTemperature) +{ + int32_t var1, var2; + var1 = ((((adcTemperature >> 3) - ((int32_t)compParams.bits.dig_T1 << 1))) * + ((int32_t)compParams.bits.dig_T2)) >> + 11; + var2 = (((((adcTemperature >> 4) - ((int32_t)compParams.bits.dig_T1)) * + ((adcTemperature >> 4) - ((int32_t)compParams.bits.dig_T1))) >> + 12) * + ((int32_t)compParams.bits.dig_T3)) >> + 14; + return var1 + var2; +} + +int32_t BMP280::compensateTemperature(int32_t fineTemperature) +{ + return (fineTemperature * 5 + 128) >> 8; +} + +uint32_t BMP280::compensatePressure(int32_t adc_P) +{ + int64_t var1, var2, p; + var1 = ((int64_t)fineTemperature) - 128000; + var2 = var1 * var1 * (int64_t)compParams.bits.dig_P6; + var2 = var2 + ((var1 * (int64_t)compParams.bits.dig_P5) << 17); + var2 = var2 + (((int64_t)compParams.bits.dig_P4) << 35); + var1 = ((var1 * var1 * (int64_t)compParams.bits.dig_P3) >> 8) + + ((var1 * ((int64_t)compParams.bits.dig_P2) << 12)); + var1 = + ((((int64_t)1) << 47) + var1) * ((int64_t)compParams.bits.dig_P1) >> 33; + if (var1 == 0) + { + return 0; // avoid exception caused by division by zero + } + p = 1048576 - adc_P; + p = (((p << 31) - var2) * 3125) / var1; + var1 = (((int64_t)compParams.bits.dig_P9) * (p >> 13) * (p >> 13)) >> 25; + var2 = (((int64_t)compParams.bits.dig_P8) * p) >> 19; + p = ((p + var1 + var2) >> 8) + (((int64_t)compParams.bits.dig_P7) << 4); + return (uint32_t)p; +} + +} // namespace Boardcore diff --git a/src/shared/sensors/BMP280/BMP280.h b/src/shared/sensors/BMP280/BMP280.h new file mode 100644 index 0000000000000000000000000000000000000000..385a40695fc386cc647cd051431198c150f9b835 --- /dev/null +++ b/src/shared/sensors/BMP280/BMP280.h @@ -0,0 +1,273 @@ +/* Copyright (c) 2022 Skyward Experimental Rocketry + * Author: Alberto Nidasio + * + * 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. + */ + +#pragma once + +#include <diagnostic/PrintLogger.h> +#include <drivers/spi/SPIDriver.h> +#include <sensors/Sensor.h> + +#include "BMP280Data.h" + +namespace Boardcore +{ + +class BMP280 : public Sensor<BMP280Data> +{ +public: + enum Oversampling + { + SKIPPED = 0x0, ///< Skipped (output set to 0x8000) + OVERSAMPLING_1 = 0x1, ///< Oversampling x1 + OVERSAMPLING_2 = 0x2, ///< Oversampling x2 + OVERSAMPLING_4 = 0x3, ///< Oversampling x4 + OVERSAMPLING_8 = 0x4, ///< Oversampling x8 + OVERSAMPLING_16 = 0x5, ///< Oversampling x16 + }; + + enum Mode + { + SLEEP_MODE = 0x0, ///< Sleep mode + FORCED_MODE = 0x1, ///< Forced mode + NORMAL_MODE = 0x3 ///< Normal mode + }; + + enum StandbyTime + { + STB_TIME_0_5 = 0x0, ///< 0.5 ms + STB_TIME_62_5 = 0x1, ///< 62.5 ms + STB_TIME_125 = 0x2, ///< 125 ms + STB_TIME_250 = 0x3, ///< 250 ms + STB_TIME_500 = 0x4, ///< 500 ms + STB_TIME_1000 = 0x5, ///< 1000 ms + STB_TIME_20 = 0x6, ///< 20 ms + STB_TIME_40 = 0x7 ///< 40 ms + }; + + enum FilterCoeff + { + FILTER_OFF = 0x0, ///< Filter off + FILTER_COEFF_2 = 0x1, ///< Filter coefficient = 2 + FILTER_COEFF_4 = 0x2, ///< Filter coefficient = 4 + FILTER_COEFF_8 = 0x3, ///< Filter coefficient = 8 + FILTER_COEFF_16 = 0x4 ///< Filter coefficient = 16 + }; + + union BMP280Config + { + struct __attribute__((packed)) BMP280ConfigBits + { + // status + /** + * '1' when the NVM data are being copied to image registers, '0' + * when the copying is done + */ + uint8_t imUpdate : 1; + uint8_t : 2; + /** + * '1' whenever a conversion is running, '0' when the result have + * been transferred to the data registers + */ + uint8_t measuring : 1; + uint8_t : 4; + + Mode mode : 2; ///< Device modes + Oversampling + oversamplingPressure : 3; ///< Oversampling of pressure + Oversampling + oversamplingTemperature : 3; ///< Oversampling of temperature + + // config + uint8_t spi3wEn : 1; ///< Enables 3-wire SPI interface + uint8_t : 1; + FilterCoeff filter : 3; ///< Time constant of the IIR filter + StandbyTime standbyTime : 3; ///< Inactive duration in normal mode + } bits; + + struct + { + uint8_t status; ///< Device status + uint8_t ctrlPressureAndTemperature; ///< Pressure and temperature + ///< options + uint8_t config; ///< Rate, filter and interface options + } bytes; + + uint8_t bytesArray[3]; + }; + + union BMP280Comp + { + struct __attribute__((packed)) + { + uint16_t dig_T1; + int16_t dig_T2; + int16_t dig_T3; + uint16_t dig_P1; + int16_t dig_P2; + int16_t dig_P3; + int16_t dig_P4; + int16_t dig_P5; + int16_t dig_P6; + int16_t dig_P7; + int16_t dig_P8; + int16_t dig_P9; + } bits; + + uint8_t bytesArray[24]; + }; + + static constexpr uint8_t REG_ID_VAL = 0x58; ///< Who am I value + + static const BMP280Config BMP280_DEFAULT_CONFIG; ///< Default register + ///< values + static const BMP280Config BMP280_CONFIG_ALL_ENABLED; ///< Datasheet + ///< values for + ///< indoor + ///< navigation + static const BMP280Config BMP280_CONFIG_TEMP_SINGLE; ///< Temperature + ///< enabled in + ///< forced mode + + explicit BMP280(SPISlave spiSlave_, + BMP280Config config_ = BMP280_CONFIG_ALL_ENABLED); + + /** + * @brief Initialize the device with the specified configuration + */ + bool init() override; + + /** + * @brief Sets the sensor mode + * + * Values: + * - SLEEP_MODE: No measurements are performed + * - FORCED_MODE: A single measurement is performed when this function + * writes the configuration, after the measurement the sensor return to + * sleep mode + * - NORMAL_MODE: Automated cycling between measurements, standby time can + * be set using setStandbyTime() + */ + void setSensorMode(Mode mode); + + /** + * @brief Sets the oversampling for pressure readings, use SKIPPED to + * disable pressure sampling + */ + void setPressureOversampling(Oversampling oversampling); + + /** + * @brief Sets the oversampling for temperature readings, use SKIPPED to + * disable temperature sampling + */ + void setTemperatureOversampling(Oversampling oversampling); + + /** + * @brief Sets the coefficient for the IIR filter (applied to temperature + * and pressure) + */ + void setFilterCoeff(FilterCoeff filterCoeff); + + /** + * @brief Sets the standby time between readings in normal mode + */ + void setStandbyTime(StandbyTime standbyTime); + + /** + * @brief Reads only the pressure, does not set the configuration + */ + PressureData readPressure(); + + /** + * @brief Reads only the temperature, does not set the configuration + */ + TemperatureData readTemperature(); + + /** + * @brief Maximum measurement time formula from datasheet page 51 + * + * @return Time in milliseconds + */ + static unsigned int calculateMaxMeasurementTime(BMP280Config config_); + + unsigned int getMaxMeasurementTime(); + + /** + * @brief Reads the WHO AM I register + * + * @return True if everything ok + */ + bool selfTest() override; + +private: + BMP280Data sampleImpl() override; + + void setConfiguration(); + + void setConfiguration(BMP280Config config_); + + BMP280Config readConfiguration(); + + void loadCompensationParameters(); + + // Compensation algorithm rev.1.1 from Bosh datasheet + + int32_t computeFineTemperature(int32_t adcTemperature); + + int32_t compensateTemperature(int32_t fineTemperature); + + uint32_t compensatePressure(int32_t adcPressure); + /** + * @brief Check the WHO AM I code from the device. + * + * @return true if the device is recognized + */ + bool checkWhoAmI(); + + enum BMP280Registers : uint8_t + { + REG_CALIB_0 = 0x88, + // Calibration register 1-25 + + REG_ID = 0xD0, + REG_RESET = 0xE0, + + REG_STATUS = 0xF3, + REG_CTRL_MEAS = 0xF4, + REG_CONFIG = 0xF5, + + REG_PRESS_MSB = 0xF7, + REG_PRESS_LSB = 0xF8, + REG_PRESS_XLSB = 0xF9, + REG_TEMP_MSB = 0xFA, + REG_TEMP_LSB = 0xFB, + REG_TEMP_XLSB = 0xFC + }; + + const SPISlave spiSlave; + BMP280Config config; + BMP280Comp compParams; + int32_t fineTemperature; // Used in compensation algorithm + + PrintLogger logger = Logging::getLogger("bmp280"); +}; + +} // namespace Boardcore diff --git a/src/shared/sensors/BMP280/BMP280Data.h b/src/shared/sensors/BMP280/BMP280Data.h new file mode 100644 index 0000000000000000000000000000000000000000..cfd3cecc200fb8be00b137bc725a6a62a2776f45 --- /dev/null +++ b/src/shared/sensors/BMP280/BMP280Data.h @@ -0,0 +1,55 @@ +/* Copyright (c) 2021 Skyward Experimental Rocketry + * Author: Alberto Nidasio + * + * 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. + */ + +#pragma once + +#include <sensors/SensorData.h> + +namespace Boardcore +{ + +struct BMP280Data : public TemperatureData, public PressureData +{ + BMP280Data() : TemperatureData{0, 0.0}, PressureData{0, 0.0} {} + + BMP280Data(uint64_t timestamp, float temperature, float pressure, + float humidity) + : TemperatureData{timestamp, temperature}, PressureData{timestamp, + pressure} + + { + } + + static std::string header() + { + return "temperatureTimestamp,temperature,pressureTimestamp,pressure,\n"; + } + + void print(std::ostream& os) const + { + os << temperatureTimestamp << "," << temperature << "," + << pressureTimestamp << "," << pressure << "," + << "\n"; + } +}; + +} // namespace Boardcore diff --git a/src/tests/sensors/test-bme280.cpp b/src/tests/sensors/test-bme280.cpp index ab960d91ffcd84e51929f4deb927f596b4fd6875..afa223a4a46bab257afa859e1f4fd0ea1fad2cf4 100644 --- a/src/tests/sensors/test-bme280.cpp +++ b/src/tests/sensors/test-bme280.cpp @@ -54,15 +54,15 @@ GpioPin csPin = GpioPin(GPIOC_BASE, 1); void initBoard() { // Alternate function configuration for SPI pins - sckPin.mode(miosix::Mode::ALTERNATE); + sckPin.mode(Mode::ALTERNATE); sckPin.alternateFunction(5); // SPI function - mosiPin.mode(miosix::Mode::ALTERNATE); + mosiPin.mode(Mode::ALTERNATE); mosiPin.alternateFunction(5); // SPI function - misoPin.mode(miosix::Mode::ALTERNATE); + misoPin.mode(Mode::ALTERNATE); misoPin.alternateFunction(5); // SPI function // Chip select pin as output starting high - csPin.mode(miosix::Mode::OUTPUT); + csPin.mode(Mode::OUTPUT); csPin.high(); } @@ -98,7 +98,7 @@ int main() { bme280.setSensorMode(BME280::FORCED_MODE); - miosix::Thread::sleep(bme280.getMaxMeasurementTime()); + Thread::sleep(bme280.getMaxMeasurementTime()); bme280.sample(); @@ -106,7 +106,7 @@ int main() bme280.getLastSample().temperature, bme280.getLastSample().pressure, bme280.getLastSample().humidity); - miosix::Thread::sleep(1000); + Thread::sleep(1000); } TRACE("Normal mode\n"); @@ -119,6 +119,6 @@ int main() bme280.getLastSample().temperature, bme280.getLastSample().pressure, bme280.getLastSample().humidity); - miosix::Thread::sleep(50); // 25Hz + Thread::sleep(50); // 25Hz } } diff --git a/src/tests/sensors/test-bmp280.cpp b/src/tests/sensors/test-bmp280.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d83478dede299492b607e2434a70c35b559b0906 --- /dev/null +++ b/src/tests/sensors/test-bmp280.cpp @@ -0,0 +1,107 @@ +/* Copyright (c) 2021 Skyward Experimental Rocketry + * Author: Alberto Nidasio + * + * 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 <drivers/spi/SPIDriver.h> +#include <drivers/timer/TimestampTimer.h> +#include <miosix.h> +#include <sensors/BMP280/BMP280.h> +#include <utils/Debug.h> + +using namespace miosix; +using namespace Boardcore; + +GpioPin sckPin = GpioPin(GPIOA_BASE, 5); +GpioPin misoPin = GpioPin(GPIOB_BASE, 4); +GpioPin mosiPin = GpioPin(GPIOA_BASE, 7); +GpioPin csPin = GpioPin(GPIOB_BASE, 2); + +void initBoard() +{ + // Alternate function configuration for SPI pins + sckPin.mode(Mode::ALTERNATE); + sckPin.alternateFunction(5); // SPI function + mosiPin.mode(Mode::ALTERNATE); + mosiPin.alternateFunction(5); // SPI function + misoPin.mode(Mode::ALTERNATE); + misoPin.alternateFunction(5); // SPI function + + // Chip select pin as output starting high + csPin.mode(Mode::OUTPUT); + csPin.high(); +} + +int main() +{ + // Enable SPI clock and set gpios + initBoard(); + + // SPI configuration setup + SPIBusConfig spiConfig; + // spiConfig.clockDivider = SPI::ClockDivider::DIV_32; + spiConfig.mode = SPI::Mode::MODE_0; + SPIBus spiBus(SPI1); + SPISlave spiSlave(spiBus, csPin, spiConfig); + + // Device initialization + BMP280 bmp280(spiSlave); + + bmp280.init(); + + // In practice the self test reads the who am i reagister, this is already + // done in init() + if (!bmp280.selfTest()) + { + TRACE("Self test failed!\n"); + + return -1; + } + + // Try forced mode + TRACE("Forced mode\n"); + for (int i = 0; i < 10; i++) + { + bmp280.setSensorMode(BMP280::FORCED_MODE); + + Thread::sleep(bmp280.getMaxMeasurementTime()); + + bmp280.sample(); + + TRACE("temp: %.2f DegC\tpress: %.2f hPa\n", + bmp280.getLastSample().temperature, + bmp280.getLastSample().pressure); + + Thread::sleep(1000); + } + + TRACE("Normal mode\n"); + bmp280.setSensorMode(BMP280::NORMAL_MODE); + while (true) + { + bmp280.sample(); + + TRACE("temp: %.2f DegC\tpress: %.2f Pa\thumid: %.2f %%RH\n", + bmp280.getLastSample().temperature, + bmp280.getLastSample().pressure); + + Thread::sleep(50); // 25Hz + } +}