#ifndef BMP180_H #define BMP180_H #include #include #include "pico/stdio.h" #include "pico/stdlib.h" #include "hardware/i2c.h" #define BMP_REG_CONTROL 0xF4 #define BMP_REG_RESULT 0xF6 #define BMP_COM_TEMP 0x2E #define BMP_COM_PRES 0x34 #define BMP_CALIB_COEFF_LEN 0x16 #define BMP_CALIB_COEFF_REG 0xAA #define BMP_CHIP_ID_REG 0xD0 #define BMP_CHIP_ID_VAL 0x55 #define BMP_MIN_TEMP_THRESHOLD -40 #define BMP_MAX_TEMP_THRESHOLD +85 #define BMP_TEMP_DELAY 5 #define ASSERT_OK(X) { if (X == false) return false; }; typedef struct { int16_t AC1; int16_t AC2; int16_t AC3; uint16_t AC4; uint16_t AC5; uint16_t AC6; int16_t B1; int16_t B2; int16_t MB; int16_t MC; int16_t MD; } bmp_calib_param_t; typedef struct { int addr; int rate; int scl; int sda; i2c_inst_t* inst; } i2c_t; typedef struct { i2c_t i2c; uint8_t oss; float temperature; int32_t pressure; float altitude; bmp_calib_param_t calib; int32_t B5; } bmp_t; const uint32_t oss_delay[] = {5, 8, 14, 26}; bool bmp_check_chip_id(bmp_t* bmp) { uint8_t chip_id_reg = BMP_CHIP_ID_REG; uint8_t chip_id_val[1]; i2c_write_blocking(bmp->i2c.inst, bmp->i2c.addr, &chip_id_reg, 1, true); i2c_read_blocking(bmp->i2c.inst, bmp->i2c.addr, chip_id_val, 1, false); if (chip_id_val[0] != BMP_CHIP_ID_VAL) { #ifdef DEBUG printf("Returned Chip ID: 0x%02x\n", chip_id_val[0]); printf("Check your I2C configuration and connection.\n"); #endif return false; } return true; } bool bmp_get_calib_coeffs(bmp_t* bmp) { uint8_t calib_coeffs_reg = BMP_CALIB_COEFF_REG; uint8_t calib_coeffs_val[BMP_CALIB_COEFF_LEN]; i2c_write_blocking(bmp->i2c.inst, bmp->i2c.addr, &calib_coeffs_reg, 1, true); i2c_read_blocking(bmp->i2c.inst, bmp->i2c.addr, calib_coeffs_val, BMP_CALIB_COEFF_LEN, false); int16_t* data = (int16_t*)&bmp->calib; for (int i = 0, j = 1; i < BMP_CALIB_COEFF_LEN / 2; i++, j += 2) { data[i] = (calib_coeffs_val[i * 2] << 8) | calib_coeffs_val[j]; if ((data[i] == 0) | (data[i] == -1)) { #ifdef DEBUG printf("Calibation data invalid.\n"); #endif return false; } } #ifdef DEBUG printf("==== CALIBRATION COEFFS ====\n"); printf("AC1: %d\n", bmp->calib.AC1); printf("AC2: %d\n", bmp->calib.AC2); printf("AC3: %d\n", bmp->calib.AC3); printf("AC4: %d\n", bmp->calib.AC4); printf("AC5: %d\n", bmp->calib.AC5); printf("AC6: %d\n", bmp->calib.AC6); printf("B1: %d\n", bmp->calib.B1); printf("B2: %d\n", bmp->calib.B2); printf("MB: %d\n", bmp->calib.MB); printf("MC: %d\n", bmp->calib.MC); printf("MD: %d\n", bmp->calib.MD); printf("============================\n"); #endif return true; } uint32_t bmp_start_temperature(bmp_t* bmp) { uint8_t temp_reg[] = { BMP_REG_CONTROL, BMP_COM_TEMP }; i2c_write_blocking(bmp->i2c.inst, bmp->i2c.addr, temp_reg, 2, false); return BMP_TEMP_DELAY; } bool bmp_read_temperature(bmp_t* bmp) { uint8_t temp_reg = BMP_REG_RESULT; uint8_t temp_val[2]; i2c_write_blocking(bmp->i2c.inst, bmp->i2c.addr, &temp_reg, 1, true); if (i2c_read_blocking(bmp->i2c.inst, bmp->i2c.addr, temp_val, 2, false) != 2) { #ifdef DEBUG printf("Wrong read length.\n"); #endif return false; } int16_t UT = (temp_val[0] << 8) + temp_val[1]; if (UT == (int16_t)0x8000) { #ifdef DEBUG printf("Non-initialized register detected.\n"); #endif return false; } int32_t X1 = (((int32_t)UT - bmp->calib.AC6) * bmp->calib.AC5) >> 15; int32_t X2 = (bmp->calib.MC << 11) / (X1 + bmp->calib.MD); bmp->B5 = X1 + X2; float temp = ((bmp->B5 + 8) >> 4) * 0.1f; if ((temp <= BMP_MIN_TEMP_THRESHOLD) || (temp >= BMP_MAX_TEMP_THRESHOLD)) { #ifdef DEBUG printf("Temperature beyond threshold: %f\n", temp); #endif return false; } bmp->temperature = temp; return true; } uint32_t bmp_start_pressure(bmp_t* bmp) { uint8_t pres_reg[] = { BMP_REG_CONTROL, BMP_COM_PRES + (bmp->oss << 6) }; i2c_write_blocking(bmp->i2c.inst, bmp->i2c.addr, pres_reg, 2, false); return oss_delay[bmp->oss]; } bool bmp_read_pressure(bmp_t* bmp) { uint8_t pres_reg = BMP_REG_RESULT; uint8_t pres_val[3]; i2c_write_blocking(bmp->i2c.inst, bmp->i2c.addr, &pres_reg, 1, true); if (i2c_read_blocking(bmp->i2c.inst, bmp->i2c.addr, pres_val, 3, false) != 3) { #ifdef DEBUG printf("Wrong read length.\n"); #endif return false; } int32_t UP = ((pres_val[0] << 16) + (pres_val[1] << 8) + pres_val[2]) >> (8 - bmp->oss); int32_t X1, X2, X3, B3, B6, p = 0; uint32_t B4, B7 = 0; B6 = bmp->B5 - 4000; X1 = (bmp->calib.B2 * ((B6 * B6) >> 12)) >> 11; X2 = (bmp->calib.AC2 * B6) >> 11; X3 = X1 + X2; B3 = (((bmp->calib.AC1 * 4 + X3) << bmp->oss) + 2) / 4; X1 = (bmp->calib.AC3 * B6) >> 13; X2 = (bmp->calib.B1 * ((B6 * B6) >> 12)) >> 16; X3 = ((X1 + X2) + 2) >> 2; B4 = (bmp->calib.AC4 * (uint32_t)(X3 + 32768)) >> 15; B7 = ((uint32_t)(UP) - B3) * (50000 >> bmp->oss); if (B7 < 0x80000000) { p = (B7 * 2) / B4; } else { p = (B7 / B4) * 2; } X1 = (p >> 8) * (p >> 8); X1 = (X1 * 3038) >> 16; X2 = (-7357 * p) >> 16; bmp->pressure = p + ((X1 + X2 + 3791) >> 4); return true; } bool bmp_get_temperature(bmp_t* bmp) { sleep_ms(bmp_start_temperature(bmp)); return bmp_read_temperature(bmp); } // User must call bmp_get_temperature() before calling this method. // Or use the combo bmp_get_pressure_temperature(). bool bmp_get_pressure(bmp_t* bmp) { sleep_ms(bmp_start_pressure(bmp)); return bmp_read_pressure(bmp); } bool bmp_get_pressure_temperature(bmp_t* bmp) { bool res = true; res &= bmp_get_temperature(bmp); res &= bmp_get_pressure(bmp); return res; } bool bmp_init(bmp_t* bmp) { i2c_init(bmp->i2c.inst, bmp->i2c.rate); if (bmp->oss < 0 || bmp->oss > 3) { #ifdef DEBUG printf("Invalid over-sampling rate (%d). Valid 0 to 3.\n", bmp->oss); #endif return false; } gpio_set_function(bmp->i2c.scl, GPIO_FUNC_I2C); gpio_set_function(bmp->i2c.sda, GPIO_FUNC_I2C); gpio_pull_up(bmp->i2c.scl); gpio_pull_up(bmp->i2c.sda); sleep_ms(100); ASSERT_OK(bmp_check_chip_id(bmp)); ASSERT_OK(bmp_get_calib_coeffs(bmp)); return true; } #endif