bmp280: read out via DMA in the background
This removes any busy waiting on the I²C from the main loop. Initialization is still done in a blocking way.
This commit is contained in:
parent
055186180f
commit
07dd91ecc2
178
src/bmp280.c
178
src/bmp280.c
|
@ -1,6 +1,8 @@
|
|||
#include <libopencm3/stm32/i2c.h>
|
||||
#include <libopencm3/stm32/gpio.h>
|
||||
|
||||
#include "i2c_dma.h"
|
||||
|
||||
#include "pinout.h"
|
||||
|
||||
#include "bmp280_comp.h"
|
||||
|
@ -8,6 +10,17 @@
|
|||
|
||||
#define BMP280_7BIT_ADDR 0x76
|
||||
|
||||
typedef enum {
|
||||
READY,
|
||||
SEND_REQUEST,
|
||||
WAIT_MEASUREMENT_COMPLETE,
|
||||
READ_RESULTS,
|
||||
COMPENSATE_TEMPERATURE,
|
||||
COMPENSATE_PRESSURE
|
||||
} 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;
|
||||
|
@ -27,6 +40,13 @@ extern int16_t dig_P9;
|
|||
static fxp_t m_last_temperature = 0;
|
||||
static fxp_t m_last_pressure = 0;
|
||||
|
||||
static int32_t m_temp_raw;
|
||||
static int32_t m_press_raw;
|
||||
|
||||
|
||||
static void cb_i2c_dma(const uint8_t *rdata, size_t rlen);
|
||||
|
||||
|
||||
bool bmp280_init(void)
|
||||
{
|
||||
// configure pins for I2C1
|
||||
|
@ -95,6 +115,8 @@ bool bmp280_init(void)
|
|||
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:
|
||||
|
@ -102,63 +124,123 @@ err_out:
|
|||
}
|
||||
|
||||
|
||||
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(const uint8_t *rdata, size_t rlen)
|
||||
{
|
||||
(void)rlen;
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
retval = true;
|
||||
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_transfer7(I2C1, BMP280_7BIT_ADDR, wdata, wsize, NULL, 0);
|
||||
i2c_dma_start_transfer7(I2C1, BMP280_7BIT_ADDR, wdata, wsize, 0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool bmp280_is_measurement_complete(void)
|
||||
{
|
||||
uint8_t wdata[1];
|
||||
uint8_t rdata[1];
|
||||
uint8_t wsize = 0, rsize;
|
||||
|
||||
wdata[wsize++] = 0xF3; // status register
|
||||
rsize = 1;
|
||||
|
||||
i2c_transfer7(I2C1, BMP280_7BIT_ADDR, wdata, wsize, rdata, rsize);
|
||||
|
||||
if((rdata[0] & 0x09) != 0) {
|
||||
// either "measuring" or "im_update" is active, so measurement is not complete yet.
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool bmp280_readout_and_compensate(void)
|
||||
{
|
||||
uint8_t wdata[1];
|
||||
uint8_t rdata[8];
|
||||
uint8_t wsize = 0, rsize;
|
||||
|
||||
wdata[wsize++] = 0xF7;
|
||||
rsize = 8;
|
||||
|
||||
i2c_transfer7(I2C1, BMP280_7BIT_ADDR, wdata, wsize, rdata, rsize);
|
||||
|
||||
int32_t 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)
|
||||
|
||||
int32_t 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_last_temperature = bmp280_comp_temperature(temp_raw);
|
||||
m_last_pressure = bmp280_comp_pressure(press_raw);
|
||||
m_readout_state = SEND_REQUEST;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -7,10 +7,9 @@
|
|||
#include <fxp.h>
|
||||
|
||||
bool bmp280_init(void);
|
||||
bool bmp280_loop(void); // returns true if measurements have been updated
|
||||
|
||||
bool bmp280_start_measurement(void);
|
||||
bool bmp280_is_measurement_complete(void);
|
||||
bool bmp280_readout_and_compensate(void);
|
||||
|
||||
fxp_t bmp280_get_temperature(void);
|
||||
fxp_t bmp280_get_pressure(void);
|
||||
|
|
148
src/i2c_dma.c
Normal file
148
src/i2c_dma.c
Normal file
|
@ -0,0 +1,148 @@
|
|||
#include <string.h>
|
||||
|
||||
#include <libopencm3/stm32/i2c.h>
|
||||
#include <libopencm3/stm32/dma.h>
|
||||
|
||||
#include "i2c_dma.h"
|
||||
|
||||
#include "rs485.h"
|
||||
|
||||
#define DMA_CHANNEL_TX DMA_CHANNEL2
|
||||
#define DMA_CHANNEL_RX DMA_CHANNEL3
|
||||
|
||||
static uint8_t m_wdata[2];
|
||||
static uint8_t m_wlen;
|
||||
static uint8_t m_rdata[32];
|
||||
static uint8_t m_rlen;
|
||||
|
||||
static uint8_t m_addr;
|
||||
static uint32_t m_i2c;
|
||||
|
||||
typedef enum {
|
||||
IDLE,
|
||||
SEND_DATA,
|
||||
RECEIVE_DATA
|
||||
} i2c_dma_state_t;
|
||||
|
||||
static i2c_dma_state_t m_state;
|
||||
|
||||
|
||||
static i2c_dma_callback_t m_callback;
|
||||
|
||||
static void setup_txdma(void)
|
||||
{
|
||||
dma_channel_reset(DMA1, DMA_CHANNEL_TX);
|
||||
|
||||
dma_set_priority(DMA1, DMA_CHANNEL_TX, DMA_CCR_PL_LOW);
|
||||
dma_set_memory_size(DMA1, DMA_CHANNEL_TX, DMA_CCR_MSIZE_8BIT);
|
||||
dma_set_peripheral_size(DMA1, DMA_CHANNEL_TX, DMA_CCR_PSIZE_8BIT);
|
||||
dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL_TX);
|
||||
dma_set_read_from_memory(DMA1, DMA_CHANNEL_TX);
|
||||
dma_set_peripheral_address(DMA1, DMA_CHANNEL_TX, (uint32_t) &I2C_TXDR(m_i2c));
|
||||
|
||||
// send m_wlen bytes from m_wdata
|
||||
dma_set_memory_address(DMA1, DMA_CHANNEL_TX, (uint32_t)m_wdata);
|
||||
dma_set_number_of_data(DMA1, DMA_CHANNEL_TX, m_wlen);
|
||||
|
||||
dma_enable_channel(DMA1, DMA_CHANNEL_TX);
|
||||
|
||||
i2c_enable_txdma(m_i2c);
|
||||
}
|
||||
|
||||
|
||||
static void setup_rxdma(void)
|
||||
{
|
||||
dma_channel_reset(DMA1, DMA_CHANNEL_RX);
|
||||
|
||||
dma_set_priority(DMA1, DMA_CHANNEL_RX, DMA_CCR_PL_LOW);
|
||||
dma_set_memory_size(DMA1, DMA_CHANNEL_RX, DMA_CCR_MSIZE_8BIT);
|
||||
dma_set_peripheral_size(DMA1, DMA_CHANNEL_RX, DMA_CCR_PSIZE_8BIT);
|
||||
dma_enable_memory_increment_mode(DMA1, DMA_CHANNEL_RX);
|
||||
dma_set_read_from_peripheral(DMA1, DMA_CHANNEL_RX);
|
||||
dma_set_peripheral_address(DMA1, DMA_CHANNEL_RX, (uint32_t) &I2C_RXDR(m_i2c));
|
||||
|
||||
// receive m_rlen bytes and store them in m_rdata
|
||||
dma_set_memory_address(DMA1, DMA_CHANNEL_RX, (uint32_t)m_rdata);
|
||||
dma_set_number_of_data(DMA1, DMA_CHANNEL_RX, m_rlen);
|
||||
|
||||
dma_enable_channel(DMA1, DMA_CHANNEL_RX);
|
||||
|
||||
i2c_enable_rxdma(m_i2c);
|
||||
}
|
||||
|
||||
|
||||
void i2c_dma_init(i2c_dma_callback_t callback)
|
||||
{
|
||||
m_callback = callback;
|
||||
|
||||
m_state = IDLE;
|
||||
}
|
||||
|
||||
|
||||
void i2c_dma_loop(void)
|
||||
{
|
||||
switch(m_state) {
|
||||
case IDLE:
|
||||
// nothing to do
|
||||
return;
|
||||
|
||||
case SEND_DATA:
|
||||
//if(i2c_transfer_complete(m_i2c)) { // does not work for some reason :(
|
||||
if(dma_get_interrupt_flag(DMA1, DMA_CHANNEL_TX, DMA_TCIF)) {
|
||||
dma_clear_interrupt_flags(DMA1, DMA_CHANNEL_TX, DMA_TCIF);
|
||||
|
||||
if(m_rlen) {
|
||||
m_state = RECEIVE_DATA;
|
||||
|
||||
i2c_set_7bit_address(m_i2c, m_addr);
|
||||
i2c_set_read_transfer_dir(m_i2c);
|
||||
i2c_set_bytes_to_transfer(m_i2c, m_rlen);
|
||||
i2c_send_start(m_i2c);
|
||||
i2c_enable_autoend(m_i2c); // important to do it after start() for a proper repeated start!
|
||||
setup_rxdma();
|
||||
} else {
|
||||
// no RX requested => we are done here
|
||||
m_state = IDLE;
|
||||
m_callback(NULL, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RECEIVE_DATA:
|
||||
//if(i2c_transfer_complete(m_i2c)) { // does not work for some reason :(
|
||||
if(dma_get_interrupt_flag(DMA1, DMA_CHANNEL_RX, DMA_TCIF)) {
|
||||
dma_clear_interrupt_flags(DMA1, DMA_CHANNEL_RX, DMA_TCIF);
|
||||
|
||||
m_state = IDLE;
|
||||
m_callback(m_rdata, m_rlen);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void i2c_dma_start_transfer7(uint32_t i2c, uint8_t addr, const uint8_t *w, size_t wn, size_t rn)
|
||||
{
|
||||
m_wlen = wn;
|
||||
m_rlen = rn;
|
||||
|
||||
memcpy(m_wdata, w, wn);
|
||||
|
||||
m_state = SEND_DATA;
|
||||
|
||||
m_i2c = i2c;
|
||||
m_addr = addr;
|
||||
|
||||
i2c_set_7bit_address(i2c, addr);
|
||||
i2c_set_write_transfer_dir(i2c);
|
||||
i2c_set_bytes_to_transfer(i2c, wn);
|
||||
|
||||
if(rn) {
|
||||
i2c_disable_autoend(i2c); // prepare repeated start
|
||||
} else {
|
||||
i2c_enable_autoend(i2c); // stop condition after write transfer
|
||||
}
|
||||
|
||||
i2c_send_start(i2c); // start the transfer. The rest is handled by the DMA.
|
||||
setup_txdma();
|
||||
}
|
21
src/i2c_dma.h
Normal file
21
src/i2c_dma.h
Normal file
|
@ -0,0 +1,21 @@
|
|||
#ifndef I2C_DMA_H
|
||||
#define I2C_DMA_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
I2C_DMA_EVT_TRANSFER_COMPLETE,
|
||||
I2C_DMA_EVT_ADDR_NAK,
|
||||
} i2c_dma_evt_t;
|
||||
|
||||
typedef void (*i2c_dma_callback_t)(const uint8_t *rdata, size_t rlen);
|
||||
|
||||
void i2c_dma_init(i2c_dma_callback_t callback);
|
||||
|
||||
void i2c_dma_loop(void);
|
||||
|
||||
void i2c_dma_start_transfer7(uint32_t i2c, uint8_t addr, const uint8_t *w, size_t wn, size_t rn);
|
||||
|
||||
#endif // I2C_DMA_H
|
10
src/main.c
10
src/main.c
|
@ -27,7 +27,6 @@ static enum {
|
|||
BMP280_NOT_PRESENT,
|
||||
BMP280_IDLE,
|
||||
BMP280_MEASURING,
|
||||
BMP280_READOUT
|
||||
} bmp280_state;
|
||||
|
||||
/* Set up systick to fire freq times per second */
|
||||
|
@ -280,14 +279,9 @@ static void handle_bmp280(uint64_t timebase_ms)
|
|||
break;
|
||||
|
||||
case BMP280_MEASURING:
|
||||
if(timebase_ms % 10 == 0 && bmp280_is_measurement_complete()) {
|
||||
bmp280_state = BMP280_READOUT;
|
||||
}
|
||||
break;
|
||||
|
||||
case BMP280_READOUT:
|
||||
bmp280_readout_and_compensate();
|
||||
if(timebase_ms % 10 == 0 && bmp280_loop()) {
|
||||
bmp280_state = BMP280_IDLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue