/**
* @file BMP280_ATY.c
*
* @param Project DEVICE_GENERAL_ATY_LIB
*
* @author ATY
*
* @copyright
* - Copyright 2017 - 2026 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 functions of BMP280 for C platform
*
* @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 *********************************/