Shared I2C1 bus (i2c1.c/h, PB8=SCL PB9=SDA 100kHz): - i2c1_init() called once in main() before sensor probes. - hi2c1 exported globally; baro and mag drivers use it directly. Barometer (bmp280.c): - Probes I2C1 at 0x76 then 0x77 (covers both SDO options). - bmp280_init() returns chip_id (0x58/0x60) on success, neg if absent. - Added bmp280_pressure_to_alt_cm() — ISA barometric formula. - Added bmp280.h (was missing). Magnetometer (mag.c / mag.h): - Auto-detects QMC5883L (0x0D, id=0xFF), HMC5883L (0x1E, id='H43'), IST8310 (0x0E, id=0x10) in that order. - mag_read_heading() returns degrees×10 (0–3599) or -1 if not ready. - HMC5883L: correct XZY byte order applied. - IST8310: single-measurement trigger mode. main.c: - i2c1_init() + bmp280_init() + mag_init() after all other inits. - Both skip gracefully (baro_ok=0, mag_type=MAG_NONE) if not present. - Telemetry JSON: incremental builder appends ",\"hd\":<n>" when mag found and ",\"alt\":<n>" when baro found. No extra bytes when absent. UI (index.html): - HEADING and ALT rows hidden until first packet with that field. - Heading shown in degrees, alt in metres (firmware sends cm). Closes #24. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
104 lines
3.6 KiB
C
104 lines
3.6 KiB
C
/*
|
||
* bmp280.c — BMP280/BME280 barometer driver (I2C1, shared bus)
|
||
*
|
||
* Probes 0x76 first, then 0x77. Requires i2c1_init() before bmp280_init().
|
||
* Returns chip_id on success (0x58=BMP280, 0x60=BME280), negative if absent.
|
||
*/
|
||
#include "bmp280.h"
|
||
#include "i2c1.h"
|
||
#include <math.h>
|
||
|
||
/* Shift I2C address for HAL (7-bit left-shifted) */
|
||
static uint16_t s_addr;
|
||
|
||
/* Calibration data */
|
||
static uint16_t dig_T1;
|
||
static int16_t dig_T2, dig_T3;
|
||
static uint16_t dig_P1;
|
||
static int16_t dig_P2, dig_P3, dig_P4, dig_P5, dig_P6, dig_P7, dig_P8, dig_P9;
|
||
static int32_t t_fine;
|
||
|
||
static uint8_t i2c_read(uint8_t reg) {
|
||
uint8_t val = 0;
|
||
HAL_I2C_Mem_Read(&hi2c1, s_addr, reg, 1, &val, 1, 100);
|
||
return val;
|
||
}
|
||
|
||
static void i2c_read_burst(uint8_t reg, uint8_t *buf, uint8_t len) {
|
||
HAL_I2C_Mem_Read(&hi2c1, s_addr, reg, 1, buf, len, 100);
|
||
}
|
||
|
||
static void i2c_write(uint8_t reg, uint8_t val) {
|
||
HAL_I2C_Mem_Write(&hi2c1, s_addr, reg, 1, &val, 1, 100);
|
||
}
|
||
|
||
static int try_init(uint16_t addr) {
|
||
s_addr = addr;
|
||
uint8_t id = i2c_read(0xD0);
|
||
if (id != 0x58 && id != 0x60) return -(int)id;
|
||
|
||
/* Read calibration */
|
||
uint8_t cal[26];
|
||
i2c_read_burst(0x88, cal, 26);
|
||
dig_T1 = (uint16_t)(cal[1] << 8 | cal[0]);
|
||
dig_T2 = (int16_t) (cal[3] << 8 | cal[2]);
|
||
dig_T3 = (int16_t) (cal[5] << 8 | cal[4]);
|
||
dig_P1 = (uint16_t)(cal[7] << 8 | cal[6]);
|
||
dig_P2 = (int16_t) (cal[9] << 8 | cal[8]);
|
||
dig_P3 = (int16_t) (cal[11] << 8 | cal[10]);
|
||
dig_P4 = (int16_t) (cal[13] << 8 | cal[12]);
|
||
dig_P5 = (int16_t) (cal[15] << 8 | cal[14]);
|
||
dig_P6 = (int16_t) (cal[17] << 8 | cal[16]);
|
||
dig_P7 = (int16_t) (cal[19] << 8 | cal[18]);
|
||
dig_P8 = (int16_t) (cal[21] << 8 | cal[20]);
|
||
dig_P9 = (int16_t) (cal[23] << 8 | cal[22]);
|
||
|
||
/* Normal mode, ×16 oversampling temp+press, 0.5 ms standby */
|
||
i2c_write(0xF5, 0x00); /* config: standby=0.5ms, filter=off */
|
||
i2c_write(0xF4, 0xB7); /* ctrl_meas: osrs_t=×16, osrs_p=×16, normal mode */
|
||
|
||
return (int)id;
|
||
}
|
||
|
||
int bmp280_init(void) {
|
||
int ret = try_init(0x76 << 1);
|
||
if (ret > 0) return ret;
|
||
return try_init(0x77 << 1);
|
||
}
|
||
|
||
void bmp280_read(int32_t *pressure_pa, int16_t *temp_x10) {
|
||
uint8_t buf[6];
|
||
i2c_read_burst(0xF7, buf, 6);
|
||
|
||
int32_t adc_P = (int32_t)((buf[0] << 12) | (buf[1] << 4) | (buf[2] >> 4));
|
||
int32_t adc_T = (int32_t)((buf[3] << 12) | (buf[4] << 4) | (buf[5] >> 4));
|
||
|
||
/* Temperature compensation (BMP280 datasheet Section 4.2.3) */
|
||
int32_t v1 = ((((adc_T >> 3) - ((int32_t)dig_T1 << 1))) * ((int32_t)dig_T2)) >> 11;
|
||
int32_t v2 = (((((adc_T >> 4) - (int32_t)dig_T1) *
|
||
((adc_T >> 4) - (int32_t)dig_T1)) >> 12) * (int32_t)dig_T3) >> 14;
|
||
t_fine = v1 + v2;
|
||
*temp_x10 = (int16_t)((t_fine * 5 + 128) >> 8); /* 0.1 °C */
|
||
|
||
/* Pressure compensation */
|
||
int64_t p1 = ((int64_t)t_fine) - 128000;
|
||
int64_t p2 = p1 * p1 * (int64_t)dig_P6;
|
||
p2 += (p1 * (int64_t)dig_P5) << 17;
|
||
p2 += ((int64_t)dig_P4) << 35;
|
||
p1 = ((p1 * p1 * (int64_t)dig_P3) >> 8) + ((p1 * (int64_t)dig_P2) << 12);
|
||
p1 = ((((int64_t)1 << 47) + p1)) * ((int64_t)dig_P1) >> 33;
|
||
if (p1 == 0) { *pressure_pa = 0; return; }
|
||
int64_t p = 1048576 - adc_P;
|
||
p = (((p << 31) - p2) * 3125) / p1;
|
||
p1 = ((int64_t)dig_P9 * (p >> 13) * (p >> 13)) >> 25;
|
||
p2 = ((int64_t)dig_P8 * p) >> 19;
|
||
*pressure_pa = (int32_t)(((p + p1 + p2) >> 8) + ((int64_t)dig_P7 << 4)) / 256;
|
||
}
|
||
|
||
int32_t bmp280_pressure_to_alt_cm(int32_t pressure_pa) {
|
||
/* Barometric formula: h = 44330 * (1 - (p/p0)^(1/5.255)) metres */
|
||
float ratio = (float)pressure_pa / 101325.0f;
|
||
float alt_m = 44330.0f * (1.0f - powf(ratio, 0.1902949f));
|
||
return (int32_t)(alt_m * 100.0f); /* cm */
|
||
}
|