Add BMP390 support.

pull/3/head
Luigi F. Cruz 2023-01-23 23:36:21 -08:00
rodzic 2e5e1ebff0
commit b16339cca5
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: CEB689C74D220F93
4 zmienionych plików z 224 dodań i 154 usunięć

Wyświetl plik

@ -4,7 +4,7 @@ project(pico-barometer)
add_executable(barometer test.c) add_executable(barometer test.c)
target_link_libraries(barometer LINK_PUBLIC bmp180) target_link_libraries(barometer LINK_PUBLIC bmp390)
pico_add_extra_outputs(barometer) pico_add_extra_outputs(barometer)

Wyświetl plik

@ -1,21 +1,20 @@
#include <stdio.h> #include <stdio.h>
#define DEBUG #define DEBUG
#include "bmp180.h" #include "bmp390.h"
int main() { int main() {
stdio_init_all(); stdio_init_all();
sleep_ms(5000);
getchar();
printf("Hello from Pi Pico!\n"); printf("Hello from Pi Pico!\n");
bmp_t bmp; bmp_t bmp;
bmp.oss = 0; bmp.oss = 5;
bmp.i2c.addr = 0x77; bmp.i2c.addr = 0x77;
bmp.i2c.inst = i2c1; bmp.i2c.inst = i2c1;
bmp.i2c.rate = 400000; bmp.i2c.rate = 400000;
bmp.i2c.scl = 27; bmp.i2c.scl = 3;
bmp.i2c.sda = 26; bmp.i2c.sda = 2;
if (!bmp_init(&bmp)) if (!bmp_init(&bmp))
return 1; return 1;
@ -23,9 +22,10 @@ int main() {
while (true) { while (true) {
if (!bmp_get_pressure_temperature(&bmp)) if (!bmp_get_pressure_temperature(&bmp))
return 1; return 1;
printf("BMP180 Temperature (C): %f\n", bmp.temperature); printf("---------------------------------------------\n");
printf("BMP180 Pressure (hPa): %f\n", (float)bmp.pressure / 100.0); printf("Temperature (ºC): %f\n", bmp.temperature);
sleep_ms(250); printf("Pressure (hPa): %f\n", bmp.pressure);
printf("Altitude (m): %f\n", bmp.altitude);
} }
printf("Bye from pico!\n\n"); printf("Bye from pico!\n\n");

Wyświetl plik

@ -49,6 +49,7 @@ typedef struct {
uint8_t oss; uint8_t oss;
float temperature; float temperature;
int32_t pressure; int32_t pressure;
float altitude;
bmp_calib_param_t calib; bmp_calib_param_t calib;
int32_t B5; int32_t B5;
} bmp_t; } bmp_t;

Wyświetl plik

@ -1,6 +1,7 @@
#ifndef BMP390_H #ifndef BMP390_H
#define BMP390_H #define BMP390_H
#include <math.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
@ -8,34 +9,55 @@
#include "pico/stdlib.h" #include "pico/stdlib.h"
#include "hardware/i2c.h" #include "hardware/i2c.h"
#define BMP_REG_CONTROL 0xF4 #define BMP_RESET_REG 0x7E
#define BMP_REG_RESULT 0xF6 #define BMP_RESET_VAL 0xB6
#define BMP_COM_TEMP 0x2E #define BMP_SENSOR_ID_REG 0x00
#define BMP_COM_PRES 0x34 #define BMP_SENSOR_ID_VAL 0x60
#define BMP_CALIB_COEFF_LEN 0x16 #define BMP_OSR_REG 0x1C
#define BMP_CALIB_COEFF_REG 0xAA #define BMP_CAL_REG 0x31
#define BMP_CHIP_ID_REG 0xD0 #define BMP_CAL_LEN 0x15
#define BMP_CHIP_ID_VAL 0x55 #define BMP_TEMPERATURE_REG 0x07
#define BMP_MIN_TEMP_THRESHOLD -40 #define BMP_PRESSURE_REG 0x04
#define BMP_MAX_TEMP_THRESHOLD +85 #define BMP_MODE_REG 0x1B
#define BMP_TEMP_DELAY 5 #define BMP_MODE_VAL 0x13 // Forced Mode, Temp Enable, Pressure Enable
#define BMP_STATUS_REG 0x03
#define ASSERT_OK(X) { if (X == false) return false; }; #define ASSERT_OK(X) { if (X == false) return false; };
typedef struct { typedef struct {
int16_t AC1; uint16_t T1;
int16_t AC2; uint16_t T2;
int16_t AC3; int8_t T3;
uint16_t AC4; int16_t P1;
uint16_t AC5; int16_t P2;
uint16_t AC6; int8_t P3;
int16_t B1; int8_t P4;
int16_t B2; uint16_t P5;
int16_t MB; uint16_t P6;
int16_t MC; int8_t P7;
int16_t MD; int8_t P8;
int16_t P9;
int8_t P10;
int8_t P11;
} bmp_calib_param_t; } bmp_calib_param_t;
typedef struct {
double T1;
double T2;
double T3;
double P1;
double P2;
double P3;
double P4;
double P5;
double P6;
double P7;
double P8;
double P9;
double P10;
double P11;
} bmp_calib_part_param_t;
typedef struct { typedef struct {
int addr; int addr;
int rate; int rate;
@ -48,21 +70,37 @@ typedef struct {
i2c_t i2c; i2c_t i2c;
uint8_t oss; uint8_t oss;
float temperature; float temperature;
int32_t pressure; float pressure;
float altitude;
bmp_calib_param_t calib; bmp_calib_param_t calib;
int32_t B5; bmp_calib_part_param_t calib_part;
} bmp_t; } bmp_t;
const uint32_t oss_delay[] = {5, 8, 14, 26}; bool bmp_reset(bmp_t* bmp) {
uint8_t data_buffer[] = {
BMP_RESET_REG,
BMP_RESET_VAL
};
i2c_write_blocking(bmp->i2c.inst, bmp->i2c.addr, data_buffer, 2, false);
#ifdef DEBUG
printf("INFO: Successfully reset sensor.\n");
#endif
sleep_ms(10);
return true;
}
bool bmp_check_chip_id(bmp_t* bmp) { bool bmp_check_chip_id(bmp_t* bmp) {
uint8_t chip_id_reg = BMP_CHIP_ID_REG; uint8_t chip_id_reg = BMP_SENSOR_ID_REG;
uint8_t chip_id_val[1]; uint8_t chip_id_val[1];
i2c_write_blocking(bmp->i2c.inst, bmp->i2c.addr, &chip_id_reg, 1, true); 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); i2c_read_blocking(bmp->i2c.inst, bmp->i2c.addr, chip_id_val, 1, false);
if (chip_id_val[0] != BMP_CHIP_ID_VAL) { if (chip_id_val[0] != BMP_SENSOR_ID_VAL) {
#ifdef DEBUG #ifdef DEBUG
printf("Returned Chip ID: 0x%02x\n", chip_id_val[0]); printf("Returned Chip ID: 0x%02x\n", chip_id_val[0]);
printf("Check your I2C configuration and connection.\n"); printf("Check your I2C configuration and connection.\n");
@ -70,171 +108,192 @@ bool bmp_check_chip_id(bmp_t* bmp) {
return false; return false;
} }
#ifdef DEBUG
printf("INFO: Successfully checked the Chip ID.\n");
#endif
return true; return true;
} }
bool bmp_set_oversampling_rate(bmp_t* bmp) {
uint8_t data_buffer[] = {
BMP_OSR_REG,
(bmp->oss << 3) | (bmp->oss << 0)
};
i2c_write_blocking(bmp->i2c.inst, bmp->i2c.addr, data_buffer, 2, false);
#ifdef DEBUG
printf("INFO: Successfully configured oversampling rate.\n");
#endif
return true;
}
bool bmp_get_calib_coeffs(bmp_t* bmp) { bool bmp_get_calib_coeffs(bmp_t* bmp) {
uint8_t calib_coeffs_reg = BMP_CALIB_COEFF_REG; uint8_t calib_coeffs_reg = BMP_CAL_REG;
uint8_t calib_coeffs_val[BMP_CALIB_COEFF_LEN]; uint8_t calib_coeffs_val[BMP_CAL_LEN];
i2c_write_blocking(bmp->i2c.inst, bmp->i2c.addr, &calib_coeffs_reg, 1, true); 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); i2c_read_blocking(bmp->i2c.inst, bmp->i2c.addr, calib_coeffs_val, BMP_CAL_LEN, false);
int16_t* data = (int16_t*)&bmp->calib; bmp->calib.T1 = (calib_coeffs_val[1] << 8) | calib_coeffs_val[0];
for (int i = 0, j = 1; i < BMP_CALIB_COEFF_LEN / 2; i++, j += 2) { bmp->calib.T2 = (calib_coeffs_val[3] << 8) | calib_coeffs_val[2];
data[i] = (calib_coeffs_val[i * 2] << 8) | calib_coeffs_val[j]; bmp->calib.T3 = calib_coeffs_val[4];
bmp->calib.P1 = (calib_coeffs_val[6] << 8) | calib_coeffs_val[5];
bmp->calib.P2 = (calib_coeffs_val[8] << 8) | calib_coeffs_val[7];
bmp->calib.P3 = calib_coeffs_val[9];
bmp->calib.P4 = calib_coeffs_val[10];
bmp->calib.P5 = (calib_coeffs_val[12] << 8) | calib_coeffs_val[11];
bmp->calib.P6 = (calib_coeffs_val[14] << 8) | calib_coeffs_val[13];
bmp->calib.P7 = calib_coeffs_val[15];
bmp->calib.P8 = calib_coeffs_val[16];
bmp->calib.P9 = (calib_coeffs_val[18] << 8) | calib_coeffs_val[17];
bmp->calib.P10 = calib_coeffs_val[19];
bmp->calib.P11 = calib_coeffs_val[20];
if ((data[i] == 0) | (data[i] == -1)) { bmp->calib_part.T1 = (double)bmp->calib.T1 / pow(2, -8.0);
#ifdef DEBUG bmp->calib_part.T2 = (double)bmp->calib.T2 / pow(2, 30.0);
printf("Calibation data invalid.\n"); bmp->calib_part.T3 = (double)bmp->calib.T3 / pow(2, 48.0);
#endif
return false; bmp->calib_part.P1 = ((double)bmp->calib.P1 - pow(2, 14.0)) / pow(2, 20.0);
} bmp->calib_part.P2 = ((double)bmp->calib.P2 - pow(2, 14.0)) / pow(2, 29.0);
} bmp->calib_part.P3 = (double)bmp->calib.P3 / pow(2, 32.0);
bmp->calib_part.P4 = (double)bmp->calib.P4 / pow(2, 37.0);
bmp->calib_part.P5 = (double)bmp->calib.P5 / pow(2, -3.0);
bmp->calib_part.P6 = (double)bmp->calib.P6 / pow(2, 6.0);
bmp->calib_part.P7 = (double)bmp->calib.P7 / pow(2, 8.0);
bmp->calib_part.P8 = (double)bmp->calib.P8 / pow(2, 15.0);
bmp->calib_part.P9 = (double)bmp->calib.P9 / pow(2, 48.0);
bmp->calib_part.P10 = (double)bmp->calib.P10 / pow(2, 48.0);
bmp->calib_part.P11 = (double)bmp->calib.P11 / pow(2.0, 65.0);
#ifdef DEBUG #ifdef DEBUG
printf("==== CALIBRATION COEFFS ====\n"); printf("==== CALIBRATION COEFFS ====\n");
printf("AC1: %d\n", bmp->calib.AC1); printf("T1: %lf\n", bmp->calib_part.T1);
printf("AC2: %d\n", bmp->calib.AC2); printf("T2: %lf\n", bmp->calib_part.T2);
printf("AC3: %d\n", bmp->calib.AC3); printf("T3: %lf\n", bmp->calib_part.T3);
printf("AC4: %d\n", bmp->calib.AC4); printf("P1: %lf\n", bmp->calib_part.P1);
printf("AC5: %d\n", bmp->calib.AC5); printf("P2: %lf\n", bmp->calib_part.P2);
printf("AC6: %d\n", bmp->calib.AC6); printf("P3: %lf\n", bmp->calib_part.P3);
printf("B1: %d\n", bmp->calib.B1); printf("P4: %lf\n", bmp->calib_part.P4);
printf("B2: %d\n", bmp->calib.B2); printf("P5: %lf\n", bmp->calib_part.P5);
printf("MB: %d\n", bmp->calib.MB); printf("P6: %lf\n", bmp->calib_part.P6);
printf("MC: %d\n", bmp->calib.MC); printf("P7: %lf\n", bmp->calib_part.P7);
printf("MD: %d\n", bmp->calib.MD); printf("P8: %lf\n", bmp->calib_part.P8);
printf("P9: %lf\n", bmp->calib_part.P9);
printf("P10: %lf\n", bmp->calib_part.P10);
printf("P11: %lf\n", bmp->calib_part.P11);
printf("============================\n"); printf("============================\n");
#endif #endif
return true; return true;
} }
uint32_t bmp_start_temperature(bmp_t* bmp) { bool bmp_read_uncalibrated_temperature(bmp_t* bmp) {
uint8_t temp_reg[] = { uint8_t temp_reg = BMP_TEMPERATURE_REG;
BMP_REG_CONTROL, uint8_t temp_val[3];
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); 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) { i2c_read_blocking(bmp->i2c.inst, bmp->i2c.addr, temp_val, 3, false);
#ifdef DEBUG
printf("Wrong read length.\n");
#endif
return false;
}
int16_t UT = (temp_val[0] << 8) + temp_val[1]; bmp->temperature = (temp_val[2] << 16) | (temp_val[1] << 8) | temp_val[0];
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; return true;
} }
uint32_t bmp_start_pressure(bmp_t* bmp) { bool bmp_read_uncalibrated_pressure(bmp_t* bmp) {
uint8_t pres_reg[] = { uint8_t pres_reg = BMP_PRESSURE_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]; uint8_t pres_val[3];
i2c_write_blocking(bmp->i2c.inst, bmp->i2c.addr, &pres_reg, 1, true); 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) { i2c_read_blocking(bmp->i2c.inst, bmp->i2c.addr, pres_val, 3, false);
#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); bmp->pressure = (pres_val[2] << 16) | (pres_val[1] << 8) | pres_val[0];
int32_t X1, X2, X3, B3, B6, p = 0; return true;
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) { bool bmp_calibrate_temperature(bmp_t* bmp) {
p = (B7 * 2) / B4; double par1 = bmp->temperature - bmp->calib_part.T1;
} else { double par2 = par1 * bmp->calib_part.T2;
p = (B7 / B4) * 2; bmp->temperature = par2 + (par1 * par1) * bmp->calib_part.T3;
}
X1 = (p >> 8) * (p >> 8);
X1 = (X1 * 3038) >> 16;
X2 = (-7357 * p) >> 16;
bmp->pressure = p + ((X1 + X2 + 3791) >> 4);
return true; return true;
} }
bool bmp_get_temperature(bmp_t* bmp) { bool bmp_calibrate_pressure(bmp_t* bmp) {
sleep_ms(bmp_start_temperature(bmp)); double out1, out2, out3;
return bmp_read_temperature(bmp);
{
double par1 = bmp->calib_part.P6 * bmp->temperature;
double par2 = bmp->calib_part.P7 * pow(bmp->temperature, 2.0);
double par3 = bmp->calib_part.P8 * pow(bmp->temperature, 3.0);
out1 = bmp->calib_part.P5 + par1 + par2 + par3;
}
{
double par1 = bmp->calib_part.P2 * bmp->temperature;
double par2 = bmp->calib_part.P3 * pow(bmp->temperature, 2.0);
double par3 = bmp->calib_part.P4 * pow(bmp->temperature, 3.0);
out2 = bmp->pressure * (bmp->calib_part.P1 + par1 + par2 + par3);
}
{
double par1 = pow(bmp->pressure, 2.0);
double par2 = bmp->calib_part.P9 + bmp->calib_part.P10 * bmp->temperature;
double par3 = par1 * par2;
out3 = par3 + bmp->calib_part.P11 * pow(bmp->pressure, 3);
}
bmp->pressure = (out1 + out2 + out3) / 100.0;
return true;
} }
// User must call bmp_get_temperature() before calling this method. bool bmp_calculate_altitude(bmp_t* bmp) {
// Or use the combo bmp_get_pressure_temperature(). bmp->altitude = ((pow((1013.25 / bmp->pressure), (1/5.257)) - 1) * (bmp->temperature + 273.15)) / 0.0065;
bool bmp_get_pressure(bmp_t* bmp) {
sleep_ms(bmp_start_pressure(bmp)); return true;
return bmp_read_pressure(bmp);
} }
bool bmp_get_pressure_temperature(bmp_t* bmp) { bool bmp_get_pressure_temperature(bmp_t* bmp) {
bool res = true; bool res = true;
res &= bmp_get_temperature(bmp);
res &= bmp_get_pressure(bmp); {
uint8_t data_buffer[] = {
BMP_MODE_REG,
BMP_MODE_VAL
};
i2c_write_blocking(bmp->i2c.inst, bmp->i2c.addr, data_buffer, 2, false);
}
{
uint8_t status = 0x00;
while ((status & 0x60) != 0x60) {
uint8_t status_reg = BMP_STATUS_REG;
i2c_write_blocking(bmp->i2c.inst, bmp->i2c.addr, &status_reg, 1, true);
i2c_read_blocking(bmp->i2c.inst, bmp->i2c.addr, &status, 1, false);
sleep_ms(1);
}
}
res &= bmp_read_uncalibrated_temperature(bmp);
res &= bmp_read_uncalibrated_pressure(bmp);
res &= bmp_calibrate_temperature(bmp);
res &= bmp_calibrate_pressure(bmp);
res &= bmp_calculate_altitude(bmp);
return res; return res;
} }
bool bmp_init(bmp_t* bmp) { bool bmp_init(bmp_t* bmp) {
i2c_init(bmp->i2c.inst, bmp->i2c.rate); i2c_init(bmp->i2c.inst, bmp->i2c.rate);
if (bmp->oss < 0 || bmp->oss > 3) { if (bmp->oss < 0 || bmp->oss > 5) {
#ifdef DEBUG #ifdef DEBUG
printf("Invalid over-sampling rate (%d). Valid 0 to 3.\n", bmp->oss); printf("Invalid over-sampling rate (%d). Valid 0 to 5.\n", bmp->oss);
#endif #endif
return false; return false;
} }
@ -246,10 +305,20 @@ bool bmp_init(bmp_t* bmp) {
sleep_ms(100); sleep_ms(100);
ASSERT_OK(bmp_reset(bmp));
ASSERT_OK(bmp_check_chip_id(bmp)); ASSERT_OK(bmp_check_chip_id(bmp));
ASSERT_OK(bmp_set_oversampling_rate(bmp));
ASSERT_OK(bmp_get_calib_coeffs(bmp)); ASSERT_OK(bmp_get_calib_coeffs(bmp));
return true; return true;
} }
bool bmp_get_temperature(bmp_t* bmp) {
return false;
}
bool bmp_get_pressure(bmp_t* bmp) {
return false;
}
#endif #endif