LNSC-2420-Firmware/src/bmp280.c

285 lines
6.4 KiB
C

#include <libopencm3/stm32/i2c.h>
#include <libopencm3/stm32/gpio.h>
#include "i2c_dma.h"
#include "pinout.h"
#include "bmp280_comp.h"
#include "bmp280.h"
#define BMP280_7BIT_ADDR 0x76
typedef enum {
READY,
SEND_REQUEST,
WAIT_MEASUREMENT_COMPLETE,
READ_RESULTS,
COMPENSATE_TEMPERATURE,
COMPENSATE_PRESSURE,
ERROR // set by the I²C callback in case of errors/timeout
} bmp280_readout_state_t;
static bmp280_readout_state_t m_readout_state;
// calibration value cache, shared with bmp280_comp.c
extern uint16_t dig_T1;
extern int16_t dig_T2;
extern int16_t dig_T3;
extern uint16_t dig_P1;
extern int16_t dig_P2;
extern int16_t dig_P3;
extern int16_t dig_P4;
extern int16_t dig_P5;
extern int16_t dig_P6;
extern int16_t dig_P7;
extern int16_t dig_P8;
extern int16_t dig_P9;
static fxp_t m_last_temperature = 0;
static fxp_t m_last_pressure = 0;
static bool m_measurements_valid = false;
static int32_t m_temp_raw;
static int32_t m_press_raw;
static void cb_i2c_dma(i2c_dma_evt_t evt, const uint8_t *rdata, size_t rlen);
bool bmp280_init(void)
{
m_measurements_valid = false;
// configure pins for I2C1
gpio_set_af(BMP280_I2C_PORT, GPIO_AF4, BMP280_I2C_SCL | BMP280_I2C_SDA);
gpio_mode_setup(BMP280_I2C_PORT, GPIO_MODE_AF, GPIO_PUPD_PULLUP, BMP280_I2C_SCL | BMP280_I2C_SDA);
// Set up I²C
i2c_reset(I2C1);
i2c_set_speed(I2C1, i2c_speed_sm_100k, 48);
i2c_peripheral_enable(I2C1);
// Set up the Chip
uint8_t wdata[2];
uint8_t rdata[32];
uint8_t wsize, rsize;
// check for chip presence by accessing the chip id register (not a proper read!)
i2c_set_7bit_address(I2C1, BMP280_7BIT_ADDR);
i2c_set_write_transfer_dir(I2C1);
i2c_set_bytes_to_transfer(I2C1, 1);
i2c_enable_autoend(I2C1);
i2c_send_start(I2C1);
uint32_t timeout = 1000000;
while(--timeout && !i2c_transmit_int_status(I2C1)); // wait for the address transfer to complete
i2c_send_data(I2C1, 0xD0);
if((timeout == 0) || i2c_nack(I2C1)) {
// something went wrong during communication
goto err_out;
}
// read the chip ID
wsize = 0;
wdata[wsize++] = 0xD0;
rsize = 1;
i2c_transfer7(I2C1, BMP280_7BIT_ADDR, wdata, wsize, rdata, rsize);
if(rdata[0] != 0x58) {
// unexpected chip id
goto err_out;
}
// read calibration data
wsize = 0;
wdata[wsize++] = 0x88; // first calibration data register of first block
rsize = 0xA1 + 1 - 0x88;
i2c_transfer7(I2C1, BMP280_7BIT_ADDR, wdata, wsize, rdata, rsize);
dig_T1 = (((uint16_t)rdata[ 1]) << 8) | rdata[ 0]; // 0x88, 0x89
dig_T2 = (((int16_t)(int8_t)rdata[ 3]) << 8) | rdata[ 2]; // 0x8A, 0x8B
dig_T3 = (((int16_t)(int8_t)rdata[ 5]) << 8) | rdata[ 4]; // 0x8C, 0x8D
dig_P1 = (((uint16_t)rdata[ 7]) << 8) | rdata[ 6]; // 0x8E, 0x8F
dig_P2 = (((int16_t)(int8_t)rdata[ 9]) << 8) | rdata[ 8]; // 0x90, 0x91
dig_P3 = (((int16_t)(int8_t)rdata[11]) << 8) | rdata[10]; // 0x92, 0x93
dig_P4 = (((int16_t)(int8_t)rdata[13]) << 8) | rdata[12]; // 0x94, 0x95
dig_P5 = (((int16_t)(int8_t)rdata[15]) << 8) | rdata[14]; // 0x96, 0x97
dig_P6 = (((int16_t)(int8_t)rdata[17]) << 8) | rdata[16]; // 0x98, 0x99
dig_P7 = (((int16_t)(int8_t)rdata[19]) << 8) | rdata[18]; // 0x9A, 0x9B
dig_P8 = (((int16_t)(int8_t)rdata[21]) << 8) | rdata[20]; // 0x9C, 0x9D
dig_P9 = (((int16_t)(int8_t)rdata[23]) << 8) | rdata[22]; // 0x9E, 0x9F
i2c_dma_init(cb_i2c_dma);
return true;
err_out:
return false;
}
static void trigger_read_status_register(void)
{
uint8_t wdata[1];
uint8_t wsize = 0, rsize;
wdata[wsize++] = 0xF3; // status register
rsize = 1;
i2c_dma_start_transfer7(I2C1, BMP280_7BIT_ADDR, wdata, wsize, rsize);
}
static void trigger_read_results(void)
{
uint8_t wdata[1];
uint8_t wsize = 0, rsize;
wdata[wsize++] = 0xF7;
rsize = 8;
i2c_dma_start_transfer7(I2C1, BMP280_7BIT_ADDR, wdata, wsize, rsize);
}
static void cb_i2c_dma(i2c_dma_evt_t evt, const uint8_t *rdata, size_t rlen)
{
(void)rlen;
switch(evt) {
case I2C_DMA_EVT_TRANSFER_COMPLETE:
switch(m_readout_state) {
case SEND_REQUEST:
m_readout_state = WAIT_MEASUREMENT_COMPLETE;
trigger_read_status_register();
break;
case WAIT_MEASUREMENT_COMPLETE:
if((rdata[0] & 0x09) != 0) {
// either "measuring" or "im_update" is active, so measurement is not complete yet.
// try again:
trigger_read_status_register();
} else {
m_readout_state = READ_RESULTS;
trigger_read_results();
}
break;
case READ_RESULTS:
m_press_raw =
((int32_t)rdata[0] << 12) // press_msb (0xF7)
| ((int32_t)rdata[1] << 4) // press_lsb (0xF8)
| ((int32_t)rdata[2] >> 4); // press_xlsb (0xF9)
m_temp_raw =
((int32_t)rdata[3] << 12) // temp_msb (0xFA)
| ((int32_t)rdata[4] << 4) // temp_lsb (0xFB)
| ((int32_t)rdata[5] >> 4); // temp_xlsb (0xFC)
m_readout_state = COMPENSATE_TEMPERATURE;
break;
default:
// should never happen
while(1);
break;
}
break;
case I2C_DMA_EVT_TIMEOUT:
case I2C_DMA_EVT_NAK:
m_readout_state = ERROR;
break;
}
}
bool bmp280_loop(void)
{
bool retval = false;
switch(m_readout_state) {
case READY:
// nothing to do in this state
break;
case SEND_REQUEST:
case WAIT_MEASUREMENT_COMPLETE:
case READ_RESULTS:
// handled in cb_i2c_dma exclusively
break;
case COMPENSATE_TEMPERATURE:
m_last_temperature = bmp280_comp_temperature(m_temp_raw);
m_readout_state = COMPENSATE_PRESSURE;
break;
case COMPENSATE_PRESSURE:
m_last_pressure = bmp280_comp_pressure(m_press_raw);
m_readout_state = READY;
m_measurements_valid = true;
retval = true; // measurement complete
break;
case ERROR:
m_readout_state = READY;
m_measurements_valid = false;
retval = true; // measurement cycle terminated
break;
}
if(m_readout_state != READY) {
i2c_dma_loop();
}
return retval;
}
bool bmp280_start_measurement(void)
{
uint8_t wdata[2];
uint8_t wsize = 0;
if(m_readout_state != READY) {
return false; // still busy
}
wdata[wsize++] = 0xF4; // measurement control register
wdata[wsize++] = (0x01 << 5) | (0x01 << 2) | 0x01; // pressure x1, temp x1, forced mode
i2c_dma_start_transfer7(I2C1, BMP280_7BIT_ADDR, wdata, wsize, 0);
m_readout_state = SEND_REQUEST;
return true;
}
bool bmp280_are_measurements_valid(void)
{
return m_measurements_valid;
}
fxp_t bmp280_get_temperature(void)
{
return m_last_temperature;
}
fxp_t bmp280_get_pressure(void)
{
return m_last_pressure;
}