All Articles

BMP085 STM32 HAL Library

For the last month I’m working on my personal project which needs Barometric sensor. As you know I prefer to use STM32 MCUs for all my devices because STMicroelectronics has the highest level of developers support and their products have a long-term support.

I decided to use the cheapest STM32F0 MCU and tried to find a library for BMP085 sensor with HAL usage. Unfortunately, there was no suitable search results for me (most of existing libraries were written on C, without using C++ futures), so I was needed to write my own library.

I searched for a datasheet for BMP085 and started to write the library based on the given information about I2C communication requirements.

First of all, I defined the address of the device:

#define BMP_ADDR			0x77

Then I defined all calibration coefficients given:

#define BMP_RA_AC1_H		(uint8_t*)(0xAA)    /* AC1_H */
#define BMP_RA_AC1_L		(uint8_t*)(0xAB)    /* AC1_L */
#define BMP_RA_AC2_H		(uint8_t*)(0xAC)    /* AC2_H */
#define BMP_RA_AC2_L		(uint8_t*)(0xAD)    /* AC2_L */
#define BMP_RA_AC3_H		(uint8_t*)(0xAE)    /* AC3_H */
#define BMP_RA_AC3_L		(uint8_t*)(0xAF)    /* AC3_L */
#define BMP_RA_AC4_H		(uint8_t*)(0xB0)    /* AC4_H */
#define BMP_RA_AC4_L		(uint8_t*)(0xB1)    /* AC4_L */
#define BMP_RA_AC5_H		(uint8_t*)(0xB2)    /* AC5_H */
#define BMP_RA_AC5_L		(uint8_t*)(0xB3)    /* AC5_L */
#define BMP_RA_AC6_H		(uint8_t*)(0xB4)    /* AC6_H */
#define BMP_RA_AC6_L		(uint8_t*)(0xB5)    /* AC6_L */
#define BMP_RA_B1_H			(uint8_t*)(0xB6)    /* B1_H */
#define BMP_RA_B1_L			(uint8_t*)(0xB7)    /* B1_L */
#define BMP_RA_B2_H			(uint8_t*)(0xB8)    /* B2_H */
#define BMP_RA_B2_L			(uint8_t*)(0xB9)    /* B2_L */
#define BMP_RA_MB_H      	(uint8_t*)(0xBA)    /* MB_H */
#define BMP_RA_MB_L      	(uint8_t*)(0xBB)    /* MB_L */
#define BMP_RA_MC_H      	(uint8_t*)(0xBC)    /* MC_H */
#define BMP_RA_MC_L      	(uint8_t*)(0xBD)    /* MC_L */
#define BMP_RA_MD_H      	(uint8_t*)(0xBE)    /* MD_H */
#define BMP_RA_MD_L      	(uint8_t*)(0xBF)    /* MD_L */

MSB, LSB, control register address and modes:

#define BMP_RA_CONTROL   	(uint8_t*)(0xF4)    /* CONTROL */
#define BMP_RA_CONTROL_16   (uint16_t)(0xF4)    /* CONTROL */
#define BMP_RA_MSB       	(uint8_t*)(0xF6)    /* MSB */
#define BMP_RA_LSB       	(uint8_t*)(0xF7)    /* LSB */
#define BMP_RA_XLSB      	(uint8_t*)(0xF8)    /* XLSB */

#define BMP_MODE_TEMPERATURE     0x2E
#define BMP_MODE_PRESSURE_0      0x34
#define BMP_MODE_PRESSURE_1      0x74
#define BMP_MODE_PRESSURE_2      0xB4
#define BMP_MODE_PRESSURE_3      0xF4

Then I implemented all methods for I2C communication and Temperature/Pressure/Altitude calculation (by the formulas given in the datasheet). I want to show you most complicated of them here :

Calibration coefficients loading:

void BMP085::loadCalibration() {
    uint8_t buf2[22];
    HAL_I2C_Master_Transmit(_hi2c, _devAddr, BMP_RA_AC1_H, 1, BMP_TIMEOUT);
    HAL_I2C_Master_Receive(_hi2c, _devAddr, buf2, 22, BMP_TIMEOUT);
    _ac1 = ((int16_t)buf2[0] << 8) + buf2[1];
    _ac2 = ((int16_t)buf2[2] << 8) + buf2[3];
    _ac3 = ((int16_t)buf2[4] << 8) + buf2[5];
    _ac4 = ((uint16_t)buf2[6] << 8) + buf2[7];
    _ac5 = ((uint16_t)buf2[8] << 8) + buf2[9];
    _ac6 = ((uint16_t)buf2[10] << 8) + buf2[11];
    _b1 = ((int16_t)buf2[12] << 8) + buf2[13];
    _b2 = ((int16_t)buf2[14] << 8) + buf2[15];
    _mb = ((int16_t)buf2[16] << 8) + buf2[17];
    _mc = ((int16_t)buf2[18] << 8) + buf2[19];
    _md = ((int16_t)buf2[20] << 8) + buf2[21];
    _calibrationLoaded = true;
}

Temperature calculation:

float BMP085::getTemperatureC() {
	int32_t ut = getRawTemperature();
	int32_t x1 = ((ut - (int32_t)_ac6) * (int32_t)_ac5) >> 15;
	int32_t x2 = ((int32_t)_mc << 11) / (x1 + (int32_t)_md);
	_b5 = x1 + x2;
	return (float)((_b5 + 8) >> 4);
}

Pressure calculation:

float BMP085::getPressure() {
	uint32_t up = getRawPressure();
	uint8_t oss = (_measureMode & 0xC0) >> 6;
	int32_t p;
	int32_t b6 = _b5 - 4000;
	int32_t x1 = ((int32_t)_b2 * ((b6 * b6) >> 12)) >> 11;
	int32_t x2 = ((int32_t)_ac2 * b6) >> 11;
	int32_t x3 = x1 + x2;
	int32_t b3 = ((((int32_t)_ac1 * 4 + x3) << oss) + 2) >> 2;
	x1 = ((int32_t)_ac3 * b6) >> 13;
	x2 = ((int32_t)_b1 * ((b6 * b6) >> 12)) >> 16;
	x3 = ((x1 + x2) +2) >> 2;
	uint32_t b4 = ((uint32_t)_ac4 * (uint32_t)(x3 + 32768)) >> 15;
	uint32_t b7 = ((uint32_t)up - b3) * (uint32_t)(50000 >> oss);
	if (b7 < 0x80000000)
		p = (b7 << 1) / b4;
	else
		p = (b7 / b4) << 1;
	x1 = (p >> 8) * (p >> 8);
	x1 = (x1 * 3038) >> 16;
	x2 = (-7357 * p) >> 16;
	return p + ((x1 + x2 + (int32_t)3791) >> 4);
}

Altitude calculation:

float BMP085::getAltitude(float pressure, float seaLevelPressure) {
	if (seaLevelPressure == 0)
		seaLevelPressure = 101325;
	return 44330 * (1.0 - pow(pressure / seaLevelPressure, 0.1903));
}

I hope this information will be useful for you and will help you to understand how to create simple DIY prototypes of embedded systems for yourself or how to use BMP085 on your professional projects.