diff --git a/CMakeLists.txt b/CMakeLists.txt index 4fe5fc432f1f6519dd90a20b41a4aecb3df307f1..f1ed2c694c70e53b8453119b438d14b6715bef71 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -487,6 +487,9 @@ sbs_target(test-lps22df stm32f767zi_nucleo) add_executable(test-lsm6dsrx src/tests/sensors/test-lsm6dsrx.cpp) sbs_target(test-lsm6dsrx stm32f407vg_stm32f4discovery) +add_executable(test-nd015x src/tests/sensors/test-nd015x.cpp) +sbs_target(test-nd015x stm32f767zi_lyra_biscotto) + #-----------------------------------------------------------------------------# # Tests - Utils # #-----------------------------------------------------------------------------# diff --git a/cmake/boardcore.cmake b/cmake/boardcore.cmake index 2d773978aa5dbfcdb7770a23a6ab0360cc89e521..12c0eff4291a2b0d1f656dee99b25d96a76c083e 100644 --- a/cmake/boardcore.cmake +++ b/cmake/boardcore.cmake @@ -120,6 +120,8 @@ set(BOARDCORE_SRC ${BOARDCORE_PATH}/src/shared/sensors/LPS28DFW/LPS28DFW.cpp ${BOARDCORE_PATH}/src/shared/sensors/LPS22DF/LPS22DF.cpp ${BOARDCORE_PATH}/src/shared/sensors/LSM6DSRX/LSM6DSRX.cpp + ${BOARDCORE_PATH}/src/shared/sensors/ND015X/ND015D.cpp + ${BOARDCORE_PATH}/src/shared/sensors/ND015X/ND015A.cpp # Calibration ${BOARDCORE_PATH}/src/shared/sensors/calibration/BiasCalibration/BiasCalibration.cpp diff --git a/src/shared/sensors/ND015X/ND015A.cpp b/src/shared/sensors/ND015X/ND015A.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3b1a610cf303e419e016ed1f213d3b5965496cfb --- /dev/null +++ b/src/shared/sensors/ND015X/ND015A.cpp @@ -0,0 +1,164 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Pietro Bortolus + * + * 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 "ND015A.h" + +#include <drivers/timer/TimestampTimer.h> +#include <utils/Constants.h> + +#include <cmath> +#include <string> + +namespace Boardcore +{ +const char ND015A::MODEL_NAME[] = "ND015A"; + +SPIBusConfig ND015A::getDefaultSPIConfig() +{ + SPIBusConfig spiConfig{}; + spiConfig.mode = SPI::Mode::MODE_1; + spiConfig.clockDivider = SPI::ClockDivider::DIV_256; + + // Datasheet specifies 100us, but 50us is enough from testing + spiConfig.csSetupTimeUs = 50; + + // The datasheet specifies a minimum CS hold time of 100us + // If this is ever an issue, implement it by waiting at the next sample + // instead of setting it in SPIBusConfig + + return spiConfig; +} + +ND015A::ND015A(SPIBusInterface& bus, miosix::GpioPin cs, SPIBusConfig spiConfig, + IOWatchdogEnable iow, BWLimitFilter bwl, NotchEnable ntc, + uint8_t odr) + : slave(bus, cs, spiConfig), sensorSettings{0x7, iow, bwl, ntc, odr} +{ +} + +bool ND015A::init() +{ + // setting the sensor settings to the correct values + SPITransaction spi(slave); + uint16_t spiDataOut; + + memcpy(&spiDataOut, &sensorSettings, sizeof(spiDataOut)); + spi.transfer16(spiDataOut); + + return true; +} + +bool ND015A::selfTest() { return true; } + +bool ND015A::checkModelMatch() +{ + ND015ADataExtended extendedData{}; + uint8_t* data = reinterpret_cast<uint8_t*>(&extendedData); + + // setting the first 2 bytes of the data to the correct sensor settings + memcpy(&extendedData, &sensorSettings, sizeof(sensorSettings)); + + SPITransaction spi(slave); + spi.transfer(data, sizeof(extendedData)); + + // this part checks if the model number returned by the sensor matches the + // correct model number + bool compareResult = + (memcmp(&extendedData.model, &MODEL_NAME, sizeof(MODEL_NAME) - 1) == 0); + + if (!compareResult) + { + auto model = + std::string(extendedData.model, sizeof(extendedData.model)); + // Replace all \0 with '.' for printing + std::replace(model.begin(), model.end(), '\0', '.'); + + LOG_ERR(logger, + "Sensor model mismatch: received {}, expected {} (. = \\0)", + model, MODEL_NAME); + return false; + } + return true; +} + +void ND015A::setOutputDataRate(uint8_t odr) +{ + sensorSettings.odr = odr; + + SPITransaction spi(slave); + uint16_t spiDataOut; + + memcpy(&spiDataOut, &sensorSettings, sizeof(spiDataOut)); + spi.transfer16(spiDataOut); +} + +void ND015A::setIOWatchdog(IOWatchdogEnable iow) +{ + sensorSettings.iow = iow; + + SPITransaction spi(slave); + uint16_t spiDataOut; + + memcpy(&spiDataOut, &sensorSettings, sizeof(spiDataOut)); + spi.transfer16(spiDataOut); +} + +void ND015A::setBWLimitFilter(BWLimitFilter bwl) +{ + sensorSettings.bwl = bwl; + + SPITransaction spi(slave); + uint16_t spiDataOut; + + memcpy(&spiDataOut, &sensorSettings, sizeof(spiDataOut)); + spi.transfer16(spiDataOut); +} + +void ND015A::setNotch(NotchEnable ntc) +{ + sensorSettings.ntc = ntc; + + SPITransaction spi(slave); + uint16_t spiDataOut; + + memcpy(&spiDataOut, &sensorSettings, sizeof(spiDataOut)); + spi.transfer16(spiDataOut); +} + +ND015XData ND015A::sampleImpl() +{ + ND015XData data; + uint16_t spiDataOut; + SPITransaction spi(slave); + + memcpy(&spiDataOut, &sensorSettings, sizeof(spiDataOut)); + uint16_t spiDataIn = spi.transfer16(spiDataOut); + + data.pressure = + ((spiDataIn - 0.05 * pow(2, 16)) / (0.9 * pow(2, 16)) * 15) * + Constants::PSI_TO_PASCAL; + data.pressureTimestamp = TimestampTimer::getTimestamp(); + + return data; +} + +} // namespace Boardcore diff --git a/src/shared/sensors/ND015X/ND015A.h b/src/shared/sensors/ND015X/ND015A.h new file mode 100644 index 0000000000000000000000000000000000000000..77d19e4992fb99b984b5f156f204832a46c276a3 --- /dev/null +++ b/src/shared/sensors/ND015X/ND015A.h @@ -0,0 +1,176 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Pietro Bortolus + * + * 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 "ND015XData.h" + +namespace Boardcore +{ + +class ND015A : public Sensor<ND015XData> +{ +public: + static const char MODEL_NAME[]; + + enum class IOWatchdogEnable : uint8_t + { + DISABLED = 0x00, + ENABLED = 0x01, + }; + + enum class BWLimitFilter : uint8_t + { + BWL_1 = 0x00, // 1.0 Hz + BWL_2 = 0x01, // 2.0 Hz + BWL_5 = 0x02, // 5.0 Hz + BWL_10 = 0x03, // 10 Hz + BWL_20 = 0x04, // 20 Hz + BWL_50 = 0x05, // 50 Hz + BWL_100 = 0x06, // 100 Hz + BWL_200 = 0x07, // 200 Hz + }; + + enum class NotchEnable : uint8_t + { + DISABLED = 0x00, + ENABLED = 0x01, + }; + + /** + * @brief Constructs the default config for the SPI bus. + * + * @return The default SPIBusConfig object. + */ + static SPIBusConfig getDefaultSPIConfig(); + + /** + * @brief Constructor for the ND015A sensor. + * + * @param bus SPI bus interface. + * @param cs Chip select GPIO pin. + * @param spiConfig SPI bus configuration. + */ + ND015A(SPIBusInterface& bus, miosix::GpioPin cs, SPIBusConfig spiConfig, + IOWatchdogEnable iow = IOWatchdogEnable::DISABLED, + BWLimitFilter bwl = BWLimitFilter::BWL_200, + NotchEnable ntc = NotchEnable::ENABLED, uint8_t odr = 0x1C); + + /** + * @brief Initializes the sensor. + * + * @return Always returns true. + */ + bool init() override; + + /** + * @brief Not implemented. + * + * @return Always returns true. + */ + bool selfTest() override; + + /** + * @brief function to set the output data rate + * + * @param odr output data rate for the sensor, + * the actual odr is calculated as + * 444Hz / odr. + * Allowed values are 0x00 to 0xFF, + * 0x00 will select the auto-select rate mode + */ + void setOutputDataRate(uint8_t odr); + + /** + * @brief function to enable the IO watchdog + * + * @param iow setting + */ + void setIOWatchdog(IOWatchdogEnable iow); + + /** + * @brief Sets the bandwidth limit filter for the sensor. + * + * @param bwl Bandwidth limit filter setting. + */ + void setBWLimitFilter(BWLimitFilter bwl); + + /** + * @brief Enables or disables the notch filter. + * + * @param ntc Notch filter setting. + */ + void setNotch(NotchEnable ntc); + + /** + * @brief Checks if the sensor model matches the expected model. + * + * @return True if the model matches, false otherwise. + * + * @warning The function might return false even when it should not as the + * SPI transaction sometimes ads some zeroes when it should not. + * This is because the sensore requires a clock cycle greater than + * the one we can provide + */ + bool checkModelMatch(); + +protected: + ND015XData sampleImpl() override; + +private: + SPISlave slave; + + /** + * @brief settings for the mode control register, + * the initial values are the ones set by default + * in the sensor + */ + struct + { + uint8_t fsr : 3; // full scale range, value cannot be changed + IOWatchdogEnable iow : 1; // IO watchdog enable + BWLimitFilter bwl : 3; // bandwidth limit filter + NotchEnable ntc : 1; // notch filter enable + uint8_t odr : 8; // output data rate + } sensorSettings; + + static_assert(sizeof(sensorSettings) == 2, + "sensorSettings size is not 2 bytes"); + + struct ND015ADataExtended + { + uint16_t pressure; + uint16_t temperature; + char model[8]; + uint8_t serial[4]; + uint8_t build[6]; + }; + + PrintLogger logger = Logging::getLogger("nd015a"); +}; + +} // namespace Boardcore + diff --git a/src/shared/sensors/ND015X/ND015D.cpp b/src/shared/sensors/ND015X/ND015D.cpp new file mode 100644 index 0000000000000000000000000000000000000000..53a3ef247ac4b6055e8a6c9b79a78464d1c0ae22 --- /dev/null +++ b/src/shared/sensors/ND015X/ND015D.cpp @@ -0,0 +1,199 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Pietro Bortolus + * + * 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 "ND015D.h" + +#include <drivers/timer/TimestampTimer.h> +#include <utils/Constants.h> + +#include <cmath> +#include <string> + +namespace Boardcore +{ +const char ND015D::MODEL_NAME[] = "ND015D"; + +SPIBusConfig ND015D::getDefaultSPIConfig() +{ + SPIBusConfig spiConfig{}; + spiConfig.mode = SPI::Mode::MODE_1; + spiConfig.clockDivider = SPI::ClockDivider::DIV_256; + + // Datasheet specifies 100us, but 50us is enough from testing + spiConfig.csSetupTimeUs = 50; + + // The datasheet specifies a minimum CS hold time of 100us + // If this is ever an issue, implement it by waiting at the next sample + // instead of setting it in SPIBusConfig + + return spiConfig; +} + +ND015D::ND015D(SPIBusInterface& bus, miosix::GpioPin cs, SPIBusConfig spiConfig, + FullScaleRange fsr, IOWatchdogEnable iow, BWLimitFilter bwl, + NotchEnable ntc, uint8_t odr) + : slave(bus, cs, spiConfig), range(rangeToPressure(fsr)), + sensorSettings{fsr, iow, bwl, ntc, odr} +{ +} + +bool ND015D::init() +{ + // setting the sensor settings to the correct values + SPITransaction spi(slave); + uint16_t spiDataOut; + + memcpy(&spiDataOut, &sensorSettings, sizeof(spiDataOut)); + spi.transfer16(spiDataOut); + + return true; +} + +bool ND015D::selfTest() { return true; } + +bool ND015D::checkModelMatch() +{ + ND015DDataExtended extendedData{}; + uint8_t* data = reinterpret_cast<uint8_t*>(&extendedData); + + // setting the first 2 bytes of the data to the correct sensor settings + memcpy(&extendedData, &sensorSettings, sizeof(sensorSettings)); + + SPITransaction spi(slave); + spi.transfer(data, sizeof(extendedData)); + + // this part checks if the model number returned by the sensor matches the + // correct model number + bool compareResult = + (memcmp(&extendedData.model, &MODEL_NAME, sizeof(MODEL_NAME) - 1) == 0); + + if (!compareResult) + { + auto model = + std::string(extendedData.model, sizeof(extendedData.model)); + // Replace all \0 with '.' for printing + std::replace(model.begin(), model.end(), '\0', '.'); + + LOG_ERR(logger, + "Sensor model mismatch: received {}, expected {} (. = \\0)", + model, MODEL_NAME); + return false; + } + return true; +} + +void ND015D::setOutputDataRate(uint8_t odr) +{ + sensorSettings.odr = odr; + + SPITransaction spi(slave); + uint16_t spiDataOut; + + memcpy(&spiDataOut, &sensorSettings, sizeof(spiDataOut)); + spi.transfer16(spiDataOut); +} + +void ND015D::setFullScaleRange(FullScaleRange fsr) +{ + sensorSettings.fsr = fsr; + + range = rangeToPressure(fsr); + + SPITransaction spi(slave); + uint16_t spiDataOut; + + memcpy(&spiDataOut, &sensorSettings, sizeof(spiDataOut)); + spi.transfer16(spiDataOut); +} + +float ND015D::rangeToPressure(FullScaleRange fsr) +{ + switch (fsr) + { + case ND015D::FullScaleRange::FS_1: + return 1; + case ND015D::FullScaleRange::FS_2: + return 2; + case ND015D::FullScaleRange::FS_4: + return 4; + case ND015D::FullScaleRange::FS_5: + return 5; + case ND015D::FullScaleRange::FS_10: + return 10; + case ND015D::FullScaleRange::FS_15: + return 15; + default: + return 0; + } +} + +void ND015D::setIOWatchdog(IOWatchdogEnable iow) +{ + sensorSettings.iow = iow; + + SPITransaction spi(slave); + uint16_t spiDataOut; + + memcpy(&spiDataOut, &sensorSettings, sizeof(spiDataOut)); + spi.transfer16(spiDataOut); +} + +void ND015D::setBWLimitFilter(BWLimitFilter bwl) +{ + sensorSettings.bwl = bwl; + + SPITransaction spi(slave); + uint16_t spiDataOut; + + memcpy(&spiDataOut, &sensorSettings, sizeof(spiDataOut)); + spi.transfer16(spiDataOut); +} + +void ND015D::setNotch(NotchEnable ntc) +{ + sensorSettings.ntc = ntc; + + SPITransaction spi(slave); + uint16_t spiDataOut; + + memcpy(&spiDataOut, &sensorSettings, sizeof(spiDataOut)); + spi.transfer16(spiDataOut); +} + +ND015XData ND015D::sampleImpl() +{ + ND015XData data; + uint16_t spiDataOut; + SPITransaction spi(slave); + + memcpy(&spiDataOut, &sensorSettings, sizeof(spiDataOut)); + uint16_t spiDataIn = spi.transfer16(spiDataOut); + + data.pressure = + (static_cast<int16_t>(spiDataIn) / (0.9 * pow(2, 15)) * range) * + Constants::PSI_TO_PASCAL; + data.pressureTimestamp = TimestampTimer::getTimestamp(); + + return data; +} + +} // namespace Boardcore diff --git a/src/shared/sensors/ND015X/ND015D.h b/src/shared/sensors/ND015X/ND015D.h new file mode 100644 index 0000000000000000000000000000000000000000..8a085a221516282156fbde9247be3ba927f4673d --- /dev/null +++ b/src/shared/sensors/ND015X/ND015D.h @@ -0,0 +1,208 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Pietro Bortolus + * + * 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 "ND015XData.h" + +namespace Boardcore +{ + +class ND015D : public Sensor<ND015XData> +{ +public: + static const char MODEL_NAME[]; + + /** + * The datasheet is unclear about the unit of measure, + * it could be either psi or inH2O but I believe it's the latter + */ + enum class FullScaleRange : uint8_t + { + FS_1 = 0x02, // 1.0 psi + FS_2 = 0x03, // 2.0 psi + FS_4 = 0x04, // 4.0 psi + FS_5 = 0x05, // 5.0 psi + FS_10 = 0x06, // 10.0 psi + FS_15 = 0x07, // 15.0 psi + }; + + /** + * @brief Converts the FullScale value to its corresponding range. + * + * @param fsr FullScale value. + * @return Pressure range. + */ + static float rangeToPressure(FullScaleRange fsr); + + enum class IOWatchdogEnable : uint8_t + { + DISABLED = 0x00, + ENABLED = 0x01, + }; + + enum class BWLimitFilter : uint8_t + { + BWL_1 = 0x00, // 1.0 Hz + BWL_2 = 0x01, // 2.0 Hz + BWL_5 = 0x02, // 5.0 Hz + BWL_10 = 0x03, // 10 Hz + BWL_20 = 0x04, // 20 Hz + BWL_50 = 0x05, // 50 Hz + BWL_100 = 0x06, // 100 Hz + BWL_200 = 0x07, // 200 Hz + }; + + enum class NotchEnable : uint8_t + { + DISABLED = 0x00, + ENABLED = 0x01, + }; + + /** + * @brief Constructs the default config for the SPI bus. + * + * @return The default SPIBusConfig object. + */ + static SPIBusConfig getDefaultSPIConfig(); + + /** + * @brief Constructor for the ND015D sensor. + * + * @param bus SPI bus interface. + * @param cs Chip select GPIO pin. + * @param spiConfig SPI bus configuration. + */ + + ND015D(SPIBusInterface& bus, miosix::GpioPin cs, SPIBusConfig spiConfig, + FullScaleRange fsr = FullScaleRange::FS_2, + IOWatchdogEnable iow = IOWatchdogEnable::DISABLED, + BWLimitFilter bwl = BWLimitFilter::BWL_200, + NotchEnable ntc = NotchEnable::ENABLED, uint8_t odr = 0x1C); + + /** + * @brief Initializes the sensor. + * + * @return Always returns true. + */ + bool init() override; + + /** + * @brief Not implemented. + * + * @return Always returns true. + */ + bool selfTest() override; + + /** + * @brief function to set the output data rate + * + * @param odr output data rate for the sensor, + * the actual odr is calculated as + * 444Hz / odr. + * Allowed values are 0x00 to 0xFF, + * 0x00 will select the auto-select rate mode + */ + void setOutputDataRate(uint8_t odr); + + /** + * @brief Sets the full-scale range for the sensor. + * + * @param fs Full-scale range. + */ + void setFullScaleRange(FullScaleRange fsr); + + /** + * @brief Enables or disables the IO watchdog. + * + * @param iow IO watchdog setting. + */ + void setIOWatchdog(IOWatchdogEnable iow); + + /** + * @brief Sets the bandwidth limit filter for the sensor. + * + * @param bwl Bandwidth limit filter setting. + */ + void setBWLimitFilter(BWLimitFilter bwl); + + /** + * @brief Enables or disables the notch filter. + * + * @param ntc Notch filter setting. + */ + void setNotch(NotchEnable ntc); + + /** + * @brief Checks if the sensor model matches the expected model. + * + * @return True if the model matches, false otherwise. + * + * @warning The function might return false even when it should not as the + * SPI transaction sometimes ads some zeroes when it should not. + * This is because the sensore requires a clock cycle greater than + * the one we can provide + */ + bool checkModelMatch(); + +protected: + ND015XData sampleImpl() override; + +private: + SPISlave slave; + float range; + + /** + * @brief settings for the mode control register, + * the initial values are the ones set by default + * in the sensor + */ + struct + { + FullScaleRange fsr : 3; // full scale range + IOWatchdogEnable iow : 1; // IO watchdog enable + BWLimitFilter bwl : 3; // bandwidth limit filter + NotchEnable ntc : 1; // notch filter enable + uint8_t odr : 8; // output data rate + } sensorSettings; + + static_assert(sizeof(sensorSettings) == 2, + "sensorSettings size is not 2 bytes"); + + struct ND015DDataExtended + { + uint16_t pressure; + uint16_t temperature; + char model[8]; + uint8_t serial[4]; + uint8_t build[6]; + }; + + PrintLogger logger = Logging::getLogger("nd015d"); +}; + +} // namespace Boardcore + diff --git a/src/shared/sensors/ND015X/ND015XData.h b/src/shared/sensors/ND015X/ND015XData.h new file mode 100644 index 0000000000000000000000000000000000000000..d7d4cdd426caeaac61e6b553ad1d996faa8b57c5 --- /dev/null +++ b/src/shared/sensors/ND015X/ND015XData.h @@ -0,0 +1,40 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Pietro Bortolus + * + * 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 ND015XData : public PressureData +{ + static std::string header() { return "timestamp,pressure\n"; } + + void print(std::ostream& os) const + { + os << pressureTimestamp << "," << pressure << "\n"; + } +}; + +} // namespace Boardcore diff --git a/src/shared/utils/Constants.h b/src/shared/utils/Constants.h index 5caf126047883890839c97eeb829c49aa69767b0..b2a8c6ca551a0ed6ca89e50a9e18aa5c8956b465 100644 --- a/src/shared/utils/Constants.h +++ b/src/shared/utils/Constants.h @@ -31,6 +31,7 @@ namespace Constants static constexpr float PI = 3.14159265f; // [rad] static constexpr float DEGREES_TO_RADIANS = PI / 180.0f; static constexpr float RADIANS_TO_DEGREES = 180.0f / PI; +static constexpr float PSI_TO_PASCAL = 6894.76f; // [Pa / psi] static constexpr float g = 9.80665f; // [m^s^2] diff --git a/src/tests/sensors/test-nd015x.cpp b/src/tests/sensors/test-nd015x.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ca715c17bfeada626c45a0c7d5892056a5c79522 --- /dev/null +++ b/src/tests/sensors/test-nd015x.cpp @@ -0,0 +1,89 @@ +/* Copyright (c) 2025 Skyward Experimental Rocketry + * Author: Pietro Bortolus + * + * 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 <miosix.h> + +#include <iostream> + +#include "sensors/ND015X/ND015A.h" +#include "sensors/ND015X/ND015D.h" + +using namespace miosix; +using namespace Boardcore; + +// Pin definitions for Lyra Biscotto +GpioPin sckPin = GpioPin(GPIOE_BASE, 2); +GpioPin misoPin = GpioPin(GPIOE_BASE, 5); +GpioPin mosiPin = GpioPin(GPIOE_BASE, 6); +GpioPin csPinND015A = GpioPin(GPIOB_BASE, 9); +GpioPin csPinND015D = GpioPin(GPIOB_BASE, 8); + +void initPins() +{ + // Setup gpio pins + + sckPin.alternateFunction(5); + sckPin.mode(Mode::ALTERNATE); + misoPin.alternateFunction(5); + misoPin.mode(Mode::ALTERNATE); + mosiPin.alternateFunction(5); + mosiPin.mode(Mode::ALTERNATE); + csPinND015A.mode(Mode::OUTPUT); + csPinND015A.high(); + csPinND015D.mode(Mode::OUTPUT); + csPinND015D.high(); +} + +int main() +{ + // Initialize SPI pins + initPins(); + + SPIBus bus(SPI4); + + ND015A sensor(bus, csPinND015A, ND015A::getDefaultSPIConfig()); + // ND015D sensor(bus, csPinND015D, ND015D::getDefaultSPIConfig()); + ND015XData sensorData; + + sensor.init(); + + if (sensor.checkModelMatch()) + { + std::cout << "Sensor initialized correctly" << std::endl; + } + else + { + std::cout << "Sensor failed to initialize" << std::endl; + return 1; + } + + while (true) + { + sensor.sample(); + sensorData = sensor.getLastSample(); + std::cout << "New data: " << sensorData.pressure << std::endl; + + Thread::sleep(1000); + } + + return 0; +}