/** * @file BMP280_ATY.c * * @param Project DEVICE_GENERAL_ATY_LIB * * @author ATY * * @copyright * - Copyright 2017 - 2025 MZ-ATY * - This code follows: * - MZ-ATY Various Contents Joint Statement - * * https://mengze.top/MZ-ATY_VCJS * - CC 4.0 BY-NC-SA - * * https://creativecommons.org/licenses/by-nc-sa/4.0/ * - Your use will be deemed to have accepted the terms of this statement. * * @brief Familiar functions of BMP280 for all embedded device * * @version * - 1_01_220804 > ATY * -# Preliminary version, first Release * - 1_02_231229 > ATY * -# add multy addr and channel ******************************************************************************** */ #ifndef __BMP280_ATY_C #define __BMP280_ATY_C #include "BMP280_ATY.h" /******************************* For user *************************************/ /******************************************************************************/ /** * @brief BMP280 read id * @param addr chip address * @param channel chip channel * @return id number like 0x58, 0x88 */ uint8_t BMP280_ReadId(uint8_t addr, uint8_t channel) { uint8_t temp_uint8; I2C_ReadReg(addr, BMP280_CHIPID_REG, &temp_uint8, 1, channel); return temp_uint8; } /** * @brief BMP280 get status * @param status_flag the bit to get * @param addr chip address * @param channel chip channel * @return 1 or 0 at bit status_flag */ uint8_t BMP280_GetStatus(uint8_t status_flag, uint8_t addr, uint8_t channel) { uint8_t flag; I2C_ReadReg(addr, BMP280_STATUS_REG, &flag, 1, channel); // status_flag = BMP280_MEASURING || BMP280_IM_UPDATE // if(flag & status_flag) return 1; // else return 0; return flag & status_flag; } /** * @brief BMP280 set oversamp * @param config Oversample_Mode value * @param addr chip address * @param channel chip channel */ void BMP280_SetOversamp(BMP280_OVERSAMPLE_MODE* config, uint8_t addr, uint8_t channel) { uint8_t temp_uint8; temp_uint8 = ((config->T_Osample) << 5) | ((config->P_Osample) << 2) | ((config)->WORKMODE); I2C_WriteReg(addr, BMP280_CTRLMEAS_REG, &temp_uint8, 1, channel); } /** * @brief BMP280 set standby filter * @param config bmp280 config * @param addr chip address * @param channel chip channel */ void BMP280_SetStandbyFilter(BMP280_CONFIG* config, uint8_t addr, uint8_t channel) { uint8_t temp_uint8; temp_uint8 = ((config->T_SB) << 5) | ((config->FILTER_COEFFICIENT) << 2) | ((config->SPI_EN)); I2C_WriteReg(addr, BMP280_CONFIG_REG, &temp_uint8, 1, channel); } BMP280_S32_t t_fine; // Use to calc compensation #ifdef BMP280_USE_FIXED_POINT_COMPENSATE /** * @brief BMP280 calculate temperature compensate for signed 32 * @param adc_T temperature adc origin data * @return signed 32 value of temperature * @note Returns temperature in DegC, resolution is 0.01 DegC. Output value of "5123" equals 51.23 DegC. * t_fine carries fine temperature as global value */ BMP280_S32_t BM280_CompensateT(BMP280_S32_t adc_T) { BMP280_S32_t var1, var2, T; var1 = ((((adc_T >> 3) - ((BMP280_S32_t)bmp280Cc.T1 << 1))) * ((BMP280_S32_t)bmp280Cc.T2)) >> 11; var2 = (((((adc_T >> 4) - ((BMP280_S32_t)bmp280Cc.T1)) * ((adc_T >> 4) - ((BMP280_S32_t)bmp280Cc.T1))) >> 12) * ((BMP280_S32_t)bmp280Cc.T3)) >> 14; t_fine = var1 + var2; T = (t_fine * 5 + 128) >> 8; return T; } /** * @brief BMP280 calculate pressure compensate for unsigned 32 * @param adc_P pressure adc origin data * @return unsigned 32 value of pressure * @note Returns pressure in Pa as unsigned 32 bit integer in Q24.8 format (24 integer bits and 8 fractional bits). * Output value of "24674867" represents 24674867/256 = 96386.2 Pa = 963.862 hPa */ BMP280_U32_t BM280_CompensateP(BMP280_S32_t adc_P) { BMP280_S64_t var1, var2, p; var1 = ((BMP280_S64_t)t_fine) - 128000; var2 = var1 * var1 * (BMP280_S64_t)bmp280Cc.P6; var2 = var2 + ((var1 * (BMP280_S64_t)bmp280Cc.P5) << 17); var2 = var2 + (((BMP280_S64_t)bmp280Cc.P4) << 35); var1 = ((var1 * var1 * (BMP280_S64_t)bmp280Cc.P3) >> 8) + ((var1 * (BMP280_S64_t)bmp280Cc.P2) << 12); var1 = (((((BMP280_S64_t)1) << 47) + var1)) * ((BMP280_S64_t)bmp280Cc.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 = (((BMP280_S64_t)bmp280Cc.P9) * (p >> 13) * (p >> 13)) >> 25; var2 = (((BMP280_S64_t)bmp280Cc.P8) * p) >> 19; p = ((p + var1 + var2) >> 8) + (((BMP280_S64_t)bmp280Cc.P7) << 4); return (BMP280_U32_t)p; } /** * @brief BMP280 get temperature * @param addr chip address * @param channel chip channel * @return temperature real value in int32 */ BMP280_S32_t BMP280_GetTemperature(uint8_t addr, uint8_t channel) { uint8_t xlsb, lsb, msb; long signed bit32; BMP280_S32_t temperature; I2C_ReadReg(addr, BMP280_TEMPERATURE_XLSB_REG, &xlsb, 1, channel); I2C_ReadReg(addr, BMP280_TEMPERATURE_LSB_REG, &lsb, 1, channel); I2C_ReadReg(addr, BMP280_TEMPERATURE_MSB_REG, &msb, 1, channel); // The value of a register forming a floating point number bit32 = ((long)(msb << 12)) | ((long)(lsb << 4)) | (xlsb >> 4); temperature = BM280_CompensateT(bit32); return temperature; } /** * @brief BMP280 get pressure * @param addr chip address * @param channel chip channel * @return pressure real value in uint32 */ BMP280_U32_t BMP280_GetPressure(uint8_t addr, uint8_t channel) { uint8_t xlsb, lsb, msb; long signed bit32; BMP280_U32_t pressure; I2C_ReadReg(addr, BMP280_PRESSURE_XLSB_REG, &xlsb, 1); I2C_ReadReg(addr, BMP280_PRESSURE_LSB_REG, &lsb, 1); I2C_ReadReg(addr, BMP280_PRESSURE_MSB_REG, &msb, 1); // The value of a register forming a floating point number bit32 = ((long)(msb << 12)) | ((long)(lsb << 4)) | (xlsb >> 4); pressure = BM280_CompensateP(bit32); return pressure; } #else /** * @brief BMP280 calculate temperature compensate for double * @param adc_T temperature adc origin data * @return double value of temperature * @note Returns temperature in DegC, double precision. Output value of "51.23" equals 51.23 DegC. * t_fine carries fine temperature as global value */ double BM280_CompensateT(BMP280_S32_t adc_T) { double var1, var2, T; var1 = (((double)adc_T) / 16384.0 - ((double)bmp280Cc.T1) / 1024.0) * ((double)bmp280Cc.T2); var2 = ((((double)adc_T) / 131072.0 - ((double)bmp280Cc.T1) / 8192.0) * (((double)adc_T) / 131072.0 - ((double)bmp280Cc.T1) / 8192.0)) * ((double)bmp280Cc.T3); t_fine = (BMP280_S32_t)(var1 + var2); T = (var1 + var2) / 5120.0; return T; } /** * @brief BMP280 calculate pressure compensate for double * @param adc_P pressure adc origin data * @return double value of pressure * @note Returns pressure in Pa as double. Output value of "96386.2" equals 96386.2 Pa = 963.862 hPa */ double BM280_CompensateP(BMP280_S32_t adc_P) { double var1, var2, p; var1 = ((double)t_fine / 2.0) - 64000.0; var2 = var1 * var1 * ((double)bmp280Cc.P6) / 32768.0; var2 = var2 + var1 * ((double)bmp280Cc.P5) * 2.0; var2 = (var2 / 4.0) + (((double)bmp280Cc.P4) * 65536.0); var1 = (((double)bmp280Cc.P3) * var1 * var1 / 524288.0 + ((double)bmp280Cc.P2) * var1) / 524288.0; var1 = (1.0 + var1 / 32768.0) * ((double)bmp280Cc.P1); if(var1 == 0.0) { return 0; // avoid exception caused by division by zero } p = 1048576.0 - (double)adc_P; p = (p - (var2 / 4096.0)) * 6250.0 / var1; var1 = ((double)bmp280Cc.P9) * p * p / 2147483648.0; var2 = p * ((double)bmp280Cc.P8) / 32768.0; p = p + (var1 + var2 + ((double)bmp280Cc.P7)) / 16.0; return p; } /** * @brief BMP280 get temperature * @param addr chip address * @param channel chip channel * @return temperature real value in double */ double BMP280_GetTemperature(uint8_t addr, uint8_t channel) { uint8_t xlsb, lsb, msb; long signed bit32; double temperature; I2C_ReadReg(addr, BMP280_TEMPERATURE_XLSB_REG, &xlsb, 1, channel); I2C_ReadReg(addr, BMP280_TEMPERATURE_LSB_REG, &lsb, 1, channel); I2C_ReadReg(addr, BMP280_TEMPERATURE_MSB_REG, &msb, 1, channel); // The value of a register forming a floating point number bit32 = ((long)(msb << 12)) | ((long)(lsb << 4)) | (xlsb >> 4); temperature = BM280_CompensateT(bit32); return temperature; } /** * @brief BMP280 get pressure * @param addr chip address * @param channel chip channel * @return pressure real value in double */ double BMP280_GetPressure(uint8_t addr, uint8_t channel) { uint8_t xlsb, lsb, msb; long signed bit32; double pressure; I2C_ReadReg(addr, BMP280_PRESSURE_XLSB_REG, &xlsb, 1, channel); I2C_ReadReg(addr, BMP280_PRESSURE_LSB_REG, &lsb, 1, channel); I2C_ReadReg(addr, BMP280_PRESSURE_MSB_REG, &msb, 1, channel); // The value of a register forming a floating point number bit32 = ((long)(msb << 12)) | ((long)(lsb << 4)) | (xlsb >> 4); pressure = BM280_CompensateP(bit32); return pressure; } #endif struct _BMP280_CC bmp280Cc = {0}; // Used to store the compensation parameters stored in the chip ROM /** * @brief BMP280 initialize * @param addr chip address * @param channel chip channel */ void BM280_Init(uint8_t addr, uint8_t channel) { uint8_t lsb, msb; // Correction value of the temperature I2C_ReadReg(addr, BMP280_DIG_T1_LSB_REG, &lsb, 1, channel); I2C_ReadReg(addr, BMP280_DIG_T1_MSB_REG, &msb, 1, channel); bmp280Cc.T1 = (((uint16_t)msb) << 8) + lsb; I2C_ReadReg(addr, BMP280_DIG_T2_LSB_REG, &lsb, 1, channel); I2C_ReadReg(addr, BMP280_DIG_T2_MSB_REG, &msb, 1, channel); bmp280Cc.T2 = (((uint16_t)msb) << 8) + lsb; I2C_ReadReg(addr, BMP280_DIG_T3_LSB_REG, &lsb, 1, channel); I2C_ReadReg(addr, BMP280_DIG_T3_MSB_REG, &msb, 1, channel); bmp280Cc.T3 = (((uint16_t)msb) << 8) + lsb; // Correction value of the pressure I2C_ReadReg(addr, BMP280_DIG_P1_LSB_REG, &lsb, 1, channel); I2C_ReadReg(addr, BMP280_DIG_P1_MSB_REG, &msb, 1, channel); bmp280Cc.P1 = (((uint16_t)msb) << 8) + lsb; I2C_ReadReg(addr, BMP280_DIG_P2_LSB_REG, &lsb, 1, channel); I2C_ReadReg(addr, BMP280_DIG_P2_MSB_REG, &msb, 1, channel); bmp280Cc.P2 = (((uint16_t)msb) << 8) + lsb; I2C_ReadReg(addr, BMP280_DIG_P3_LSB_REG, &lsb, 1, channel); I2C_ReadReg(addr, BMP280_DIG_P3_MSB_REG, &msb, 1, channel); bmp280Cc.P3 = (((uint16_t)msb) << 8) + lsb; I2C_ReadReg(addr, BMP280_DIG_P4_LSB_REG, &lsb, 1, channel); I2C_ReadReg(addr, BMP280_DIG_P4_MSB_REG, &msb, 1, channel); bmp280Cc.P4 = (((uint16_t)msb) << 8) + lsb; I2C_ReadReg(addr, BMP280_DIG_P5_LSB_REG, &lsb, 1, channel); I2C_ReadReg(addr, BMP280_DIG_P5_MSB_REG, &msb, 1, channel); bmp280Cc.P5 = (((uint16_t)msb) << 8) + lsb; I2C_ReadReg(addr, BMP280_DIG_P6_LSB_REG, &lsb, 1, channel); I2C_ReadReg(addr, BMP280_DIG_P6_MSB_REG, &msb, 1, channel); bmp280Cc.P6 = (((uint16_t)msb) << 8) + lsb; I2C_ReadReg(addr, BMP280_DIG_P7_LSB_REG, &lsb, 1, channel); I2C_ReadReg(addr, BMP280_DIG_P7_MSB_REG, &msb, 1, channel); bmp280Cc.P7 = (((uint16_t)msb) << 8) + lsb; I2C_ReadReg(addr, BMP280_DIG_P8_LSB_REG, &lsb, 1, channel); I2C_ReadReg(addr, BMP280_DIG_P8_MSB_REG, &msb, 1, channel); bmp280Cc.P8 = (((uint16_t)msb) << 8) + lsb; I2C_ReadReg(addr, BMP280_DIG_P9_LSB_REG, &lsb, 1, channel); I2C_ReadReg(addr, BMP280_DIG_P9_MSB_REG, &msb, 1, channel); bmp280Cc.P9 = (((uint16_t)msb) << 8) + lsb; // Reset I2C_WriteReg(addr, BMP280_RESET_REG, (uint8_t*)BMP280_RESET_VALUE, 1, channel); BMP280_OVERSAMPLE_MODE BMP_OVERSAMPLE_MODEStructure; BMP_OVERSAMPLE_MODEStructure.P_Osample = BMP280_P_MODE_3; BMP_OVERSAMPLE_MODEStructure.T_Osample = BMP280_T_MODE_1; BMP_OVERSAMPLE_MODEStructure.WORKMODE = BMP280_NORMAL_MODE; BMP280_SetOversamp(&BMP_OVERSAMPLE_MODEStructure, addr, channel); BMP280_CONFIG BMP_CONFIGStructure; BMP_CONFIGStructure.T_SB = BMP280_T_SB1; BMP_CONFIGStructure.FILTER_COEFFICIENT = BMP280_FILTER_MODE_4; BMP_CONFIGStructure.SPI_EN = DISABLE; BMP280_SetStandbyFilter(&BMP_CONFIGStructure, addr, channel); } /** * @brief BMP280 check whether the device exists * @param addr chip address * @param channel chip channel * @return errCode, 0: success, !0: error */ uint8_t BMP280_InitAndCheck(uint8_t addr, uint8_t channel) { BM280_Init(addr, channel); DelayMs(50); uint8_t bmp280Id = 0; bmp280Id = BMP280_ReadId(addr, channel); #ifdef __DEBUG_BMP280_ATY printf("\r\nBMP280 ID: 0x%02X", bmp280Id); #endif /* __DEBUG_BMP280_ATY */ // 0xD8 = 0x88 | 0x58 if(bmp280Id & 0xD8 != 0xD8) return 1; DelayMs(200); return 0; } /** * @brief BMP280 read temperature and pressure * @param tp data group to save tp value * @param addr chip address * @param channel chip channel * @return errCode, 0: success, !0: error */ uint8_t BMP280_ReadTP(double* tp, uint8_t addr, uint8_t channel) { uint16_t errCount = 1000; for(uint8_t i = 0; i < errCount; i++) { if(BMP280_GetStatus(BMP280_MEASURING, addr, channel) == 0) break; if(i == errCount - 1) return 2; DelayMs(1); } for(uint8_t i = 0; i < errCount; i++) { if(BMP280_GetStatus(BMP280_IM_UPDATE, addr, channel) == 0) break; if(i == errCount - 1) return 3; DelayMs(1); } tp[0] = BMP280_GetTemperature(addr, channel); tp[1] = BMP280_GetPressure(addr, channel); return 0; } uint8_t bmp280InitFlag = 0; /** * @brief BMP280 get data flow path * @param tp data group to save tp value * @param addr chip address * @param channel chip channel * @return errCode, 0: success, !0: error */ uint8_t BMP280_TempPreGet(uint16_t* tp, uint8_t addr, uint8_t channel) { double bmp280Value[2]; if(bmp280InitFlag == 0) { if(BMP280_InitAndCheck(addr, channel)) { bmp280InitFlag = 2; } else { bmp280InitFlag = 1; } } if(bmp280InitFlag == 2) { #ifdef __DEBUG_BMP280_ATY printf("\r\nDevice wrong!"); #endif /* __DEBUG_BMP280_ATY */ return 0xFF; } else if(bmp280InitFlag == 1) { uint8_t errValue = 0; errValue = BMP280_ReadTP(bmp280Value, addr, channel); if(errValue == 0) { if(bmp280Value[1] != 0) { tp[0] = ((bmp280Value[0] * 1000) + 5) / 10; tp[1] = (((bmp280Value[1] - 90000) * 10) + 5) / 10; #ifdef __DEBUG_BMP280_ATY printf("\r\nTemperature %f C Pressure %f Pa", bmp280Value[0], bmp280Value[1]); #endif /* __DEBUG_BMP280_ATY */ } else { #ifdef __DEBUG_BMP280_ATY printf("\r\nData get wrong!"); #endif /* __DEBUG_AHT20_ATY */ return 0xFD; } } else { #ifdef __DEBUG_BMP280_ATY printf("\r\nBMP280 timeout! Err: %d", errValue); #endif /* __DEBUG_BMP280_ATY */ return 0xFE; } } return 0; } // todo: not tested #endif /* __BMP280_ATY_C */ /******************************** End Of File *********************************/