diff --git a/BMP180_proc.c b/BMP180_proc.c index 8407dbd43..b797b1f64 100644 --- a/BMP180_proc.c +++ b/BMP180_proc.c @@ -10,9 +10,6 @@ #if defined(HAL_I2C_MODULE_ENABLED) #if defined(I2C_BMP180) /****************************************************************/ -// std libs -#include -/****************************************************************/ //#define BMP180_TST //!< Defined to check calculations with datasheet @@ -24,6 +21,22 @@ extern uint8_t BMP180_OSS_time[4]; /****************************************************************/ +#if defined(BMP180_TST) +static void BMP180_Test_Result(const char * str, const bool res) +{ + printf("BMP180 %s result: TEST %s\r\n", str, res ? "PASS" : "FAIL"); +} +#endif + + +__WEAK FctERR NONNULL__ BMP180_Set_SeaLevel_Pressure(BMP180_t * pCpnt) +{ + pCpnt->cfg.SeaLevelPressure = Get_SeaLevel_Pressure(); + + return ERROR_OK; +} + + __WEAK FctERR NONNULL__ BMP180_Init_Sequence(BMP180_t * pCpnt) { FctERR err; @@ -34,8 +47,10 @@ __WEAK FctERR NONNULL__ BMP180_Init_Sequence(BMP180_t * pCpnt) if (err) { return err; } if (pCpnt->cfg.ID != BMP180_CHIP_ID) { return ERROR_COMMON; } // Unknown device + err = BMP180_Set_SeaLevel_Pressure(pCpnt); + #if !defined(BMP180_TST) - err = BMP180_Get_Calibration(pCpnt, &pCpnt->cfg.Calib); + err |= BMP180_Get_Calibration(pCpnt, &pCpnt->cfg.Calib); #else pCpnt->cfg.OSS = BMP180__OSS_1_TIME; pCpnt->cfg.Calib.AC1 = 408; @@ -58,32 +73,6 @@ __WEAK FctERR NONNULL__ BMP180_Init_Sequence(BMP180_t * pCpnt) /****************************************************************/ -/**!\brief Calculates the altitude (in meters) from the specified atmospheric -** pressure (in hPa), and sea-level pressure (in hPa). -** \param[in] pressure - Atmospheric pressure in hPa -**/ -__STATIC_INLINE float INLINE__ BMP180_Pressure_To_Altitude(const float pressure) -{ - // Equation from BMP180 datasheet from page 16 - return (44330.0f * (1.0f - pow(pressure / SEA_LEVEL_PRESSURE, (1.0f / 5.255f)))); -} - - -/**!\brief Calculates the pressure at sea level (in hPa) from the specified altitude -** (in meters), and atmospheric pressure (in hPa). -** \param[in] altitude - Altitude in meters -** \param[in] pressure - Atmospheric pressure in hPa -**/ -__STATIC_INLINE float INLINE__ BMP180_seaLevel_Pressure_For_Altitude(const float altitude, const float pressure) -{ - // Equation from BMP180 datasheet from page 17 - return (pressure / pow(1.0f - (altitude / 44330.0f), 5.255f)); -} - - -/****************************************************************/ - - FctERR NONNULL__ BMP180_Set_Oversampling(BMP180_t * pCpnt, const BMP180_oversampling oss) { if (oss > BMP180__OSS_8_TIME) { return ERROR_VALUE; } // Unknown Oversampling @@ -93,9 +82,9 @@ FctERR NONNULL__ BMP180_Set_Oversampling(BMP180_t * pCpnt, const BMP180_oversamp } -FctERR NONNULL__ BMP180_Get_Calibration(BMP180_t * pCpnt, BMP180_calib * calib) +FctERR NONNULL__ BMP180_Get_Calibration(BMP180_t * pCpnt, BMP180_calib * pCalib) { - int16_t * addr = (int16_t *) calib; + int16_t * addr = (int16_t *) pCalib; FctERR err = ERROR_OK; for (int i = 0 ; i < SZ_OBJ(BMP180_calib, int16_t) ; i++) @@ -108,25 +97,37 @@ FctERR NONNULL__ BMP180_Get_Calibration(BMP180_t * pCpnt, BMP180_calib * calib) } -/**!\brief Compute B5 coefficient used in temperature & pressure calculations. -** \param[in] pCpnt - Pointer to BMP180 component +/**!\brief Determine B5 compensated value used in temperature & pressure calculations. +** \param[in] pCalib - Pointer to BMP180 calibration structure ** \param[in] UT - UT value (temperature data) +** \return B5 value (compensated temperature value) **/ -static int32_t NONNULL__ computeB5(const BMP180_t * pCpnt, const int32_t UT) +static int32_t NONNULL__ UT_To_B5(const BMP180_calib * const pCalib, const int32_t UT) { - int32_t X1 = (UT - pCpnt->cfg.Calib.AC6) * pCpnt->cfg.Calib.AC5 / 32768; - int32_t X2 = pCpnt->cfg.Calib.MC * 2048 / (X1 + pCpnt->cfg.Calib.MD); + const int32_t X1 = (UT - pCalib->AC6) * pCalib->AC5 / 32768; + const int32_t X2 = pCalib->MC * 2048 / (X1 + pCalib->MD); return X1 + X2; } +/**!\brief Convert B5 compensated value to Celsius degrees +** \param[in] B5 - B5 value (compensated temperature value) +** \return Celsius degrees value +**/ +static float B5_To_Celcius(const int32_t B5) +{ + const int32_t t = (B5 + 8) / 16; + return ((float) t / 10.0f); // temperature given in 0.1°C (thus divide by 10 to get °C) +} + + FctERR NONNULLX__(1) BMP180_Get_Pressure(BMP180_t * pCpnt, float * pres) { - int32_t UT = 0, UP = 0, compp = 0; - int32_t x1, x2, b5, b6, x3, b3, p; - uint32_t b4, b7; - float t; - FctERR err = ERROR_OK; + int32_t UT = 0, UP = 0, compp = 0; + int32_t x1, x2, b6, x3, b3, p; + uint32_t b4, b7; + FctERR err = ERROR_OK; + const BMP180_calib * const pCalib = &pCpnt->cfg.Calib; /* Get the raw pressure and temperature values */ #if !defined(BMP180_TST) @@ -140,18 +141,22 @@ FctERR NONNULLX__(1) BMP180_Get_Pressure(BMP180_t * pCpnt, float * pres) #endif /* Temperature compensation */ - b5 = computeB5(pCpnt, UT); + const int32_t b5 = UT_To_B5(pCalib, UT); + + #if defined(BMP180_TST) + BMP180_Test_Result("B5", binEval(b5 == 2399)); + #endif /* Pressure compensation */ b6 = b5 - 4000; - x1 = (pCpnt->cfg.Calib.B2 * ((b6 * b6) / 4096)) / 2048; - x2 = pCpnt->cfg.Calib.AC2 * b6 / 2048; + x1 = (pCalib->B2 * ((b6 * b6) / 4096)) / 2048; + x2 = pCalib->AC2 * b6 / 2048; x3 = x1 + x2; - b3 = ((((pCpnt->cfg.Calib.AC1 * 4) + x3) << pCpnt->cfg.OSS) + 2) / 4; - x1 = (pCpnt->cfg.Calib.AC3 * b6) / 8192; - x2 = (pCpnt->cfg.Calib.B1 * ((b6 * b6) / 4096)) / 65536; + b3 = ((((pCalib->AC1 * 4) + x3) << pCpnt->cfg.OSS) + 2) / 4; + x1 = (pCalib->AC3 * b6) / 8192; + x2 = (pCalib->B1 * ((b6 * b6) / 4096)) / 65536; x3 = ((x1 + x2) + 2) / 4; - b4 = pCpnt->cfg.Calib.AC4 * (x3 + 32768) / 32768; + b4 = pCalib->AC4 * (x3 + 32768) / 32768; b7 = (UP - b3) * (50000 >> pCpnt->cfg.OSS); if (b7 < 0x80000000) { p = (b7 * 2) / b4; } @@ -163,14 +168,15 @@ FctERR NONNULLX__(1) BMP180_Get_Pressure(BMP180_t * pCpnt, float * pres) x2 = (-7357 * p) / 65536; compp = p + ((x1 + x2 + 3791) / 16); - /* Assign compensated pressure value */ - pCpnt->Pressure = compp / 100; // From kPa to hPa - - t = ((float) b5 + 8) / 16; - pCpnt->Temperature = t / 10; // temperature given in 0.1�C (thus divide by 10 to get �C) + /* Set results */ + pCpnt->Pressure = compp / 100; // From Pa to hPa (mbar) + pCpnt->Temperature = B5_To_Celcius(b5); + pCpnt->Altitude = Atmospheric_Pressure_To_Altitude(pCpnt->Pressure, pCpnt->cfg.SeaLevelPressure); - pCpnt->Altitude = BMP180_Pressure_To_Altitude(pCpnt->Pressure); - pCpnt->SeaLevelPressure = BMP180_seaLevel_Pressure_For_Altitude(pCpnt->Altitude, pCpnt->Pressure); + #if defined(BMP180_TST) + BMP180_Test_Result("Pressure", binEval(compp == 69994)); + BMP180_Test_Result("Temperature", binEval(pCpnt->Temperature == 15)); + #endif if (pres) { *pres = pCpnt->Pressure; } @@ -180,8 +186,7 @@ FctERR NONNULLX__(1) BMP180_Get_Pressure(BMP180_t * pCpnt, float * pres) FctERR NONNULLX__(1) BMP180_Get_Temperature(BMP180_t * pCpnt, float * temp) { - int32_t UT = 0, b5; - float t; + int32_t UT = 0; FctERR err = ERROR_OK; #if !defined(BMP180_TST) @@ -191,10 +196,18 @@ FctERR NONNULLX__(1) BMP180_Get_Temperature(BMP180_t * pCpnt, float * temp) UT = 27898; //!< For test purposes #endif - b5 = computeB5(pCpnt, UT); - t = ((float) b5 + 8) / 16; + const int32_t b5 = UT_To_B5(&pCpnt->cfg.Calib, UT); + + #if defined(BMP180_TST) + BMP180_Test_Result("B5", binEval(b5 == 2399)); + #endif - pCpnt->Temperature = t / 10; // temperature given in 0.1�C (thus divide by 10 to get �C) + /* Set results */ + pCpnt->Temperature = B5_To_Celcius(b5); + + #if defined(BMP180_TST) + BMP180_Test_Result("Temperature", binEval(pCpnt->Temperature == 15)); + #endif if (temp) { *temp = pCpnt->Temperature; } @@ -214,9 +227,9 @@ __WEAK FctERR NONNULL__ BMP180_handler(BMP180_t * pCpnt) #if defined(VERBOSE) const uint8_t idx = pCpnt - BMP180; - printf("BMP180 id%d: Pressure %ldhPa, Temperature %d.%02ld°C, Alt %ldm, Pressure at sea level %ldhPa\r\n", + printf("BMP180 id%d: Pressure %ldhPa, Temperature %d.%02ld°C, Alt %ldm, Pressure at sea level %ldhPa (for reference)\r\n", idx, (int32_t) pCpnt->Pressure, (int16_t) pCpnt->Temperature, get_fp_dec(pCpnt->Temperature, 2), - (int32_t) pCpnt->Altitude, (int32_t) pCpnt->SeaLevelPressure); + (int32_t) pCpnt->Altitude, (int32_t) pCpnt->cfg.SeaLevelPressure); #endif return err; diff --git a/BMP180_proc.h b/BMP180_proc.h index 9608d6d37..51e1f5f9b 100644 --- a/BMP180_proc.h +++ b/BMP180_proc.h @@ -15,6 +15,8 @@ #include "sarmfsw.h" #include "BMP180.h" +#include "shared_APS.h" + #if defined(HAL_I2C_MODULE_ENABLED) /****************************************************************/ @@ -24,8 +26,6 @@ // ***************************************************************************** #define BMP180_CHIP_ID 0x55 //!< BMP180 Chip ID to check against -#define SEA_LEVEL_PRESSURE 1013.25f //!< Sea pressure level (hPa) - // ***************************************************************************** // Section: Types @@ -54,13 +54,13 @@ typedef struct BMP180_t { float Pressure; //!< Current atmospheric pressure float Temperature; //!< Current temperature float Altitude; //!< Current altitude - float SeaLevelPressure; //!< Current atmospheric pressure at sea level uint32_t hStartConversion; //!< Last conversion start tick struct { I2C_slave_t * slave_inst; //!< Slave structure BMP180_oversampling OSS; //!< Oversampling BMP180_calib Calib; //!< Calibration values uint8_t ID; //!< Chip ID + float SeaLevelPressure; //!< Current atmospheric pressure at sea level } cfg; } BMP180_t; @@ -74,6 +74,13 @@ extern BMP180_t BMP180[I2C_BMP180_NB]; //!< BMP180 User structure /*** Procedures ***/ /******************/ +/*!\brief Setter of Sea Level pressure for BMP180 peripheral +** \weak BMP180 Sea Level pressure setter may be user implemented +** \param[in] pCpnt - Pointer to BMP180 component +** \return FctERR - error code +**/ +FctERR NONNULL__ BMP180_Set_SeaLevel_Pressure(BMP180_t * pCpnt); + /*!\brief Initialization Sequence for BMP180 peripheral ** \weak BMP180 Init sequence may be user implemented if custom initialization sequence needed ** \param[in] pCpnt - Pointer to BMP180 component @@ -92,10 +99,10 @@ FctERR NONNULL__ BMP180_Set_Oversampling(BMP180_t * pCpnt, const BMP180_oversamp /*!\brief Get calibration parameters from BMP180 peripheral ** \param[in] pCpnt - Pointer to BMP180 component -** \param[in,out] calib - pointer to calibration structure to read to +** \param[in,out] pCalib - pointer to calibration structure to read to ** \return FctERR - error code **/ -FctERR NONNULL__ BMP180_Get_Calibration(BMP180_t * pCpnt, BMP180_calib * calib); +FctERR NONNULL__ BMP180_Get_Calibration(BMP180_t * pCpnt, BMP180_calib * pCalib); /*!\brief Gets the compensated pressure level ** \param[in] pCpnt - Pointer to BMP180 component diff --git a/README.md b/README.md index f9629a5e6..bdd905681 100644 --- a/README.md +++ b/README.md @@ -110,9 +110,9 @@ Please keep in mind some components are somewhat custom and needs to be accesses ## Multiple component type on single project (singleton components excluded) - If multiple component type are used on the same MCU device: - - A for loop can be used passing I2C_$CPNT$ if all components are tied to the same i2C instance (physical bus) - - In case same multiple same component type are used on different physical busses, please pass each instance to $CPNT$_Init - - note: formerly, I2C_$CPNT$ was used to determine device type enabling (and its instance); as long as enabled with former I2C_$CPNT$, init function can be called using other params + - A for loop can be used passing I2C_$CPNT$ if all components are tied to the same i2C instance (physical bus) + - In case same multiple same component type are used on different physical busses, please pass each instance to $CPNT$_Init + - note: formerly, I2C_$CPNT$ was used to determine device type enabling (and its instance); as long as enabled with former I2C_$CPNT$, init function can be called using other params ## Following peripherals (?) @@ -135,7 +135,7 @@ You may also: ## TODO -- Any RTOS compatibility (blocking transactions doesn't match with RTOS task timings) +- Any RTOS compatibility (blocking transactions doesn't match with RTOS task timings) - (FreeRTOS) compatibility using R/W functions using interrupts with callbacks (when possible) ## Misc diff --git a/ReleaseNotes.md b/ReleaseNotes.md index e1a46b682..c680fed75 100644 --- a/ReleaseNotes.md +++ b/ReleaseNotes.md @@ -28,8 +28,8 @@ SOFTWARE. * strict aliasing types in printf statements * Some informations added in components doxygen documentation for compatibility with other devices * implementation of multiple peripheral of the same type (for devices that may most likely not being alone in a project) - - needed a few refactoring (inlines becoming functions / some typedefs moved) - - except: DS_GPMS, PCF8523 & DRV2605L + * needed a few refactoring (inlines becoming functions / some typedefs moved) + * except: DS_GPMS, PCF8523 & DRV2605L * template update (splitted into singleton/multiple device(s)) * more NONNULL__ checks * possibility to set different device base address than default set in header (at project level or in globals.h) @@ -54,14 +54,15 @@ SOFTWARE. * AT42QT1244: Few delay added between each communication for calibration pending in FHM setup procedure * AT42QT1244: weak handler & init implementations * BMP180: fixed temperature and pressure calculation (no use of unsigned LSHIFT or RSHIFT for signed calculations) +* BMP180: changes to allow (custom) intialization of sea level pressure for the component (relying on shared_APS module) * DRV2605: fixed init following timeout configuration using defaults from structure * FM24C: added possibility to customize FM24C_SIZE macro if needed * MCP4725: Fixed general call function (transmit command, not receive), and taking I2C_HandleTypeDef pointer as parameter * MTCH6102: 1ms delay between each transaction end has been added to ensure proper communication flow * MTCH6102: As MTCH6102 seems somewhat erratic on first try when it comes to configure it, I often get default RX/TX number values after config performed with custom projects: - - added a while loop to test if parameters are taken into account (it works well the second time if not on the first time)... - - I already encountered a lot of issues with this chip, related to timing between transactions, power on, etc... mostly when it comes to configure chip or store to NV storage - - As the Datasheet is very light about these subjects (and I got no answers from Microchip), here are some workarounds that seems to be working all the time (so far) + * added a while loop to test if parameters are taken into account (it works well the second time if not on the first time)... + * I already encountered a lot of issues with this chip, related to timing between transactions, power on, etc... mostly when it comes to configure chip or store to NV storage + * As the Datasheet is very light about these subjects (and I got no answers from Microchip), here are some workarounds that seems to be working all the time (so far) * MTCH6102: splitted init function into several functions (to also be able to store settings to Non-volatile storage) * MTCH6102: period calculation doesn't use floats anymore to save some flash space * MTCH6102: simplified MTCH6102_Get_MFG_Results @@ -80,6 +81,7 @@ SOFTWARE. * PCA9685: Refactoring and changes to handle PCA9685 outputs shifting (delay on) * PCA9685: Dedicated functions to compute PWM register values (useful to manually fill an array with registers values to send multiple channels at once in one I2C transaction) * README.md: updated (and added limitations section) +* shared_APS: added files for common code regarding ambient pressure sensing * shared_ALS: added files for common code regarding ambient light sensing * shared_CLS: added files for common code regarding color light sensing (RGB to chromacity CIE1931 xy conversion and CCT calculation) * shared_CLS: double precision now single precision @@ -105,7 +107,7 @@ SOFTWARE. * rationalization of includes * init sequence returns with error value if something goes wrong during initialization * implementation of slave instance (slave_inst) in xxx_proc files (easier debug access to component structure except special cases) -* I2C_component: instance inst becomes bus_inst (new slave_inst implementation in xxx_proc files to avoid name confusions) +* I2C_component: instance inst becomes bus_inst (new slave_inst implementation in xxx_proc files to avoid name confusions) * FM24C & MB85RC256V: added macros to create simple inlines to read/write values from addresses (to use in some header) * MTCH6102: added standalone function for commands & inlines for configuration, restore to defaults & tests commands * MTCH6102: added filtering type configuration function & inlines for configuration diff --git a/shared_ALS.c b/shared_ALS.c index db06a65e2..267b4a70b 100644 --- a/shared_ALS.c +++ b/shared_ALS.c @@ -6,8 +6,6 @@ /****************************************************************/ #include "shared_ALS.h" -#include "I2C_Component.h" - #if defined(HAL_I2C_MODULE_ENABLED) #if defined(I2C_APDS9930) || defined(I2C_TSL2591) /****************************************************************/ diff --git a/shared_ALS.h b/shared_ALS.h index eb2be3636..382689ca6 100644 --- a/shared_ALS.h +++ b/shared_ALS.h @@ -13,6 +13,8 @@ #include "sarmfsw.h" +#include "I2C_Component.h" + #if defined(HAL_I2C_MODULE_ENABLED) /****************************************************************/ diff --git a/shared_APS.c b/shared_APS.c new file mode 100644 index 000000000..3587f938b --- /dev/null +++ b/shared_APS.c @@ -0,0 +1,38 @@ +/*!\file shared_APS.c +** \author SMFSW +** \copyright MIT (c) 2017-2020, SMFSW +** \brief Ambient Pressure Sensing shared +**/ +/****************************************************************/ +#include "shared_APS.h" + +#if defined(HAL_I2C_MODULE_ENABLED) +#if defined(I2C_BMP180) || defined(I2C_BMP388) || defined(I2C_LPS35HW) +/****************************************************************/ +// std libs +#include +/****************************************************************/ + + +__WEAK float Get_SeaLevel_Pressure(void) +{ + return SEA_LEVEL_PRESSURE; +} + + +float Atmospheric_Pressure_To_Altitude(const float pressure, const float sea_level_pressure) +{ + return (44330.0f * (1.0f - pow(pressure / sea_level_pressure, (1.0f / 5.255f)))); +} + + +float Altitude_To_SeaLevel_Pressure(const float pressure, const float altitude) +{ + return (pressure / pow(1.0f - (altitude / 44330.0f), 5.255f)); +} + + +/****************************************************************/ +#endif +#endif +/****************************************************************/ diff --git a/shared_APS.h b/shared_APS.h new file mode 100644 index 000000000..993c4825b --- /dev/null +++ b/shared_APS.h @@ -0,0 +1,64 @@ +/*!\file shared_APS.h +** \author SMFSW +** \copyright MIT (c) 2017-2020, SMFSW +** \brief Ambient Pressure Sensing shared +**/ +/****************************************************************/ +#ifndef __SHARED_APS_H__ + #define __SHARED_APS_H__ + +#ifdef __cplusplus + extern "C" { +#endif + +#include "sarmfsw.h" + +#include "I2C_Component.h" + +#if defined(HAL_I2C_MODULE_ENABLED) +/****************************************************************/ + + +#ifndef SEA_LEVEL_PRESSURE +//! \note Define SEA_LEVEL_PRESSURE to use a custom average SeaLevel pressure value (hard value) +//! \note It is highly recommended to implement \ref Get_SeaLevel_Pressure function in project instead. +#define SEA_LEVEL_PRESSURE 1013.25f //!< Average sea level pressure (hPa / mbar) +#endif + + +/**!\brief Sea Level pressure getter (in hPa / mbar) +** \weak Get_SeaLevel_Pressure is recommended to be user implemented (average value returned by weak implementation) +** \return Sea Level pressure in hPa/mbar +**/ +float Get_SeaLevel_Pressure(void); + + +/**!\brief Calculates the altitude (in meters) using international barometric formula +** from the specified atmospheric pressure (in hPa / mbar), and sea-level pressure (in hPa / mbar). +** \note A pressure change of ∆p = 1hPa corresponds to 8.43m at sea level +** \param[in] pressure - Atmospheric pressure in hPa / mbar +** \param[in] sea_level_pressure - Sea Level pressure in hPa / mbar +** \return Altitude in meters +**/ +float Atmospheric_Pressure_To_Altitude(const float pressure, const float sea_level_pressure); + + +/**!\brief Calculates the pressure at sea level (in hPa / mbar) using international barometric formula +** from the specified altitude (in meters), and atmospheric pressure (in hPa / mbar). +** \note A difference in altitude of ∆altitude = 10m corresponds to 1.2hPa pressure change at sea level +** \param[in] pressure - Atmospheric pressure in hPa / mbar +** \param[in] altitude - Altitude in meters +** \return Sea Level pressure estimation in hPa / mbar +**/ +float Altitude_To_SeaLevel_Pressure(const float pressure, const float altitude); + + +/****************************************************************/ +#endif + +#ifdef __cplusplus + } +#endif + +#endif /* __SHARED_APS_H__ */ +/****************************************************************/ diff --git a/shared_CLS.c b/shared_CLS.c index a5ae33a69..deef282e2 100644 --- a/shared_CLS.c +++ b/shared_CLS.c @@ -6,8 +6,6 @@ /****************************************************************/ #include "shared_CLS.h" -#include "I2C_Component.h" - #if defined(HAL_I2C_MODULE_ENABLED) #if defined(I2C_APDS9960) || defined(I2C_S11059) || defined(I2C_TCS3400) || defined(I2C_TCS3472) /****************************************************************/ diff --git a/shared_CLS.h b/shared_CLS.h index 6e8249185..0875d4c32 100644 --- a/shared_CLS.h +++ b/shared_CLS.h @@ -13,6 +13,8 @@ #include "sarmfsw.h" +#include "I2C_Component.h" + #if defined(HAL_I2C_MODULE_ENABLED) /****************************************************************/