#include #include #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; }